Skip to content

Latest commit

 

History

History
3633 lines (2664 loc) · 107 KB

File metadata and controls

3633 lines (2664 loc) · 107 KB

Návrh systému pro skládání obrazu proměnlivé síťové reality

1. Účel dokumentu

Tento dokument popisuje návrhový rámec pro systém, který má pomáhat průběžně skládat pracovní obraz síťové infrastruktury z více neúplných, časově proměnlivých a někdy konfliktních zdrojů dat.

Cílem není pouze vytvořit databázi zařízení ani jednoduchý reporting. Cílem je navrhnout způsob, jak:

  • sbírat pozorování z externích zdrojů,
  • udržet stabilní identitu objektů,
  • odvozovat aktuální stav,
  • zachovat auditovatelnost původu dat,
  • zobrazovat člověku použitelné pracovní pohledy,
  • průběžně upravovat model podle nových zjištění.

Pracovní formulace problému:

Jak nad proměnlivou infrastrukturou průběžně sbírat pozorování, udržet identitu objektů, odvozovat aktuální stav a zobrazovat člověku pracovní obraz reality, aniž by se surová data, interpretace a reporty slily do jednoho chaosu.

1.1 Hranice tohoto dokumentu

Tento dokument popisuje co systém eviduje a jaká pravidla musí každá implementace dodržet. Nepopisuje jak se to konkrétně programuje. Tedy:

Co je v dokumentu:
  - jaké tabulky existují a jaké mají sloupce
  - jaké jsou vztahy mezi tabulkami
  - co skripty smí a nesmí dělat z pohledu dat
  - kontrakt mezi skripty a databází

Co v dokumentu NENÍ:
  - konkrétní SNMP MIBy, ifIndex stabilita, vendor specifika
  - jak normalizovat názvy portů
  - v jakém jazyce skripty psát
  - jak agent řeší autentizaci k zařízení

Implementační detaily se rozhodují v okamžiku implementace – buď ručně, nebo s pomocí agenta. Dokument se jich nemá držet, jinak by zastaral u první vendor-specific zvláštnosti.

1.2 Pro implementátora

Pokud implementuješ skripty podle tohoto dokumentu, čti v tomto pořadí:

1. kapitola 4    architektura vrstev (co je core, obs, plan, ...)
2. kapitola 5.1  role skriptů obecně
3. kapitola 5.1.4 pravidla zakládání identit (co skript smí auto-zakládat)
4. kapitola 7    skripty první fáze - hranice, kontrakt, konvence
5. kapitola 6.7  SQL schéma databáze

Zbytek dokumentu je kontext a vysvětlení, proč jsou pravidla taková, jaká jsou. Není povinný, ale pomáhá pochopit hranice.

V dokumentu jsou dva typy pravidel:

> Datový kontrakt (závazné). Porušení = porušení modelu.
>                            Vymáhá se reviewem schématu a dat.

> Implementační konvence.    Porušení = technický dluh.
>                            Vymáhá se reviewem kódu.

Sekce, které spadají do prvního typu, jsou v textu označeny „Datový kontrakt (závazné)" v blockquote na začátku. Pokud jsi agent nebo implementátor, dodrž je doslovně.


2. Primární účely systému

Systém nemá sloužit pouze k evidenci aktuální infrastruktury. Má podporovat několik souvisejících, ale odlišných cílů.

2.1 Rekonstrukce aktuální reality

Prvním účelem je průběžně skládat pracovní obraz existující síťové infrastruktury z více zdrojů dat.

Tato část odpovídá na otázky:

Co v síti reálně existuje?
Kde se to nachází?
Jak to spolu souvisí?
Co je jisté?
Co je pouze pozorování?
Co je konflikt?
Co je zastaralé?
Co je potřeba ručně ověřit?

2.2 Návrh nové infrastruktury

Druhým účelem je vytvořit obraz cílového stavu nové infrastruktury.

Cílový stav nevzniká nezávisle „od stolu“. Vychází z pozorování a interpretace stávajícího stavu, ale zároveň umožňuje evidovat další parametry, které jsou specifické pro plánovaný cílový stav.

Jinými slovy:

observed/current reality
  → interpreted current state
  → target design
  → migration mapping

Nejde pouze o evidenci toho, co existuje dnes, ale také o návrh toho, co má existovat po upgradu nebo výměně síťových prvků.

Tato část odpovídá na otázky:

Jaká zařízení budou v nové infrastruktuře?
Jaké porty/interfacy budou mít?
Jaké VLANy, subnety a segmenty budou použity?
Jaké role budou mít nové porty?
Jak se změní topologie?
Jaké části staré infrastruktury budou nahrazeny?

2.3 Mapování staré a nové infrastruktury

Třetím účelem je promapovat starou infrastrukturu na novou pro účely migrace.

Primární migrační jednotkou je port/interface, ne celé zařízení. Reálná migrační otázka typicky zní:

Který starý port odpovídá kterému novému portu?

Mapování zařízení jako celek může být ve většině případů odvozené ze souboru portových mapování. Nemusí proto být v minimálním modelu samostatnou primární tabulkou.

Tato část odpovídá na otázky:

Který starý port odpovídá kterému novému portu?
Které VLANy/segmenty se přenášejí beze změny?
Které VLANy/segmenty se mění?
Které vazby jsou ověřené a které pouze předpokládané?
Co ještě není namapované?
Co je konfliktní nebo nejasné?
Jaký souhrnný vztah starých a nových zařízení plyne z mapování portů?

Tento účel je důležitý zejména pro výměnu síťových prvků, kdy je potřeba pochopit současný stav, navrhnout cílový stav a řídit přechod mezi nimi.


2.4 Pracovní kontrola a plánování migrace

Čtvrtým účelem je poskytovat pracovní pohledy pro samotnou migraci.

Příklady:

starý port → nový port
starý port → zjištěné MAC/IP/segmenty
nový port → plánovaná konfigurace
rozdíl mezi zjištěnou realitou a plánem
nenamapované porty
porty s konfliktními daty
porty připravené k migraci
souhrn starý switch → nový switch odvozený z portových mapování

Systém tedy musí umět pracovat nejen s tím, co bylo pozorováno, ale i s tím, co je plánováno.

Důležité rozlišení:

Současný stav
  je rekonstruovaný z pozorování a ruční evidence.

Cílový stav
  je plánovaný návrh, který může vycházet ze současného stavu,
  ale může obsahovat nové vlastnosti, nové role, nové konfigurace
  a nové vazby, které ve staré infrastruktuře neexistují.

Migrační mapování
  propojuje současný stav s cílovým stavem a říká,
  co se přenáší, co se mění a co je potřeba ověřit.

3. Konceptuální rámec

Tato část popisuje, jaké problémy systém řeší a jak je strukturován konceptuálně, nezávisle na konkrétní implementaci.


3.1 Základní problém

Síťová infrastruktura není statická evidence. Je to proměnlivý systém, u kterého se skutečný stav skládá z více zdrojů:

  • MAC tabulky ze switchů,
  • ARP tabulky z firewallů/routerů,
  • konfigurace zařízení,
  • ručně udržovaná evidence,
  • subnety a segmenty,
  • VLANy,
  • historická pozorování,
  • případně data z NetBoxu nebo jiných systémů.

Každý zdroj říká pouze část pravdy. Některé informace mohou být neúplné, zastaralé nebo konfliktní.

Příklad:

SNMP MAC tabulka říká:
  MAC X byla v čase T viděna na portu P.

ARP tabulka říká:
  IP Y byla v čase T spojena s MAC X.

Evidence subnetů říká:
  IP Y patří do subnetu S.

Evidence segmentů říká:
  subnet S patří do segmentu Z.

Konfigurace portu říká:
  port P je access/trunk/uplink/neznámý.

Otázka „jaké segmenty jsou za portem“ proto není jen prostý JOIN. Je to interpretace více vrstev reality.


3.2 Základní rozlišení vrstev

Systém musí důsledně rozlišovat minimálně tři úrovně:

Pozorování
  Co bylo někde viděno nebo zjištěno.

Odvozený stav
  Co z pozorování považujeme za aktuálně platný nebo pravděpodobný stav.

Lidský pohled
  Jak chceme výsledek zobrazit člověku pro práci, kontrolu nebo rozhodování.

Toto rozlišení je klíčové. Pokud se pozorování, interpretace a reportovací pohledy smíchají do jedné vrstvy, systém se rychle stane nepřehledným a nedůvěryhodným.


3.3 Dimenze problému

Při skládání obrazu síťové reality se překrývá více dimenzí.

3.3.1 Identita

Je potřeba určit, co je stejný objekt:

  • zařízení,
  • interface,
  • MAC adresa,
  • IP adresa,
  • subnet,
  • VLAN,
  • segment.

Příklad:

aa-bb-cc-dd-ee-ff
AA:BB:CC:DD:EE:FF
aabb.ccdd.eeff

mohou reprezentovat stejnou MAC adresu, ale musí být normalizovány.


3.3.2 Čas

Pozorování má vždy časový kontext.

Příklad:

MAC X byla viděna na portu P před 5 minutami.
MAC X byla viděna na portu Q před 3 dny.

Systém musí rozlišovat:

  • historické pozorování,
  • aktuálně relevantní pozorování,
  • zastaralé pozorování,
  • konflikt mezi pozorováními.

3.3.3 Zdroj

Každá informace má původ.

Příklady zdrojů:

  • SNMP,
  • ARP,
  • konfigurace zařízení,
  • ruční evidence,
  • NetBox,
  • importní skript,
  • jednorázový CSV import.

Zdroj je důležitý pro důvěryhodnost i auditovatelnost.


3.3.4 Důvěryhodnost

Různé zdroje mohou mít různou váhu.

Příklad:

Ruční potvrzená evidence může mít vyšší váhu než jednorázové pozorování.
Aktuální SNMP výstup může mít vyšší váhu než starý ARP záznam.
MAC viděná na uplinku nemusí znamenat koncové zařízení za portem.

Důvěryhodnost by neměla být schovaná v neviditelné logice skriptu bez možnosti zpětného vysvětlení.


3.3.5 Topologie

Informace je potřeba zasadit do síťového kontextu:

  • zařízení,
  • port,
  • VLAN,
  • subnet,
  • segment,
  • uplink/access/trunk role portu,
  • vztah mezi porty a zařízeními.

3.3.6 Kardinalita

Vztahy nejsou vždy jednoduché 1:1.

Příklady:

Jeden port může vidět více MAC adres.
Jedna MAC může být v historii viděna na více portech.
Jedna MAC může mít více IP adres.
Jedna IP může být časově spojena s různými MAC adresami.
Jeden segment může být dosažitelný přes více portů.

Právě zde vzniká riziko duplicit, násobení řádků a chybných agregací.


3.3.7 Význam

Technická vazba neznamená automaticky správný význam.

Příklad:

Port vidí MAC adresy z více segmentů.

To může znamenat:

  • port je trunk,
  • port je uplink,
  • za portem je další switch,
  • je tam virtualizační host,
  • je tam chyba v datech,
  • jde o historický/stale stav.

Systém musí umožnit zobrazit nejen výsledek, ale i signály nejistoty.


3.4 Pracovní shrnutí

Systém nemá být jen databází ani jen reportingem.

Má být navržen jako pipeline:

Evidence objektů
→ Pozorování
→ Interpretace
→ Odvozený stav
→ Pracovní pohledy
→ Reportovací pohledy

Nejdůležitější myšlenka:

Síťová realita je rekonstruovaná z pozorování. Proto musí systém odlišovat, co bylo viděno, co z toho odvozujeme a jak to zobrazujeme člověku.


4. Architektura vrstev

Tato část popisuje jednotlivé vrstvy navrženého modelu a jejich odpovědnosti.


4.1 Navržené vrstvy systému

Pracovní návrh vrstev:

core.*
  stabilní identity objektů současné/reálné infrastruktury

obs.*
  surová pozorování a importní dávky

work.*
  pracovní interpretační views nad aktuální realitou

derived.*
  fyzicky uložený odvozený stav, pokud už nestačí jednoduchý SQL view

plan.*
  plánovaný cílový stav nové infrastruktury

migration.*
  mapování mezi současným a cílovým stavem

report.*
  stabilnější lidské pohledy pro reporting / UI

4.2 Vrstva core.*

Vrstva core drží stabilní entity a jejich základní integritu.

Příklady tabulek:

core.devices
core.interfaces
core.mac_addresses
core.ip_addresses
core.subnets
core.segments
core.vlans

Tato vrstva odpovídá na otázku:

Jaké objekty v našem modelu existují a jaké mají stabilní identity?

Typické vlastnosti:

  • primární klíče,
  • foreign keys,
  • unique constraints,
  • normalizované hodnoty,
  • základní popisné atributy,
  • ručně ověřené nebo dlouhodobě platné údaje.

4.3 Vrstva obs.*

Vrstva obs drží pozorování ze skriptů a externích zdrojů.

Příklady tabulek:

obs.import_batches
obs.mac_port_observations
obs.arp_observations
obs.interface_status_observations
obs.config_observations

Tato vrstva odpovídá na otázku:

Co jsme kdy, kde a z jakého zdroje viděli?

Důležité pravidlo:

Importní skript by měl primárně ukládat pozorování, ne neviditelně rozhodovat o finální pravdě.

Příklad interpretace:

Špatně:
  MAC X je za portem P.

Lépe:
  V čase T zdroj S tvrdil, že MAC X byla viděna na portu P.

Každé pozorování by mělo mít minimálně:

  • observed_at,
  • source,
  • batch_id,
  • vazbu na normalizované entity, pokud existují,
  • případně raw hodnoty a raw payload.

4.4 Vrstva work.*

Vrstva work obsahuje pracovní interpretační pohledy.

Tato vrstva odpovídá na otázku:

Co z dostupných pozorování pravděpodobně plyne podle aktuálně definovaných pravidel?

Příklady:

work.current_mac_locations
work.current_ip_mac_bindings
work.current_port_segments
work.conflicting_mac_locations
work.data_quality_issues
work.stale_observations

Tyto views nejsou jen reporty. Jsou to pracovní čočky, které pomáhají skládat obraz reality.

Příklad pravidla:

Za aktuální lokaci MAC považuj poslední pozorování za posledních 24 hodin,
pokud nejde o port označený jako uplink/trunk.

Pokud je pravidlo jednoduché, může být vyjádřeno SQL view.

Pokud začne být pravidlo složité, může být vhodnější přesun do skriptu nebo do derived vrstvy.


4.5 Vrstva derived.*

Vrstva derived slouží pro fyzicky uložený odvozený stav.

Použije se tehdy, když:

  • interpretační logika je složitá,
  • výpočet je drahý,
  • je potřeba uchovat výsledek rozhodnutí,
  • je potřeba auditovat, proč byl stav odvozen právě takto,
  • je potřeba kombinovat více pravidel, scoring nebo ruční override.

Příklad tabulky:

derived.current_mac_locations

Možná pole:

mac_id
interface_id
device_id
vlan_id
observed_at
source
confidence
decision_reason
updated_at

Důležité pravidlo:

Pokud systém něco interpretuje jako aktuální stav, měl by být dohledatelný důvod tohoto rozhodnutí.


4.6 Vrstva plan.*

Vrstva plan obsahuje cílový návrh nové infrastruktury.

Tato vrstva odpovídá na otázku:

Co má existovat po upgradu nebo výměně síťových prvků?

Příklady tabulek:

plan.target_devices
plan.target_interfaces
plan.target_vlans
plan.target_interface_configs
plan.target_port_roles

Cílový stav může vycházet ze současné infrastruktury, ale není jejím prostým kopírováním.

Může obsahovat cílově specifické parametry, například:

nový název zařízení
nový model zařízení
nové číslování portů
cílová role portu
cílová VLAN konfigurace
cílový popis portu
plánovaný access/trunk režim
poznámka k fyzickému přepojení
migrační priorita
stav připravenosti

Důležité pravidlo:

Cílový stav musí být možné odvodit nebo předvyplnit ze současného stavu, ale následně ručně upravit a doplnit o parametry, které ve staré infrastruktuře neexistují.


4.7 Vrstva migration.*

Vrstva migration propojuje současnou a cílovou infrastrukturu.

Tato vrstva odpovídá na otázku:

Jak se starý svět mapuje na nový svět?

Příklady tabulek:

migration.interface_mappings
migration.vlan_mappings
migration.segment_mappings
migration.migration_notes

Primární tabulkou pro první implementační model je migration.interface_mappings.

Explicitní migration.device_mappings není součástí minimálního modelu. Vztah starého a nového zařízení bude nejprve odvozován jako souhrnný pohled nad portovým mapováním.

Příklad mapování:

starý interface → nový target interface
starý VLAN/segment → cílová VLAN/segment

Mapování by mělo umožnit evidovat:

stav mapování: proposed / verified / migrated / blocked
zdroj mapování: ručně / import / odvozeno z názvu / odvozeno z port pořadí
jistotu mapování
poznámku
blokující problém

Důležité pravidlo:

Mapování není jen technická vazba. Je to pracovní rozhodnutí, které může být navržené, ověřené, změněné nebo odmítnuté.

Z toho vyplývá, že mapování musí umět nést historii rozhodnutí, ne jen aktuální stav. Pokud někdo navrhne mapování, později ho zamítne a navrhne jiné, musí být dohledatelné, proč se to stalo.

První verze proto v migration.interface_mappings zavádí příznak is_current a partial unique indexy:

- v jednu chvíli je za daný source port aktivní nejvýše jedno mapování,
- v jednu chvíli je za daný target port aktivní nejvýše jedno mapování,
- starší (neaktuální) mapování zůstávají uložená pro audit.

Tento přístup řeší 1:1 případy, návrhové iterace a rejecty. M:N případy (LAG, split, merge) zůstávají jako otevřená otázka a vyžadují diskuzi (viz sekce 9).

4.7.1 Device mapping jako odvozený souhrn

Mapování zařízení není v první verzi považováno za primární vstupní entitu.

Důvod:

Pokud máme dostatečně přesné mapování portů,
souhrnný vztah staré zařízení → nové zařízení
lze odvodit z těchto portových mapování.

Příklad:

old huawei-sw01 Gi0/0/1 → new cisco-sw01 Gi1/0/1
old huawei-sw01 Gi0/0/2 → new cisco-sw01 Gi1/0/2
old huawei-sw01 Gi0/0/3 → new cisco-sw01 Gi1/0/3

Z toho lze reportovat:

huawei-sw01 → cisco-sw01
mapped_ports: 3
mapping_type: full/partial podle rozsahu

Explicitní tabulka migration.device_mappings se doplní až tehdy, pokud bude potřeba evidovat zařízení jako samostatný migrační objekt nezávisle na portech.

Typické důvody pro pozdější doplnění:

- jeden starý switch se rozpadá do více nových switchů,
- více starých switchů se slučuje do jednoho nového,
- je potřeba řídit migrační fázi na úrovni zařízení,
- je potřeba evidovat fyzickou výměnu kus za kus,
- cílové zařízení se plánuje dříve než detailní port mapping.

Pro první implementaci bude zařízení mapováno přes reportovací pohled:

report.device_mapping_summary

4.8 Vrstva report.*

Vrstva report obsahuje stabilnější pohledy pro člověka nebo nástroje typu Metabase.

Tato vrstva odpovídá na otázku:

Jak chceme výsledek přehledně zobrazit člověku?

Příklady:

report.segments_behind_port
report.device_mapping_summary
report.device_overview
report.interface_overview
report.mac_inventory
report.ip_inventory

Tato vrstva už může obsahovat lidsky čitelné agregace:

port → seznam MAC adres
port → seznam IP adres
port → seznam segmentů
port → počet segmentů
port → příznaky konfliktů

Příklad výstupu:

Device: sw01
Interface: Gi1/0/5
MAC count: 4
Segments: LAN, Printers
Flags: mixed_segments, stale_arp

5. Pravidla a postupy

Tato část popisuje, jak má systém pracovat v praxi: jak se chovají importní skripty, jak se interpretují data a jaké zásady drží návrh konzistentní.


5.1 Role externích skriptů

Externí skripty slouží hlavně pro sběr dat a případně pro složitější interpretaci.

5.1.1 Primární role skriptů

Skript by měl:

  • vytvořit importní dávku,
  • stáhnout data ze zdroje,
  • normalizovat základní formát,
  • uložit raw hodnoty,
  • propojit data na core entity, pokud je to možné,
  • uložit pozorování do obs.*,
  • aktualizovat základní metadata typu first_seen / last_seen,
  • ukončit importní dávku.

5.1.2 Co by skript neměl dělat bez záznamu

Skript by neměl neviditelně:

  • přepisovat finální pravdu,
  • mazat starší kontext,
  • schovávat konflikty,
  • rozhodovat bez auditní stopy,
  • míchat pozorování a interpretaci do jedné tabulky.

5.1.3 Kdy použít skript pro interpretaci

Skript dává smysl pro interpretaci, pokud pravidla zahrnují:

  • scoring,
  • více kroků,
  • ruční override,
  • porovnávání více zdrojů,
  • detekci konfliktů,
  • ignorování portů podle role,
  • historické okno,
  • složitější heuristiky.

Výsledek takové interpretace by měl být uložen do derived.*, ideálně s poli jako:

confidence
decision_reason
source_basis
updated_at

5.1.4 Pravidla zakládání identit

Datový kontrakt (závazné). Každý skript, který importuje data do obs.* nebo core.*, musí dodržet pravidla v této sekci. Porušení vede k nekonzistentním identitám napříč zdroji.

Skripty při importu narážejí na entity, které ještě v core.* neexistují. Otázka „co se s nimi má stát" nesmí být schovaná v implementaci jednotlivých skriptů – jinak se rychle objeví nekonzistentní stav: jeden skript MAC založí, jiný ne, třetí v jiném formátu.

Pracovní pravidla pro první verzi:

MAC adresy
  Auto-zakládat v core.mac_addresses.
  Riziko nízké, MAC je technicky jednoznačná identita.
  Skript nastaví first_seen a last_seen.

IP adresy
  Auto-zakládat v core.ip_addresses.
  Pokud existuje matching subnet, propojit přes subnet_id.
  Pokud ne, založit s subnet_id = NULL a označit pro pozdější revizi.

VLANy
  Auto-zakládat v core.vlans, pokud jsou viděné v pozorování.
  Source nastavit na 'discovered', label může zůstat prázdný.
  Operátor doplní label a purpose ručně později.

Interfaces na známém zařízení
  Auto-zakládat v core.interfaces.
  Switche běžně získávají/ztrácí porty (subinterfaces, LAGy, VLAN interfaces);
  blokovat tohle by znamenalo bránit normálnímu provozu.
  Source nastavit na 'discovered'.

Zařízení (devices)
  NEAUTO-zakládat.
  Skript při neznámém zařízení skončí s jasnou chybou
  nebo uloží pozorování bez device_id (raw_device se uloží vždy).
  Důvod: zařízení je primární migrační kotva,
  jeho založení patří k záměrnému operátorskému rozhodnutí.

Důležité pravidlo:

Auto-zakládat lze entity, které jsou technicky jednoznačné a nesou nízké riziko nesprávné identifikace. Operátorské rozhodnutí (typicky zařízení) musí zůstat explicitní.

Skript musí v každém případě:

- zaznamenat raw_* hodnoty pro pozdější ruční dohledání,
- propojit s batch_id pro auditovatelnost,
- ne maskovat selhání tichým ignorováním.

5.2 Příklad: segmenty za portem

Otázka:

Jaké segmenty jsou za tímto portem?

Na první pohled jde o jednoduchý report. Ve skutečnosti je to řetězec interpretací.

Potřebné kroky:

1. Najít MAC adresy nedávno viděné za portem.
2. Rozhodnout, která MAC pozorování jsou aktuálně relevantní.
3. Najít IP adresy spojené s těmito MAC adresami.
4. Rozhodnout, která ARP pozorování jsou aktuálně relevantní.
5. Najít subnety těchto IP adres.
6. Najít segmenty těchto subnetů.
7. Zohlednit roli portu.
8. Označit konflikty nebo nejistoty.
9. Zobrazit člověku souhrnný výsledek.

Technická cesta může vypadat takto:

interface
→ mac_port_observations
→ mac_addresses
→ arp_observations
→ ip_addresses
→ subnets
→ segments

Ale správný pohled musí navíc řešit:

  • časovou platnost,
  • konflikty,
  • roli portu,
  • duplicitní hodnoty,
  • agregaci do lidsky čitelného seznamu,
  • signály nejistoty.

5.3 Zásady návrhu

Pracovní zásady:

1. Surová pozorování nemažeme bez důvodu.
2. Každý import má batch_id.
3. Každé pozorování má observed_at a source.
4. Core entity mají stabilní ID.
5. Pozorování a interpretace jsou oddělené vrstvy.
6. Aktuální stav je odvozený, ne automaticky surový fakt.
7. Každé složitější rozhodnutí musí být vysvětlitelné.
8. Reportovací pohledy se staví nad interpretační vrstvou, ne přímo nad raw chaosem.
9. Opakovaná otázka se může stát pracovním view.
10. Složitý view je lepší rozdělit na menší vrstvy.

6. První implementace

Tato část je konkrétním návrhem první implementační verze: postup, datový model, SQL schéma a otevřené body k doplnění.


6.1 Praktický postup návrhu implementace

Nejrychlejší cesta k cíli nebude začít psaním SQL ani kreslením kompletního modelu. Vhodnější je postupovat po krátkých rozhodovacích blocích, kde se vždy zodpoví jedna oblast a výsledek se rovnou promítne do návrhu.

Navržený pracovní postup:

1. Definovat první reálný use-case.
2. Vyjmenovat zdroje dat pro tento use-case.
3. Rozlišit, co je stabilní entita a co je pozorování.
4. Navrhnout minimální core tabulky.
5. Navrhnout minimální observation tabulky.
6. Definovat pravidla aktuálnosti.
7. Definovat první pracovní interpretační views.
8. Definovat první reportovací pohledy.
9. Teprve potom vytvořit SQL CREATE skripty.
10. Nakonec navrhnout importní skripty a jejich odpovědnosti.

Důležité pravidlo:

Nejdřív modelovat jeden konkrétní pracovní problém. Teprve potom model zobecňovat.

Jako první kandidát use-case se nabízí:

Pro každý switchport zobrazit:
- zařízení,
- interface,
- MAC adresy viděné za portem,
- IP adresy spojené s těmito MAC adresami,
- subnety,
- segmenty,
- informaci o aktuálnosti,
- případné konflikty nebo nejistoty.

Tento use-case je vhodný, protože pokrývá většinu klíčových vrstev systému:

core.devices
core.interfaces
core.mac_addresses
core.ip_addresses
core.subnets
core.segments
obs.mac_port_observations
obs.arp_observations
work.current_mac_locations
work.current_ip_mac_bindings
report.segments_behind_port

6.2 První implementační blok: výměna switchů

První konkrétní implementační use-case bude příprava výměny síťových prvků.

6.2.1 Cíl use-case

Cílem je pro každý port staré infrastruktury zjistit nebo evidovat:

- na kterém zařízení a portu se nachází,
- jaké MAC adresy byly za portem pozorovány,
- jaké IP adresy byly s těmito MAC adresami spojeny,
- do jakých subnetů a segmentů tyto IP adresy patří,
- zda jsou data aktuální nebo zastaralá,
- zda existují konflikty nebo nejistoty,
- na jaký cílový port nové infrastruktury se má starý port mapovat,
- jaká má být cílová konfigurace nového portu,
- zda je migrace daného portu připravena.

6.2.2 Minimální datové oblasti

Pro tento use-case budou potřeba tyto oblasti:

current/core
  stabilní entity současné infrastruktury

observations
  zjištění ze SNMP, ARP, konfigurací a dalších zdrojů

interpreted/current state
  pracovní odvození aktuálního stavu

target/plan
  cílová infrastruktura a plánované parametry

migration mapping
  mapování starých prvků a portů na nové

report/work views
  lidsky použitelné pohledy pro kontrolu a migraci

6.2.3 Minimální tabulky pro první návrh

Pracovní minimální sada tabulek:

core.devices
core.interfaces
core.mac_addresses
core.ip_addresses
core.subnets
core.segments
core.vlans

obs.import_batches
obs.mac_port_observations
obs.arp_observations
obs.interface_config_observations
obs.interface_status_observations

plan.target_devices
plan.target_interfaces
plan.target_interface_configs

migration.interface_mappings
migration.migration_notes

6.2.4 První pracovní views

První pracovní interpretační views:

work.current_mac_locations
work.current_ip_mac_bindings
work.current_port_segments
work.conflicting_mac_locations
work.stale_observations
work.data_quality_issues

První reportovací/migrační views:

report.segments_behind_port
report.port_migration_overview
report.device_mapping_summary
report.unmapped_current_ports
report.target_ports_without_mapping
report.port_readiness_overview

6.2.5 První implementační rozhodnutí

Před vytvořením SQL CREATE skriptů je potřeba rozhodnout:

1. Jak budeme rozlišovat stará/current zařízení od cílových/target zařízení.
2. Zda cílová zařízení budou samostatně v plan.*, nebo společně v core.* s lifecycle stavem.
3. Jaké stavy bude mít mapování portů.
4. Jak bude odvozován souhrnný device mapping z portových mapování.
5. Jaké základní role portů budeme evidovat.
6. Jaké časové okno znamená aktuální MAC/ARP pozorování.
7. Jak budeme označovat konfliktní nebo neověřená data.

Pracovní návrh výchozích odpovědí:

1. Současnou realitu držet v core.* a obs.*.
2. Cílový plán držet odděleně v plan.*.
3. Primární migrační mapování držet na úrovni portů v migration.interface_mappings.
4. Device mapping zatím neukládat jako primární tabulku, ale odvozovat přes report.device_mapping_summary.
5. Aktuální stav odvozovat nejdříve přes work.* views.
6. Složitější rozhodnutí později přes derived.* tabulky.

6.3 Evidenční základ a hranice mezi current / observed / target

Před návrhem konkrétních tabulek je potřeba ujasnit, co je základní evidenční vrstva a co už je pouze pozorování nebo plán.

6.3.1 Základní evidenční vrstva

Základní evidenční vrstva musí obsahovat minimálně objekty, které jsou stabilními kotvami pro pozorování i migraci.

Pro první implementační model jsou primární kotvy:

current device
current interface

Důvod:

Migrace se reálně plánuje a kontroluje na úrovni portů.
Pozorování MAC/ARP/config/status musí být vztažitelné ke konkrétnímu zařízení a portu.
Cílový plán musí být mapovatelný na konkrétní nový port.

Základní evidenční vrstva tedy není pouze seznam switchů. Musí obsahovat i porty/interfacy.

Pracovní minimum:

core.devices
core.interfaces

Další entity, například MAC, IP, subnet, segment a VLAN, jsou také důležité, ale pro migrační mapování mají jinou roli:

core.mac_addresses
core.ip_addresses
core.subnets
core.segments
core.vlans

Tyto entity pomáhají vysvětlit, co je za portem, ale nejsou primární migrační jednotkou.


6.3.2 Evidence současného stavu vs cílového stavu

Současný stav a cílový stav je vhodné tabulkově oddělit.

Pracovní rozhodnutí:

core.*
  aktuální / existující / zjištěná infrastruktura

plan.*
  plánovaný cílový stav nové infrastruktury

Důvod oddělení:

Současný stav je rekonstruovaný z reality a pozorování.
Cílový stav je návrh, který může obsahovat vlastnosti, které dnes ještě neexistují.
Cílový stav může vzniknout předvyplněním ze současného stavu, ale musí být ručně upravitelný.
Migrace musí umět porovnat „co je“ proti „co má být“.

Pokud by současná a cílová zařízení byla ve stejné tabulce, bylo by nutné zavést silnou lifecycle logiku a hlídat, kdy je záznam realita a kdy plán. To by v první verzi model zbytečně komplikovalo.


6.3.3 Kam patří observed svět

Observed svět patří do obs.*.

Observed data nejsou primárně evidence cílového ani aktuálního stavu. Jsou to časově označená tvrzení zdrojů.

Příklad:

MAC tabulka neříká definitivně „toto zařízení je za portem“.
Říká: „v čase T byla MAC M viděna na portu P“.

ARP tabulka neříká definitivně „tato IP patří tomuto zařízení“.
Říká: „v čase T byla IP I spojena s MAC M“.

Proto:

obs.mac_port_observations
obs.arp_observations
obs.interface_status_observations
obs.interface_config_observations

Observed vrstva má být historická, auditovatelná a nesmí se tvářit jako definitivní pravda.


6.3.4 Nestačí MAC table + ARP

Pro základní technickou rekonstrukci vazby port → MAC → IP → subnet → segment může stačit:

MAC table
ARP table
subnets
segments

Pro migrační účely to ale nestačí vždy.

Důvod:

Při migraci často nechceme vědět jen „jaké MAC/IP jsou za portem“.
Chceme vědět i „co to je zač“ a zda je to migračně důležité.

Příklady:

Za portem je tiskárna.
Za portem je kamera.
Za portem je AP.
Za portem je server.
Za portem je virtualizační host.
Za portem je neznámé zařízení, které se musí ověřit.
Za portem je kritické zařízení, které se musí hlídat při migraci.

Proto je nutné přidat evidenční vrstvu pro koncová nebo sledovaná zařízení.

Pracovní název:

core.assets

Tato vrstva není povinná pro každý endpoint v síti – evidovat každou pracovní stanici by bylo nereálné. Ale je povinná pro každé zařízení, u kterého má migrace dopad: kritické servery, AP, kamery, tiskárny, virtualizační hosty, neznámé entity k ověření. Kritičnost zařízení za portem často rozhoduje, zda migrační okno použít v noci nebo ve špičce, takže core.assets přestává být vedlejší informací a stává se klíčovým vstupem pro plánování.

Příklad:

core.assets
- id
- label
- asset_type
- criticality
- owner
- migration_watch
- last_verified_at
- verified_by
- note

Pole last_verified_at a verified_by jsou důležitá proto, že evidence významných zařízení rychle stárne. Při migraci je rozdíl mezi „evidence říká, že tohle je server zákazníka A, ale nikdo to neviděl dva roky" a „ověřeno před měsícem".

A vazby:

asset ↔ MAC address
asset ↔ IP address
asset ↔ expected/current interface

Důležité pravidlo:

MAC/IP pozorování ukazuje technickou stopu. Asset evidence říká, co tato stopa znamená pro člověka a migraci.


6.3.5 Doporučené minimální vrstvy

Pro první implementaci se nabízí tyto vrstvy:

core.devices
  současné síťové prvky

core.interfaces
  porty/interfacy současných síťových prvků

core.mac_addresses
  normalizované MAC adresy

core.ip_addresses
  normalizované IP adresy

core.subnets
  subnety

core.segments
  logické segmenty

core.vlans
  VLAN evidence

core.assets
  evidence významných koncových nebo sledovaných zařízení (servery, AP, kamery, kritické endpointy)

obs.*
  pozorování ze sítě

work.*
  interpretace aktuálního stavu

plan.*
  cílový návrh nové infrastruktury

migration.*
  mapování starý port → nový port

report.*
  pracovní a migrační pohledy

6.3.6 Mermaid diagram vrstev

Tento diagram ukazuje systém po vrstvách. Nejde ještě o přesný ER model tabulek, ale o logické rozdělení odpovědností.

flowchart TB
    subgraph CURRENT[Současná evidenční vrstva - core]
        D[core.devices<br/>aktuální síťové prvky]
        I[core.interfaces<br/>aktuální porty/interfacy]
        M[core.mac_addresses]
        IP[core.ip_addresses]
        S[core.subnets]
        SEG[core.segments]
        V[core.vlans]
        A[core.assets<br/>významná sledovaná zařízení<br/>servery, AP, kamery, kritické endpointy]
    end

    subgraph OBS[Observed vrstva - obs]
        B[obs.import_batches]
        MPO[obs.mac_port_observations<br/>MAC viděná na portu v čase]
        ARP[obs.arp_observations<br/>IP-MAC vazba v čase]
        CFG[obs.interface_config_observations]
        ST[obs.interface_status_observations]
    end

    subgraph WORK[Interpretační vrstva - work/derived]
        CML[work.current_mac_locations]
        CIM[work.current_ip_mac_bindings]
        CPS[work.current_port_segments]
        DQ[work.data_quality_issues]
    end

    subgraph PLAN[Cílový stav - plan]
        TD[plan.target_devices]
        TI[plan.target_interfaces]
        TC[plan.target_interface_configs]
    end

    subgraph MIG[Migrační vrstva - migration]
        IM[migration.interface_mappings<br/>starý port → nový port]
        MN[migration.migration_notes]
    end

    subgraph REPORT[Reportovací/pracovní pohledy - report]
        R1[report.segments_behind_port]
        R2[report.port_migration_overview]
        R3[report.device_mapping_summary]
        R4[report.port_readiness_overview]
    end

    D --> I
    I --> MPO
    M --> MPO
    M --> ARP
    IP --> ARP
    IP --> S
    S --> SEG
    V --> I
    A -. může být spojeno s .-> M
    A -. může být spojeno s .-> IP
    B --> MPO
    B --> ARP
    B --> CFG
    B --> ST

    MPO --> CML
    ARP --> CIM
    CML --> CPS
    CIM --> CPS
    CPS --> R1
    DQ --> R4

    TD --> TI
    TI --> TC
    I --> IM
    TI --> IM
    IM --> R2
    IM --> R3
    IM --> R4
    R1 --> R2
Loading

6.3.7 Mermaid ER diagram minimálního modelu

Tento diagram ukazuje konkrétnější vztahy mezi hlavními tabulkami první implementační verze. Je to pracovní návrh, ne finální databázové schéma.

erDiagram
    core_devices ||--o{ core_interfaces : has
    core_vlans ||--o{ core_interfaces : native_or_default_vlan

    core_segments ||--o{ core_subnets : contains
    core_vlans ||--o{ core_subnets : may_map_to
    core_subnets ||--o{ core_ip_addresses : contains

    core_mac_addresses ||--o{ obs_mac_port_observations : observed_as
    core_interfaces ||--o{ obs_mac_port_observations : observed_on
    core_devices ||--o{ obs_mac_port_observations : observed_on_device
    core_vlans ||--o{ obs_mac_port_observations : observed_in_vlan
    obs_import_batches ||--o{ obs_mac_port_observations : contains

    core_ip_addresses ||--o{ obs_arp_observations : observed_as
    core_mac_addresses ||--o{ obs_arp_observations : paired_with
    core_devices ||--o{ obs_arp_observations : observed_on_device
    core_interfaces ||--o{ obs_arp_observations : optionally_observed_on
    obs_import_batches ||--o{ obs_arp_observations : contains

    core_assets ||--o{ core_asset_mac_addresses : identified_by
    core_mac_addresses ||--o{ core_asset_mac_addresses : belongs_to_asset
    core_assets ||--o{ core_asset_ip_addresses : identified_by
    core_ip_addresses ||--o{ core_asset_ip_addresses : belongs_to_asset

    plan_target_devices ||--o{ plan_target_interfaces : has
    plan_target_interfaces ||--o{ plan_target_interface_configs : configured_by
    plan_target_vlans ||--o{ plan_target_interface_configs : access_or_native_target_vlan
    core_segments ||--o{ plan_target_vlans : target_segment_for

    core_interfaces ||--o{ migration_interface_mappings : source_current_interface
    plan_target_interfaces ||--o{ migration_interface_mappings : target_planned_interface

    migration_interface_mappings ||--o{ migration_migration_notes : has

    core_devices {
        bigint id PK
        text label
        text hostname
        inet mgmt_ip
        text vendor
        text model
    }

    core_interfaces {
        bigint id PK
        bigint device_id FK
        text name
        text role
        bigint native_vlan_id FK
    }

    core_mac_addresses {
        bigint id PK
        macaddr mac
        timestamptz first_seen
        timestamptz last_seen
    }

    core_ip_addresses {
        bigint id PK
        inet address
        bigint subnet_id FK
        timestamptz first_seen
        timestamptz last_seen
    }

    core_subnets {
        bigint id PK
        cidr prefix
        bigint segment_id FK
        bigint vlan_id FK
    }

    core_segments {
        bigint id PK
        text label
    }

    core_vlans {
        bigint id PK
        integer vlan_id
        text label
    }

    core_assets {
        bigint id PK
        text label
        text asset_type
        text criticality
        boolean migration_watch
    }

    core_asset_mac_addresses {
        bigint asset_id FK
        bigint mac_id FK
    }

    core_asset_ip_addresses {
        bigint asset_id FK
        bigint ip_address_id FK
    }

    obs_import_batches {
        bigint id PK
        text source
        text source_type
        timestamptz started_at
        timestamptz finished_at
        text status
    }

    obs_mac_port_observations {
        bigint id PK
        bigint batch_id FK
        timestamptz observed_at
        bigint device_id FK
        bigint interface_id FK
        bigint mac_id FK
        bigint vlan_id FK
    }

    obs_arp_observations {
        bigint id PK
        bigint batch_id FK
        timestamptz observed_at
        bigint device_id FK
        bigint interface_id FK
        bigint ip_address_id FK
        bigint mac_id FK
    }

    plan_target_devices {
        bigint id PK
        text label
        text vendor
        text model
        text planned_role
    }

    plan_target_vlans {
        bigint id PK
        integer vlan_id
        text label
        bigint target_segment_id FK
        text purpose
        text status
    }

    plan_target_interfaces {
        bigint id PK
        bigint target_device_id FK
        text name
        text planned_role
    }

    plan_target_interface_configs {
        bigint id PK
        bigint target_interface_id FK
        text mode
        bigint access_target_vlan_id FK
        bigint native_target_vlan_id FK
        jsonb allowed_target_vlans
        jsonb config_payload
    }

    migration_interface_mappings {
        bigint id PK
        bigint source_interface_id FK
        bigint target_interface_id FK
        text status
        text mapping_source
        integer confidence
        boolean is_current
        timestamptz superseded_at
    }

    migration_migration_notes {
        bigint id PK
        bigint interface_mapping_id FK
        text note
        text severity
    }
Loading

Poznámka k diagramu:

- work.* a report.* nejsou v ER diagramu jako tabulky, protože půjde primárně o views.
- Device mapping není samostatná tabulka; bude odvozen přes report.device_mapping_summary.
- core.assets je evidenční vrstva pro významná zařízení za porty (viz sekce 6.3.4).
- plan.target_vlans je samostatná entita; current core.vlans a plan.target_vlans se porovnávají přes VLAN ID, ale nemají FK vazbu.

6.4 Implementační rozhodnutí: evidenční jádro první verze

Tato sekce převádí dosavadní návrh do prvních konkrétních implementačních rozhodnutí.

6.4.1 Co je primární evidence

Primární evidenční vrstva první verze bude postavená na těchto objektech:

core.devices
core.interfaces

Důvod:

Migrace se plánuje a kontroluje primárně na úrovni portů.
Port musí vždy patřit ke konkrétnímu zařízení.
Pozorování ze sítě musí být možné vztáhnout ke konkrétnímu portu.
Cílový plán musí být možné mapovat na konkrétní nový port.

Základní evidenční kotva tedy není pouze zařízení, ale dvojice:

device + interface

6.4.2 Co je podpůrná evidence

Podpůrná evidenční vrstva vysvětluje, co se za porty nachází nebo jak je síť logicky členěná.

Do první verze patří:

core.mac_addresses
core.ip_addresses
core.subnets
core.segments
core.vlans

Tyto tabulky nejsou primární migrační jednotkou, ale jsou nutné pro odpovědi typu:

Jaké MAC adresy jsou za portem?
Jaké IP adresy jsou za portem?
Do jakých subnetů patří?
Jaké segmenty jsou za portem?
Které VLANy se portu týkají?

6.4.3 Co je důležitá podpůrná evidence pro migraci

Pro migrační účely je nutné mít tabulku:

core.assets

Ta neeviduje každé zařízení v síti (to by bylo nereálné). Eviduje objekty, které mají význam pro člověka nebo migraci.

Příklady:

tiskárna
kamera
AP
server
virtualizační host
kritické koncové zařízení
neznámé zařízení k ověření

Smysl:

MAC/IP pozorování říká technickou stopu.
Asset evidence říká, co tato stopa znamená.

Proto core.assets pomáhá odpovědět nejen:

Co je za portem technicky vidět?

ale také:

Co je to za zařízení a má se při migraci hlídat?

6.4.4 Co je observed vrstva

Observed vrstva bude oddělená v obs.*.

Do první verze patří:

obs.import_batches
obs.mac_port_observations
obs.arp_observations
obs.interface_status_observations
obs.interface_config_observations

Observed tabulky nebudou reprezentovat definitivní stav. Budou reprezentovat časově označená pozorování.

Příklad:

obs.mac_port_observations
  V čase T byla MAC M viděna na portu P.

obs.arp_observations
  V čase T byla IP I spojena s MAC M.

Důležité pravidlo:

Observed data nejsou pravda sama o sobě. Jsou to vstupy pro interpretaci.


6.4.5 Co je cílový plán

Cílový stav bude oddělený v plan.*.

Do první verze patří:

plan.target_devices
plan.target_interfaces
plan.target_interface_configs

Důvod oddělení:

Současný stav je rekonstruovaná realita.
Cílový stav je plán.
Plán může obsahovat vlastnosti, které ve staré infrastruktuře neexistují.

Cílový port může být předvyplněný podle současného portu, ale následně musí být ručně upravitelný.


6.4.6 Co je migrační vazba

Primární migrační tabulkou bude:

migration.interface_mappings

Ta bude vyjadřovat vztah:

current/core interface → plan/target interface

Device mapping nebude v první verzi samostatná tabulka. Bude odvozený z portového mapování přes view:

report.device_mapping_summary

Důvod:

Migrační rozhodnutí se prakticky děje na úrovni portů.
Vztah starý switch → nový switch lze většinou odvodit z množiny portových mapování.

6.4.7 První implementační minimum

Pro první SQL návrh se použije tato minimální sada tabulek:

core.devices
core.interfaces
core.mac_addresses
core.ip_addresses
core.subnets
core.segments
core.vlans
core.assets
core.asset_mac_addresses
core.asset_ip_addresses

obs.import_batches
obs.mac_port_observations
obs.arp_observations
obs.interface_status_observations
obs.interface_config_observations

plan.target_devices
plan.target_interfaces
plan.target_interface_configs

migration.interface_mappings
migration.migration_notes

První implementace nebude ještě řešit všechno. Jejím cílem je umožnit:

1. evidovat současné switche a porty,
2. importovat MAC/ARP/config/status pozorování,
3. evidovat významná zařízení za porty,
4. navrhnout cílové switche a porty,
5. mapovat starý port na nový port,
6. vytvořit první pracovní views pro kontrolu migrace.

6.4.8 Další krok

Dalším krokem je navrhnout konkrétní sloupce těchto tabulek.

Priorita návrhu:

1. core.devices
2. core.interfaces
3. plan.target_devices
4. plan.target_interfaces
5. migration.interface_mappings
6. core.assets
7. obs.import_batches
8. obs.mac_port_observations
9. obs.arp_observations

Teprve po odsouhlasení sloupců bude vhodné vytvořit první SQL CREATE TABLE skript.


6.5 Návrh sloupců: hlavní osa migrace

Tato sekce navrhuje konkrétní sloupce pro první sadu tabulek, které tvoří hlavní osu migrace.

Cílem není ještě vytvořit finální SQL, ale ujasnit, jaké informace musí model nést.


6.5.1 core.devices

Účel:

Evidence současných / existujících síťových prvků, ke kterým se vztahují aktuální porty a pozorování.

Navržené sloupce:

id
  technický primární klíč

label
  lidský název zařízení; hlavní pracovní identifikátor

hostname
  DNS/konfigurační hostname, pokud se liší od labelu

mgmt_ip
  management IP adresa zařízení

vendor
  výrobce, například Huawei, Cisco, MikroTik, UniFi

model
  model zařízení

serial_number
  sériové číslo, pokud je dostupné

role
  pracovní role zařízení, například access_switch, distribution_switch, firewall

site
  lokalita / zákazník / objekt

status
  stav evidence zařízení, například active, planned_retirement, retired, unknown

source
  zdroj založení záznamu, například manual, import, snmp, netbox

note
  volná poznámka

created_at
updated_at

Pracovní poznámky:

- label by měl být unikátní v rámci první implementace.
- mgmt_ip může být unikátní, ale nemusí být povinná.
- status zde neznamená cílový plán, pouze stav současného evidovaného zařízení.

6.5.2 core.interfaces

Účel:

Evidence současných / existujících portů a interfaců na aktuálních síťových prvcích.

Navržené sloupce:

id
  technický primární klíč

device_id
  vazba na core.devices

name
  název portu/interface tak, jak je používán na zařízení

label
  volitelný lidský popis nebo alias

interface_type
  typ interface, například physical, vlan, lag, routed, loopback

port_role
  pracovní role portu, například access, trunk, uplink, downlink, unused, unknown

admin_status
  poslední známý administrativní stav, pokud je evidován

oper_status
  poslední známý operační stav, pokud je evidován

native_vlan_id
  volitelná vazba na core.vlans pro native/default/access VLAN

description
  popis portu ze zařízení nebo ruční evidence

source
  zdroj založení nebo poslední významné aktualizace záznamu

note
  volná poznámka

created_at
updated_at

Důležité constrainty:

UNIQUE(device_id, name)

Pracovní poznámky:

- Port/interface je primární migrační jednotka.
- Port role je pracovní interpretace/evidence, nikoliv nutně přesný opis konfigurace.
- Detailní konfigurační pozorování patří do obs.interface_config_observations.

6.5.3 plan.target_devices

Účel:

Evidence plánovaných cílových síťových prvků nové infrastruktury.

Navržené sloupce:

id
  technický primární klíč

label
  lidský název cílového zařízení

hostname
  plánovaný hostname

mgmt_ip
  plánovaná management IP adresa, pokud je známá

vendor
  plánovaný výrobce

model
  plánovaný model

serial_number
  sériové číslo, pokud je již známé

planned_role
  plánovaná role zařízení, například access_switch, distribution_switch

site
  lokalita / zákazník / objekt

migration_phase
  plánovaná migrační fáze nebo etapa

status
  stav cílového zařízení, například proposed, approved, ordered, installed, configured, migrated

note
  volná poznámka

created_at
updated_at

Pracovní poznámky:

- target device nemusí fyzicky existovat v době založení záznamu.
- status zde popisuje stav plánovaného/cílového zařízení, ne současnou realitu.
- Vazba staré zařízení → nové zařízení se v první verzi neukládá přímo, ale odvozuje z mapování portů.

6.5.4 plan.target_interfaces

Účel:

Evidence plánovaných cílových portů/interfaců na cílových zařízeních.

Navržené sloupce:

id
  technický primární klíč

target_device_id
  vazba na plan.target_devices

name
  plánovaný název portu/interface

label
  volitelný lidský alias nebo pracovní popis

interface_type
  typ interface, například physical, vlan, lag, routed, loopback

planned_port_role
  plánovaná role portu, například access, trunk, uplink, downlink, unused, unknown

planned_description
  plánovaný popis portu

migration_phase
  plánovaná migrační fáze portu

status
  stav cílového portu, například proposed, approved, configured, ready, migrated, blocked

note
  volná poznámka

created_at
updated_at

Důležité constrainty:

UNIQUE(target_device_id, name)

Pracovní poznámky:

- Target interface je cílový bod migrace.
- Detailní cílová konfigurace portu patří do plan.target_interface_configs.
- Target interface může být předvyplněn podle current interface, ale musí zůstat ručně upravitelný.

6.5.5 plan.target_vlans

Účel:

Evidence plánovaných cílových VLAN v nové infrastruktuře.

Důvod pro samostatnou evidenci cílových VLAN:

VLAN ID je technické číslo, které lze porovnat mezi současným a cílovým stavem.
Význam VLAN ale nemusí být automaticky stejný.
Cílová VLAN může mít nový název, nový účel, nový segment, jiný status nebo jiný migrační význam.

Navržené sloupce:

id
  technický primární klíč

vlan_id
  číslo VLAN; technický identifikátor v rozsahu 1–4094

label
  plánovaný název VLAN

target_segment_id
  volitelná vazba na core.segments, pokud cílová VLAN patří ke známému logickému segmentu

purpose
  účel VLAN, například users, printers, cameras, management, voice, servers

status
  stav cílové VLAN, například proposed, approved, configured, migrated, deprecated

note
  volná poznámka

created_at
updated_at

Důležité constrainty:

UNIQUE(vlan_id)

Pracovní poznámky:

- VLAN ID lze použít pro porovnání current VLAN vs target VLAN.
- Shodné VLAN ID neznamená automaticky shodný význam.
- Pokud bude potřeba explicitně mapovat starou VLAN na cílovou VLAN, doplní se migration.vlan_mappings.

6.5.6 plan.target_interface_configs

Účel:

Evidence plánované cílové konfigurace portu.

Navržené sloupce:

id
  technický primární klíč

target_interface_id
  vazba na plan.target_interfaces

mode
  plánovaný režim portu, například access, trunk, routed, disabled

access_target_vlan_id
  vazba na plan.target_vlans pro plánovanou access VLAN, pokud dává smysl

native_target_vlan_id
  vazba na plan.target_vlans pro plánovanou native VLAN, pokud dává smysl

allowed_target_vlan_ids
  JSONB nebo později samostatná vazební tabulka pro seznam povolených cílových VLAN

planned_description
  plánovaný config description

config_payload
  JSONB pro vendor-specific nebo strukturované detaily konfigurace

config_text
  volitelný textový výstup/plán konfigurace

status
  stav konfigurace, například draft, reviewed, approved, generated, applied

note
  volná poznámka

created_at
updated_at

Pracovní poznámky:

- Cílová konfigurace portu odkazuje na plan.target_vlans, ne přímo na core.vlans.
- Pro první verzi je praktičtější použít kombinaci strukturovaných sloupců + JSONB.
- Pokud se práce s allowed VLAN ukáže jako důležitá, později se doplní samostatná tabulka plan.target_interface_allowed_vlans.
- Tato tabulka reprezentuje plán, nikoliv observed konfiguraci ze starého zařízení.

6.5.7 migration.interface_mappings

Účel:

Primární migrační vazba mezi současným portem a cílovým portem.

Navržené sloupce:

id
  technický primární klíč

source_interface_id
  vazba na core.interfaces; starý/current port

target_interface_id
  vazba na plan.target_interfaces; nový/cílový port

status
  stav mapování, například proposed, verified, ready, migrated, blocked, rejected

mapping_source
  původ mapování, například manual, imported, name_match, port_order, ai_suggested

confidence
  pracovní míra jistoty 0–100

requires_manual_review
  příznak, že mapování musí člověk ověřit

blocking_reason
  důvod blokace, pokud status = blocked

is_current
  příznak, zda jde o aktuální (živé) rozhodnutí, nebo o historický záznam

superseded_at
  čas, kdy bylo mapování nahrazeno novým rozhodnutím

superseded_reason
  důvod nahrazení (například „špatný target port", „LAG redesign", „změna návrhu")

note
  volná poznámka

created_at
updated_at

Důležité constrainty:

PARTIAL UNIQUE INDEX (source_interface_id) WHERE is_current = true
PARTIAL UNIQUE INDEX (target_interface_id) WHERE is_current = true

Pracovní poznámky:

- V daný čas je za jedním source portem aktivní nejvýše jedno mapování.
- V daný čas je za jedním target portem aktivní nejvýše jedno mapování.
- Historická rozhodnutí (rejecty, superseded návrhy) se nemažou, jen se přepne is_current = false.
- M:N případy (LAG, split, merge) první verze úmyslně neřeší; jejich řešení je otevřená otázka (sekce 9).

6.5.8 Rozhodnutí k hlavní ose migrace

Potvrzená rozhodnutí:

1. V daný čas je vztah source interface → target interface nejvýše 1:1, ale model nese historii rozhodnutí.
2. Cílové VLANy budou evidované samostatně v plan.target_vlans.
3. VLAN ID bude možné používat pro technické porovnání současné a cílové VLAN.
4. Shodné VLAN ID ale nebude automaticky znamenat shodný význam.
5. M:N případy (LAG, split, merge) první verze neřeší – viz sekce 9.

Důsledky pro model:

migration.interface_mappings
  bude mít partial UNIQUE(source_interface_id) WHERE is_current = true
  bude mít partial UNIQUE(target_interface_id) WHERE is_current = true
  bude mít sloupce is_current, superseded_at, superseded_reason

plan.target_interface_configs
  bude odkazovat na plan.target_vlans
  nebude přímo odkazovat na core.vlans

6.6 Zjednodušení první implementace

Tato sekce nahrazuje detailní rozpad otevřených rozhodnutí jednodušším pravidlem pro první implementaci.

Cíl první verze není vytvořit dokonalý relační model. Cíl první verze je vytvořit model, kterému půjde rozumět, který půjde naplnit daty a nad kterým půjde vytvořit první užitečné pohledy.

Pracovní pravidlo:

Pokud existuje složitější, ale čistší varianta a jednodušší, srozumitelnější varianta, první verze zvolí jednodušší variantu.


6.6.1 Co v první verzi nebudeme komplikovat

Pro první implementaci se nebudeme snažit hned řešit všechny možné hraniční případy.

Zjednodušená rozhodnutí:

1. Portové mapování bude v daný čas nejvýše 1:1, ale s historií.
   Jeden aktivní záznam per source/target port přes is_current + partial unique index.
   M:N (LAG) odložené k diskuzi (sekce 9).

2. Cílové VLANy budou v plan.target_vlans.
   VLAN ID je číslo, podle kterého lze current a target VLAN porovnat.

3. Allowed VLANy na trunk portu budou zatím uložené jednoduše.
   V první verzi jako text nebo JSONB v plan.target_interface_configs.
   Samostatná vazební tabulka se doplní až tehdy, pokud to bude opravdu potřeba.

4. Statusy budou zatím obyčejný TEXT.
   Hodnoty budeme používat konzistentně, ale nebudeme je hned tvrdě vynucovat přes CHECK constraint.

5. core.assets zatím ponecháme jako jednoduchou a volitelnou evidenci významných zařízení.
   Ne jako plnohodnotnou CMDB.

6. migration.vlan_mappings zatím nezavádíme.
   Current VLAN a target VLAN budeme nejdřív porovnávat přes VLAN ID.

6.6.2 Proč takto

Důvodem je srozumitelnost.

První verze má odpovědět hlavně na otázky:

Jaké staré zařízení a port řeším?
Co je za tím portem vidět?
Na jaký nový port se to má převést?
Jaká má být cílová konfigurace nového portu?
Je tento port připravený k migraci?

Pokud model hned rozdělíme do příliš mnoha pomocných tabulek, bude sice databázově čistší, ale hůř pochopitelný a hůř použitelný při prvním nasazení.


6.6.3 Zjednodušený pohled na vrstvy

Pro první verzi stačí držet v hlavě tento obraz:

core.*
  co dnes existuje

obs.*
  co jsme v síti viděli

plan.*
  co chceme v nové síti

migration.*
  jak starý port mapujeme na nový port

report.*
  co chceme vidět jako člověk

Detailní vrstvy work.* a derived.* zůstávají v návrhu, ale první implementace je nemusí hned fyzicky plnit tabulkami. Mohou začít jako SQL views.


6.6.4 První jednoduchý datový tok

1. core.devices + core.interfaces
   evidují současné switche a porty.

2. obs.mac_port_observations + obs.arp_observations
   ukládají, co bylo zjištěno ze sítě.

3. plan.target_devices + plan.target_interfaces + plan.target_interface_configs
   popisují plán nové infrastruktury.

4. migration.interface_mappings
   propojuje starý port s novým portem.

5. report views
   ukazují člověku přehled: starý port, zjištěná realita, nový port, cílová konfigurace, stav migrace.

6.6.5 Co odložíme na později

Na později odkládáme:

- samostatnou tabulku pro allowed VLANy,
- tvrdé CHECK constrainty pro všechny statusy,
- explicitní VLAN mapping,
- složité scoring/confidence modely,
- složité derived tabulky,
- plnohodnotnou CMDB nad core.assets.

Tyto části nejsou zahozené. Jen nejsou nutné pro první pochopitelnou verzi.


6.6.6 Aktuální jednoduchý závěr

První implementace bude jednoduchá:

Eviduj staré switche a porty.
Importuj pozorování MAC/ARP/config/status.
Eviduj cílové switche a porty.
U portu drž jednoduchou cílovou konfiguraci.
Mapuj starý port na nový port.
Nad tím vytvoř první reportovací pohledy.

Teprve až se na reálných datech ukáže, že některá část nestačí, rozdělí se do čistší samostatné tabulky nebo vrstvy.


6.7 První SQL CREATE návrh

Tato sekce obsahuje první jednoduchý SQL návrh pro založení databázového modelu.

Cílem není pokrýt všechny budoucí varianty, ale vytvořit použitelný základ, který umožní:

- evidovat současné switche a porty,
- evidovat podpůrné objekty jako VLAN, subnety, segmenty, MAC a IP,
- ukládat pozorování MAC/ARP/config/status,
- evidovat cílová zařízení, porty a VLANy,
- držet jednoduchou cílovou konfiguraci portů,
- mapovat starý port na nový port,
- později nad tím vytvořit reportovací views.

Záměrná zjednodušení první verze:

- statusy jsou TEXT bez CHECK constraintů,
- allowed VLANy jsou v JSONB,
- device mapping se neukládá jako tabulka,
- VLAN mapping se zatím neukládá jako tabulka,
- derived tabulky se zatím nezavádí,
- složité confidence/scoring modely se zatím nezavádí.
-- ============================================================
-- SCHEMAS
-- ============================================================

CREATE SCHEMA IF NOT EXISTS core;
CREATE SCHEMA IF NOT EXISTS obs;
CREATE SCHEMA IF NOT EXISTS plan;
CREATE SCHEMA IF NOT EXISTS migration;
CREATE SCHEMA IF NOT EXISTS work;
CREATE SCHEMA IF NOT EXISTS report;


-- ============================================================
-- CORE: současná evidenční vrstva
-- ============================================================

CREATE TABLE core.devices (
    id              BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    label           TEXT NOT NULL UNIQUE,
    hostname        TEXT,
    mgmt_ip         INET,
    vendor          TEXT,
    model           TEXT,
    serial_number   TEXT,
    role            TEXT,                                   -- allowed (pracovně): access_switch, distribution_switch, core_switch, firewall, router, other
    site            TEXT,
    status          TEXT,                                   -- allowed: active, planned_retirement, retired, unknown
    source          TEXT,                                   -- allowed (pracovně): manual, import, snmp, netbox, discovered
    note            TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE core.vlans (
    id              BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    vlan_id         INTEGER NOT NULL UNIQUE,
    label           TEXT,
    purpose         TEXT,                                   -- allowed (pracovně): users, printers, cameras, management, voice, servers, guest, other
    status          TEXT,                                   -- allowed: active, deprecated, planned
    note            TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE core.segments (
    id              BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    label           TEXT NOT NULL UNIQUE,
    description     TEXT,
    note            TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE core.subnets (
    id              BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    prefix          CIDR NOT NULL UNIQUE,
    label           TEXT,
    segment_id      BIGINT REFERENCES core.segments(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    vlan_id         BIGINT REFERENCES core.vlans(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    note            TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE core.interfaces (
    id              BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    device_id       BIGINT NOT NULL REFERENCES core.devices(id)
                        ON UPDATE CASCADE
                        ON DELETE CASCADE,
    name            TEXT NOT NULL,
    label           TEXT,
    interface_type  TEXT,                                   -- allowed (pracovně): physical, vlan, lag, routed, loopback, sub_interface
    port_role       TEXT,                                   -- allowed (pracovně): access, trunk, uplink, downlink, unused, unknown
    admin_status    TEXT,                                   -- allowed: up, down, unknown
    oper_status     TEXT,                                   -- allowed: up, down, unknown
    native_vlan_id  BIGINT REFERENCES core.vlans(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    description     TEXT,
    source          TEXT,                                   -- allowed (pracovně): manual, import, snmp, discovered
    note            TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now(),

    CONSTRAINT core_interfaces_device_name_unique
        UNIQUE (device_id, name)
);

CREATE TABLE core.mac_addresses (
    id              BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    mac             MACADDR NOT NULL UNIQUE,
    label           TEXT,
    first_seen      TIMESTAMPTZ,
    last_seen       TIMESTAMPTZ,
    note            TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE core.ip_addresses (
    id              BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    address         INET NOT NULL UNIQUE,
    label           TEXT,
    subnet_id       BIGINT REFERENCES core.subnets(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    first_seen      TIMESTAMPTZ,
    last_seen       TIMESTAMPTZ,
    note            TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE core.assets (
    id                BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    label             TEXT NOT NULL,
    asset_type        TEXT,                                 -- allowed (pracovně): printer, camera, ap, server, vm_host, voip_phone, endpoint, unknown
    criticality       TEXT,                                 -- allowed: low, medium, high, critical
    owner             TEXT,
    migration_watch   BOOLEAN NOT NULL DEFAULT false,
    last_verified_at  TIMESTAMPTZ,                          -- kdy člověk naposledy potvrdil, že záznam odpovídá realitě
    verified_by       TEXT,                                 -- kdo potvrdil (jméno nebo identifikátor)
    note              TEXT,
    created_at        TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at        TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE core.asset_mac_addresses (
    asset_id        BIGINT NOT NULL REFERENCES core.assets(id)
                        ON UPDATE CASCADE
                        ON DELETE CASCADE,
    mac_id          BIGINT NOT NULL REFERENCES core.mac_addresses(id)
                        ON UPDATE CASCADE
                        ON DELETE CASCADE,
    note            TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),

    PRIMARY KEY (asset_id, mac_id)
);

CREATE TABLE core.asset_ip_addresses (
    asset_id        BIGINT NOT NULL REFERENCES core.assets(id)
                        ON UPDATE CASCADE
                        ON DELETE CASCADE,
    ip_address_id   BIGINT NOT NULL REFERENCES core.ip_addresses(id)
                        ON UPDATE CASCADE
                        ON DELETE CASCADE,
    note            TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),

    PRIMARY KEY (asset_id, ip_address_id)
);


-- ============================================================
-- OBS: observed / importní vrstva
-- ============================================================

CREATE TABLE obs.import_batches (
    id              BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    source          TEXT NOT NULL,                          -- volný popis zdroje, např. 'snmp-collector-v3', 'manual-csv-2026-05'
    source_type     TEXT,                                   -- allowed (pracovně): snmp_mac, snmp_arp, ssh_config, ssh_status, netbox, manual, csv
    device_id       BIGINT REFERENCES core.devices(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    script_name     TEXT,
    script_version  TEXT,
    started_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    finished_at     TIMESTAMPTZ,
    status          TEXT NOT NULL DEFAULT 'running',        -- allowed: running, success, partial, failed
    note            TEXT
);

CREATE TABLE obs.mac_port_observations (
    id              BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    batch_id        BIGINT REFERENCES obs.import_batches(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    observed_at     TIMESTAMPTZ NOT NULL DEFAULT now(),

    device_id       BIGINT REFERENCES core.devices(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    interface_id    BIGINT REFERENCES core.interfaces(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    mac_id          BIGINT NOT NULL REFERENCES core.mac_addresses(id)
                        ON UPDATE CASCADE
                        ON DELETE CASCADE,
    vlan_id         BIGINT REFERENCES core.vlans(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,

    source          TEXT,
    raw_device      TEXT,
    raw_interface   TEXT,
    raw_mac         TEXT,
    raw_vlan        TEXT,
    raw_payload     JSONB,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE obs.arp_observations (
    id              BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    batch_id        BIGINT REFERENCES obs.import_batches(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    observed_at     TIMESTAMPTZ NOT NULL DEFAULT now(),

    device_id       BIGINT REFERENCES core.devices(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    interface_id    BIGINT REFERENCES core.interfaces(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    ip_address_id   BIGINT NOT NULL REFERENCES core.ip_addresses(id)
                        ON UPDATE CASCADE
                        ON DELETE CASCADE,
    mac_id          BIGINT REFERENCES core.mac_addresses(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    vlan_id         BIGINT REFERENCES core.vlans(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,

    source          TEXT,
    raw_device      TEXT,
    raw_interface   TEXT,
    raw_ip          TEXT,
    raw_mac         TEXT,
    raw_vlan        TEXT,
    raw_payload     JSONB,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE obs.interface_status_observations (
    id              BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    batch_id        BIGINT REFERENCES obs.import_batches(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    observed_at     TIMESTAMPTZ NOT NULL DEFAULT now(),

    device_id       BIGINT REFERENCES core.devices(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    interface_id    BIGINT REFERENCES core.interfaces(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,

    admin_status    TEXT,
    oper_status     TEXT,
    speed           BIGINT,
    duplex          TEXT,
    source          TEXT,
    raw_payload     JSONB,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE obs.interface_config_observations (
    id              BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    batch_id        BIGINT REFERENCES obs.import_batches(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    observed_at     TIMESTAMPTZ NOT NULL DEFAULT now(),

    device_id       BIGINT REFERENCES core.devices(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    interface_id    BIGINT REFERENCES core.interfaces(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,

    mode            TEXT,
    access_vlan_id  BIGINT REFERENCES core.vlans(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    native_vlan_id  BIGINT REFERENCES core.vlans(id)
                        ON UPDATE CASCADE
                        ON DELETE SET NULL,
    allowed_vlans   JSONB,
    description     TEXT,
    config_text     TEXT,
    config_payload  JSONB,
    source          TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);


-- ============================================================
-- PLAN: cílový plán nové infrastruktury
-- ============================================================

CREATE TABLE plan.target_devices (
    id              BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    label           TEXT NOT NULL UNIQUE,
    hostname        TEXT,
    mgmt_ip         INET,
    vendor          TEXT,
    model           TEXT,
    serial_number   TEXT,
    planned_role    TEXT,                                   -- allowed (pracovně): access_switch, distribution_switch, core_switch, firewall, router, other
    site            TEXT,
    migration_phase TEXT,                                   -- volný text pro vlnu/fázi migrace, např. 'wave_1', 'pilot'
    status          TEXT,                                   -- allowed: proposed, approved, ordered, installed, configured, migrated
    note            TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE plan.target_vlans (
    id                BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    vlan_id           INTEGER NOT NULL UNIQUE,
    label             TEXT,
    target_segment_id BIGINT REFERENCES core.segments(id)
                          ON UPDATE CASCADE
                          ON DELETE SET NULL,
    purpose           TEXT,                                 -- allowed (pracovně): users, printers, cameras, management, voice, servers, guest, other
    status            TEXT,                                 -- allowed: proposed, approved, configured, migrated, deprecated
    note              TEXT,
    created_at        TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at        TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE plan.target_interfaces (
    id                  BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    target_device_id    BIGINT NOT NULL REFERENCES plan.target_devices(id)
                            ON UPDATE CASCADE
                            ON DELETE CASCADE,
    name                TEXT NOT NULL,
    label               TEXT,
    interface_type      TEXT,                               -- allowed (pracovně): physical, vlan, lag, routed, loopback, sub_interface
    planned_port_role   TEXT,                               -- allowed (pracovně): access, trunk, uplink, downlink, unused, unknown
    planned_description TEXT,
    migration_phase     TEXT,
    status              TEXT,                               -- allowed: proposed, approved, configured, ready, migrated, blocked
    note                TEXT,
    created_at          TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at          TIMESTAMPTZ NOT NULL DEFAULT now(),

    CONSTRAINT plan_target_interfaces_device_name_unique
        UNIQUE (target_device_id, name)
);

CREATE TABLE plan.target_interface_configs (
    id                      BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    target_interface_id      BIGINT NOT NULL REFERENCES plan.target_interfaces(id)
                                ON UPDATE CASCADE
                                ON DELETE CASCADE,
    mode                    TEXT,                           -- allowed: access, trunk, routed, disabled
    access_target_vlan_id    BIGINT REFERENCES plan.target_vlans(id)
                                ON UPDATE CASCADE
                                ON DELETE SET NULL,
    native_target_vlan_id    BIGINT REFERENCES plan.target_vlans(id)
                                ON UPDATE CASCADE
                                ON DELETE SET NULL,
    allowed_target_vlans     JSONB,                         -- JSON pole VLAN ID nebo plan.target_vlans.id (volně, viz sekce 6.6.5)
    planned_description      TEXT,
    config_payload           JSONB,
    config_text              TEXT,
    status                   TEXT,                          -- allowed: draft, reviewed, approved, generated, applied
    note                     TEXT,
    created_at               TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at               TIMESTAMPTZ NOT NULL DEFAULT now()
);


-- ============================================================
-- MIGRATION: mapování současného a cílového stavu
-- ============================================================

CREATE TABLE migration.interface_mappings (
    id                      BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    source_interface_id      BIGINT NOT NULL REFERENCES core.interfaces(id)
                                ON UPDATE CASCADE
                                ON DELETE CASCADE,
    target_interface_id      BIGINT NOT NULL REFERENCES plan.target_interfaces(id)
                                ON UPDATE CASCADE
                                ON DELETE CASCADE,
    status                  TEXT,                           -- allowed: proposed, verified, ready, migrated, blocked, rejected
    mapping_source          TEXT,                           -- allowed (pracovně): manual, imported, name_match, port_order, ai_suggested
    confidence              INTEGER,                        -- 0–100; pracovní jistota
    requires_manual_review  BOOLEAN NOT NULL DEFAULT false,
    blocking_reason         TEXT,
    is_current              BOOLEAN NOT NULL DEFAULT true,  -- false = historický záznam (viz superseded_*)
    superseded_at           TIMESTAMPTZ,                    -- kdy bylo mapování nahrazeno
    superseded_reason       TEXT,                           -- proč bylo nahrazeno (např. 'redesign', 'wrong target', 'LAG merge')
    note                    TEXT,
    created_at              TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at              TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- V jednu chvíli je za daný source/target port aktivní nejvýše jedno mapování.
-- Historická (is_current = false) mapování se nepočítají do unikátnosti.
CREATE UNIQUE INDEX migration_interface_mappings_source_active
    ON migration.interface_mappings(source_interface_id)
    WHERE is_current = true;

CREATE UNIQUE INDEX migration_interface_mappings_target_active
    ON migration.interface_mappings(target_interface_id)
    WHERE is_current = true;

CREATE TABLE migration.migration_notes (
    id                      BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    interface_mapping_id     BIGINT REFERENCES migration.interface_mappings(id)
                                ON UPDATE CASCADE
                                ON DELETE CASCADE,
    severity                TEXT,                           -- allowed: info, warning, error, critical
    note                    TEXT NOT NULL,
    created_at              TIMESTAMPTZ NOT NULL DEFAULT now(),
    created_by              TEXT
);


-- ============================================================
-- INDEXES
-- ============================================================

CREATE INDEX idx_core_interfaces_device_id
    ON core.interfaces(device_id);

CREATE INDEX idx_core_interfaces_native_vlan_id
    ON core.interfaces(native_vlan_id);

CREATE INDEX idx_core_subnets_segment_id
    ON core.subnets(segment_id);

CREATE INDEX idx_core_subnets_vlan_id
    ON core.subnets(vlan_id);

CREATE INDEX idx_core_ip_addresses_subnet_id
    ON core.ip_addresses(subnet_id);

CREATE INDEX idx_obs_import_batches_device_id
    ON obs.import_batches(device_id);

CREATE INDEX idx_obs_mac_port_observations_observed_at
    ON obs.mac_port_observations(observed_at DESC);

CREATE INDEX idx_obs_mac_port_observations_interface_id
    ON obs.mac_port_observations(interface_id);

CREATE INDEX idx_obs_mac_port_observations_mac_id
    ON obs.mac_port_observations(mac_id);

CREATE INDEX idx_obs_arp_observations_observed_at
    ON obs.arp_observations(observed_at DESC);

CREATE INDEX idx_obs_arp_observations_ip_address_id
    ON obs.arp_observations(ip_address_id);

CREATE INDEX idx_obs_arp_observations_mac_id
    ON obs.arp_observations(mac_id);

CREATE INDEX idx_obs_interface_status_observations_interface_id
    ON obs.interface_status_observations(interface_id);

CREATE INDEX idx_obs_interface_config_observations_interface_id
    ON obs.interface_config_observations(interface_id);

CREATE INDEX idx_plan_target_interfaces_device_id
    ON plan.target_interfaces(target_device_id);

CREATE INDEX idx_plan_target_interface_configs_interface_id
    ON plan.target_interface_configs(target_interface_id);

-- Indexy idx_migration_interface_mappings_source/target_interface_id
-- nejsou potřeba samostatně - jsou pokryté partial unique indexy
-- migration_interface_mappings_source_active a _target_active výše.

6.8 Kontrolní poznámky k prvnímu SQL návrhu

První SQL návrh je záměrně jednoduchý, ale obsahuje několik vědomě zavedených mechanismů, které dříve nebyly dořešené.

6.8.1 Co už řeší

- současná zařízení a porty,
- současné VLANy, subnety a segmenty,
- MAC a IP identity,
- významná sledovaná zařízení přes core.assets, včetně sledování čerstvosti ověření
  (last_verified_at, verified_by),
- pravidla zakládání identit (sekce 5.1.4): co skript smí auto-založit a co ne,
- observed MAC za portem,
- observed ARP vazby,
- observed interface status,
- observed interface config,
- cílová zařízení,
- cílové porty,
- cílové VLANy,
- cílová konfigurace portu,
- mapování starý port → nový port,
- historie rozhodnutí o mapování přes is_current + partial unique indexy,
- dokumentace povolených hodnot status sloupců přímo v SQL.

6.8.2 Co vědomě neřeší

- plnohodnotný lifecycle systém,
- kompletní CMDB,
- explicitní mapping starý switch → nový switch,
- explicitní mapping stará VLAN → nová VLAN,
- samostatnou relační tabulku allowed VLAN,
- M:N portová mapování (LAG, split, merge) – viz sekce 9,
- automatické generování finálních konfigurací,
- složité rozhodování o aktuálním stavu,
- historizaci ručních změn nad rámec mapování,
- fyzickou vrstvu (kabely, patch panely, pozice v rozvaděči) – viz sekce 9.2.

6.8.3 Co bude další krok

Dalším krokem po vytvoření tabulek budou první views:

work.current_mac_locations
work.current_ip_mac_bindings
work.current_port_segments
report.segments_behind_port
report.port_migration_overview
report.device_mapping_summary

6.9 Otevřené otázky k doplnění

Tento dokument zatím nedefinuje finální implementaci. Je potřeba doplnit zejména:

1. Konkrétní sloupce minimálních core tabulek.
2. Konkrétní sloupce minimálních observation tabulek.
3. Konkrétní sloupce plan tabulek.
4. Konkrétní sloupce migration tabulek.
5. Pravidla normalizace identit.
6. Časová okna pro aktuální stav.
7. Pravidla důvěryhodnosti zdrojů.
8. Role portů a jejich vliv na interpretaci.
9. Kdy použít SQL view a kdy derived tabulku.
10. Jak řešit ruční override.
11. Jak řešit konflikty.
12. Jaké reportovací pohledy jsou potřeba jako první.

7. Skripty první fáze

Tato kapitola popisuje skripty, které ve fázi 1 (sběr dat před výměnou hardware) plní obs.* a podpůrné core.* tabulky. Cílem kapitoly je definovat hranice, datový kontrakt a implementační konvence, ne konkrétní implementaci.

7.1 Hranice a princip

Skripty první fáze dělají jednu věc: sbírají data ze síťových zdrojů a ukládají je do databáze podle pravidel popsaných v dokumentu. Nic víc.

Co skripty nedělají:

- neinterpretují, co data znamenají (to je úloha views work.* a derived.*),
- nevytváří reporty (to je úloha report.*),
- nezakládají paralelní struktury mimo dokumentovaný model,
- nezpracovávají rozhodování o cílové infrastruktuře (plan.*, migration.*).

Jak konkrétně skript komunikuje se zařízením (SNMP verze, MIBy, autentizace, normalizace názvů, vendor-specific zvláštnosti) není součástí dokumentu. To je věc implementace a může se v čase měnit.

7.2 Datový kontrakt

Datový kontrakt (závazné). Každý skript, který ukládá data do databáze, musí dodržet pravidla v této sekci. Porušení ohrozí integritu dat. Vymáhá se reviewem schématu a observed dat.

K1. Žádné paralelní struktury. Skript zapisuje výhradně do tabulek popsaných v tomto dokumentu (kapitola 4 a 6.7). Skript nevytváří vlastní pomocné tabulky, vlastní schémata, pomocné soubory v databázi, ani sdílený stav mimo databázi.

K2. Pravidla zakládání identit. Skript respektuje pravidla v sekci 5.1.4. Co dokument nepovoluje auto-založit (typicky core.devices), skript nezakládá.

K3. Raw data u každého zápisu v obs.*. Každý řádek, který skript vytváří v obs.*, obsahuje raw hodnoty zdroje v polích raw_* (např. raw_interface, raw_mac, raw_payload). Důvod: dohledatelnost při ladění a možnost zpětně zrekonstruovat, co zdroj reálně řekl.

K4. Skript pracuje v kontextu batche. Každý import začíná vytvořením záznamu v obs.import_batches (status 'running'). Každý observed záznam má batch_id. Na konci skript batch uzavírá se statusem:

'success'  všechno proběhlo, data validní
'partial'  proběhla jen část (např. timeout u některých zařízení)
'failed'   skript selhal, observed data v tomto batchi nejsou důvěryhodná

Žádné observation bez batch_id. Žádný batch ve stavu 'running' po skončení skriptu.

K5. Spojování dat zachovává raw stopu. Pokud skript před zápisem spojuje data z více zdrojů (např. SNMP ifTable + ifAlias, nebo joinuje MAC tabulku s VLAN kontextem), výsledné spojené hodnoty zapíše do strukturovaných polí, ale i tak ukládá raw podklady do raw_*. Skript nedrží spojený výsledek jako jedinou pravdu.

K6. Skript selhává viditelně. Při neznámém zařízení, selhání ověření identity, nebo nečekané chybě skript končí s batch status 'failed' (nebo 'partial', pokud část dat je validní). Skript nesmí tiše ignorovat chyby ani maskovat selhání úspěchem.

7.3 Implementační konvence

Implementační konvence (doporučené). Sekce popisuje očekávané vlastnosti kódu skriptů, ne dat. Porušení neohrozí integritu DB, ale ohrozí dlouhodobou udržovatelnost. Vymáhá se reviewem kódu.

M1. Jeden krok workflow = jeden modul. Každý logický krok (otevření batche, dotaz na zařízení, normalizace dat, upsert do DB, uzavření batche) je samostatně volaná funkce nebo modul. Cíl: každý krok lze samostatně otestovat a verifikovat.

M2. Primitivní datové struktury. Skript pracuje s primitivními Python strukturami: list, dict, tuple, případně dataclass. Skript nepoužívá pandas DataFrame, numpy arrays, ORM (SQLAlchemy modely, Django ORM), ani jiné komplexní data containery. Cíl: čitelnost a snadná dohledatelnost toho, co skript dělá.

M3. Čitelný kód, žádné mini-frameworky. Kód je psán pro člověka, který ho bude číst za rok. Krátké funkce, popisná jména, žádné chytrácké zkratky. Skript nesmí budovat vlastní abstrakce nad SNMP knihovnami, plugin architektury, ani interní DSL. Pokud má skript dělat víc různých věcí, je to víc různých skriptů.

M4. Žádná sdílená knihovna mezi skripty (v první verzi). Skripty první fáze jsou samostatné. Pokud se ukáže, že se nějaká logika opravdu opakuje (typicky open/close batche), refaktor přijde až po dokončení první funkční verze. Předčasná abstrakce = předčasná komplexita.

7.4 Skripty první fáze

Tato sekce popisuje co skripty dělají z pohledu modelu. Jak konkrétně (SNMP MIBy, knihovny, vendor specifika) není součástí dokumentu.

discover_interfaces

Vstup:  identifikátor zařízení v core.devices.
Akce:   zjistí seznam interface na zařízení, normalizuje na kanonický tvar,
        upsert do core.interfaces. Aktualizuje doplňková pole
        (např. snmp_if_index, snmp_if_name, label).
Výstup: počet nových / aktualizovaných záznamů, seznam neklasifikovaných
        položek pro ruční revizi.

collect_mac_table

Vstup:  identifikátor zařízení v core.devices.
Akce:   stáhne MAC adresové tabulky ze zařízení, normalizuje MAC adresy,
        propojí s core.interfaces (přes pravidla 5.1.4), zapíše do
        obs.mac_port_observations. Auto-založí MAC adresy v core.mac_addresses,
        pokud zatím neexistují.
Výstup: počet zápisů, počet auto-založených MAC, případné konflikty.

collect_arp_table

Vstup:  identifikátor zařízení v core.devices (typicky firewall/router).
Akce:   stáhne ARP tabulku, normalizuje IP a MAC, propojí s core,
        zapíše do obs.arp_observations. Auto-založí IP adresy v
        core.ip_addresses, pokud zatím neexistují.
Výstup: počet zápisů, počet auto-založených IP, případné nesoulady.

verify_device

Vstup:  identifikátor zařízení v core.devices.
Akce:   připojí se na zařízení, ověří identitu proti evidenci (typicky
        porovnáním hostname, případně dalších atributů). Zapíše batch
        se statusem 'success' (identita souhlasí) nebo 'failed' (neshoda).
Výstup: výsledek ověření. Data sama nezapisuje (jen meta-záznam v batchi).

7.5 Co dělat, když skript selže

- batch zůstává v DB se statusem 'failed' nebo 'partial';
  to je samo o sobě informace (data quality issue),
- raw_* pole zachovávají to, co zdroj řekl, i když interpretace selhala,
- skript loguje, co se stalo, ale primárně reportuje selhání
  přes status batche, ne přes side-channel (e-mail, slack atp.),
- žádný retry uvnitř skriptu; pokud se má import opakovat,
  je to věc operátora nebo nadřazené orchestrace.

8. Worked example: jeden switch a jeho cesta modelem

Tato sekce ukazuje na konkrétním (zjednodušeném) příkladu, jak data tečou modelem od pozorování až do reportu. Cílem je otestovat, zda model unese realistický scénář, a sloužit jako návod pro implementaci skriptů.

8.1 Vstupní scénář

Současný switch:
  label:    huawei-sw01
  vendor:   Huawei
  model:    S5720-28X-LI
  role:     access_switch
  site:     HQ
  status:   active

Porty na switchi:
  Gi0/0/1   access port, uživatelská VLAN 10
  Gi0/0/2   access port, uživatelská VLAN 10
  Gi0/0/3   access port, VLAN 20 (tiskárny)
  Gi0/0/4   nepoužívaný
  Gi0/0/24  uplink na distribuční switch, trunk

Cílový switch po výměně:
  label:    cisco-sw01
  vendor:   Cisco
  model:    C9200L-24P
  role:     access_switch

8.2 Krok 1: založení core entit (ručně, jednorázově)

Operátor založí současné i cílové zařízení. Pro porty první verze předpokládá, že je založí buď ručně, nebo automaticky první import.

core.devices
  id=1  label='huawei-sw01'  vendor='Huawei'  status='active'  source='manual'

plan.target_devices
  id=1  label='cisco-sw01'   vendor='Cisco'   status='proposed'

core.vlans (založené buď ručně, nebo discoverem)
  id=1  vlan_id=10  label='users'      status='active'
  id=2  vlan_id=20  label='printers'   status='active'

plan.target_vlans
  id=1  vlan_id=10  label='users'      status='proposed'
  id=2  vlan_id=20  label='printers'   status='proposed'

8.3 Krok 2: import pozorování ze SNMP MAC tabulky

Skript se připojí ke switchi, stáhne MAC tabulku a uloží do obs.*.

obs.import_batches
  id=42  source='snmp-collector-v1'  source_type='snmp_mac'
         device_id=1  started_at='2026-05-23 09:00:00'
         status='running'

Při importu skript narazí na MAC, kterou ještě nezná: 'aa:bb:cc:11:22:33'.
Podle pravidel 5.1.4 ji auto-založí:

core.mac_addresses
  id=101  mac='aa:bb:cc:11:22:33'  first_seen='2026-05-23 09:00:00'

obs.mac_port_observations
  id=1001  batch_id=42  observed_at='2026-05-23 09:00:00'
           device_id=1  interface_id=??(Gi0/0/1)
           mac_id=101  vlan_id=1
           raw_mac='AA-BB-CC-11-22-33'  raw_vlan='10'

Pokud interface Gi0/0/1 ještě v core.interfaces neexistuje,
skript ji podle pravidel 5.1.4 také auto-založí:

core.interfaces
  id=11  device_id=1  name='Gi0/0/1'  source='discovered'

Skript ukončí dávku:
obs.import_batches.id=42  finished_at='2026-05-23 09:00:15'  status='success'

8.4 Krok 3: import ARP tabulky z firewallu

Druhý skript stáhne ARP tabulku z firewallu, který má L3 interface v VLAN 10.

obs.import_batches
  id=43  source='fw-arp-collector'  source_type='snmp_arp'
         device_id=99(firewall)

Skript najde IP, kterou nezná: '10.0.10.50' v subnetu 10.0.10.0/24.

core.ip_addresses
  id=201  address='10.0.10.50'  subnet_id=??(10.0.10.0/24)
          first_seen='2026-05-23 09:01:00'

obs.arp_observations
  id=2001  batch_id=43  observed_at='2026-05-23 09:01:00'
           device_id=99(firewall)
           ip_address_id=201  mac_id=101  vlan_id=1
           raw_ip='10.0.10.50'  raw_mac='AA-BB-CC-11-22-33'

8.5 Krok 4: identifikace assetu (ručně)

Operátor v UI nebo přes import doplní, že MAC aa:bb:cc:11:22:33 patří tiskárně.

core.assets
  id=301  label='Printer HQ-Sales-01'  asset_type='printer'
          criticality='medium'  migration_watch=true
          last_verified_at='2026-05-23 10:00:00'  verified_by='admin'

core.asset_mac_addresses
  asset_id=301  mac_id=101

core.asset_ip_addresses
  asset_id=301  ip_address_id=201

Poznámka: scénář ukazuje, že tiskárna byla v MAC tabulce viděna na portu Gi0/0/1, ale operátor ji eviduje – to znamená, že buď tiskárna byla přepojena na port 1 (v evidenci je u portu 3), nebo evidence neodpovídá realitě. Tohle je přesně typ konfliktu, který má model zachytit, ne maskovat.

8.6 Krok 5: založení target portů a konfigurace

plan.target_interfaces
  id=51  target_device_id=1  name='Gi1/0/1'   status='proposed'
  id=52  target_device_id=1  name='Gi1/0/2'   status='proposed'
  id=53  target_device_id=1  name='Gi1/0/3'   status='proposed'
  id=54  target_device_id=1  name='Gi1/0/4'   status='proposed'
  id=74  target_device_id=1  name='Gi1/0/24'  status='proposed'

plan.target_interface_configs
  id=1001  target_interface_id=51  mode='access'
           access_target_vlan_id=1  status='draft'
  id=1002  target_interface_id=52  mode='access'
           access_target_vlan_id=1  status='draft'
  id=1003  target_interface_id=53  mode='access'
           access_target_vlan_id=2  status='draft'
  ... atd.

8.7 Krok 6: portové mapování

migration.interface_mappings
  id=1  source_interface_id=11(Gi0/0/1)  target_interface_id=51(Gi1/0/1)
        status='proposed'  mapping_source='port_order'
        confidence=80  is_current=true

  id=2  source_interface_id=12(Gi0/0/2)  target_interface_id=52(Gi1/0/2)
        status='proposed'  mapping_source='port_order'
        confidence=80  is_current=true

  ... atd.

8.8 Krok 7: změna návrhu (demonstrace historie)

Operátor zjistí, že port Gi0/0/3 (tiskárny) chce v nové infrastruktuře přesunout na Gi1/0/10 místo Gi1/0/3 kvůli novému kabeláži.

-- původní mapování se zaarchivuje
UPDATE migration.interface_mappings
   SET is_current = false,
       superseded_at = now(),
       superseded_reason = 'kabeláž – přesun tiskáren na Gi1/0/10'
 WHERE id = 3;

-- nové mapování se založí
INSERT INTO migration.interface_mappings
  (source_interface_id, target_interface_id, status, mapping_source,
   confidence, is_current)
VALUES
  (13, 60, 'proposed', 'manual', 95, true);

Partial unique index migration_interface_mappings_source_active zajistí, že současně nemůže existovat dvě aktivní mapování pro Gi0/0/3. Historický záznam zůstane uložený.

8.9 Krok 8: výsledný reportovací pohled

report.segments_behind_port pro Gi0/0/3:

device:           huawei-sw01
interface:        Gi0/0/3
target_device:    cisco-sw01
target_interface: Gi1/0/10  (po změně z Gi1/0/3)
mac_count:        1
ips:              10.0.10.50
segments:         printers
assets:           Printer HQ-Sales-01 (verified 2026-05-23)
mapping_status:   proposed
flags:            none

8.10 Co tento příklad ukázal

- pozorování (obs.*) nezná „pravdu", jen časově označená tvrzení;
- pravidla 5.1.4 fungují: MAC, IP, interface se auto-založí, device ne;
- core.assets překlene mezi technickou stopou a lidským významem;
- historie mapování drží předchozí návrhy bez ztráty auditu;
- partial unique index brání kolizi aktivních mapování;
- konflikty (port 1 vs port 3) se ukázaly v datech, ne ve výjimce skriptu.

9. Otevřené otázky pro diskuzi

Tato sekce obsahuje koncepční otázky, kde rozhodnutí ovlivní strukturu modelu (přidání tabulek, změna constraintů, nový sloupec). Stojí za to o nich mluvit, než se začne psát kód, ale není to nutné mít vyřešené před první implementací.

Implementační otázky (jak konkrétně se zachází s názvy portů, jak se zařízení identifikuje, atd.) jsou v samostatné sekci 10.

9.1 LAG a M:N port mapování

První verze umí v daném čase pouze 1:1 mapování (přes partial unique index na is_current = true).

V reálu existují případy:

- 4 fyzické porty na starém switchi tvoří LAG → 1 fyzický uplink na novém,
- 2 staré switche se slučují do 1 stack/VSS na novém,
- 1 starý port se rozpadá do více virtuálních interfaců.

Možné varianty řešení:

Varianta A: zavést logical_interface
  Vrstva nad fyzickými porty: LAG je logický interface s vazbou na členské porty.
  Migrační mapování běží na úrovni logical_interface, ne fyzických portů.
  Plus: čistší abstrakce. Mínus: další tabulka, další vrstva.

Varianta B: zavést mapping_group
  Group_id v migration.interface_mappings, jedna group = jedno logické mapování,
  members přes více řádků.
  Plus: minimální změna schématu. Mínus: kardinalita constraintů se ztratí.

Varianta C: počkat
  Sledovat, jak často LAG case nastane v reálné migraci.
  Pokud zřídka, řešit ad-hoc přes migration_notes.
  Plus: žádná změna. Mínus: dluh.

Doporučení: počkat na první LAG case v reálné migraci, pak se rozhodnout mezi A a B. C zatím stačí.

9.2 Fyzická vrstva (kabely, patch panely, pozice v rozvaděči)

Model je logicky-only. V reálné migraci je často 80 % práce „najít správný kabel". Pokud se to neřeší v tomto modelu, lidé povedou paralelní Excel, který se rozejde s realitou.

Možnosti:

Varianta A: nic, řeší se mimo
  Akceptovat, že fyzická vrstva žije v patch panel dokumentaci.
  Plus: jednoduché. Mínus: dva zdroje pravdy.

Varianta B: minimální evidence cabling
  Přidat core.cables (cable_id, label, source_endpoint, target_endpoint, length, type).
  Endpoint je buď interface, nebo patch panel port.
  Plus: jeden zdroj pravdy. Mínus: nová oblast modelu.

Varianta C: free-text na interface
  Přidat core.interfaces.physical_location nebo cable_label.
  Plus: triviální. Mínus: nestrukturované, neauditovatelné.

Otázka pro tebe: jak moc je fyzická vrstva součástí této migrace? Pokud jde o site v jedné serverovně s krátkými kabely a operátoři vědí, kde co je, varianta A stačí. Pokud jde o multi-rack instalaci, varianta B se vyplatí.

9.3 Auto-zakládání VLANů: ano, ne, jak?

Pravidla 5.1.4 říkají, že VLANy se auto-zakládají. Otázka: opravdu?

Pro:

- VLAN ID je technicky jednoznačná identita,
- discovered VLAN se stejně musí někam zapsat,
- operátor potřebuje vidět, jaké VLANy v síti reálně jsou.

Proti:

- špatně nakonfigurovaná zařízení mohou „discoverovat" desítky náhodných VLAN ID,
- core.vlans by se mohly zaplnit šumem,
- VLAN má často business význam, který skript nezná.

Možná varianta: discovered VLANy ukládat v obs.*, ale do core.vlans se přesouvat až po ručním schválení. To je čistší, ale komplikuje to import.

Doporučení: pro první verzi auto-zakládat, ale dát do core.vlans indikátor source = 'discovered', aby šly snadno najít a ověřit. Pokud se ukáže šum, zpřísnit.

9.4 Migrační vlny a závislosti

Současný model má migration_phase TEXT na target devices a interfaces, ale nemá strukturu pro vlny migrace a jejich závislosti.

Reálný scénář:

- ve vlně 1 se migruje switch A (přístupový),
- vlna 2 závisí na hotové vlně 1 (B navazuje na A přes uplink),
- v každé vlně je window (sobota 22:00–04:00),
- VoIP a kamery mají vlastní okna.

Možná struktura:

migration.waves
  id, label, planned_start, planned_end, status

migration.wave_dependencies
  wave_id, depends_on_wave_id

migration.interface_mappings.wave_id
  vazba mapování na vlnu

Otázka: bude tahle konkrétní migrace probíhat ve vlnách, nebo „big bang"? Pokud big bang, sekce 9.4 se odkládá.

9.5 Aktivní vs ukončená pozorování

V obs.* se neukládá konec platnosti pozorování. Pokud MAC dnes není vidět na portu, neexistuje záznam „nevidíme ji od T". Aktuálnost se odvozuje z observed_at.

Tohle pro první verzi funguje. Ale při ladění bude pravděpodobně potřeba odpovědět:

- kdy byla MAC X naposledy vidět?
- kdy se z portu Y úplně ztratila?
- jak dlouho je port Z prázdný?

Možná pole last_not_seen_at nebo samostatná tabulka obs.absence_observations – ale to už je třetí úroveň interpretace. Doporučení: počkat, řešit přes work views.


10. Implementační otázky k vyřešení v kódu

Tato sekce je seznam otázek, na které dokument záměrně neodpovídá, ale které musí každá implementace skriptů nějak vyřešit. Není to seznam problémů – je to seznam míst, kde implementace má prostor pro vlastní rozhodnutí, a kde se nemá hledat odpověď v dokumentu.

Pozor. Toto je sekce pro implementátora, ne pro skript. Není to datový kontrakt. Žádná z odpovědí níže neovlivní strukturu databáze – pouze způsob, jakým k ní skripty přistupují.

10.1 Normalizace názvů portů

Stejný fyzický port může být v různých kontextech pojmenován různě:

SNMP ifName:        "GigabitEthernet1/0/1"
SNMP ifDescr:       "GigabitEthernet1/0/1"
CLI 'show int br':  "Gi1/0/1"
LLDP neighbor:      "Gi1/0/1"
Operátorský label:  "Uplink to FW1"

Pravidlo core.interfaces.name jako kanonická forma neříká, jaká forma to konkrétně je. Implementace musí:

  • zvolit konkrétní kanonickou formu (typicky dlouhá SNMP ifName),
  • normalizovat raw názvy ze všech zdrojů na tuto formu,
  • raw hodnoty ukládat do raw_interface v obs.*,
  • udržovat tuto konvenci napříč všemi skripty.

10.2 Identita zařízení – ověření po připojení

Skript při importu dostává device_id (přes label v core.devices). Po připojení k zařízení musí ověřit, že mluví se správnou identitou. Implementace musí rozhodnout:

  • co konkrétně porovnává (hostname přes sysName, sériové číslo přes entPhysicalSerialNum, kombinaci),
  • jak se chová při neshodě (typicky: batch status 'failed', žádné observed záznamy),
  • co dělá, když evidence nemá s čím srovnávat (nový device, prázdný hostname a serial_number).

10.3 Stabilita identity interface

core.interfaces.name je primární klíč (spolu s device_id), ale jeho stabilita závisí na konkrétním zařízení:

Cisco IOS / IOS-XE:    ifName stabilní, ifIndex stabilní s 'snmp-server ifindex persist'
Huawei VRP:            ifName stabilní, ifIndex obvykle stabilní
MikroTik RouterOS:     ifName lze v configu přejmenovat (admin to udělá)
Ubiquiti UniFi:        ifName obvykle stabilní, ale verze firmwaru mění chování

Implementace musí mít strategii, co se stane, když se pozorovaný ifName na zařízení změní (typicky přejmenováním admina). Možnosti:

  • detekovat změnu (stejný ifIndex, nový ifName) a logovat jako data quality issue,
  • zachovat původní core.interfaces.name a nový tvar uložit jen do snmp_if_name,
  • nový tvar přijmout jako kanonický (riskantní – ztratí se historická pozorování).

10.4 Vendor-specifické rozdíly v MAC tabulkách

SNMP poskytuje MAC tabulky přes různé MIBy s různou strukturou:

BRIDGE-MIB::dot1dTpFdbTable      starší, MAC + port, bez VLAN
Q-BRIDGE-MIB::dot1qTpFdbTable    novější, MAC + port + VLAN
Cisco IOS legacy:                BRIDGE-MIB per-VLAN přes community indexing
                                 (community@vlan-id)

Implementace collect_mac_table musí pro každý typ zařízení zvolit správnou cestu. Dokument neříká kterou – jen to, že výsledek musí být zapsán do obs.mac_port_observations s odpovídajícím vlan_id (nebo NULL, pokud zdroj VLAN neposkytuje).

10.5 Filtrace ifTable

SNMP ifTable vrací více než jen fyzické porty: VLAN interfaces, loopbacky, tunely, interní virtuální interfaces. Pro účely této evidence:

  • relevantní pro MAC/ARP analýzu jsou physical, lag (případně subinterface),
  • vlan a routed jsou relevantní u L3 zařízení (firewall, router) pro ARP kontext,
  • loopback, tunnel, other typicky balast.

Implementace discover_interfaces musí rozhodnout, jak mapuje SNMP ifType na core.interfaces.interface_type. Filtrace „co je balast" je pak věc reportovacích views, ne importu (model ukládá vše s odpovídající kategorií).

10.6 SNMP credentials a jejich uložení

Skripty potřebují credentials pro SNMP přístup (community string pro v2c, user/auth/priv pro v3). Implementace musí rozhodnout:

  • kde credentials žijí (env proměnné, samostatný config soubor, vault),
  • jak jsou předány skriptu (parametr, env, soubor),
  • co dělá skript, když credentials chybí (selhání s jasnou hláškou),
  • co dělá s credentials v logu (nikdy je nesmí logovat).

Dokument nepředpokládá, že credentials jsou v databázi. Mohou tam být v budoucí samostatné šifrované tabulce, ale to není první verze.

10.7 Konkrétní volba jazyka, knihoven a runtime

Dokument nepředepisuje:

  • jazyk implementace (Python je doporučen kvůli SNMP knihovnám a čitelnosti),
  • konkrétní SNMP knihovnu (pysnmp, easysnmp, shell wrappery kolem snmpwalk),
  • způsob připojení k DB (psycopg, psycopg2, něco jiného),
  • způsob spouštění (ručně, cron, systemd timer, něco jiného).

Pravidla v sekci 7.2 (datový kontrakt) a 7.3 (implementační konvence) jsou jediná omezení.