From 810a282a306ca4fefc024468b2afdb35806607f0 Mon Sep 17 00:00:00 2001
From: "th.l" <thl-cmk@outlook.com>
Date: Wed, 5 Feb 2025 19:06:11 +0100
Subject: [PATCH] - reworked CLI options - reworkwd neighbour to host matching
 - added support for filter by folder/labels/tags - added post support for
 RESTAPI

---
 README.md                          |   2 +-
 mkp/nvdct-0.9.8-20250205.mkp       | Bin 0 -> 51806 bytes
 source/bin/nvdct/conf/nvdct.toml   |  83 +++--
 source/bin/nvdct/lib/args.py       | 241 ++++++++-------
 source/bin/nvdct/lib/backends.py   | 478 +++++++++++++++++++----------
 source/bin/nvdct/lib/constants.py  | 186 ++++++-----
 source/bin/nvdct/lib/settings.py   | 382 ++++++++++++++---------
 source/bin/nvdct/lib/topologies.py | 322 +++++++++++++------
 source/bin/nvdct/lib/utils.py      | 165 +++-------
 source/bin/nvdct/nvdct.py          | 161 +++++++---
 source/packages/nvdct              |   2 +-
 11 files changed, 1226 insertions(+), 796 deletions(-)
 create mode 100644 mkp/nvdct-0.9.8-20250205.mkp
 mode change 100755 => 100644 source/bin/nvdct/conf/nvdct.toml

diff --git a/README.md b/README.md
index 09483c9..ac47e7c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[PACKAGE]: ../../raw/master/mkp/nvdct-0.9.7-20241230.mkp "nvdct-0.9.7-20241230.mkp"
+[PACKAGE]: ../../raw/master/mkp/nvdct-0.9.8-20250205.mkp "nvdct-0.9.8-20250205.mkp"
 # Network Visualization Data Creation Tool (NVDCT)
 
 This script creates the topology data file needed for the [Checkmk Exchange Network visualization](https://exchange.checkmk.com/p/network-visualization) plugin.\
diff --git a/mkp/nvdct-0.9.8-20250205.mkp b/mkp/nvdct-0.9.8-20250205.mkp
new file mode 100644
index 0000000000000000000000000000000000000000..823b375ef1b71252b7cb8541fee51db3b1a2e5e8
GIT binary patch
literal 51806
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%0A<
zjVt4DxdEfkt{cKx3j;z!2Wjybx=7J9h2n3axP(G6XZ1`=%aGAn@k=5<)2D_Zb`lmU
z92sPx8RLgkR}Z5^0W2WP!#LKz<#UtrIdPx<g918mOl2TqT+2uk;PI7%DaD&BM-y)#
z%@v>-2u#VD+MsqsElrBjfPD>uFS@Dl?M0$gXA!V96sZ%1Bq;-mq+Ep>-Z_;-lp;%n
zM#qTO6(GJI`s%|TmCeEX1nfT>;ZF!pq!FfyXe8N>-y4dlv?|4^z>Wl-UzJcEqIhU0
zj|Tf*z$S!Hdkc|82s%u;gn2h*7DbH8Pl`7`vz$?@#J!;9<{Mwme&NPh!mY9-o1j^j
zU}o-&)ZH;qF7iS9ki0k22AsQTu^d-o##8bQYq$w<?8nx0LbXC#jSW^W=wc0=tx~)r
zf=?zeztL+I;IWA!9hm3C$0iI;6tq|C#ehAWs09mBhzyBDLdy$^OXtW5A!D&*8xWb9
z{xn<`2nsqSpLBO=iyBRXvxb(m^pM`<;JT0$m0{PQ4j03=h)^-nPYSrJOMAv}dK)Bg
zZ-bSZqoG0$(3zq)s}UNH86Sq>YZ34^nd7iSCAB3npnOCQ0joIn!V2>cE?_ukw8y<O
zIv5=F3%!i{S;QTFv($x($;f4AWXACj>}m?aX{PCNI3s7F;kXDV)7}_W+>a-+ggEe>
zgi!7WDX8K{smB7mWPr!Wh@hci&^04~77WL!)#%llOy)TRuMYf+#myA(SqwDLaKFGg
zE_(ss35_7c$5-IRxyoU2s!-3_z#XxZhnL}TJ%a1SclY&@O1)ep;Qww;UY_j*Mi*fE
zU?38&G9DWEz(S1FGy**!a8d_@2<p~c0fXM6ejSiioD_lssxZCy_Nwf3ENGn*A6-U-
z;Aw}^iesl5uuwXHGKc{{*@0~UN;u-IVaOP?SZ!*I_b__yP5>^#VNU~n4LoJ>b3t?R
z1wTX&JIBYOONf19s#d8-&wfIFbFK{~2BgQ*PIuTMK`)5MxrmU?K$zvQsr?cIGv}0J
z{t<@yzS)c;3=M&KgdrZmavfHfCkmov@KAvjz<n@)&z67mXi#F!CSIQ(5sEQf<I*q&
z^nXGO{y(Na`bS0QP_*C4qBAxW|D?<e<~-Ia1yOO#gDSx}n0IEzwI8;TQzMX*;*S~B
z><muKV$;vCY4A}!C*JWw;|l#40x*`L+h>76X&c7NeCjbIEX-V;XD&Ey1xq^ynAEKi
zY=K6j;IZAjW9vFz1=`vU=Y|&Rd5%E_K!yROb_Q5}aB!20582_a$&p7#0$U<jwfyE#
ziNPtb2(cxl5Q8o$a4$x_ke*r?9(3q9TqYZNIYDyBM1=IjnFk*y0}M7sV9mmY2m0ti
z$A=VWo<IQ905k>|%K}0V7KV&dj3GCczOls9TWh36IA@dT(R8e!tBGeUBvg~q2I<iW
z@!ExE9svcYG-Wy|a5C}O2J?SJ>NKz+lRD0vCS!pKac|eu`!z$aBl!jrF_Ks#Ru<^t
z3w-0)l!2Rg1?o1Rduxr?=@3aYC)L#UiWH_zQHc_ix@2WWhB622JeeAuN-y}CZG_wd
z3jCJJFka+I2SBj7PRuz6a2;auHVbpTWt2L~@@A9gsJsFVcK;I%K#v8S^*368d2PT;
zU^XEEJlV_s(fXs<tW`Hb*i^j<%FHTbw^doGO3jbfn?!=O^5zs_ss+?C=vy6`Kf!gj
zfuC5yQ$bk5;|x+GWYNNS16dV`40{q$2gCBlJQdrb#hF3TP%Wbb4I&JA{XzCbf)d;q
zX$|4A-I<d+rir)FexWY0lvs2e)*EE$HCdmQYR|d$5m$}4*24i?v8fXuTfrUZ^Ocn@
zyL!TwtAYmcnpBx+w7mknUck_ZE7nwrsk6247m#J|A|a2JGA*Vr=Kz6dRm5hjqjUv$
zA%e6apKqT_ZA-pTV1!<4QJY*?M}SDHEEguN5vWfvbnOzO3jiN3B5FNSJFwLQT?0Q!
zt4#o5ij_dgfWAA2y)K5O(kP<JMx_o75vzn6wRF_`RUCAA2l5OK@4yLGrQ<PFh=#Qu
zEH~sen)Q7c8KH@{33!%?_>as)DY_|87!|D(jmgkNb4Ki^*beI-F1|yvOU!h}(S)o+
zOWv>y<4a2A9%M2qGSUKxfSm%C=|ytc+bP&5lrQ1ihoYPge<IxiJ!dEIq2DI<<%6Sa
zGiqHDcKczLqp=;@yw-6UZ9ogD>_@6BjR#3NoCOKFD{d}BGMJLQ>=-yxV1fXZb4hj2
z7qYM@C0}5qd9*>tTH|G|0Aa$!4BYNyfI~?J*q}L7aD)Wn#dmWsWkzLGm?0gTkY9|$
z5s+{MP7=NwvqMM{B#UANWhYb>iS-c6Zf=|g#(JiR#HVU?D(WmS7&p=GzVP!uc1R}~
zL1C(mNK7?eawig)k4rDA{|oX}EW^bF6$IDHlP?X7xSb$00Kn81|88$6$D7UKlvM>o
zQRhHvq~G+2RrF>e@K_R;vkFp!seUUc@PzjnZ^QKjglI?eD1(m+8u*PKZz@Bn)~WcR
zT74jRqM4@BAgeaK95bk3fEN*5sRUf9U|J=*FElJUO_`#?18ky!z8ok%3pwLr=2DuK
z4~j+j*U{0D92k@B6}kX(2gG6yf$2wKwP{%xUqJ0CP;5eJ%AUqG<gZv%W1)~nh{PJe
zZjwretmvZt6!Mq~5{ADP>Kp<&-b~v^XpTT14Jrtr<((*iq;p|!<U97%Azem$%|jYH
zZ}Jb?Qy$WAdXs-R@MxPFZehb}{Ef1d!Gwlp&<GWx2E#`7Ye3ekFzAAaVnqVYIoR#o
zbjiqwDb$Hjd9GtJmkK-s(Wvxbi$T8Add!wDfc|DlKz>RlOp-cD8K<UzC8?QUdX<I+
zQm<ku6sdF$3#^}(37ed$NkBG77U+OXCUlxoqfCMe0e}c0H-@CUBL<e-d-l*;g?$vM
zBSaoT!xe-M9SqhtRM1qPn!#l%sYb)mn8NUak&RiZY_z7hT{absEA}!KjA0;Su=&Bf
zm;!||g&(nS?d*~}g}5VhfNU>x1RnMDI$$+rPp6N=a6BzAfTQE!O{+`{ogVv5B5T`;
z>S6w)IqT*?uLdyh;hFARk|7!9;5ku^9AOB<G0@-Sr-Kw$h5|BbScFE11}LN|!W<$}
zG-3S|OFRJ4kpOT&kH78@KnCaHMVJoh2*>{HLx5U2VOqpA_;LV5JBD^fLI?8@6I8l*
z1Rm@+0*8{LD^t}<9kpN{_eC=3U@W4#25TZK+Y&HD*BRp?)EtR6)f1pn8L5&zYrP?W
zK*-R`0KR~2Y5^fAxiDQ}k~G>SLZUxFJZe6OUCpG3G?h%$XoHPQLZ}598cR72Yk~-+
znwLwfW_}(YIjbS*q_Xq#xTC|9z}UKr#6c{bM#?qab!28Vw*#34{JL%=(KgwcjCvUB
zQU)-aeh@7YbTPDV1#TLVm4znP5w2@=G)H&YkOrkUKzC(3s&xtKB(>gN7)$R;3N;oZ
z#cTxvf&1G7)`m!uJsHUX*f4q{IWP*l6U&7HaG6XeqSkN`Vb<a2&|$Bl#-^Gil|4qy
z0dE@To`;MB$WMTmgb3Hb6fa4eTy;K&NgjL}K|+J^)s6KojX}uB9GZ2V9*+`*@+)8z
zY};U)DkWNjNzFi-3!(D)x#UXa3rw%iv^H^4;_-}Vw3w4rqovGK=9J{)a}uu}V=A&5
zJKLf$1>tc+OwyF2@9s4!<P6DAhFT6jcuzwQ0S!+nC2k23QU<9me6nI@ReyygJVjQa
z{J>0DV*jUR@mRmoYF*+UHput*n{p0Uc^o~>5ETt4Mh(el04WuB#}9lHk~$wx{v~~i
z5yQF^cT($U=v8AKiXs0@UmBWI!VWbIC)AeEgkrF$BfVx%9dzu7Bcu!quz^+Ta-hvQ
z$CES-qck4tTFW>ffJKscnZVIs^rD!OI$b_z5vsX_AES_%gvo>ff)$I9Ym``ea*hsv
zMMb9>%Xw;LKMld(4)jiEc|V>gaPlI(`4fN#3yj`^S4gsKeTFJc1p^u($JY3if2vB1
z;wgR&!BrPinZdg2F$lru7!;XTh@WI(6MfaXcr8D)b6CW$X&TS$8I8X?rzjc>B89=B
zB599>8x&o&kGiC`$A1b-uIi^~&xs|0?&>=B7u^q#F99;sq0)i9qF4#uD&qx^E(X{y
zAiM=JXf%^pfdk9R49CUHE})nJ>UbuU_Z;SkoVJKGznXN8A(4VQw_LI#b4O;F&wPZ3
z3^?=+g04zuceYLJg`~?SLrh7Q#SM;?H~DJCkqCaq<Gy(O@!AmaE`MERU@T%<b8)vl
zrp%ZfIX#;GNOy9zf*hlb9Sei-=4uGfwB-}4H89ndnYCyzA|+`c%=bq4ovW_-?Q&Vk
z<+EHUGtKzuyz<YDq!Cv9r2|QB#=kg@CYr}KjO3b9N0A^_;z6Xa1{4lTJi<TJ6TgT-
ze|c9iq$$p|ixexEPwho`bS299@Acb{^$vwu$elyZz#`KUb^GT#=YPaGIyMHuf=T?l
z-0_Q9fG#ixJO7NZ%#4hPnNhGpK?6HhfDCOqg$AKO{>5fEB%2W0i&(5|JY=i_hE2vQ
z0`KSz7iqCflLn-_6o%6|n`cv10v?x<rf8r6lA}D5yXs#*C_n{sOt3Q_v%xTOUf|*a
zatID0iyf838YC>3N<-je6@W+0WEw5$6bayllN5RiN7bqHbZaOSL9bOLX}K`S<4VIe
zuo_S$fgdzOtrD>uc-6s<z(p0QH)^gz4Zvlf$r8+nos|(lMg|oRxXhrGO#Gm^;9x71
zh2=r-l2c8cEy+(Y$)m=0pi}{aF?OXQ7)?GNJ+%p|4$&Zs^-cys<%|~zHw<qEkYl=*
zemza&P_SjNM3JGl6VuwNb#SN^%2c4u!4OM&;|S}DhjWd~!D8+j3Fbn08(}OTm{y-y
z<`}GJzB<2z>SuN?Hh#J_)R=uUhjj!x1_MDOCm`-40fG&S@g@p1DeIdvVsJep28oy`
zCTnfJgD9-t90N+)5oy{U*iOUs6{^5y>1jmF0?zE<V=ANsbq1J);D!Xe9Rd6fZ^PIo
zf<0y70d7mFWkTgAIZ~cus4UPwpnX_WxR85-aH}cR8X`Ifte~N2%zZL464V-)KXJf4
zsy3aE`f*Mqkyh)7X{prY^j79LIy$m;2Bjk~m%KeQ5jLRo61C6Rx`DlZ<StLu#w%0N
zxZ!5>d2pDoPp~{JJo1<HIV>B#)%XRS&dNt-H;iDWU)DIo5dNYDqW80CpD{ejvvvh_
zYza5OMqDCOIfAVpNHo1^;3e-fA}$YMZ~zt?14U9~G-^@#Yz~A;Rck`8oAE7_$MG<Q
zVabzDBMZ#!%&9Jb%OzLe4Aqc<iQ(?x#lFuPHV4D9z}g%l+nf02U>s1PPVhE+-ULd8
z90yzSeDtRLq9U+co0=meQG1HR;>P#}Y?!Sixe1xlshJ5Xu__z>HONua0`ocEzp_tb
z_R%GKvLKyqI5ej;85BsEv_9Z*^`$*?Z8W|xPzgKmnd>W;T`#R9Zx+;_d=F{fdO}@^
zz4(mr!{#fsYWV^)A5z1$ht}36Y>oBeFuF3Mp8y!b5CT1LX@~3r=Jz7^`euBMqm_@W
zU&fx2(i&=J55raQ2YcOshJ`nv%8z(BC#KQ@b!$#>TSn*!c}4~Y2ygfy7QUSZF;PGi
z4iW?m`Jv2Gs#C!+o$@%EZ9cM`nM>vjk~ydGt)e9Bw#n&>oqf66@*^|-CkK$dGDbs&
z;*LGM*m{TX`r*LlZO-6MgB_XTaKpiv=a~{-2>-wZu*0vrj;5{s3?Q-tFmLLB{==2#
zh)+e-9GJ$yjnIKB3I_xs$+`}lk8!=(eg88$01%`J&<r+)YEYDeO_mQeBqpIscKiBk
zdX^?^81G}6uwZ5<(y&Z*q{%{v&NK=6D>}3!S4Qx<$Oyj@H$$ZJ1MZ6cAFGSW5hiLy
z+rQf}gyVmtj^=d7&sCG;3XVndf<7Xpq?!ME6b5|IQB^~RNNo541UwvrMI+y{)Ji_Q
zA~2|nBB8_AY&}%80YAq?U@Nel8VKWVW&sN$l%RzbdW<sSR+iQ<)VVB95$@N}dkoSH
zF<M8S@u#tX0MQs4C8XM%PGp&0%u(Y-_6w32O0^jd%W;3@UbH(uvNtHqIXOku$$D8;
z0_8411$3x(ZNbh8X?U1iK}cfIu8mSSyJ?$=R^t)9Od3t&<3VaHO$g)BWmT?=F2;kr
z3>D^~3IhW(LaiwQ5wzCSsv%@D<Lp^TJFv7x$R#Y3se1j&lT(H&O`C-(4j3a%46-6z
zCJ*-Ul_7uqWu5cAjGn6O^KzLD%g+O*vuH2i--({c&l3tt;Q#A~8OkVQk>?V52%C7b
zjMFb20@R<?uN(sQ5df<n90O8XN&20A>XFFtcqJVoCP})ZT^0z21&PbAupl9fBbe49
zKW+7adVhj_VBGow15~Gz!3-Yb*`blWG9g}>kw8L;#**j=sA>?e2(zU)K&n=o9tWNn
zu)tswm+gT!(~=}!osglBu#qujyErSHoIN>V)QQ|#>6}^VnQ^J=c-HL9v@~Ugx{u17
zF2=m2z-N~KBYSyZ1mnUFlxO8)mC274a2vvqd6{I$f~8QQjj$zf%1p{-#pC2s12O`_
zifW8y4cIsojRcd4VK@g;D$O{AlVRcd1v6<DA<5d_v^hX30?%NvA|bOoZbO2maRHN=
zpMrsAu%O}d0f$y6vH_*jfYMoj$a4e-MAK-%#%Byb#UZGO7{h|NBj`6%b~NZ~Rx&Pi
zr-e~f0_C01JV#_wxO%Z3_!Z#o9FwQ*jbBY*H#G=OMh?nJ;8p>>K;hoe!J_X6VH|^f
z+RK6?9AHu~Fu6iDahFc6itw<gNLjc8KUkfmihvOl!eHPKZHA+)W0X&@nY%Fi1qsWn
z;4}|`{lahI{V%jRAS@y>qQp%$G2HGY8t$KHq{#-lmwZtFL&N9(%Z5*3WW;E39dJ7w
zWs(vEX2otU;WJo1)H9)Gj5@JWug_4&W$J+=2kLF7Va}u@8wg53k*d;QE)s~64RDmo
zz(T-L5PQi?%Ed>@l-XOXnI7A0A#EKWLx!$U$ra2O=Te)HFJL}s<a(S2wc|P=koeKy
zJsRwmAJQhD&`D4B(MiN@vdA-ilnK@H?SV5>;Ak{nnGWuggnX=1Gc^g0s1gYjY6DEM
z3{eYJI>)q>bQ_jajR3kv5UVtR80z}l@ZuW$GB5^^cH)uKERvC|)bTSiHI4!!YCO6@
zrHMYpT)1LlM=nExBKIoPCLo6$1{<-OXX21_TM8VTDXjg7X(dV^fkmg|q6qX44GWi{
zb3Wf3lS{VyJo<=o8Go=FS^nrd|6muPDT54i-tz~_iEyyw2mSbMJZhR)LQ_=Edn=Xz
z^*Gm1I}x{8BzLP9hwdNW?{MwEK|(V>b|898fU+*RP~-qYx`ma>ItBX5Voi8OBjW!8
z>60-0@}~SWswFX21s|z2Tow@#i!74Hz<dLx;j!kML{Js3;38b*Wsa=}hw>C*Da?M3
zkD9L%c2?9ngA`T1V~Oo?Y@x&sU)ifMhbPF=%SS!LiI5#5NuMl6SRhTQOEG?tj~-F5
zwUsu2ju;a;qIBV2>dlw@h`AEpEARyq7#qHg#D>x>^s<3*BINpnA2L^B62wm=O}veq
zGJaB@hc^>AY5>P}azXh9je?oYE`fs1xak&dl14e)a*R15*$~IypBQMhj&C?c<LzZ@
zusZ!`hnjw>g$<)R%^@`|Jwu&^96x`uZTR{5pKTfY=%_U*=#@Rk2ogtcZ@&A_4k1#O
z2$p4(X@9b%wrG)1R56GUe6fo&xN8$TIeUUjO5lM6)Jj9djWlCrLscmzmN!ji8cB2k
zVPk*I5@&B{fbgGO>rf$4ryw+M_9li%_)~1bEkp*&^Z&(m;yS{AyS1HM@%Bas21|W?
z!lnOctMQ<uWW!f1O@S)ka_p&b;JtsWL)c}>R2KzP#rF%g$_Sij0vA(7M1_R-ga>w&
zf$lZht(aXV8qm{xGgz+B#CVTmZVtJY4a!z+Y~=8B9HwyQ^i^z{E5lI=31rfYe#5T|
zLuHZCVc|jWREKvC>?v|(h`6;}ylLnRR<x3F60UML%jE|tTr&388;_rh+dQFv*pzE}
zSU&4LFhwH5)avXf8oi+rM&F^}2Uk4N8$C=3O^7RChu6WFEO3J&tMKs*BL?-xt&pJ6
z5oQ1nO^UFt<1hy|yHYSXqw!kN#t@q}+-rl0w~Lgsr8gK+Zn%=IgWPo3OzI4D9ZGDm
zK<yM}{4e8&VdLT!VN1pr1md8`50nLepr)pn3aJuaAYrU#ZXJKFWMcOJf^u289y0e2
z&P6j#Qdw<CG2M&7WR!B13vEWhTIBFkQwV6)j8nmlV9|pAT`ot<q%gBwgnMLk;F}oe
ze~Ha$mHgG*Y?8d{f4lY?<BR{!>ihrg<SeOMAi{Q;EJpmoSzx?SmNkK!@yy>3%%b@Z
zHR(t8FEi^)i6TTl9U5%E4YSC&@i+Pkk$QSDn1b+3UMPU=)F8c6OBN!O;*czbHW8QW
zjh}_MZso}LHt@OxrHdIQla%bBmG;CDNXWslnAIWkbjM@4DTvHTSFzY;6ZYAh_NHKr
zD5@Icg9GeysKcU8r{4<#W2|2{B<l2EG#c2YDQcd<J|yaNQ=`EMRBXEc85flpDKxA+
z%ODVXq>>B;GIPn7b>P}eZ%^8BX&p?xAI3|hk0fCm$Kqp~Tv{`N0&px(paIn-krAiX
zE7FyE@Jy5QS`-~iGo1;L1{m6E0SgRth>7yiu@-O<WaR-vOAI+;Ln{7-Ep#SlWDyG2
zP=}v~hB0Cd;7ueU`Oxf>6vvxnVHAlYpwWvEIJAQaLU?2jC!pE}FK&@xh6XX(L@c^O
zh>u(mA(zRdipVfUWI&*AP^c^d^?VKZ0}CQ<h&is-kZMAC<v^-3zqL1?CHi+<hDwR5
z%CVjx#T4hE?}fNxz2ff(QBPWiy$3`DQ_$S?fE|t{Ndco>&@$lu$CgHchP?|4M%x1-
zilWxv-h2W4;!VW!=3(7|R~UWdiZu%LVou%Q3~@_wAd-795+PDPN)vPt8$lx8DZ|uI
z&>E(<q4KXFL7nA@*PowD%LLbENg2QdK?LKrdhs)o;*|Ct5(o4@M<-z`I*K+UL6srO
zP$sA|5e{@q&!$6Qv2$4S^69YLb<?Y|^&({|GWWgs@gNRnhACpu2b_5X0n3!UWsCwd
zxJxqv>!r~ri{g`!6~|tcrP2t^9Tq2v!~y^3=mJLOM~c)YB>Y&3&SsRDTw-2xaz@Bw
z|IkJmVI~;oTBB1ZsL+rUXoOT|$i$2!wT6VE&D292WL<=j2$@*SmLzbuq$CX9T1=mn
zB)dSB5@9%&iL3`X^oiVBpFvAX1d#k(V!_I96RJ&6wHE7>O#-5!rOg9H<4m)_B8_9z
z38c|#FEq9q#-J-5({dVke8tzgd`2CSRgF?f20%I`JK85aG%(cPiywlIDYUz4(psF(
z(thZp!Wi{jbmSOgt&h_t<WO49Gu7-C<aUF?GdM~Hc?r~kf!iY(m;~c+hW*hDVAB3b
zhbE6V)-*h32G&Asr#BMO8r|Y)TQ290>VRyOLh^>_Wfm_XoeJs-#<5LnF~TcYrRlY`
zpno)33GsSysya@LNcTFuQlr;7rsr7v(vJlBct|ARuak$X2m03y{zX2{?oO_57EZ1n
zZW0e?G~db9#nsh<FR}O+KA9j|DW7i<mx*i@M&L~4|3^QzO~jeH3~`)VBUWj$_~|+N
zWUa=9XUi8g7a<2ebk)z)CyG2l3eOhF305P*32>$D9~#B?S7}rk%2d7_H4wt`_*u>f
zpid@3489pp6~(8e#3=Q#$i9@S)uzNGX!Wu2+B5_xDm)nBQ!jq9UXPr%#bONDF<G0g
z*E(u5k`NfFQm+Cq&Jt%=k(0B?$q7mH1D;xuM0O75@!<1zl_oQduTF!$Xk^HD2Y#kT
zjhr?d_{vPZmWKuQO-cgxgcw*b@>7-J0Q5cRR}@OC!vT<fQfFv2Xfm=Qk#POg(66m6
zFSL`?H&W3_79J587V3qj;}0kV633Q~o+6|%ZW1SnhX4T&3y~_q!@?qIkXT7EHh8{G
zECTE!4^#l(gbJz5&nGIFKthg20LcxRT|CwxX!LqeqhJYmz9B)1h`>l0h_K>E14gz0
zyb}3hl|EjqO-m5#)Or<&^^%C-A}E3W3ctW$8NC=dK*(_yS*UoR8gie=0Atj%lo?|9
zW<imbBF0b2Vzi3r@!AU;Bm|QfC3``JR;vd}4+I9*gWf+}Wg+CnXdRJn0$xa9r~>o^
zHh4%^N6<+KoE=>pCF#yC0$zk~c%Yn--&YJxyWq4!`4cT5Jx9Qc3=EMe{KCRRd?FP-
z;r?=;@CeW%DgisYE+RX-G~{1Gq@7)Wmz`Y*@-M;>NuyF=Z=(R9BiINsf{UOcm<S^N
z$QR6nPGG;Y2j3{~dx4fyf$3}1a5}{!lSSJbndPLdi0pd-i;v4|ht4d#h-|gq7m>|$
z_C#SAepnDvLG<ZfdmpJHOb#OXD1rkcBEj2bWL9EMltE@>SV%C|QhKIukStWH2n~x=
z1cu0iWg#+z*QHb-pC|<12a=DF`G(6PA-G>yR45HFG)xif(?u4pK)E<7oK6X{6ALoE
z)x@(ZMytWAIt0%Z|KX1OaA)8niy^ba;95c-V9w=IoTS#LD&x=@giIjhxRs#IfbnJ1
zk*~-x0OdtgKCtEcO69+jHeu%vzA54p($O~xcxl1G%;M0rUsq^qDpo39Xhdj;ToEW&
zU<U60TyqVIHln?Op;4U`NCgx=a```7c&a8FM2<tmLaglQz))#ebi}{X^4`ERx1sRJ
zFnOS_BD7P93shyNBL`)bI6XrP?-jxNua4J=Ls9LzowKBqT9>IzRrgWCNQUU_3=5A6
zQAGF#gvuhjauu1V&B#o1#2N(dAjBXFE2JvQBGVMRI=OpF1dO@a9y!e;ZD-EU;J{8Y
z5SlEC(Z7Zqw4x!%N*xGxSUMn)fx?9YEO{Uu3QJ~I%~z>YF{%YtKRN}Fh87VR8X*fs
zN<?Z%2O?#mu)$%`%pD3QqWMvBIWr$7qWNKdbZJ6>Br%&24&A0mwP{K<a{*<FQDLFR
z$s6wkW*_3uq8LS!W2*{?ADC*?5FD2*QR!F;NL2@GHA&2I!o)dxHANz2x>^LpN&$Jx
zV}iw@jEO=aVxMmiwvRDEA^VGf#HLQd;JF}4FdQ<5NQ8(C%FI-(e$ueeNTlEpM4*we
zfNC{*5xDtNW?-8EvZqFZEjKCx*`*O#i;06i(-g%cM>K63vaeF`z--~h0g@>OA0(5>
z6(K&I5n)0ehRBA2Y-lMeRk|omnJvQPO*M`k?5yzhiC}3^s<Q~(JP=UQzz8`aM39^~
zvEpC`5F&&BC<X<<491b6$2$TK*;o7mJF_C7&x;7ia9K!LCqynrq@uu3Rs_^@5i(j}
z4ib0~LC78=>x^`Js828}9x&(<;jT+DJQt!x=;JoFHn|XO!puGh+`v%Z;3z5fe3BIt
zd!;ObuQwTZ!C_%?FlCIQ;ty#EY}iup!~i&)U_pfK1n(b25*8fX?}SlA7v%iv>`BEA
z^y5ZSC(>Z$Mqtyau=1$(!GXTq@aSV33<pTSY(1^aA?6-_v|@qMCV{!9GQb7PI?00B
z0aI03s#F7HWSAl%QW_Q&$qud8igfw}Z6>zPfb$tPmAfc>{8{Rfrq<x|I7g&R!s>+3
zmq#H7roeESZ)8|_7bYH3M0%zk;m8a`^dMGvWO^z5WRboBOax?li4s-BHCe}~Kp9IL
zdS$9rSlvk{Hzu53oraLpW~3?g_`Zr9mt~xbd1g9tCRR~aY7T{iNQAvyV%?P@K+s{v
zw#2pF#2MW;;*IYZ2}W5h)y#p?8gFbCvz4fisZeDa%Xt(mFv!_X%3vhIBP}K|af;D&
zu|H6bzYt)7#AHrlo?>qb=#U1Je#4B$Uv03!5++0_bc8G%thlVrB0DhU>M7Vi%&rPV
zMje@Q{^2rzc&uhL58*|HGASuTKmrezg-0r)Lc?V~z5zb%nQI4%l&Z?mD>5}1D&pJ*
zvE{*0{)jxP@C^=&lCol^r)DOpH9(uM%}k(=l)_WO90J_<RpBY2j~6>UZuP}>DlmBX
zVDuLAWNm}$!o<l+9XuM7gaE!E2ig2=u(8HHy>Q^Cx@5U3@D)ygaw-`Yb)sS>GH@-S
z$}ph2n=0C!yJ&ZlMZ0qs9c;9};HjRa!`KD{^}@m!5WlcsDf6HLb}bl59b1}!=N85c
zgCmTs;XeM1e7!P>MdD|=d0|uux_zNj^TsA|6mo)!VD2b=nI#3H@{A1i*TS5)s7%B>
z=)q*PG+Ad>7%Z8kaU>t|OqCTb1T%EO6wlze2!KTrV)Q!U)>lBK$_7mJ>PtfilyZZs
zu~oxAIIO)-FcT831r;jPCq%{$hc=7RSuRF8i(^-zY$>P&O$R|`W^6-71W<ZDkT=<w
z1@-b_#)2ilmOiy6$$`-2IT&W4LnOFybO_h>#_EZXP@r9879}6T=a7S3sm7&D!?lGG
zB8H<jqY}X`M(~K|$V{|gqwOcuibgvC5HZ~YBwEZaU@#99z|9#gB#FBzAWcB*5sX}9
zS0HlTS#sU6MJW>5`57k)G?k_5+N5A0>$B88JP^TPb`qYBo%MYpnIJF`TeQN%!x&r-
z6VZIa3W5rOCF9)Y7sgx}{lf5x*FK7Y4--x4w$L63QERX(0kcL>;^^V%1g-{MB`(eq
z?!g3Ch%R(EsB1O|Kg{VPag;c^LM$g|Ck%_G9A`&zry(&_%j|eABs@BAkRR7F_r(xI
zQVe0x#F8nBp!Qt|9TJ067!Y(!c9DRdPsf5s1}G_TV;W#su@Nc41TF$W$7E9-Q*#Z<
zB&tJk#)M`-5SSd^ky$8kK(bImPWZ4*AwF`WbVzUp<r6UG<<KC}vJJ}{-H!lsL1V%g
zR7fPpT}(tuHXVnloFV7Y4}HW#auy^yi0NY>IS*5nuo^=%E+1H-aOFo>BytLed0g64
zHW>UE8X1YKvkVM8oxG(0HAg5y202znux~G92mxVtrB<YBHA&Fi99dB8K|;DRLzzZ-
zhT}yxh9W>5ksNipy+9-o;;-+)4+p+LArNw`S9;)vPiL+YpHV&R62K9M7aGyEa3W*i
zLQ@KVP6A{cA-6h*8=r*9uIk}L#lVH8t_-*dkZ^?NTqJN(qT>loT`!oT1aX9BUOI3R
zV?h-fxr*SVgMkapzMwEhiHR&U=i0&?k`Pa5^fJR385BWi&J~9-G88<aITs(ss8R5Q
z=3R$y(_>%?&AB8oMvsCgH1q1j9D+Ei(Da3hF>)j<p_$h#=8;2Dg~l&mOp*%p6B@sg
zF-<`(G<{KHk_w6?<kqQkQ-IJyW0yMIxEQ?9=v9wVbci4{c>!dU27x3r=URx96dg}!
z&K(3NH9DTq=oJzt83IAbd9lQa3xS0uuA4ZqNhm^-mr$HkC`dw{#qZDW&+pIg&+pIg
z&+pIg&+pIg&+pIg&+pIg&+pIg&+pIg&+pIg&+pH_{pqcWbJQy{{?Rgkmk)PWSIpms
zg!b1Be#iWOxH?OKUl0#xkAKzw2U6t!r{kZjPteBe#OgF<k_!0)Txf`)DZRo|;+U>U
z`eozK>HkII;_T)Q#^1%&6&ZIAXA6mwyTsk;xBr)a<1<_y>R*Xh4_why3Jj2jBY)+{
zKS!%_$p3e=oYesNQ&BJLtoPNX0Uu%(X=+{ki%nq)3yX5Gfl{By>={p|rwof!)j81b
z`P7!Wt4ES~og#T%^X;vxx~4v97T$L;Kj=@5G_uEr%CT0Tt0-$p$4EQLd)yhC;a1aa
z^T>*crv{IBVAHc~r$I~Bx@=ji^A2l#?b6JaE^F87-p#l=HTKi4-A}fh*m^?u?S6jY
z>+L6UqE9`1(&)j$rrvyA|3+W7w|21!yb*n?=j?5}`uFT@k@@7<lsh9vuDa7fdcMcv
zO=Z3~&9xkSz5Ec-9m^iAUX*=NtJlPtPtsfY@2spV_wY&cK?kaI84})R^MESuyOP#j
zJ~Dl0xm%Ck54S!Wnq;+c-uG$_-F*{;77HuKDPMfv|M%@?w;ok3+MVL}x$Ko|-#sU_
zuCUj_YU;`QD`zgXn9;aE7ktoqhQls#%T<2QKU$?%@AY=ipuMpR^GiKDQsz(%ciW8j
z0Wk*$b)K(`ygzkS@r7%X*7SJ2WMOyS&dj~%7B!Mysj?%<J1uB`K~Tq@H4mQK*<g?r
z&vIg^mSrB<x4B(<Oyfg$Jou4&U)S$*XH-Ve0-u<_=JX4{t6co(Y4DBsYHOx#>~rF+
zoBPhMR!@SCTdZ$%CvlK2=FO-2yIaTnebm0p^y?=UD5umem{Qq>xBbL!!N5=2+U1lv
z(vctgZno(;wLulZ%I!TrZtl8wW<mbf7FBZ^PI<hvFreLEZE9bsJJ99Gr~74!-|ZPb
z_t>4VSKaoyHGg=xV<k~h<<uuf20aYYND~B_eG$C5KGCrjUt;Y#$#<>T)~8Q&FL%pp
z=}#AzyKkEw)=XNws_a;|S<)(%+J_y?^eDG#X?@3~n`?iy%YM7=*z!TM4iD|tYWb_j
zf2Q87>wl{*uT)>}kS)Glmp`kbFPnMoaaP+;?Zip%7Tz4XbX!1C<(0!PAI;lYeU_)g
z?bI^w?4oVotWS9C!moESX#Dx!)n=tfSG+gj^&E?$!XXKV5}i*hyV~J#^!dquTGl_X
zE~3L<tGdMY=@Ysp+v54V=vo(f8<&6ks9zSny+OB`n@cNwbk!FWE{xqZq}A|4aRE2(
zcf0%EdO)9NoqA12Agt`)yFCwl_%-FCzV0}Wrp^;jG%0KSWJ#7~g_dWm+pc&jEw&TO
zHm+I{5*BcO%O6dm23T5eKJ1&YFyHD^|Bp?Z4!Ab3eARI~ohG$vF?^Kh#>xqWTQf@^
z^Gi8dc}_v~9o0vdZE$4I?pM6PNACwTww(BO{BcjaYOmbVt+I!2{1ord_E`0E@4hWt
zeWz@XQxClDJ#Ci;$r}z)>>4LYkcqXtQSN7tJ@nl;`R=^u-RoIz%$+^5z1nutmE-AC
z)voQE9UbO5I+f>CX1U$UERRp|U)Nn3`^ZjlWq@5}=SkTXO`h|-o;HhfZO~EKqjrT&
z%TD!v@@`A0J?~zpy}P{BSsNT>A-!r1z4>Zb_h)(22Y(${>DWra%6E|-7mK!5$v!wX
zdxW}4qn-PFwq0>LlB2tNv_s~i5xoNret9(H@HuVJap@hkfA^@**R8Vm73F+>ezn|{
zMg!MAvGFU2URu9-spgFqOq}5n^0D>2lwm`c4RS4Td#hSKvFV*xJ?md=S;cqbfz=nv
zja;_m=*v=DCl4qZ(mc(lY_#lguz!}SYr7*|%N@;n($ONN&v2_35BUw2)EoY~^u&fC
zQ|ku?)Q&9E%}JxZw0q-@%ZHu>MYoy0G`vxZGqO$96z8KZ&2kuhu;zi{sRJqwcMPz4
z5#2HDdA&(H4|eZ-yxxXIsg@l&#XRa;{n)OGc`fJrciG-1;@E(jRqb!*ZE_9ndHTeg
z;CeE({WYtuo_AkO>0E5#d&Z@<`_0C64)uSv{_(SMb?qnZFP`4!OWBD-5B8p=&&f!?
z+$OSTK<T!-0{)nA)wNCETNQ(DoaI*xzCNRKV%uJs4ThB6YB#COwRg)u>;DLyuaw;1
zm-bLv{71jVk)^Zd-&1z+A6vomRW0YteV4WC-lom0ROobV!G)?ln)Grjv;OVQnGYum
zCp1cLe6>cC2Se+2Ty(&?Q)HpXy6XeRY-;42e=|fMQP8bpQKPQ+FJ0a<Ez7dVdcm;4
zMf;{)j9=YQR(!Ij&mFr#_j}fVJbvehc5T<@PKx`q{Uv|v5}(&=x|Pdrv@gOTI&<*1
zdu#iJ-D{FDxc2IVPg`3q-S_69uJx#oWjp(K%Xgo)>*B-L1s^_me3gv&EbMyh#QtEn
ziVOa_c<H+Oe*b|xo87Q@c~8)Frgy(Kjz<&uTj~Vei|?Y8KMV6KF274#WBA|&Uv}8b
zZYXX@YPHo3_PYAz@%PtlXYTITYS9j1^~YEK2D?<CEL6I(eUAaZ<@J9<{-ZpR`~^aw
z3Hi_6$=!n{|9ME9oPW!I|3mpt4&}emRbQ>(mj5a?L*>6?Gjb->I9GFjzYR;19+w;9
zlc*k7z1F2NQX3!3=ysAi0n!@6dA%;|9Y4djeW>e@JA3a0oS(OU@2IBr_63IwY__th
zpR7)~+S0O8@6-|2a|<LDk5+U!6D#glk-w+iw&<(pyz}~1ioO2%+0(zGi@INZ-`#qq
z)m=~7uo(s6i!2BBciI_Uu(F-7%GGysCJP3XKU=zfjntYK9d_^En%AYz?r}>i&3s>}
zT}DIsu^C+o4tf;S-6`$C@6mYVj;FpSqQeF~wK_3OvSZc3m}}emD4U-h<aTgs|DN}b
z7Bz7$z0tBxkISJ+H*Qv$Q}aViU&|e>l!0;nFB}HiJ}%e%fUvZt?8-8m%cnKoyK_&I
zS{43|%jyymm(sP=!d`3amN!^b{)P38dZ#7N_@~`oNUlWH7+*NCwc^~gB_~gfsabmB
z?LRH=l@=r}tvR6ouvJkGnkAiQWxK|&wq3ucM#Shdm8O)=D=ZeM_gn4RIk3vjmAe~#
zTpKiNR^w^O$w<i$%U-DzTsf%vZnrA!2Aw-qXZ*-z)e8sDNgCW_Zi7YQ7Y&+BpIpg)
z-?07V&iC#6Zh`OalSej|Z+@VzMe9lx4+q#++con2?vV#N+Sz4a-R#@3YO5@}swW0s
z%^lNl%a~&^J&p*bE*xdGMKsdo@MOWYBLRZ(`w#aTVtK-1+nJt?M~<!MIqZCe>Mw6?
zlkeNt&~wW>*M<w`?C!d$UYFO7=SQv|)8Vsw(MYN1w&l&nzWY1)GN|kyk)`JiD1LkR
zu)1N}{&yb6%VWP5y7{}UinZaTJZ#a~y3nV%Usczd1(j2$cf5O~y4r2@{4)<Mp4rXL
zS-Nq`ByIlmI}e-|C%X<jIn<|&_Dbiiz2aBT%CDNc%jM+BI?_2s_g}WX@n+rbMP~w9
z9$&HZsn6x6L;srnaoS^f?B+5f7Ci0m-)`Ew__mz~Z++8YRo3nqA4W)LKQE)Mv$#Q$
z)v8i^Hdsf=+dZD}{G@Z=QZ;InsZ{&o&Vj3D<yQDy7}fvs#j3|eMS%|1H3U{4cW!)d
zweX&7o~t6av}n9I;K<hC8zGJSGoBsWb$Nbv)8rYmU++B9e{3^p%~7ZG-exupbDNt~
zwr7vF<N9r^$$NF|!-%4^R<}>rAFx-leQ@Q-sB5MBBp>?t@yU)axlIqus@rt(Je#XA
zAJRKNPuu(LZehs7+*j5K0VgIeZR2+Ok1+@Sk~mdP-k#pa{@&oS&*xjt@2j6E+_rIj
z)YrWeQWI|UU;VsKbxqF)l5t5h)9STNdU3q&K(DkxRilNPPjS1CYMOOB(<r-=d}XBu
zqfURh+Cnaq4Oz2E_ULo;lu<)R%sro&IdAyFhtfVvD=c}tH85M%wXm}Cly!^jo*gTd
z{g~rfJM7-Nh@n|G^4`4cw6s?1T|-7q@3=VYf=zLVOx99RsnO1}*3GJp{1|pTYJ+2T
zboT*n&#zy;GU3*NorU_=Pkp5=j|+G96cq(U*VBIYZeHt5v+}F@-mZ1;=(fp2h7=DS
zH0;Gm?;Q)5Cw*4F8@g6|d&N0z%G<cr_HAdrExp|-V^G`GtA}sxvSUbWrdQy^$>Kdh
zlRK?=Z9DbKZoi_GLoUZ%-7DW1AGV>fbLDozeV=2z92LvEd>%cvyRWb9j;XojmtE_8
zJM7O|H$$B3&9l3-wAsUeX625bbaohd-sOaI;G-%N!u<+!X5}o93HLqiw{hhDX5W{D
z^?c>IYSfT^{{AnbI!=!5WHDoD^}x5!2G)MA8YkaBY~S2BkN=+DH$S&}(F4zG&Bg~_
z+1t;}rsLURqi;VQD<7e3bz<hF)%&yRd}(acp;EcflVv-MuGsBVhx&rb-oq7FoFAXu
zF<I1aaNFxS+E&L~6s^2<d70zub1(f0M|>TUl`4AE`dsDg$DPZ>wrwM;ZOP9Jw?FrC
zUHb7Z@pW1+?ziPc*3*^qmZ*1VTfNQ9uUsY|=fn1@4=SF0#+RS6?=BnM(fLyQF6tBS
z8rz>!9@aIDd>?nFrLyskBVMI%cl~<$^o;1Pe;g3RY*^hV<nvU&z`)+TN*Uq#+j~}<
z``2~ZRq5u)Mb9$DPJuSAsn=JJKfJe}#l<bX8yBs}8~<tB=gYl@RBHIAs%~plZk@Z=
z&OD!cyxr5%(oB~=s*07K_j}O<`FGQ)$Uk+u_4by#0)jH1Cx(xHC7Sd2+jQRgS088g
z{@ke2<lyh_1IxX;P~Ea(pC#en8a?bY`+GpeDm~^-54l);%<jx0XWjn6Ek`YLk;fdH
zu&`Z=9&_d`UOdL9bm*O1BNsY<xzl9W6!Gf--P&1aid(&#a<cBmi26O=YtpJI<KC!k
zdpXW5w{BGLyLT7VnBU6!qI$yTHakXom2P`_NzC6HrZ=tH{L|Zz6kDnP6G`Ww+Ib$y
zkNi45ZC2w_#Ly=zE(kJa_;ejVZ2SDOBi{C%oLNqDzTAt}%fm*USBw_tt*hx;lxt!2
z{B!L2y2<MfHpqEhsruz!WgoAPKVSZ`PmcvfqqXrDu9i*iy~V%#`%%5VG`ZU(<=VN^
z(u;LRt~vBqe0Ws1*_9%9l+TS{G^=-oGAU=uAG{a8<5+_s_qTKs<eiu~rB1TG{(bKq
z7YAiootisZ+|qygo`x-Dr$h~=?p&IGb(mo8_h-$@wW;j-AYknJlvTz3G~;SpEUH*P
zAm`y^>wpE`L3P^9kAItbxNP;xUmK2;wbbS%rW}9ua#6ssWzEjkTAkf#S<>A34Fvsn
zT@HA(_nYh2ywSs(T7-O_-sR11Uihp7dv*R1S61B4{k+mU#9?Xo-Nh%0U9#TTm#cL2
z$sOsPZ~kLzPfE$LaTrk)5Pbeo-Thx}%ayx4-9x!!+oZFH_KcE_<XKN~S8bj2{#an2
z(G`|&xbA{X!$F__bRD&~+xtqF-D{~kemId6@U>C(o|;o9hkb3gplZ~hx2G0&P1s#&
z&87#@e!Jd3o%dpUZmEO!EIL%(^uFqcqfgeKS+j9j%-aFAY&8W<XO-$SRsM8Teft?n
z`tk{l57l@#u)<#bt<;x2m1lEzMqazvZpqS~bDl0<TGgh7V&09L<wG8J8Td8Q?YvH0
z_jTEOHN@Q)7Y%IGZg#_^w>E1nD^Gv*deQ~C=kss;CcBXZ;@y`6tD6@}G;v*8cj`Dj
z+bLkm4gYP@l?P^b&{Y{bv8dX%r_l#%s~heKus(e<;Lfa}t7k3$<Mi_5Yo<2z>~yl6
z#o=E2PZvjAZ;+GoWXC<Paq|W=JYcnX--WJU|9E$`vd`CR?Vj$9>$=tbrF6DmLRPrC
zR(jIOtnSa-xQ1;Rc2=@>)7&Nf*Ql-E#h<WQcD`JQ@TrT<n2%jHb*PvnX;^8=oIS}6
zb>$A7@gI1?Ye-Rlk*M?I^YNnYLzc%>KIZ50mu;`!%a>*9UdCOR?Rl~4)OvL+yWP08
zPWI|mz~}}tWT5AltF*gCk;UgDjbA1w_3*V_v#8OfR~615c02efWL13h<vF5}i$-p~
zcxGhnsD)p37IPYG>@<39nL4$H)G8hoeY*5!z0cjdFF(2$J$1czEkD55_Uz!bc|E$E
z?b9eCciW7`N2G%Bdxj|5C)jjYQOYl2`NR&&r&lan{rzZTwO7O6mhLR7Rr~GIV@F$C
zJ>0uFveUxk$NlT?IeEnSe1&TtUQJ8y8{Mo%Y0JHtj|(c)S#H<crubrUw+8)kl*uD6
z&8cg}U-{W7&D&{(plhd7S$n_leswo$`;iGgRhRa+xV8Cl!RmJRpM6aJ%d6_jm5W1?
zi(i!Ye^74soZk0_ca77?3nz;`_NP=2OZ08sqFkwAkGEG0J~U~2ldgMj?LWOB>hi>W
z`mt`H<ifHmk0n-YY=7idadw)`#no>M4np#kORZhg@V?c$Vf$u`xV-mDYWc&PL2^IK
zwX$jQTV1}m%_{wx?+++nE0gdxt5lVd<x{6$Pi%GE=g!e~7Sfe-pC-*+Q|idAO}nSO
zy}d>?e1^#S-r06bmZvtG`sI&RuKKh&mtSvOp0IlQ$G;rPwA1AB`PQ|Y-^g?Qdw;20
zqQCDg4Q|omA^$$0Up)J)bmx1?+Od;X?HZll7ZECBtd^AZTU5u29~Jd>XLiMR%FjL4
z*Bq<cvvm3VfuSx{+I8z)UQ!`okym)qeW!VOU7{Af**fq+Bxvo0(n+O0`PNt5vTn8e
z%`4UL(4w|kuwBnjpRXPwPx9ZmdTX=QO{ce<dTXNuA+(`f{>wR`gBnk`(6rGBS!gp)
zO~paJ`-aAUYd@~@7l-XOTlV#z?YXjInJO2)mQT<8Tsd&~z9-pfx1P@&e{OZJD{utG
zH=50^Fz0x+dwWx=_ts~0_22j1vP$gAZw;gO#{E9X|98&+2A4<w!uj9b!;NwNcmI9<
z|G$3zuat`2|IEl8(=BS|fwuRr^gWh4Zfe5rJC+rEs_mJw_eHh-t6G;?(|qLSEh`tO
z9*#TqXoTnf<_$ZAtW_=Wxe^@SyqQbYwX>5)jI!pfsn=+PjdZ8ypwu!Gx9)G6kT`B?
z-?q=TC4GMo?Cg@SeZTGMhNQTOg-attKFxUjdFHowo8HfO-D*c}*@!=@j&?1rEj_8!
z<kGb*M(qrq-RNXNMTx~1%k1jeH9xF4IwWhv+nM)j=s&y0HJ?%M=E2|_jSHstZ&7g1
zIpl7^q5^?D^}@49ea=j8<dqa&)^+#TWd&|K%NO*h*)6qPqm#R1Peku3wi|CVyXxp}
zWv*2CV#^oox_f44TGx#!{nj^(ifWf1JKx7@dzs}6zZ_Xt%{{QNTZ`=<a^`jYV`(#h
zQtnFSt?RqCX^=7HOY)a4ZhuD0+SxYv=<q@{`%p-$wZ*m8&asT|W1X1q9Z;b?U*E9q
zNSk>b*Y&p?v~u#96=5kiR~Ah^v#QQ4-s1CDTLjPfUj9YhS3zUO-MM$%c0&)#O&?!7
zO`9g)B^Qc6jBc>6_ni}Ey7z0?@4K>g!^o;nSGIbtzc$--Rs{stHbnW(;(NC>8c9In
zskqZE9QP-utvFcZ?kat7ce2&foU(tO-r(hw{c6#x`igB!Y!<~o%~2h+@_$yYjpE95
z{@P@xLlav#)sKz6YB%<!r`wLcqfb81s$DdtgYA%c?~jPC+Vx8oWP={km#RPFoKyY!
z!>?z%Ahp|;t{&NUcH#NL)+1{DbxAL6GP+Vh6~(w1_q+w+{c2h#53+4+(a56blSc{S
zjRhY<mdV|UqgwBgG@0hC?w417efe(*UHoVK+1{qYv~I!;`nEB{cZ?|GdVWRW&?;LB
zx@2XZJ~Day);<dc9@^FFR$bZof~h+`)D6jXaD15hN3T4q9#eYE_U-%n+P94SsNxn?
zZduo;u6^iS&Etyal3SL8zI^P}EAQ@>I-8Pnlx25RcCBzS*m1*(Y6=yo)v|-nB@TzS
zW>z?q@@-+a*EMgB=ZD3{94N}E{c`Zg{n1rQefjp~`ohh}yPfHh<$t7_e1$?>aH3S3
zWf}9<Pu%;~WA=b^!)kl(DVq85^Tkom?Of)ib=$=IaN^5>$m`+@e=V?BAX$5>(+#g>
z&Uasr-T2Nc^5C~MS9Ox(+V=;RR0vr4bc$cf!0T(eyDoU0a6Pcx+)V?VoAB>UUg!Sl
z;gskDSI)ouR%#l5-m<*HK{qajY)F4L&^xL5hy8h(3oai$*E=uro#vDH@6f;HlM3$m
z7ur1x65IBk^)>u%@6WeW^B-Al9{WAx#Mi!qwzSP_<JSLWZQo`|8P9EpI_#T%@UQL-
z5^gT9cdhrwinZ3=P3m)fP)duxT}R!#JNfk1&mJEu@BgEt#oj(U*B7i5-;9{Eq<7be
zOXS1O+D)#J_WeqV?V^1Hx5`iKa1WPjpWhs0`_k{@?bS6D`FE;_w>B#K$~H2%b9}|_
zeck$8UD9x&yYh+sInBz4d0XO#F0#MhD0$0(u-Lr7Lv=kC)%Lp)64hC}EGF+kLE4E1
zcP9?)b>pbidBkbg&~AO~CJnY2lCveI-^wzsd&c&;v8|J;c@_TGcQrL%qX(yjHMSe;
z<lr_!)NN2puT3Eh=6;MX7rn8Xe3x^N{!v}teHknh{2@+W{<hcaSCh_sU)kk!@XqMP
z;m#?qmgNuLP}ahwPE+?@U35dbzE)h=-1YRvnbOqfRi}>rWAef)XFAX8b!5Tqc^=O<
zZJhYFZR>;b)GuFOEodv-6!+3KsMV>FlTNMMP<hqHkUD<lU;KHd&8BVTul0C4hd1Qp
z-$`3s?^eoeGU~me&Ac10;{|Jr)0{;vr&1m}ye+NK-@d-0VC0H6t_#Eb%a(Es$?;E#
zzv&h3G4bx5GPCD5{rgH>l4ffC>!ZP<<n?Ok)v^!PE=qhe>hy-T-CuMG+SYjO!LhlU
z&%Ih!5biQ6Y<Xsb<$7J^*5#8|HJ2<5>#f}1s+xM>%%T}L4s9P_R;BCe>mHpvt6Tiq
z*s)>ltb2G{hb(_G|Fp2)Q{K_$C$`UfQ(5(4ckdH>Kdjjt=p65TbkeM(k1>V&7q`er
zh@ZBv{$IOB6%;pLB7a+Y!Muus4uuCZXT*dZ*z`7GWP`C&_6^)}{>+B5nr2lWH*EXo
z{`1Fqm#3A^d$80!!!CSu<V~AT72A6@s;b$Zv%h%$R$bQ<Q~$1a=ghKu^7?P)ba+xm
zwz{t0lKByZ$HmHh6}FVNs2k?#e%q~$aEhB~XDi3lTO+KeFRl>acY4A5Q(fl<6xJNP
zcHGQ<i)vP_c)4camg<tfre1n*|LgOE;<ob-isw%6neTex<H^vpJeNM-dMv5&t;yd-
zrKkPr^>lo^WzB@L^H+K1p9!6j-gBW%slJC!zkZSwwtzoTS8HE|iWlcs<IP=oLLt5S
z(sO(HPrGA#-N;<#jI5;(YeqGl7c+GKa;r=4y1e<ky>IWEhp&{|aOQraAlp+_rOof2
zp0MFg#ryNZf|^-1TJ3%4%H#b-F_zzZL|4f>7akLKYI&)=JN~h)myLMVdVlx^t+@Ei
zx+=xjPw{$}YTh?By2j<#BU(gsS2iEEAnK6nNauQ=N8C9P@G`>V+ttxK^LKV!Rw^yo
zD(&RUZ5OIntR7c)@XhvHPu}z$^vA`7Z#=&-YfgkT?p9&()pHY@753{kEc~qZ>k0LB
zi+W8{I2X@q(|p#Ep$(>Wzc%>j<tG&eT@*e4JZ&C-xBcBGyHX=mUq8Q}++gt2GuD;=
zP6}!9r`>V2aQTJv4cZ9<9*^j}zs<EI{~j+qM~S~K45)PTQ^D2s1>uW_TKs)|+qdC`
zV_K{k-tYa0NoQ<&hOf#9bg5l@q1M^yXR6(qIr8|RyZ4S+t?nsnvHDM&sp|_%r+%J!
z=wxwx?&>!;lx0>v>nBglnpAak>VktW@9jKyVSMN91IC2an58JZwezK`Wnqu=>t^45
zc*$N+-Ai!G>#bYgQhgtG-Pm#A*H`zCW`F7UnK$6+n%-y1U%f06|2g+Vr`5O4-=DSX
z!u2qRd36GX<67?9d;7-g#39Lr8qqA9wm$PScuUm5DOR`MPZW1Q*54yJXQ_1D_6gS=
z4`g*I)w6fzw%~6$Zb5^TC%=98<I5>!#j+0@NY9L@e(1}#7GtC4XN*`k>$-Qso4lFQ
zPE|u%42c*qcAd>cU2&0U$oD%HF7Mb-X{uxK$=YJ!u?@=_e4be5)rf@$4(q3Fs@!&1
zTd%(1N9yM63U3*Z_&ToHnvWf)oqD}CWs_~1&8~R=sjr_NOI}@V*B^)6pKi}>SiZ`B
z4^ef|mGuqnr(IgIVBFEGFZum%9;)E}=Yo4TEEBbl_qFwuFMVV=|BGf*-{zI<zUMCC
zb+y^G_4)O%+bJuHr_?<4=9cx*Yv%`QFZy&28`fx3&cJ2suXk=<O)l&^EM!ok!){6P
z-yiGf8hy0v>J>Zu$lT;ZW$w5BQYEcbvF*$<9`ijHKWcw<;)gdjLEp9g4_sMSw|iEF
zfzivQfz#ba2d$G1czB{s$eES*gMw>Suivxrgc$eKqP-iUPYiEu-R<teQKwz!22H9K
zd)BAV!n9eNMb6`!WIwU0RP^rD$qn^Ik23zKax1E%Vn;W9zXfwkH!Ldi#NzND2Ohkw
z)U?*t!Ecu94!Um`laji-;*ik785Pd78!0$htylZ6L#@7UDxBu=E#OA>rOJ!_$G%Fu
zbmr^hj7oprx;wACvd7HmcN4DvHLltkFPnP4^G^?3p1*rci$Nb2KDqiqC=Q-D-8OdE
zt+rM3>Udta*4d2rkyaljJ-uaclY%Or<&Q?rn)>>1m4d3H9hx<+CceH>Z8Nt*z(V_J
z4I>-1%)2bgcs)e0)%k(CL;qUOE)N~JevaLc3bsyrSNjYTEN#-rs@GqWZ~4^hZqcOX
zdyC`mCvIvG=Q$!<XHj+8xO9v5t<^&YywBF0PZ?<8=jmKOyVBydD<&MWiR*Rn-i$)o
zq>)wpUw&RW;eGzoN+&{c)6#3$HrUjE_^FgV1ybJF;Qrm$*H#BN9X?^1D(t=QIZ5|(
z7iQJDdC)_0te@>npQdNV2yG{HYgzy9`H-3CoS$8F-d}3T=?%*~;<dXZ4&%=(8(pPU
zg>pN(Mb$p%`p4{N4^BtgTq^Idq`0V;)#}=3Cn8fOJ?d)YnGg2&cIb<HO#41GWZdTa
zRxiu7$=UYh{E($PKB>dQgOGx&gm*7Jq2>Wy38MDf%)wOz6+URESA5xH-1TdTD<5vH
zxF>JU2*)8W)z8OI_wej{dU1F&w@qC%53dcbF{yHkK0B>mygXE+a<eY&EH15mc_859
z`$p48+3vl6=X}Wb+pDjR3l8kF?a9M$+W8;G_}_~Q_}KE&mR`&J-*t1cE3#;~uiKfA
znu@ReYrcsu)1=dpYojXXUJ`o*KP=rSBj|m6Ku-SDV?$4^`+HG%>nXRYdXCt%Sy%IL
z-WAz+<)9B!kRcCP|LM+zwe8zkye@7vsmWfk{P4S}tt|%Ck&bUXdAi5N#3P~2l13F+
z+!I_l-elSL2A|{mw;8lC>YZlB1lgME$D3PP9t~SQv%ltxxWlM^+X|{|kw0p-Y5nHh
zm<^t_UL4x>cS?<(O+I>zjILcasPUM>3V!=mbeNq~|7?8uqE6TA&MF$RO+P-obpMTg
zle<Yfw0l-~Jm;Hik$VH{jk*^RVHUHtdA&|;qG`}Hu|b3PQ`_afv3<Jb`|-eBk0Vo#
zbUXWX<Swy8;fn3&)t}ei_qo19{;m0Jx6z8rcl~xQZE!f)jyLenX;XadgXL}0d*1$T
zS!4bfQ5D<SMZZ{!!gl@#o=kyTzF%bmYRdl8)zy`5|LOMo{Qo~a|A)czf8N;kw~g3;
zp25%mF{w3Wbq@4<YT0$r?y8ro1zUBNTp2K|%uCBse$v}zSKC*;v(Tn$4X1X}xl{I6
z$(K0ouRnWG(5=$aj&WA4XU8oWUQlPuq-C?^QdyO*9p=amf08VE`Q*v7C(kZDy)@^U
zc<TJMtzNgio_E#7rPb?W{ijTs@_5RO#g$ggu|8S9&XiB}Z@8v6xLCT|i2V-R)Yh#n
zA}#J)`i^Z=cA+eOVc&J-_gfC_*y@;LROvHowQJhcux#<-xo68Xi_5A@?+yyfEKSck
zT=RzZhJAX)#nBVo3Vgp@P!#tWx3k%khP}#k9#*H_s|9`X`$tz9I6U@he!Byc+`E@|
z?~#^gySrE5U-e$6xmN5Ebg;PRff~z4wHaBb|5s<vsOa;#t!o6iHLdXF&rPfE_rGcB
zI<!aT4MjUm72ix%z4QBzTl@50I_jBqlLtNhTi)~gR`9+}n9MP*M(BlW^#`o%q%Z$S
z;*qg(XrG3yAGNhrd+--_STUqy=N*apHSUhz>3pMxymVegefR|Nno~Pk%|6!Esk-j=
z@f%ymj2$^;^ua$qp1<o78gw_QtlMSlOZ8UVS+pqUlkz~?&V2U^{!N-)44pCI)z#Kr
zYK=*4DQR-INO-uG{j;~7u54+3ckP<U$)fV^0UuY6Sku;Wa@xuZ^Pjam)#dPoP19@6
zNNxF|vQS~Qu9tmG>7$cY#+JKQD%A6=y8N37D`&n_7QTD4+spn<<`S1uoq1cWuiOkT
z-s{&-dhgyN|D?SGeEO8GJ!tWccBi)w*(|O!Dm8y_N`LMAlz88>jcx93IDPn2yMmeF
zeLdw32572fOE<rZx7vKU<Dcsb`?!~h%^9b$@bx`;Zrsc%_51ws`0(1syn+saYn#s=
z)zzi$<1=d{1=rfw-&S{5@YzCz#A9Qlx9$5^kFD~0w|>R#FO}cPI;?KvR+uz<?P0(B
zOU9(0oyA)|O8mBCKi>Jb`*_QXbSozH?c6ZiDf8;6S;I1SzuCNgXRIi0$^MIJA&vaJ
zJ%US3PkXfO)VLx4uRS|`XTuHvI9{@et|cPPY9<d6RkA&<?vYS&#Wm_KPgoTXEh>%&
zMWn7OO+3!IYC88BM^G)EP(x~M#5FfXB(w=NLp*DvVSBfKVK084|KR)j%b*#VwkmWg
z>|WnJMG72$pBzYDa=3W6X!J@zP%ZgDE&yd<hxN~+G5e?aUxz!$XsQm2Z4`8{k<BUl
z7{N#aD|ZD&&9x|F8rvOX3s1}-hP5GgHgASVAkGexB41K4S$89lyz*kSufIP310K@V
zWTv56@m%0y!>fPg9&vQt)FkSGR_Q%%JE=)KUX;MAp^@h*j`N-Mc1s3kW+=UJj;i7I
z8z)J{#+*<j@lg1i6$_^4JJR<Dy_71rdZU*A#^lD{EYnDKR&9opZb@N{jcsto-bVGi
z8;9z=IzFD4GpWFIj>Fs4p6L8^3;Mk-eqbYpIhu}5D?@y@(K7YI58D%?Vfk<CFR}OU
zA0AqSe5=PajENbdjc6kS2k4h%5%AgU&WD3Xg?zaqWjbf9u`w1MigV3)30=42T44lM
z>`Lb99C0x&UH&p!g3%P!#amCumC)8h7eWHsI>N#mU0(OVxb|(s4S!Qua3|-O0$1%g
zNM%t+vSs3?DlBNdpY=#yc48$vZ|z|-R?O8;ju3kl`lN`$>MZbx8iOW7ANm*a^t(+B
zMKitbEDG;9%ac})!l4z}m(Y?6<+ikvpB;JO?Dz)tY!AtrXX-fD_K}>rRa<b?aZ8mA
zTTqM_P7$AupCCVG7mNyS6K&v$sCm`&AI-(@gHm^1>q;SJ`@~db{<*xWLX)24(&y$D
zc*4YA)k8m|ood1)*WjCx<wE_VYGyEgq0ihxq{(mHXIs9_?`C*FcG{<vKNFVY!|FL!
zk;yzVH+C?IZI?HD5`^PQgP`oJPbZ4oO|m$LMMiX`lQw*&V`2+!y}BN!lL}i%{D8rq
z{b?<$qUA?b?Fy@cQw`2=-qdD{`o@`?yZ5?4%FQ^9Z<*A#T`uuh-s`JMk=QVvxq2-{
zs1{w_hV>Uim#%Dk$&J@U*b{`~!$o_@JFAYL5PegMTga$os5YMxVCg%Op7O4Ak#K-e
zDOgIEp`%oka8QQgm!{}^Uj_k6<WPO!h4zspxrZU+b=|0L1r7nxt9Uk0oO<5ynT5dZ
zC4o=6XF6{#4na2>x#%BKNEjb4$>BGH2eh&bPsQ!nF1@V<tk|#IsdKTqwb?(j5juxU
zJO7n55G@>Hhzl-6C^_9MPx)wiS*k|nLz3(F`p%*=*F*_Sk=9NE#r5KF($jUPl}yR4
z=xc<|)19ocIHe<M7vfj=`yA<xI>e4er}~XeLf&blpz2t)w=vP@mgL)p{m%|G`A@82
znWspb(S;^)_yge-q|);9Qsyjg`4P!Ys!Ylw5p(GOnaN3&aWfo#GveL<7|bdF00000
n000000000000000000000000000000{;&Q4o$Bcv0B{2U9~!9F

literal 0
HcmV?d00001

diff --git a/source/bin/nvdct/conf/nvdct.toml b/source/bin/nvdct/conf/nvdct.toml
old mode 100755
new mode 100644
index d31f64e..524e395
--- a/source/bin/nvdct/conf/nvdct.toml
+++ b/source/bin/nvdct/conf/nvdct.toml
@@ -16,8 +16,8 @@
 # [0-9-a-zA-Z\.\_\-]{1,253} -> host
 L2_SEED_DEVICES = [
     # "CORE01",
-    # "LOCATION01",
-    # "LOCATION02",
+    # "CORE2",
+    # "router01",
 ]
 
 # drop CDP/LLDP neighbours names
@@ -44,11 +44,11 @@ L3_IGNORE_IP = [
 
 # ignore IPs by wildcard
 # if comparing an ip address:
-# each 0 bit in the wildcad has to be exacly as in the pattern
-# each 1 bit in the wildacrd will be ignored
+# each 0 bit in the wildcard has to be exactly as in the pattern
+# each 1 bit in the wildcard will be ignored
 L3V4_IGNORE_WILDCARD = [
     # [ pattern    ,  wildcard ]
-    # ["172.17.0.1", "0.0.255.0"],  # ignore all IPs ending with 1 from 172.17.128.0/16
+    # ["172.17.0.1", "0.0.255.0"],  # ignore all IPs ending with 1 from 172.17.0.0/16
     # ["172.17.128.0", "0.0.127.3"],  # ignore all IPs ending with 0-3 from 172.17.128.0/17
     # ["172.17.128.3", "0.0.127.0"],  # ignore all IPs ending with 3 from 172.17.128.0/17
 ]
@@ -62,7 +62,7 @@ L3_SUMMARIZE = [
    # "fd00::/8"
 ]
 
-# topologies will not be deleted by "--keep"
+# topologies will not be deleted by "--keep_max_topologies"
 PROTECTED_TOPOLOGIES = [
     # "2023-10-17T14:08:05.10",
     # "your_important_topology"
@@ -82,17 +82,17 @@ STATIC_CONNECTIONS = [
     # connection: "left_host"<->"right_host"
 ]
 
-# list customers to include/excluse, use with option --filter-costumers INCLUDE/EXCLUDE
+# list customers to include/exclude, use with option --filter-costumers INCLUDE/EXCLUDE
 # [0-9-a-zA-Z\.\_\-]{1,16} -> customer
-CUSTOMERS = [
+FILTER_BY_CUSTOMER = [
     # "customer1",
     # "customer2",
     # "customer3",
 ]
 
-# list site to include/excluse, use with option --filter-sites INCLUDE/EXCLUDE
+# list site to include/exclude, use with option --filter-sites INCLUDE/EXCLUDE
 # [0-9-a-zA-Z\.\_\-]{1,16} -> site
-SITES = [
+FILTER_BY_SITE = [
     # "site1",
     # "site2",
     # "site3",
@@ -100,7 +100,7 @@ SITES = [
 
 # map inventory CDP/LLDP neighbour name to Checkmk host name
 # [0-9-a-zA-Z\.\_\-]{1,253} -> host
-[L2_HOST_MAP]
+[L2_NEIGHBOUR_TO_HOST_MAP]
 # "inventory_neighbour1" = "cmk_host1"
 # "inventory_neighbour2" = "cmk_host2"
 # "inventory_neighbour3" = "cmk_host3"
@@ -114,7 +114,7 @@ SITES = [
 
 # replace _network objects_ in L§ topologies (takes place after summarize)
 # [0-9-a-zA-Z\.\_\-]{1,253} -> host
-[L3_REPLACE]
+[L3_REPLACE_NETWORKS]
 # "10.193.172.0/24" = "MPLS"
 # "10.194.8.0/23" = "MPLS"
 # "10.194.12.0/24" = "MPLS"
@@ -123,7 +123,7 @@ SITES = [
 
 [EMBLEMS]
 # can use misc icons from CMK or upload your own in the misc category
-# for built-in icons use "icon_" as prefix to the name from CMK
+# for built-in icons use "icon_" as l2_prefix to the name from CMK
 # max size 80x80px
 # emblems will only be used for non CMK objects
 # "host_node" = "icon_alert_unreach"
@@ -145,31 +145,46 @@ SITES = [
 "1e9" = 3  # 1 gbit
 "1e10" = 5 # 10 gbit
 
+[FILTER_BY_FOLDER]
+# "/folder1/subfolder1" = "INCLUDE" | "EXCLUDE"
+# "/folder2/subfolder2" = "INCLUDE" | "EXCLUDE"
+
+[FILTER_BY_HOST_LABEL]
+# "hostlabel1:value" = "INCLUDE" | "EXCLUDE"
+# "hostlabel2:value" = "INCLUDE" | "EXCLUDE"
+
+[FILTER_BY_HOST_TAG]
+# "host_tag1:value" =  "INCLUDE" | "EXCLUDE"
+# "host_tag2:value" =  "INCLUDE" | "EXCLUDE"
+
 [SETTINGS]
 # api_port = 5001
 # backend = "MULTISITE" | "RESTAPI" | "LIVESTATUS"
-# case = "LOWER" | "UPPER"
-# default = false
-# display_l2_neighbours = false
-# dont_compare = false
+# default = false | true
+# dont_compare = false | true
 # filter_customers = "INCLUDE" |"EXCLUDE"
 # filter_sites = "INCLUDE" | "EXCLUDE"
-# include_l3_hosts = false
-# include_l3_loopback = false  # most likely dropped from inventory (SNMP) before
-keep = 10
-# layers = ["LLDP", "CDP", "L3v4", "STATIC", "CUSTOM"]
+# keep_max_topologies = 10
+# l2_case = "OFF" | "LOWER" | "UPPER" | "IGNORE" | "AUTO"
+# l2_display_neighbours = false | true
+# l2_display_ports = false | true
+# l2_prefix = ""
+# l2_remove_domain = "OFF" | "ON" | "AUTO"
+# l2_skip_external = false | true
+# l3_display_devices = false | true
+# l3_include_hosts = false | true
+# l3_include_loopback = false | true # most likely dropped from inventory (SNMP) before
+# l3_skip_cidr_0 = false | true
+# l3_skip_cidr_32_128 = false | true
+# l3_skip_if = false | true
+# l3_skip_ip = false | true
+# l3_skip_public = false | true
+# layers = ["LLDP", "CDP", "L3v4", "STATIC"]
 # log_file = "~/var/log/nvdct.log"
-# log_level = "WARNING"
-# log_to_stdout = false
-min_age = 1
-output_directory = 'nvdct'  # remove to get date formated directory
-# pre_fetch = false
-# prefix = ""
-# quiet = true
-# remove_domain = false
-# skip_l3_cidr_0 = false
-# skip_l3_cidr_32_128 = false
-# skip_l3_if = false
-# skip_l3_ip = false
-# skip_l3_public = false
+# log_level = "WARNING" | "DEBUG" | "INFO" | "EROR" | "FATAL" | "CRITICAL" | "OFF"
+# log_to_stdout = false | true
+# min_topology_age = 1
+output_directory = "nvdct"  # remove to get date formated directory
+# pre_fetch = false | true
+# quiet = false | true
 # time_format = "%Y-%m-%dT%H:%M:%S.%m"
diff --git a/source/bin/nvdct/lib/args.py b/source/bin/nvdct/lib/args.py
index 1d1be87..3f9a81f 100755
--- a/source/bin/nvdct/lib/args.py
+++ b/source/bin/nvdct/lib/args.py
@@ -10,32 +10,39 @@
 #
 # options used
 # -b --backend
+# -c --config
 # -d --default
 # -l --layers
 # -o --output-directory
-# -p --prefix
-# -u --user-data-file
 # -v --version
 # --api-port (deprecated ?)
-# --case
-# --check-user-data-only
-# --display-l2-neighbours
+# --check-config-only
 # --dont-compare
 # --filter-customers
 # --filter-sites
-# --fix-toml
-# --include-l3-hosts
-# --keep
+# --keep-max-topologies
+# --l2-case
+# --l2-display-ports
+# --l2-display-neighbours
+# --l2-prefix
+# --l2-remove-domain
+# --l2-skip-external
+# --l3-display-devices
+# --l3-include-hosts
+# --l3-include-loopback
+# --l3-skip-cidr-0
+# --l3-skip-cidr-32-128
+# --l3-skip-if
+# --l3-skip-ip
+# --l3-skip-public
 # --log-file
 # --log-level
 # --log-to-stdout
-# --min-age
+# --min-topology-age
 # --pre-fetch
 # --quiet
-# --remove-domain
-# --skip-l3-if
-# --skip-l3-ip
 # --time-format
+# --update-config
 
 
 from argparse import (
@@ -47,19 +54,21 @@ from pathlib import Path
 
 from lib.constants import (
     Backends,
+    CONFIG_FILE,
     Case,
     CliLong,
+    CliShort,
     ExitCodes,
     IncludeExclude,
     Layers,
     LogLevels,
     MinVersions,
     NVDCT_VERSION,
+    RemoveDomain,
     SCRIPT,
     TIME_FORMAT_ARGPARSER,
     TomlSections,
     URLs,
-    USER_DATA_FILE,
 )
 
 
@@ -86,36 +95,29 @@ def parse_arguments() -> arg_Namespace:
                f' {ExitCodes.AUTOMATION_SECRET_NOT_FOUND} - Automation secret not found\n'
                f' {ExitCodes.NO_LAYER_CONFIGURED} - No layer to work on\n'
                '\nUsage:\n'
-               f'{SCRIPT} -u ~/local/bin/nvdct/conf/my_{USER_DATA_FILE} \n\n'
+               f'{SCRIPT} -u ~/local/bin/nvdct/conf/my_{CONFIG_FILE} \n\n'
     )
-
     parser.add_argument(
-        '-b', CliLong.BACKEND,
+        CliShort.BACKEND, CliLong.BACKEND,
         choices=[Backends.LIVESTATUS, Backends.MULTISITE, Backends.RESTAPI],
         # default='MULTISITE',
         help='Backend used to retrieve the topology data\n'
              f' - {Backends.LIVESTATUS} : fetches data via local Livestatus (local site only)\n'
              f' - {Backends.MULTISITE}  : like LIVESTATUS but for distributed environments (default)\n'
-             f' - {Backends.RESTAPI}    : uses the CMK REST API.',
+             f' - {Backends.RESTAPI}    : uses the Checkmk REST API.',
     )
     parser.add_argument(
-        '-d', CliLong.DEFAULT, action='store_const', const=True,  # default=False,
-        help='Set the created topology data as default. Will be created automatically\n'
-             'if it doesnt exists.',
+        CliShort.CONFIG, CliLong.CONFIG, type=str,
+        help='Set the name of the config file.\n'
+             f'Default is ~/local/bin/nvdct/conf/{CONFIG_FILE}',
     )
     parser.add_argument(
-        '-o', CliLong.OUTPUT_DIRECTORY, type=str,
-        help='Directory name where to save the topology data.\n'
-             'I.e.: my_topology. Default is the actual date/time\n'
-             f'in "{CliLong.TIME_FORMAT}" format.\n'
-             'NOTE: the directory is a sub directory under "~/var/check_mk/topology/data/"\n',
-    )
-    parser.add_argument(
-        '-p', CliLong.PREFIX, type=str,
-        help='Prepends each host with the prefix. (Needs more testing)\n'
+        CliShort.DEFAULT, CliLong.DEFAULT, action='store_const', const=True,  # default=False,
+        help='Set the created topology data as default. Will be created automatically\n'
+             'if it doesnt exists.',
     )
     parser.add_argument(
-        '-l', CliLong.LAYERS,
+        CliShort.LAYERS, CliLong.LAYERS,
         nargs='+',
         choices=[
             Layers.CDP,
@@ -125,69 +127,34 @@ def parse_arguments() -> arg_Namespace:
         ],
         # default=['CDP'],
         help=(
-            f' - {Layers.CDP}      : needs inv_cdp_cache package at least in version {MinVersions.CDP}\n'
-            f' - {Layers.LLDP}     : needs inv_lldp_cache package at least in version {MinVersions.LLDP}\n'
-            f' - {Layers.L3V4}     : needs inv_ip_address package at least in version {MinVersions.SNMP_IP_ADDRESSES} for SNMP based hosts\n'
-            f'              for Linux based hosts inv_lnx_ip_if in version {MinVersions.LINUX_IP_ADDRESSES}\n'
-            f'              for Windows based hosts inv_win_ip_if in version {MinVersions.WINDOWS_IP_ADDRESSES}\n'
-            f' - {Layers.STATIC}   : creates a topology base on the "[{TomlSections.STATIC_CONNECTIONS}]" section in the TOML file\n'
+            f' - {Layers.CDP}    : needs inv_cdp_cache package at least in version {MinVersions.CDP}\n'
+            f' - {Layers.LLDP}   : needs inv_lldp_cache package at least in version {MinVersions.LLDP}\n'
+            f' - {Layers.L3V4}   : needs inv_ip_address package at least in version {MinVersions.SNMP_IP_ADDRESSES} for SNMP based hosts\n'
+            f'            for Linux based hosts inv_lnx_ip_if in version {MinVersions.LINUX_IP_ADDRESSES}\n'
+            f'            for Windows based hosts inv_win_ip_if in version {MinVersions.WINDOWS_IP_ADDRESSES}\n'
+            f' - {Layers.STATIC} : creates a topology base on the "[{TomlSections.STATIC_CONNECTIONS}]" section in the TOML file\n'
         )
     )
     parser.add_argument(
-        '-u', CliLong.USER_DATA_FILE, type=str,
-        help='Set the name of the user provided data file\n'
-             f'Default is ~/local/bin/nvdct/conf/{USER_DATA_FILE}\n',
+        CliShort.OUTPUT_DIRECTORY, CliLong.OUTPUT_DIRECTORY, type=str,
+        help='Directory name where to save the topology data.\n'
+             'I.e.: my_topology. Default is the actual date/time\n'
+             f'in "{CliLong.TIME_FORMAT}" format.\n'
+             'NOTE: the directory is a sub directory under "~/var/check_mk/topology/data/"\n',
     )
     parser.add_argument(
-        '-v', CliLong.VERSION, action='version',
+        CliShort.VERSION, CliLong.VERSION, action='version',
         version=f'{Path(SCRIPT).name} version: {NVDCT_VERSION}',
         help='Print version of this script and exit',
     )
-    parser.add_argument(
-        CliLong.ADJUST_TOML, action='store_const', const=True,  # default=False,
-        help='Adjusts old options in TOML file.',
-    )
     parser.add_argument(
         CliLong.API_PORT, type=int,  # default=False,
         help='TCP Port to access the REST API. By NVDCT will try to automatically\n'
              'detect the site apache port.',
     )
     parser.add_argument(
-        CliLong.CASE,
-        choices=[Case.LOWER, Case.UPPER],
-        # default='NONE',
-        help='Change L2 neighbour name to all lower/upper case before matching to CMK host',
-    )
-    parser.add_argument(
-        CliLong.CHECK_USER_DATA_ONLY, action='store_const', const=True,  # default=False,
-        help=f'Only tries to read/parse the user data from {USER_DATA_FILE} and exits.',
-    )
-    parser.add_argument(
-        CliLong.LOG_FILE, type=str,
-        help='Set the log file. Default is "~/var/log/nvdct.log"\n',
-    )
-    parser.add_argument(
-        CliLong.LOG_LEVEL,
-        # nargs='+',
-        choices=[
-            LogLevels.CRITICAL,
-            LogLevels.FATAL,
-            LogLevels.ERROR,
-            LogLevels.WARNING,
-            LogLevels.INFO,
-            LogLevels.DEBUG,
-            LogLevels.OFF
-        ],
-        # default='WARNING',
-        help=f'Sets the log level. The default is "{LogLevels.WARNING}"\n'
-    )
-    parser.add_argument(
-        CliLong.LOG_TO_STDOUT, action='store_const', const=True,  # default=False,
-        help='Send log to stdout.',
-    )
-    parser.add_argument(
-        CliLong.DISPLAY_L2_NEIGHBOURS, action='store_const', const=True,  # default=False,
-        help='Use L2 neighbour name as display name in L2 topologies',
+        CliLong.CHECK_CONFIG, action='store_const', const=True,  # default=False,
+        help=f'Only tries to read/parse the config file from {CONFIG_FILE} and exits.',
     )
     parser.add_argument(
         CliLong.DONT_COMPARE, action='store_const', const=True,  # default=False,
@@ -201,69 +168,133 @@ def parse_arguments() -> arg_Namespace:
         CliLong.FILTER_CUSTOMERS,
         choices=[IncludeExclude.INCLUDE, IncludeExclude.EXCLUDE],
         # default='INCLUDE',
-        help=f'{IncludeExclude.INCLUDE}/{IncludeExclude.EXCLUDE} customer list "[{TomlSections.CUSTOMERS}]" from TOML file.\n'
-             f'Note: {Backends.MULTISITE} backend only.',
+        help=f'{IncludeExclude.INCLUDE}/{IncludeExclude.EXCLUDE} customer list "[{TomlSections.FILTER_BY_CUSTOMER}]" from TOML file.'
+             f'NOTE: {Backends.MULTISITE} backend only.',
     )
     parser.add_argument(
         CliLong.FILTER_SITES,
         choices=[IncludeExclude.EXCLUDE, IncludeExclude.EXCLUDE],
         # default='INCLUDE',
-        help=f'{IncludeExclude.INCLUDE}/{IncludeExclude.EXCLUDE} site list "[{TomlSections.SITES}]" from TOML file.\n'
+        help=f'{IncludeExclude.INCLUDE}/{IncludeExclude.EXCLUDE} site list "[{TomlSections.FILTER_BY_SITE}]" from TOML file.'
     )
     parser.add_argument(
-        CliLong.INCLUDE_L3_HOSTS, action='store_const', const=True,  # default=False,
-        help='Include hosts (single IP objects) in layer 3 topologies',
+        CliLong.KEEP_MAX_TOPOLOGIES, type=int,
+        help='Number of topologies to keep. The oldest topologies above keep\n'
+             'will be deleted.\n'
+             'NOTE: The default/protected topologies will be kept always.'
     )
     parser.add_argument(
-        CliLong.INCLUDE_L3_LOOPBACK, action='store_const', const=True,  # default=False,
-        help='Include loopback ip-addresses in layer 3 topologies',
+        CliLong.L2_CASE,
+        choices=[Case.INSENSITIVE, Case.LOWER, Case.UPPER, Case.AUTO, Case.OFF],
+        help='Change L2 neighbour name case before matching to Checkmk host.\n'
+             f'- {Case.OFF}         : Do not change the case of the neighbour name.'
+             f'- {Case.INSENSITIVE} : search for a matching host by ignoring the case of neighbour name and host name\n'
+             f'- {Case.LOWER}       : change to all lower case\n'
+             f'- {Case.UPPER}       : change to all upper case, without the domain part of the neighbour name\n'
+             f'- {Case.AUTO}        : try all the above variants\n'
+             f'Default is let the case of the neighbour name untouched ("OFF").\n'
+             f'Takes place after "{CliLong.L2_REMOVE_DOMAIN}" and before "{CliLong.L2_PREFIX}"',
     )
     parser.add_argument(
-        CliLong.REMOVE_DOMAIN, action='store_const', const=True,  # default=False,
-        help='Remove the domain name from the L2 neighbor name before matching CMK host.',
+        CliLong.L2_DISPLAY_PORTS, action='store_const', const=True,  # default=False,
+        help='Use L2 port names as display name for interfaces in L2 topologies',
     )
     parser.add_argument(
-        CliLong.KEEP, type=int,
-        help='Number of topologies to keep. The oldest topologies above keep\n'
-             'will be deleted.\n'
-             'NOTE: The default/protected topologies will be kept always.\n'
+        CliLong.L2_DISPLAY_NEIGHBOURS, action='store_const', const=True,  # default=False,
+        help='Use L2 neighbour name as display name in L2 topologies',
     )
     parser.add_argument(
-        CliLong.MIN_AGE, type=int,
-        help=f'The minimum number of days before a topology is deleted by "{CliLong.KEEP}"'
+        CliLong.L2_PREFIX, type=str,
+        help=f'Prepends each L2 neighbour name with the prefix before matching to a Checkmk host name.\n'
+             f'Takes place after "{CliLong.L2_REMOVE_DOMAIN}" and "{CliLong.L2_CASE}"'
     )
     parser.add_argument(
-        CliLong.PRE_FETCH, action='store_const', const=True,  # default=False,
-        help=f'Try to fetch host data, with less API calls. Can improve {Backends.RESTAPI} backend\n'
-             'performance',
+        CliLong.L2_REMOVE_DOMAIN,
+        choices=[RemoveDomain.OFF, RemoveDomain.ON, RemoveDomain.AUTO],
+        help=f'Handle the the domain name part of a neighbour name before matching it to a Checkmk host.\n'
+             f'- {RemoveDomain.OFF} : dont touch the neighbour name, keep host name and domain part\n'
+             f'- {RemoveDomain.ON}  : will remove the domain part from the neighbour name, keep only the host name part\n'
+             f'- {RemoveDomain.AUTO} : try all of the above variants\n'
+             f'Default: "{RemoveDomain.OFF}". Takes place after "{CliLong.L2_REMOVE_DOMAIN}" and before "{CliLong.L2_PREFIX}"',
     )
     parser.add_argument(
-        CliLong.QUIET, action='store_const', const=True,  # default=False,
-        help='Suppress all output to stdtout',
+        CliLong.L2_SKIP_EXTERNAL, action='store_const', const=True,  # default=False,
+        help='Skip L2 neighbours external to Checkmk (that have no matching host)',
     )
     parser.add_argument(
-        CliLong.SKIP_L3_CIDR_0, action='store_const', const=True,  # default=False,
+        CliLong.L3_DISPLAY_DEVICES, action='store_const', const=True,  # default=False,
+        help='Use L3 device names as display name for interfaces in L3 topologies',
+    )
+    parser.add_argument(
+        CliLong.L3_INCLUDE_HOSTS, action='store_const', const=True,  # default=False,
+        help='Include hosts (single IP objects) in layer 3 topologies',
+    )
+    parser.add_argument(
+        CliLong.L3_INCLUDE_LOOPBACK, action='store_const', const=True,  # default=False,
+        help='Include loopback ip-addresses in layer 3 topologies',
+    )
+    parser.add_argument(
+        CliLong.L3_SKIP_CIDR_0, action='store_const', const=True,  # default=False,
         help='Skip ip-address with CIDR "/0" in layer 3 topologies',
     )
     parser.add_argument(
-        CliLong.SKIP_L3_CIDR_32_128, action='store_const', const=True,  # default=False,
+        CliLong.L3_SKIP_CIDR_32_128, action='store_const', const=True,  # default=False,
         help='Skip ip-address with CIDR "/32" or "/128" in layer 3 topologies',
     )
     parser.add_argument(
-        CliLong.SKIP_L3_IF, action='store_const', const=True,  # default=False,
+        CliLong.L3_SKIP_IF, action='store_const', const=True,  # default=False,
         help='Dont show interface in layer 3 topologies',
     )
     parser.add_argument(
-        CliLong.SKIP_L3_IP, action='store_const', const=True,  # default=False,
+        CliLong.L3_SKIP_IP, action='store_const', const=True,  # default=False,
         help='Dont show ip-addresses in layer 3 topologies',
     )
     parser.add_argument(
-        CliLong.SKIP_L3_PUBLIC, action='store_const', const=True,  # default=False,
+        CliLong.L3_SKIP_PUBLIC, action='store_const', const=True,  # default=False,
         help='Skip public ip-addresses in layer 3 topologies',
     )
+    parser.add_argument(
+        CliLong.LOG_FILE, type=str,
+        help='Set the log file. Default is "~/var/log/nvdct.log"',
+    )
+    parser.add_argument(
+        CliLong.LOG_LEVEL,
+        # nargs='+',
+        choices=[
+            LogLevels.CRITICAL,
+            LogLevels.FATAL,
+            LogLevels.ERROR,
+            LogLevels.WARNING,
+            LogLevels.INFO,
+            LogLevels.DEBUG,
+            LogLevels.OFF
+        ],
+        # default='WARNING',
+        help=f'Sets the log level. The default is "{LogLevels.WARNING}"'
+    )
+    parser.add_argument(
+        CliLong.LOG_TO_STDOUT, action='store_const', const=True,  # default=False,
+        help='Send log to stdout.',
+    )
+    parser.add_argument(
+        CliLong.MIN_TOPOLOGY_AGE, type=int,
+        help=f'The minimum number of days before a topology is deleted by "{CliLong.KEEP_MAX_TOPOLOGIES}"'
+    )
+    parser.add_argument(
+        CliLong.PRE_FETCH, action='store_const', const=True,  # default=False,
+        help=f'Try to fetch host data, with less API calls. Can improve {Backends.RESTAPI} backend\n'
+             'performance',
+    )
+    parser.add_argument(
+        CliLong.QUIET, action='store_const', const=True,  # default=False,
+        help='Suppress all output to stdtout',
+    )
     parser.add_argument(
         CliLong.TIME_FORMAT, type=str,
         help=f'Format string to render the time. (default: "{TIME_FORMAT_ARGPARSER}")',
     )
-
+    parser.add_argument(
+        CliLong.UPDATE_CONFIG, action='store_const', const=True,  # default=False,
+        help='Adjusts old options in TOML file.',
+    )
     return parser.parse_args()
diff --git a/source/bin/nvdct/lib/backends.py b/source/bin/nvdct/lib/backends.py
index 80adf5a..8fce63c 100755
--- a/source/bin/nvdct/lib/backends.py
+++ b/source/bin/nvdct/lib/backends.py
@@ -12,14 +12,18 @@
 # 2024-09-25: fixed crash on missing "customer" section in site config file
 # 2024-12-22: refactoring, leave only backend specific stuff in the backend
 #             removed not strictly needed properties, renamed functions to better understand what the do
+# 2025-01-21: added support for RESTAPI post requests (CMK >= 2.3.0p23, Werk #17003)
+#             fixed REST API query for interface services
+# 2025-01-25: fixed check if post can be used
 
 from abc import abstractmethod
 from ast import literal_eval
-from collections.abc import Mapping, MutableSequence, Sequence
+from collections.abc import Mapping, MutableMapping, MutableSet, MutableSequence, Sequence
+from json import dumps
 from pathlib import Path
 from requests import session
 from sys import exit as sys_exit
-from typing import Dict, List, Tuple, MutableMapping
+from typing import Dict, List, Tuple, Set
 
 from livestatus import MultiSiteConnection, SiteConfigurations, SiteId
 
@@ -29,102 +33,135 @@ from lib.constants import (
     CacheItems,
     Case,
     ExitCodes,
+    HostFilter,
     IncludeExclude,
     InvPaths,
+    L2InvColumns,
+    LiveStatusOperator,
+    MIN_CMK_VERSION_POST,
     OMD_ROOT,
-    TomlSections,
+    RemoveDomain,
 )
 from lib.utils import (
     LOGGER,
     get_data_form_live_status,
-    get_table_from_inventory,
+    get_data_from_inventory,
+    get_local_cmk_version,
 )
 
 HOST_EXIST: Dict = {'exists': True}
 
-def hosts_to_query(hosts: List[str]) -> Tuple[str, List[str]]:
-    # WORKAROUND for: Apache HTTP Error 414: Request URI too long
-    # https://httpd.apache.org/docs/current/mod/core.html#limitrequestfieldsize
-    # Default:	LimitRequestFieldSize 8190
-    # max seems to be 8013 (inventory)/7831 (interfaces), so 7800 is a save distance from it
-    _max_str_len = 7800
-    # 8190 - 8013 - 168 = 9 chrs  -> Inventory
-    # 8190 - 7813 - 356 = 21 chrs -> Interfaces
-    # host (19/19): http://localhost:80 -> sould not count
-    # uri (57/60): /build/check_mk/api/1.0/domain-types/host/collections/all / /build/check_mk/api/1.0/domain-types/service/collections/all  # noqa: E501
-    # query (77/231): ?query=%7B%22op%22%3A+%22~~%22%2C+%22left%22%3A+%22name%22%2C+%22right%22%3A+ / ?query=%7B%22op%22%3A+%22and%22%2C+%22expr%22%3A+%5B%7B%22op%22%3A+%22~%22%2C+%22left%22%3A+%22description%22%2C+%22right%22%3A+%22Interface+%22%7D%2C%7B%22op%22%3A+%22~~%22%2C+%22left%22%3A+%22host_name%22%2C+%22right%22%3A+%22%5E  # noqa: E501
-    # collumns (34/65): &columns=name&columns=mk_inventory / &columns=host_name&columns=description&columns=long_plugin_output  # noqa: E501
-    # %22 -> "
-    # %24 -> $
-    # %2C -> ,
-    # %3A -> :
-    # %5B -> [
-    # %5D -> ]
-    # %7B -> {
-    # %7D -> }
-
-    temp_hosts = []
-    open_hosts = []
-    for host in hosts:
-        temp_hosts.append(f'^{host}$')
-    hosts_str = '|'.join(temp_hosts)
-    # 6 comes from 3 chrs ($|^) between hosts, coded as 9 chr(%24%7C%5E) in the URL
-    # 3 are already in the str, so we need to add 6 for each host
-    if len(hosts_str) > _max_str_len - len(hosts) * 6:
-        open_hosts = hosts.copy()
-        hosts_str = f'^{hosts[0]}$'
-        hosts = hosts[1:]
-        count = 1
-        for host in hosts:
-            count += 1
-            if len(hosts_str) + 9 + len(host) + (6 * count) < _max_str_len:
-                hosts_str = hosts_str + f'|^{host}$'
-                open_hosts.remove(host)
-            else:
-                break
-
-    LOGGER.debug(f'hosts len: {len(hosts_str) + (len(hosts) - len(open_hosts)) * 6}')
-    LOGGER.debug(f'open hosts {open_hosts}')
-
-    return hosts_str, open_hosts
 
 class HostCache:
     def __init__(
             self,
             backend: str,
             pre_fetch: bool,
+
     ):
-        LOGGER.info('init HOST_CACHE')
+        LOGGER.info(f'{backend} init HOST_CACHE')
 
         self.cache: Dict = {}
         self.neighbour_to_host: MutableMapping[str, str] = {}
-        self._inventory_pre_fetch_list: List[str] = [
-            InvPaths.INTERFACES,
-        ]
+        self._inventory_pre_fetch_list: List[str] = [InvPaths.INTERFACES]
 
         self.backend: str = str(backend)
         self.case: str = ''
-        self.l2_host_map: Dict[str, str] = {}
+        self.l2_neighbour_to_host_map: Dict[str, str] = {}
         self.l2_neighbour_replace_regex: List[Tuple[str, str]] = []
         self.pre_fetch: bool = bool(pre_fetch)
         self.prefix: str = ''
-        self.remove_domain: bool = False
+        self.remove_domain: str = RemoveDomain.OFF
+        self.filter_include: MutableSequence[str] = []
+        self.filter_exclude: MutableSequence[str] = []
+        self.no_case_host_map: MutableMapping[str, str] = {}
+
+        self.use_post = True
+
+        if self.backend == f'[{Backends.RESTAPI}]' and get_local_cmk_version() < MIN_CMK_VERSION_POST:
+            self.use_post = False
+            LOGGER.info(
+                f'{self.backend} Using get request, Checkmk version {get_local_cmk_version()} < {MIN_CMK_VERSION_POST}'
+            )
+        else:
+            LOGGER.info(
+                f'{self.backend} Using post request, Checkmk version {get_local_cmk_version()} >= {MIN_CMK_VERSION_POST}'
+            )
 
         if self.pre_fetch:
             for host in self.query_all_hosts():
                 self.cache[host] = HOST_EXIST.copy()
 
-    def init_neighbour_to_host(
+    def init_filter_lists(
+            self,
+            filter_by_folder: Mapping[str, Sequence[str]],
+            filter_by_host_label: Mapping[str, Sequence[str]],
+            filter_by_host_tag: Mapping[str, Sequence[str]],
+    ):
+        for folder in filter_by_folder[IncludeExclude.INCLUDE]:
+            self.filter_include += self.query_hosts_by_filter(HostFilter.FOLDER, folder, LiveStatusOperator.SUPERSET)
+
+        for folder in filter_by_folder[IncludeExclude.EXCLUDE]:
+            self.filter_exclude += self.query_hosts_by_filter(HostFilter.FOLDER, folder, LiveStatusOperator.SUPERSET)
+
+        for host_label in filter_by_host_label[IncludeExclude.INCLUDE]:
+            self.filter_include += self.query_hosts_by_filter(HostFilter.LABELS, host_label, LiveStatusOperator.EQUAL)
+
+        for host_label in filter_by_host_label[IncludeExclude.EXCLUDE]:
+            self.filter_exclude += self.query_hosts_by_filter(HostFilter.LABELS, host_label, LiveStatusOperator.EQUAL)
+
+        for host_tag in filter_by_host_tag[IncludeExclude.INCLUDE]:
+            self.filter_include += self.query_hosts_by_filter(HostFilter.TAGS, host_tag, LiveStatusOperator.EQUAL)
+
+        for host_tag in filter_by_host_tag[IncludeExclude.EXCLUDE]:
+            self.filter_exclude += self.query_hosts_by_filter(HostFilter.TAGS, host_tag, LiveStatusOperator.EQUAL)
+
+        # drop any host from the include list that is on the exclude list
+        # -> Issue: can render filter_include empty -> disabling include
+        if self.filter_include:
+            self.filter_include = [host for host in self.filter_include if host not in self.filter_exclude]
+            if not self.filter_include:
+                self.filter_include.append(self.filter_exclude[0])
+
+        LOGGER.info(f'{self.backend} {IncludeExclude.INCLUDE} # of hosts: {len(self.filter_include)}')
+        LOGGER.info(f'{self.backend} {IncludeExclude.EXCLUDE} # of hosts: {len(self.filter_exclude)}')
+        LOGGER.debug(f'{self.backend} {IncludeExclude.INCLUDE} hosts: {self.filter_include}')
+        LOGGER.debug(f'{self.backend} {IncludeExclude.EXCLUDE} hosts: {self.filter_exclude}')
+
+    def init_neighbour_to_host_map(
             self,
             case: str,
             l2_host_map: Dict[str, str],
             prefix: str,
-            remove_domain: bool,
+            remove_domain: str,
     ):
         self.case: str = case
-        self.l2_host_map: Dict[str, str] = l2_host_map
         self.prefix: str = prefix
-        self.remove_domain: bool = remove_domain
+        self.remove_domain: str = remove_domain
+
+        for host, data in self.cache.items():
+            try:
+                self.neighbour_to_host[data[CacheItems.inventory][InvPaths.LLDP_GLOBAL][L2InvColumns.GLOBALID]] = host
+            except (KeyError, TypeError):
+                pass
+            try:
+                self.neighbour_to_host[data[CacheItems.inventory][InvPaths.LLDP_GLOBAL][L2InvColumns.GLOBALNAME]] = host
+            except (KeyError, TypeError):
+                pass
+            try:
+                self.neighbour_to_host[data[CacheItems.inventory][InvPaths.CDP_GLOBAL][L2InvColumns.GLOBALNAME]] = host
+            except (KeyError, TypeError):
+                pass
+
+        for neighbour, host in l2_host_map.items():
+            self.neighbour_to_host[neighbour] = host
+
+    def filter_host_list(self, host_list: MutableSequence[str] | MutableSet[str]) -> Sequence[str] | MutableSet[str]:
+        if self.filter_include:
+            host_list = [host for host in host_list if host in self.filter_include]
+        if self.filter_exclude:
+            host_list = [host for host in host_list if host not in self.filter_exclude]
+        return host_list
 
     def get_inventory_data(self, hosts: List[str]) -> Dict[str, Dict]:
         """
@@ -136,17 +173,13 @@ class HostCache:
             the inventory data as dictionary
         """
 
-        inventory_data: Dict[str, Dict | None] = {}
         # init inventory_data with None
-        for host in hosts:
-            inventory_data[host] = None
-
+        inventory_data: Dict[str, Dict | None] = {host: None for host in hosts}
         open_hosts = hosts.copy()
         while open_hosts:
-            hosts_str, open_hosts = hosts_to_query(open_hosts)
+            hosts_str, open_hosts = self.hosts_to_query(open_hosts)
             for host, inventory in self.query_inventory_data(hosts_str).items():
                 inventory_data[host] = inventory
-
         return inventory_data
 
     def get_interface_data(self, hosts: List[str]) -> Dict[str, Dict | None]:
@@ -165,7 +198,7 @@ class HostCache:
             host_data[host] = None
         open_hosts = hosts.copy()
         while open_hosts:
-            hosts_str, open_hosts = hosts_to_query(open_hosts)
+            hosts_str, open_hosts = self.hosts_to_query(open_hosts)
             host_data.update(self.query_interface_data(hosts_str))
 
         return host_data
@@ -179,6 +212,9 @@ class HostCache:
         except KeyError:
             pass
 
+        if self.pre_fetch:
+            return False
+
         # get host from CMK and init host in cache
         if exists := self.query_host(host):
             self.cache[host] = HOST_EXIST.copy()
@@ -187,16 +223,13 @@ class HostCache:
 
         return exists
 
-    def get_hosts_by_label(self, label: str) -> Sequence[str]:
-        """
-        Returns list of hosts from CMK filtered by label
-        Args:
-            label: hostlabel to filter by
+    def is_host_allowed(self, host: str) -> bool:
+        if self.filter_include and host not in self.filter_include:
+            return False
+        if self.filter_exclude and host in self.filter_exclude:
+            return False
 
-        Returns:
-            List of hosts
-        """
-        return self.query_hosts_by_label(label)
+        return True
 
     def fill_cache(self, hosts: List[str]) -> None:
         """
@@ -209,6 +242,7 @@ class HostCache:
 
         Returns: None, the data is directly writen to self.cache
         """
+
         inventory_of_hosts: Mapping[str, Mapping | None] = self.get_inventory_data(hosts=hosts)
         if inventory_of_hosts:
             for host, inventory in inventory_of_hosts.items():
@@ -216,14 +250,13 @@ class HostCache:
                     self.cache[host] = HOST_EXIST.copy()
                 self.cache[host][CacheItems.inventory] = {}
                 self.cache[host][CacheItems.inventory].update({
-                    entry: get_table_from_inventory(
+                    entry: get_data_from_inventory(
                         inventory=inventory,
                         raw_path=entry
                     ) for entry in self._inventory_pre_fetch_list
                 })
 
         interfaces_of_hosts: Mapping[str, Mapping | None] = self.get_interface_data(hosts)
-
         for host, interfaces in interfaces_of_hosts.items():
             if host not in self.cache:
                 self.cache[host] = HOST_EXIST.copy()
@@ -244,7 +277,7 @@ class HostCache:
         """
         if self.host_exists(host=host):
             if self.cache[host] == HOST_EXIST:
-                LOGGER.info(f'fetch data for: {host}')
+                LOGGER.info(f'{self.backend} fetch data for: {host}')
                 self.fill_cache(hosts=[host])
             try:
                 return self.cache[host][item][path]
@@ -256,61 +289,161 @@ class HostCache:
     def add_inventory_path(self, path: str) -> None:
         self._inventory_pre_fetch_list = list(set(self._inventory_pre_fetch_list + [path]))
 
-    def get_host_from_neighbour(self, neighbour: str) -> str | None:
+    def get_host_from_neighbour(
+            self,
+            raw_neighbour: str,
+            neighbour: str,
+            neighbour_id: str | None = None,
+    ) -> str | None:
         """
         Tries to get the CMK host name from a L2 neighbour name. It will test:
+        - the neighbour id
+        - the raw neighbour name
+        - the adjusted neighbour name
+        - map the neighbour to a host via L2_NEIGHBOUR_TO_HOST_MAP
         - the neighbour without domain name
-        - map the neighbour to a host via L2_HOST_MAP
         - the neighbour in UPPER case (without domain)
         - the neighbour in lower case (including domain)
-        - the neighbour with prefix
+        - the neighbour with prefix (UPPER/lower case, with/without domain)
         Args:
+            raw_neighbour: the L2 neighbour name "as is" in the HW/SW inventory
             neighbour: the L2 neighbour name to find a CMK host for
+            neighbour_id: the L2 neighbour id to find a CMK host for (LLDP only)
 
         Returns:
             The CMK host name for the L2 neighbour or None if no host is found
-
         """
+
+        try:
+            match = self.neighbour_to_host[neighbour_id]
+            LOGGER.debug(f'{self.backend} Match by neighbour id: |{neighbour_id}| -> |{match}|')
+            return match
+        except KeyError:
+            pass
+
+        try:
+            match = self.neighbour_to_host[raw_neighbour]
+            LOGGER.debug(f'{self.backend} Match by raw neighbour: |{raw_neighbour}| -> |{match}|')
+            return match
+        except KeyError:
+            pass
+
         try:
-            return self.neighbour_to_host[neighbour]
+            match = self.neighbour_to_host[neighbour]
+            LOGGER.debug(f'{self.backend} Match by neighbour: |{neighbour}| -> |{match}|')
+            return match
         except KeyError:
             pass
 
-        host = neighbour
+        LOGGER.debug(f'{self.backend} neighbour not in list: |{neighbour}|')
+
+        host: str = neighbour
 
-        # rewrite neighbour if inventory neighbour and checkmk host don't match
-        if host in self.l2_host_map:
-            LOGGER.info(f'Replace neighbour by [{TomlSections.L2_HOST_MAP}]: {neighbour} -> {host}')
-            host = self.l2_host_map[host]
+        possible_hosts: Set[str] | List[str] = set()
 
-        if self.remove_domain:
-            LOGGER.debug(f'Remove domain: {host} -> {host.split(".")[0]}')
-            host = host.split('.')[0]
+        host_no_domain: str = host.split('.')[0]
+        match self.remove_domain:
+            case RemoveDomain.ON:
+                LOGGER.debug(f'{self.backend} Remove domain: {host} -> {host_no_domain}')
+                host = host_no_domain
+                possible_hosts.add(host_no_domain)
+            case RemoveDomain.AUTO:
+                possible_hosts.add(host)
+                possible_hosts.add(host_no_domain)
+            case RemoveDomain.OFF | _:
+                possible_hosts.add(host)
 
         match self.case:
             case Case.UPPER:
-                LOGGER.debug(f'Change neighbour to upper case: {host} -> {host.upper()}')
-                host = host.upper()
-
+                if not '.' in host:  # use UPPER only for names without domain
+                    LOGGER.debug(f'{self.backend} Change neighbour to upper case: {host} -> {host.upper()}')
+                    possible_hosts.add(host.upper)
             case Case.LOWER:
-                LOGGER.debug(f'Change neighbour to lower case: {host} -> {host.lower()}')
-                host = host.lower()
-            case _:
-                pass
+                LOGGER.debug(f'{self.backend} Change neighbour to lower case: {host} -> {host.lower()}')
+                possible_hosts.add(host.lower())
+            case Case.AUTO:
+                possible_hosts.add(host)
+                possible_hosts.add(host.lower())
+                possible_hosts.add(host_no_domain.lower())
+                possible_hosts.add(host_no_domain.upper())
+            case Case.OFF | _:
+                possible_hosts.add(host)
+
+        possible_hosts = [f'{self.prefix}{host}' for host in possible_hosts]
+        possible_hosts = set(self.filter_host_list(possible_hosts))
+        # try longest match first
+        possible_hosts = list(possible_hosts)
+        possible_hosts.sort(key=len, reverse=True)
+
+        for entry in possible_hosts:
+            if self.host_exists(entry):
+                self.neighbour_to_host[neighbour] = entry
+                LOGGER.debug(f'{self.backend} Matched neighbour to host: |{neighbour}| -> |{entry}|')
+                return entry
+
+        if self.case in [Case.AUTO, Case.INSENSITIVE]:
+            if not self.no_case_host_map:
+                self.no_case_host_map = {host.lower():host for host in self.cache}
+            for entry in possible_hosts:
+                if entry.lower() in self.no_case_host_map:
+                    self.neighbour_to_host[neighbour] = self.no_case_host_map[entry.lower()]
+                    LOGGER.debug(
+                        f'{self.backend} Matched neighbour to host: '
+                        f'|{neighbour}| -> |{self.no_case_host_map[entry.lower()]}|'
+                    )
+                    return entry
+
+        self.neighbour_to_host[neighbour] = None
+        LOGGER.debug(f'{self.backend} No match found for neighbour: |{neighbour}|')
+        return None
 
-        if self.prefix:
-            LOGGER.debug(f'Prepend neighbour with prefix: {host} -> {self.prefix}{host}')
-            host = f'{self.prefix}{host}'
+    def hosts_to_query(self, hosts: List[str]) -> Tuple[str, List[str]]:
+        if self.use_post:
+            return '|'.join(hosts), []
+
+        # WORKAROUND for: Apache HTTP Error 414: Request URI too long
+        # https://httpd.apache.org/docs/current/mod/core.html#limitrequestfieldsize
+        # Default:	LimitRequestFieldSize 8190
+        # max seems to be 8013 (inventory)/7831 (interfaces), so 7800 is a save distance from it
+        _max_str_len = 7800
+
+        # 8190 - 8013 - 168 = 9 chrs  -> Inventory
+        # 8190 - 7813 - 356 = 21 chrs -> Interfaces
+        # host (19/19): http://localhost:80 -> sould not count
+        # uri (57/60): /build/check_mk/api/1.0/domain-types/host/collections/all / /build/check_mk/api/1.0/domain-types/service/collections/all  # noqa: E501
+        # query (77/231): ?query=%7B%22op%22%3A+%22~~%22%2C+%22left%22%3A+%22name%22%2C+%22right%22%3A+ / ?query=%7B%22op%22%3A+%22and%22%2C+%22expr%22%3A+%5B%7B%22op%22%3A+%22~%22%2C+%22left%22%3A+%22description%22%2C+%22right%22%3A+%22Interface+%22%7D%2C%7B%22op%22%3A+%22~~%22%2C+%22left%22%3A+%22host_name%22%2C+%22right%22%3A+%22%5E  # noqa: E501
+        # collumns (34/65): &columns=name&columns=mk_inventory / &columns=host_name&columns=description&columns=long_plugin_output  # noqa: E501
+        # %22 -> "
+        # %24 -> $
+        # %2C -> ,
+        # %3A -> :
+        # %5B -> [
+        # %5D -> ]
+        # %7B -> {
+        # %7D -> }
+
+        hosts = [host for host in hosts if self.is_host_allowed(host)]
+        open_hosts = []
+        temp_hosts: Sequence[str] = [f'^{host}$' for host in hosts]
+        hosts_str = '|'.join(temp_hosts)
+        # 6 comes from 3 chrs ($|^) between hosts, coded as 9 chr(%24%7C%5E) in the URL
+        # 3 are already in the str, so we need to add 6 for each host
+        if len(hosts_str) > _max_str_len - len(hosts) * 6:
+            open_hosts = hosts.copy()
+            hosts_str = f'^{hosts.pop()}$'
+            count = 1
+            for host in hosts:
+                count += 1
+                if len(hosts_str) + 9 + len(host) + (6 * count) < _max_str_len:
+                    hosts_str = hosts_str + f'|^{host}$'
+                    open_hosts.remove(host)
+                else:
+                    break
 
+        LOGGER.debug(f'{self.backend} hosts len: {len(hosts_str) + (len(hosts) - len(open_hosts)) * 6}')
+        LOGGER.debug(f'{self.backend} open hosts {open_hosts}')
 
-        if self.host_exists(host):
-            self.neighbour_to_host[neighbour] = host
-            LOGGER.debug(f'Matched neighbour to host: |{neighbour}| -> |{host}|')
-            return host
-        else:
-            self.neighbour_to_host[neighbour] = None
-            LOGGER.debug(f'No match found for neighbour: |{neighbour}|')
-            return None
+        return hosts_str, open_hosts
 
     @abstractmethod
     def query_host(self, host: str) -> bool:
@@ -335,12 +468,13 @@ class HostCache:
         raise NotImplementedError
 
     @abstractmethod
-    def query_hosts_by_label(self, label: str) -> Sequence[str]:
+    def query_hosts_by_filter(self, filter_name: str, filter_value: str, filter_operator: str) -> MutableSequence[str]:
         """
         Queries Livestatus for a list of hosts filtered by a host label
         Args:
-            label: Host label to filter list of host by
-
+            filter_name: The column name to filter on
+            filter_value: the value for the attribute to filter on
+            filter_operator: Live status filter operator
         Returns: List of hosts
         """
         raise NotImplementedError
@@ -353,6 +487,7 @@ class HostCache:
     def query_interface_data(self, hosts: str) -> Dict[str, Dict]:
         raise NotImplementedError
 
+
 class HostCacheLiveStatus(HostCache):
     def __init__(
             self,
@@ -361,8 +496,8 @@ class HostCacheLiveStatus(HostCache):
     ):
         self.backend = backend
         super().__init__(
-            pre_fetch = pre_fetch,
-            backend = self.backend,
+            pre_fetch=pre_fetch,
+            backend=self.backend,
         )
 
     def get_raw_data(self, query: str) -> any:
@@ -378,9 +513,9 @@ class HostCacheLiveStatus(HostCache):
         data: Sequence[Sequence[str]] = self.get_raw_data(query=query)
         LOGGER.debug(f'{self.backend} data for host {host}: {data}')
         if [host] in data:
-            LOGGER.debug(f'{self.backend} Host {host} found in CMK')
+            LOGGER.debug(f'{self.backend} Host found in Checkmk: {host} ')
             return True
-        LOGGER.warning(f'{self.backend} Host {host} not found in CMK')
+        LOGGER.warning(f'{self.backend} Host not found in Checkmk: {host}')
         return False
 
     def query_all_hosts(self) -> Sequence[str]:
@@ -397,20 +532,20 @@ class HostCacheLiveStatus(HostCache):
         LOGGER.warning(f'{self.backend} no hosts found')
         return []
 
-    def query_hosts_by_label(self, label: str) -> Sequence[str]:
+    def query_hosts_by_filter(self, filter_name: str, filter_value: str, filter_operator: str) -> MutableSequence[str]:
         query = (
             'GET hosts\n'
             'Columns: name\n'
             'OutputFormat: python3\n'
-            f'Filter: labels = {label}\n'
+            f'Filter: {filter_name} {filter_operator} {filter_value}\n'
         )
         data: Sequence[Sequence[str]] = self.get_raw_data(query=query)
-        LOGGER.debug(f'{self.backend} hosts matching label: {data}')
+        LOGGER.debug(f'{self.backend} hosts matching filter {filter_value}: {data}')
         if data:
-            LOGGER.info(f'{self.backend} # of hosts found: {len(data)}')
+            LOGGER.info(f'{self.backend} # of hosts matching filter {filter_value}: {len(data)}')
             return [host[0] for host in data]
 
-        LOGGER.warning(f'{self.backend} no hosts found matching label {label}')
+        LOGGER.warning(f'{self.backend} no hosts found matching filter: {filter_value}')
         return []
 
     def query_inventory_data(self, hosts: str) -> Dict[str, Dict]:
@@ -426,7 +561,7 @@ class HostCacheLiveStatus(HostCache):
         if data:
             for host, inventory in data:
                 if not inventory:
-                    LOGGER.warning(f'{self.backend} Device: {host}: no inventory data found!')
+                    LOGGER.warning(f'{self.backend} No inventory data found for host: {host}')
                     continue
                 inventory = literal_eval(inventory.decode('utf-8'))
                 inventory_data[host] = inventory
@@ -445,7 +580,7 @@ class HostCacheLiveStatus(HostCache):
         )
         interface_data = {}
         data: List[Tuple[str, str, str]] = self.get_raw_data(query=query)
-        LOGGER.debug(f'{self.backend} interface data for hosts {hosts}: {data}')
+        LOGGER.debug(f'{self.backend} interface data for hosts: {hosts}: {data}')
         if data:
             for host, description, long_plugin_output in data:
                 if interface_data.get(host) is None:
@@ -454,18 +589,19 @@ class HostCacheLiveStatus(HostCache):
                     'long_plugin_output': long_plugin_output.split('\\n')
                 }
         else:
-            LOGGER.warning(f'{self.backend} No Interfaces items found for hosts {hosts}')
+            LOGGER.warning(f'{self.backend} No Interfaces items found for hosts: {hosts}')
 
         return interface_data
 
+
 class HostCacheMultiSite(HostCacheLiveStatus):
     def __init__(
-        self,
-        pre_fetch: bool,
-        filter_sites: str | None = None,
-        sites: List[str] | None = None,
-        filter_customers: str | None = None,
-        customers: List[str] = None,
+            self,
+            pre_fetch: bool,
+            filter_sites: str | None = None,
+            sites: List[str] | None = None,
+            filter_customers: str | None = None,
+            customers: List[str] = None,
     ):
         if not sites:
             sites = []
@@ -481,7 +617,7 @@ class HostCacheMultiSite(HostCacheLiveStatus):
         self.dead_sites = [site['site']['alias'] for site in self.c.dead_sites().values()]
         if self.dead_sites:
             dead_sites = ', '.join(self.dead_sites)
-            LOGGER.warning(f'{self.backend} use of dead site(s) {dead_sites} is disabled')
+            LOGGER.warning(f'{self.backend} use of dead site(s) is disabled: {dead_sites}')
             self.c.set_only_sites(self.c.alive_sites())
         super().__init__(
             pre_fetch=pre_fetch,
@@ -535,7 +671,7 @@ class HostCacheMultiSite(HostCacheLiveStatus):
             }})
 
             LOGGER.critical(
-                f'{self.backend} file {str(sites_mk.absolute())} not found. Fallback to '
+                f'{self.backend} file {str(sites_mk.absolute())} not found. Falling back to '
                 'local site only. Try -b RESTAPI if you have a distributed environment.'
             )
 
@@ -561,6 +697,7 @@ class HostCacheMultiSite(HostCacheLiveStatus):
             case _:
                 return
 
+
 class HostCacheRestApi(HostCache):
     def __init__(
             self,
@@ -580,7 +717,7 @@ class HostCacheRestApi(HostCache):
             ).read_text().strip('\n')
         except FileNotFoundError as e:
             LOGGER.exception(f'{self.backend} automation.secret not found, {e}')
-            print(f'{self.backend} automation.secret not found, {e}')
+            print(f'{self.backend} automation secret not found, {e}')
             sys_exit(ExitCodes.AUTOMATION_SECRET_NOT_FOUND)
 
         self.__api_port = api_port
@@ -593,21 +730,30 @@ class HostCacheRestApi(HostCache):
         self.__session = session()
         self.__session.headers['Authorization'] = f"Bearer {self.__user} {self.__secret}"
         self.__session.headers['Accept'] = 'application/json'
-
-        self.sites: MutableSequence[str]  = self.query_sites()
+        self.__session.headers['Content-Type'] = 'application/json'
+        self.sites: MutableSequence[str] = self.query_sites()
         self.filter_sites(filter_=filter_sites, sites=sites)
-        LOGGER.info(f'{self.backend} filtered sites : {self.sites}')
+        LOGGER.info(f'{self.backend} filtered sites: {self.sites}')
         super().__init__(
             pre_fetch=pre_fetch,
             backend=self.backend,
         )
 
-    def get_raw_data(self, url: str, params: Mapping[str, object] | None):
-        resp = self.__session.get(
-            url=url,
-            params=params,
-        )
+    def get_raw_data(self, url: str, params: Mapping[str, object] | None, allow_post: bool = False):
+        if allow_post and self.use_post:
+            resp = self.__session.post(
+                url=url,
+                data=dumps(params),
+                # timeout=3,
+            )
+        else:
+            resp = self.__session.get(
+                url=url,
+                params=params,
+                # timeout=3,
+            )
         LOGGER.debug(f'{self.backend} raw data: {resp.text}')
+
         if resp.status_code == 200:
             return resp.json()
         else:
@@ -617,10 +763,10 @@ class HostCacheRestApi(HostCache):
         LOGGER.debug(f'{self.backend} get_sites')
         url = f"{self.__api_url}/domain-types/site_connection/collections/all"
         sites = []
-        if raw_data:= self.get_raw_data(url, None):
+        if raw_data := self.get_raw_data(url, None):
             raw_sites = raw_data.get("value")
             sites = [site.get('id') for site in raw_sites]
-            LOGGER.debug(f'{self.backend} sites : {sites}')
+            LOGGER.debug(f'{self.backend} sites: {sites}')
         else:
             LOGGER.warning(f'{self.backend} got no site information!')
 
@@ -644,14 +790,15 @@ class HostCacheRestApi(HostCache):
             'sites': self.sites,
         }
 
-        if raw_data := self.get_raw_data(url, params):
+        if raw_data := self.get_raw_data(url, params, self.use_post):
             try:
                 data = raw_data['value'][0]['extensions']['name']
                 LOGGER.debug(f'{self.backend} data for host {host}: {data}')
             except IndexError:
-                LOGGER.warning(f'Host {host} not found in CMK')
+                LOGGER.warning(f'{self.backend} Host not found in Checkmk: {host}')
             else:
                 if data == host:
+                    LOGGER.debug(f'{self.backend} Host found in Checkmk: {host}')
                     return True
 
         return False
@@ -663,29 +810,31 @@ class HostCacheRestApi(HostCache):
             'sites': self.sites,
         }
 
-        if raw_data := self.get_raw_data(url, params):
+        if raw_data := self.get_raw_data(url, params, self.use_post):
             if data := raw_data.get('value', []):
                 LOGGER.info(f'{self.backend} # of hosts found: {len(data)}')
                 return [host.get('extensions', {}).get('name') for host in data]
 
+        LOGGER.warning(f'{self.backend} no hosts found')
         return []
 
-    def query_hosts_by_label(self, label: str) -> Sequence[str]:
-        query = '{"op": "=", "left": "labels", "right": "' + label + '"}'
-
+    def query_hosts_by_filter(self, filter_name: str, filter_value: str, filter_operator: str) -> MutableSequence[str]:
+        query = '{"op": "' + filter_operator + '", "left": "' + filter_name + '", "right": "' + filter_value + '"}'
         url = f'{self.__api_url}/domain-types/host/collections/all'
         params = {
-            'columns': ['name', 'labels'],
+            'columns': ['name'],
             'query': query,
             'sites': self.sites,
         }
 
-        if raw_data := self.get_raw_data(url, params):
+        raw_data = self.get_raw_data(url, params, self.use_post)
+        LOGGER.debug(f'{self.backend} hosts matching filter {filter_value}: {raw_data}')
+        if raw_data:
             if data := raw_data.get('value'):
-                LOGGER.info(f'{self.backend} # of hosts found: {len(data)}')
+                LOGGER.info(f'{self.backend} # of hosts matching filter {filter_value}: {len(data)}')
                 return [host['extensions']['name'] for host in data]
 
-        LOGGER.warning(f'{self.backend} no hosts found matching label {label}')
+        LOGGER.warning(f'{self.backend} no hosts found matching filter: {filter_value}')
         return []
 
     def query_inventory_data(self, hosts: str) -> Dict[str, Dict]:
@@ -699,23 +848,23 @@ class HostCacheRestApi(HostCache):
 
         inventory_data = {}
 
-        if raw_data := self.get_raw_data(url, params):
-            LOGGER.debug(f'{self.backend} raw inventory data: {raw_data}')
+        raw_data = self.get_raw_data(url, params, self.use_post)
+        LOGGER.debug(f'{self.backend} raw inventory data: {raw_data}')
+        if raw_data:
             if data := raw_data.get('value', []):
                 for raw_host in data:
                     if host := raw_host.get('extensions', {}).get('name'):
                         inventory = raw_host['extensions'].get('mk_inventory')
                         if not inventory:
-                            LOGGER.warning(f'{self.backend} Device: {host}: no inventory data found!')
+                            LOGGER.warning(f'{self.backend} No inventory data found for host: {host}!')
                         inventory_data[host] = inventory
 
         return inventory_data
 
     def query_interface_data(self, hosts: str) -> Dict[str, Dict]:
-        query_host = f'{{"op": "~~", "left": "host_name", "right": "{hosts}"}}'
-        query_item = '{"op": "~", "left": "description", "right": "Interface "}'
-        query = f'{{"op": "and", "expr": [{query_item},{query_host}]}}'
-
+        query_host = '{"op": "~~", "left": "host_name", "right": "' + hosts + '"}'
+        query_item = '{"op": "~", "left": "description", "right": "^Interface "}'
+        query = '{"op": "and", "expr": [' + query_host + ',' + query_item +']}'
         url = f'{self.__api_url}/domain-types/service/collections/all'
         params = {
             'query': query,
@@ -725,15 +874,16 @@ class HostCacheRestApi(HostCache):
 
         interface_data = {}
 
-        if raw_data := self.get_raw_data(url, params):
-            LOGGER.debug(f'{self.backend} raw interface data: {raw_data}')
-
+        raw_data = self.get_raw_data(url, params) #  , self.use_post  -> Endpoint not changed to Post :-(
+        LOGGER.debug(f'{self.backend} interface data for host(s) {hosts}: {raw_data}')
+        if raw_data:
             if data := raw_data.get('value', []):
                 for raw_service in data:
-                    LOGGER.debug(f'{self.backend} data for service : {raw_service}')
+                    LOGGER.debug(f'{self.backend} data for service: {raw_service}')
                     service = raw_service.get('extensions')
                     host, description, long_plugin_output = service.values()
                     if interface_data.get(host) is None:
+                        # LOGGER.warning(f'{self.backend} No Interfaces items found for hosts {host}')
                         interface_data[host] = {}
                     interface_data[host][description[10:]] = {
                         'long_plugin_output': long_plugin_output.split('\\n')
diff --git a/source/bin/nvdct/lib/constants.py b/source/bin/nvdct/lib/constants.py
index 4dd6c55..579eb0d 100755
--- a/source/bin/nvdct/lib/constants.py
+++ b/source/bin/nvdct/lib/constants.py
@@ -13,45 +13,47 @@ from os import environ
 from typing import Final
 
 #
-NVDCT_VERSION: Final[str] = '0.9.7-20241230'
+NVDCT_VERSION: Final[str] = '0.9.8-20250107'
 #
 OMD_ROOT: Final[str] = environ["OMD_ROOT"]
 #
-API_PORT: Final[int] = 5001
+API_PORT_DEFAULT: Final[int] = 5001
 CACHE_INTERFACES_DATA: Final[str] = 'interface_data'
 CMK_SITE_CONF: Final[str] = f'{OMD_ROOT}/etc/omd/site.conf'
+CONFIG_FILE: Final[str] = 'nvdct.toml'
+DATAPATH: Final[str] = f'{OMD_ROOT}/var/check_mk/topology/data'
 LOGGER: Logger = getLogger('root)')
-LOG_FILE: Final[str] = f'{OMD_ROOT}/var/log/nvdct.log'
+LOG_FILE_DEFAULT: Final[str] = f'{OMD_ROOT}/var/log/nvdct.log'
+MIN_CMK_VERSION_POST: Final[str] = '2.3.0p23'
 SCRIPT: Final[str] = '~/local/bin/nvdct/nvdct.py'
-TIME_FORMAT: Final[str] = '%Y-%m-%dT%H:%M:%S.%m'
 TIME_FORMAT_ARGPARSER: Final[str] = '%%Y-%%m-%%dT%%H:%%M:%%S.%%m'
-USER_DATA_FILE: Final[str] = 'nvdct.toml'
-DATAPATH: Final[str] = f'{OMD_ROOT}/var/check_mk/topology/data'
+TIME_FORMAT_DEFAULT: Final[str] = '%Y-%m-%dT%H:%M:%S.%m'
 
 
-class MyEnum(Enum):
+class EnumValue(Enum):
     def __get__(self, instance, owner):
         return self.value
 
 
 @unique
-class ExitCodes(MyEnum):
+class ExitCodes(EnumValue):
     OK = 0
     BAD_OPTION_LIST = auto()
     BAD_TOML_FORMAT = auto()
     BACKEND_NOT_IMPLEMENTED = auto()
     AUTOMATION_SECRET_NOT_FOUND = auto()
     NO_LAYER_CONFIGURED = auto()
+    FILE_NOT_FOUND = auto()
 
 
 @unique
-class IPVersion(MyEnum):
+class IPVersion(EnumValue):
     IPv4 = 4
     IPv6 = 6
 
 
 @unique
-class URLs(MyEnum):
+class URLs(EnumValue):
     NVDCT: Final[str] = 'https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/nvdct'
     # CDP: Final[str] = 'https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/inventory/inv_cdp_cache'
     # LLDP: Final[str] = 'LLDP: https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/inventory/inv_lldp_cach'
@@ -63,60 +65,84 @@ class URLs(MyEnum):
 
 
 @unique
-class Backends(MyEnum):
+class Backends(EnumValue):
     LIVESTATUS: Final[str] = 'LIVESTATUS'
     MULTISITE: Final[str] = 'MULTISITE'
     RESTAPI: Final[str] = 'RESTAPI'
 
 
 @unique
-class Case(MyEnum):
+class Case(EnumValue):
+    AUTO: Final[str] = 'AUTO'
+    INSENSITIVE: Final[str] = 'INSENSITIVE'
     LOWER: Final[str] = 'LOWER'
     UPPER: Final[str] = 'UPPER'
+    OFF: Final[str] = 'OFF'
+
+
+@unique
+class RemoveDomain(EnumValue):
+    ON: Final[str] = 'ON'
+    OFF: Final[str] = 'OFF'
+    AUTO: Final[str] = 'AUTO'
+
 
 @unique
-class CacheItems(MyEnum):
+class CacheItems(EnumValue):
     inventory = 'inventory'
     interfaces = 'interfaces'
 
+
 @unique
-class CliLong(MyEnum):
-    ADJUST_TOML: Final[str] = '--adjust-toml'
+class CliLong(EnumValue):
     API_PORT: Final[str] = '--api-port'
     BACKEND: Final[str] = '--backend'
-    CASE: Final[str] = '--case'
-    CHECK_USER_DATA_ONLY: Final[str] = '--check-user-data-only'
+    CHECK_CONFIG: Final[str] = '--check-config'
+    CONFIG: Final[str] = '--config'
     DEFAULT: Final[str] = '--default'
-    DISPLAY_L2_NEIGHBOURS: Final[str] = '--display-l2-neighbours'
     DONT_COMPARE: Final[str] = '--dont-compare'
     FILTER_CUSTOMERS: Final[str] = '--filter-customers'
     FILTER_SITES: Final[str] = '--filter-sites'
-    INCLUDE_L3_HOSTS: Final[str] = '--include-l3-hosts'
-    INCLUDE_L3_LOOPBACK: Final[str] = '--include-l3-loopback'
-    KEEP: Final[str] = '--keep'
+    KEEP_MAX_TOPOLOGIES: Final[str] = '--keep-max-topologies'
+    L2_CASE: Final[str] = '--l2-case'
+    L2_DISPLAY_NEIGHBOURS: Final[str] = '--l2-display-neighbours'
+    L2_DISPLAY_PORTS: Final[str] = '--l2-display-ports'
+    L2_PREFIX: Final[str] = '--l2-prefix'
+    L2_REMOVE_DOMAIN: Final[str] = '--l2-remove-domain'
+    L2_SKIP_EXTERNAL: Final[str] = '--l2-skip-external'
+    L3_DISPLAY_DEVICES: Final[str] = '--l3-display-devices'
+    L3_INCLUDE_HOSTS: Final[str] = '--l3-include-hosts'
+    L3_INCLUDE_LOOPBACK: Final[str] = '--l3-include-loopback'
+    L3_SKIP_CIDR_0: Final[str] = '--l3-skip-cidr-0'
+    L3_SKIP_CIDR_32_128: Final[str] = '--l3-skip-cidr-32-128'
+    L3_SKIP_IF: Final[str] = '--l3-skip-if'
+    L3_SKIP_IP: Final[str] = '--l3-skip-ip'
+    L3_SKIP_PUBLIC: Final[str] = '--l3-skip-public'
     LAYERS: Final[str] = '--layers'
     LOG_FILE: Final[str] = '--log-file'
     LOG_LEVEL: Final[str] = '--log-level'
     LOG_TO_STDOUT: Final[str] = '--log-to-stdout'
-    MIN_AGE: Final[str] = '--min-age'
+    MIN_TOPOLOGY_AGE: Final[str] = '--min-topology-age'
     OUTPUT_DIRECTORY: Final[str] = '--output-directory'
-    PREFIX: Final[str] = '--prefix'
     PRE_FETCH: Final[str] = '--pre-fetch'
     QUIET: Final[str] = '--quiet'
-    REMOVE_DOMAIN: Final[str] = '--remove-domain'
-    SEED_DEVICES: Final[str] = '--seed-devices'
-    SKIP_L3_CIDR_0: Final[str] = '--skip-l3-cidr-0'
-    SKIP_L3_CIDR_32_128: Final[str] = '--skip-l3-cidr-32-128'
-    SKIP_L3_IF: Final[str] = '--skip-l3-if'
-    SKIP_L3_IP: Final[str] = '--skip-l3-ip'
-    SKIP_L3_PUBLIC: Final[str] = '--skip-l3-public'
     TIME_FORMAT: Final[str] = '--time-format'
-    USER_DATA_FILE: Final[str] = '--user-data-file'
+    UPDATE_CONFIG: Final[str] = '--update-config'
     VERSION: Final[str] = '--version'
 
 
 @unique
-class EmblemNames(MyEnum):
+class CliShort(EnumValue):
+    BACKEND: Final[str] = '-b'
+    CONFIG: Final[str] = '-c'
+    DEFAULT: Final[str] = '-d'
+    LAYERS: Final[str] = '-l'
+    OUTPUT_DIRECTORY: Final[str] = '-o'
+    VERSION: Final[str] = '-v'
+
+
+@unique
+class EmblemNames(EnumValue):
     HOST_NODE: Final[str] = 'host_node'
     IP_ADDRESS: Final[str] = 'ip_address'
     IP_NETWORK: Final[str] = 'ip_network'
@@ -126,7 +152,7 @@ class EmblemNames(MyEnum):
 
 
 @unique
-class EmblemValues(MyEnum):
+class EmblemValues(EnumValue):
     ICON_AGGREGATION: Final[str] = 'icon_aggr'
     ICON_ALERT_UNREACHABLE: Final[str] = 'icon_alert_unreach'
     ICON_PLUGINS_CLOUD: Final[str] = 'icon_plugins_cloud'
@@ -135,7 +161,7 @@ class EmblemValues(MyEnum):
 
 
 @unique
-class HostLabels(MyEnum):
+class HostLabels(EnumValue):
     CDP: Final[str] = "'nvdct/has_cdp_neighbours' 'yes'"
     L3V4_HOSTS: Final[str] = "'nvdct/l3v4_topology' 'host'"
     L3V4_ROUTER: Final[str] = "'nvdct/l3v4_topology' 'router'"
@@ -145,36 +171,53 @@ class HostLabels(MyEnum):
 
 
 @unique
-class IncludeExclude(MyEnum):
+class HostFilter(EnumValue):
+    FOLDER: Final[str] = 'filename'
+    LABELS: Final[str] = 'labels'
+    TAGS: Final[str] = 'tags'
+
+
+@unique
+class LiveStatusOperator(EnumValue):
+    EQUAL: Final[str] = '='
+    SUPERSET: Final[str] = '~'
+
+
+@unique
+class IncludeExclude(EnumValue):
     INCLUDE: Final[str] = 'INCLUDE'
     EXCLUDE: Final[str] = 'EXCLUDE'
 
 
 @unique
-class L2InvColumns(MyEnum):
+class L2InvColumns(EnumValue):
     NEIGHBOUR: Final[str] = 'neighbour_name'
     LOCALPORT: Final[str] = 'local_port'
     NEIGHBOURPORT: Final[str] = 'neighbour_port'
+    NEIGHBOURID: Final[str] = 'neighbour_id'
+    GLOBALID: Final[str] = 'local_id'
+    GLOBALNAME: Final[str] = 'local_name'
 
 
 @unique
-class L3InvColumns(MyEnum):
+class L3InvColumns(EnumValue):
     ADDRESS: Final[str] = 'address'
     DEVICE: Final[str] = 'device'
     CIDR: Final[str] = 'cidr'
 
 
 @unique
-class InvPaths(MyEnum):
-    CDP: Final[str] = 'networking,cdp_cache,neighbours'
-    INTERFACES: Final[str] = 'networking,interfaces'
-    L3: Final[str] = 'networking,addresses'
-    LLDP: Final[str] = 'networking,lldp_cache,neighbours'
-    LLDP_ATTRIBUTE: Final[str] = 'networking,lldp_cache'
+class InvPaths(EnumValue):
+    CDP: Final[str] = 'networking,cdp_cache,neighbours,Table,Rows'
+    CDP_GLOBAL: Final[str] = 'networking,cdp_cache,Attributes,Pairs'
+    INTERFACES: Final[str] = 'networking,interfaces,Table,Rows'
+    L3: Final[str] = 'networking,addresses,Table,Rows'
+    LLDP: Final[str] = 'networking,lldp_cache,neighbours,Table,Rows'
+    LLDP_GLOBAL: Final[str] = 'networking,lldp_cache,Attributes,Pairs'
 
 
 @unique
-class Layers(MyEnum):
+class Layers(EnumValue):
     CDP: Final[str] = 'CDP'
     LLDP: Final[str] = 'LLDP'
     L3V4: Final[str] = 'L3v4'
@@ -183,7 +226,7 @@ class Layers(MyEnum):
 
 
 @unique
-class LogLevels(MyEnum):
+class LogLevels(EnumValue):
     CRITICAL: Final[str] = 'CRITICAL'
     FATAL: Final[str] = 'FATAL'
     ERROR: Final[str] = 'ERROR'
@@ -193,7 +236,7 @@ class LogLevels(MyEnum):
     OFF: Final[str] = 'OFF'
 
 
-class MinVersions(MyEnum):
+class MinVersions(EnumValue):
     CDP: Final[str] = '0.7.1-20240320'
     LLDP: Final[str] = '0.9.3-20240320'
     LINUX_IP_ADDRESSES: Final[str] = '0.0.4-20241210'
@@ -202,22 +245,25 @@ class MinVersions(MyEnum):
 
 
 @unique
-class TomlSections(MyEnum):
-    CUSTOMERS: Final[str] = 'CUSTOMERS'
+class TomlSections(EnumValue):
     EMBLEMS: Final[str] = 'EMBLEMS'
+    FILTER_BY_CUSTOMER: Final[str] = 'FILTER_BY_CUSTOMER'
+    FILTER_BY_FOLDER: Final[str] = 'FILTER_BY_FOLDER'
+    FILTER_BY_HOST_LABEL: Final[str] = 'FILTER_BY_HOST_LABEL'
+    FILTER_BY_HOST_TAG: Final[str] = 'FILTER_BY_HOST_TAG'
+    FILTER_BY_SITE: Final[str] = 'FILTER_BY_SITE'
     L2_DROP_NEIGHBOURS: Final[str] = 'L2_DROP_NEIGHBOURS'
-    L2_HOST_MAP: Final[str] = 'L2_HOST_MAP'
     L2_NEIGHBOUR_REPLACE_REGEX: Final[str] = 'L2_NEIGHBOUR_REPLACE_REGEX'
+    L2_NEIGHBOUR_TO_HOST_MAP: Final[str] = 'L2_NEIGHBOUR_TO_HOST_MAP'
     L2_SEED_DEVICES: Final[str] = 'L2_SEED_DEVICES'
     L3V4_IGNORE_WILDCARD: Final[str] = 'L3V4_IGNORE_WILDCARD'
     L3_IGNORE_HOSTS: Final[str] = 'L3_IGNORE_HOSTS'
     L3_IGNORE_IP: Final[str] = 'L3_IGNORE_IP'
-    L3_REPLACE: Final[str] = 'L3_REPLACE'
+    L3_REPLACE_NETWORKS: Final[str] = 'L3_REPLACE_NETWORKS'
     L3_SUMMARIZE: Final[str] = 'L3_SUMMARIZE'
     MAP_SPEED_TO_THICKNESS: Final[str] = 'MAP_SPEED_TO_THICKNESS'
     PROTECTED_TOPOLOGIES: Final[str] = 'PROTECTED_TOPOLOGIES'
     SETTINGS: Final[str] = 'SETTINGS'
-    SITES: Final[str] = 'SITES'
     STATIC_CONNECTIONS: Final[str] = 'STATIC_CONNECTIONS'
 
 
@@ -226,35 +272,37 @@ def cli_long_to_toml(cli_param: str) -> str:
 
 
 @unique
-class TomlSettings(MyEnum):
-    ADJUST_TOML: Final[str] = cli_long_to_toml(CliLong.ADJUST_TOML)
+class TomlSettings(EnumValue):
     API_PORT: Final[str] = cli_long_to_toml(CliLong.API_PORT)
     BACKEND: Final[str] = cli_long_to_toml(CliLong.BACKEND)
-    CASE: Final[str] = cli_long_to_toml(CliLong.CASE)
-    CHECK_USER_DATA_ONLY: Final[str] = cli_long_to_toml(CliLong.CHECK_USER_DATA_ONLY)
+    CHECK_CONFIG: Final[str] = cli_long_to_toml(CliLong.CHECK_CONFIG)
+    CONFIG: Final[str] = cli_long_to_toml(CliLong.CONFIG)
     DEFAULT: Final[str] = cli_long_to_toml(CliLong.DEFAULT)
-    DISPLAY_L2_NEIGHBOURS: Final[str] = cli_long_to_toml(CliLong.DISPLAY_L2_NEIGHBOURS)
     DONT_COMPARE: Final[str] = cli_long_to_toml(CliLong.DONT_COMPARE)
     FILTER_CUSTOMERS: Final[str] = cli_long_to_toml(CliLong.FILTER_CUSTOMERS)
     FILTER_SITES: Final[str] = cli_long_to_toml(CliLong.FILTER_SITES)
-    INCLUDE_L3_HOSTS: Final[str] = cli_long_to_toml(CliLong.INCLUDE_L3_HOSTS)
-    INCLUDE_L3_LOOPBACK: Final[str] = cli_long_to_toml(CliLong.INCLUDE_L3_LOOPBACK)
-    KEEP: Final[str] = cli_long_to_toml(CliLong.KEEP)
+    KEEP_MAX_TOPOLOGIES: Final[str] = cli_long_to_toml(CliLong.KEEP_MAX_TOPOLOGIES)
+    L2_CASE: Final[str] = cli_long_to_toml(CliLong.L2_CASE)
+    L2_DISPLAY_NEIGHBOURS: Final[str] = cli_long_to_toml(CliLong.L2_DISPLAY_NEIGHBOURS)
+    L2_DISPLAY_PORTS: Final[str] = cli_long_to_toml(CliLong.L2_DISPLAY_PORTS)
+    L2_PREFIX: Final[str] = cli_long_to_toml(CliLong.L2_PREFIX)
+    L2_REMOVE_DOMAIN: Final[str] = cli_long_to_toml(CliLong.L2_REMOVE_DOMAIN)
+    L2_SKIP_EXTERNAL: Final[str] = cli_long_to_toml(CliLong.L2_SKIP_EXTERNAL)
+    L3_DISPLAY_DEVICES: Final[str] = cli_long_to_toml(CliLong.L3_DISPLAY_DEVICES)
+    L3_INCLUDE_HOSTS: Final[str] = cli_long_to_toml(CliLong.L3_INCLUDE_HOSTS)
+    L3_INCLUDE_LOOPBACK: Final[str] = cli_long_to_toml(CliLong.L3_INCLUDE_LOOPBACK)
+    L3_SKIP_CIDR_0: Final[str] = cli_long_to_toml(CliLong.L3_SKIP_CIDR_0)
+    L3_SKIP_CIDR_32_128: Final[str] = cli_long_to_toml(CliLong.L3_SKIP_CIDR_32_128)
+    L3_SKIP_IF: Final[str] = cli_long_to_toml(CliLong.L3_SKIP_IF)
+    L3_SKIP_IP: Final[str] = cli_long_to_toml(CliLong.L3_SKIP_IP)
+    L3_SKIP_PUBLIC: Final[str] = cli_long_to_toml(CliLong.L3_SKIP_PUBLIC)
     LAYERS: Final[str] = cli_long_to_toml(CliLong.LAYERS)
     LOG_FILE: Final[str] = cli_long_to_toml(CliLong.LOG_FILE)
     LOG_LEVEL: Final[str] = cli_long_to_toml(CliLong.LOG_LEVEL)
     LOG_TO_STDOUT: Final[str] = cli_long_to_toml(CliLong.LOG_TO_STDOUT)
-    MIN_AGE: Final[str] = cli_long_to_toml(CliLong.MIN_AGE)
+    MIN_TOPOLOGY_AGE: Final[str] = cli_long_to_toml(CliLong.MIN_TOPOLOGY_AGE)
     OUTPUT_DIRECTORY: Final[str] = cli_long_to_toml(CliLong.OUTPUT_DIRECTORY)
-    PREFIX: Final[str] = cli_long_to_toml(CliLong.PREFIX)
     PRE_FETCH: Final[str] = cli_long_to_toml(CliLong.PRE_FETCH)
     QUIET: Final[str] = cli_long_to_toml(CliLong.QUIET)
-    REMOVE_DOMAIN: Final[str] = cli_long_to_toml(CliLong.REMOVE_DOMAIN)
-    SKIP_L3_CIDR_0: Final[str] = cli_long_to_toml(CliLong.SKIP_L3_CIDR_0)
-    SKIP_L3_CIDR_32_128: Final[str] = cli_long_to_toml(CliLong.SKIP_L3_CIDR_32_128)
-    SKIP_L3_IF: Final[str] = cli_long_to_toml(CliLong.SKIP_L3_IF)
-    SKIP_L3_IP: Final[str] = cli_long_to_toml(CliLong.SKIP_L3_IP)
-    SKIP_L3_PUBLIC: Final[str] = cli_long_to_toml(CliLong.SKIP_L3_PUBLIC)
     TIME_FORMAT: Final[str] = cli_long_to_toml(CliLong.TIME_FORMAT)
-    USER_DATA_FILE: Final[str] = cli_long_to_toml(CliLong.USER_DATA_FILE)
-
+    UPDATE_CONFIG: Final[str] = cli_long_to_toml(CliLong.UPDATE_CONFIG)
diff --git a/source/bin/nvdct/lib/settings.py b/source/bin/nvdct/lib/settings.py
index 5b82bff..e069f0c 100755
--- a/source/bin/nvdct/lib/settings.py
+++ b/source/bin/nvdct/lib/settings.py
@@ -9,6 +9,7 @@
 
 # fixed path to default user data file
 # 2024-12-17: fixed wrong import for OMD_ROOT (copy&paste) (ThX to BH2005@forum.checkmk.com)
+# 2025-01-19: moved --check-toml to utils/get_data_from_toml
 
 from collections.abc import Mapping
 from ipaddress import AddressValueError, NetmaskValueError, ip_address, ip_network
@@ -16,32 +17,31 @@ from logging import CRITICAL, DEBUG, ERROR, FATAL, INFO, WARNING
 from pathlib import Path
 from sys import exit as sys_exit
 from time import strftime
-from typing import Dict, List, NamedTuple, Tuple
+from typing import Dict, List, NamedTuple, Set, Tuple
 
 from lib.constants import (
-    API_PORT,
+    API_PORT_DEFAULT,
     Backends,
+    CONFIG_FILE,
     Case,
-    EmblemValues,
     EmblemNames,
+    EmblemValues,
     ExitCodes,
     IncludeExclude,
     LOGGER,
-    LOG_FILE,
+    LOG_FILE_DEFAULT,
     LogLevels,
     OMD_ROOT,
-    TIME_FORMAT,
+    RemoveDomain,
+    TIME_FORMAT_DEFAULT,
     TomlSections,
     TomlSettings,
-    USER_DATA_FILE,
-
 )
 from lib.utils import (
     get_data_from_toml,
     get_local_cmk_api_port,
     is_valid_customer_name,
     is_valid_hostname,
-    is_valid_log_file,
     is_valid_output_directory,
     is_valid_site_name,
 )
@@ -83,36 +83,39 @@ class Settings:
     ):
         # cli defaults
         self.__settings = {
-            TomlSettings.ADJUST_TOML: False,
             TomlSettings.API_PORT: None,
             TomlSettings.BACKEND: Backends.MULTISITE,
-            TomlSettings.CASE: None,
-            TomlSettings.CHECK_USER_DATA_ONLY: False,
+            TomlSettings.CHECK_CONFIG: False,
+            TomlSettings.CONFIG: f'{OMD_ROOT}/local/bin/nvdct/conf/{CONFIG_FILE}',
             TomlSettings.DEFAULT: False,
-            TomlSettings.DISPLAY_L2_NEIGHBOURS: False,
             TomlSettings.DONT_COMPARE: False,
             TomlSettings.FILTER_CUSTOMERS: None,
             TomlSettings.FILTER_SITES: None,
-            TomlSettings.INCLUDE_L3_HOSTS: False,
-            TomlSettings.INCLUDE_L3_LOOPBACK: False,
-            TomlSettings.KEEP: False,
+            TomlSettings.KEEP_MAX_TOPOLOGIES: False,
+            TomlSettings.L2_CASE: None,
+            TomlSettings.L2_DISPLAY_PORTS: False,
+            TomlSettings.L2_DISPLAY_NEIGHBOURS: False,
+            TomlSettings.L2_PREFIX: None,
+            TomlSettings.L2_REMOVE_DOMAIN: None,
+            TomlSettings.L2_SKIP_EXTERNAL: False,
+            TomlSettings.L3_DISPLAY_DEVICES: False,
+            TomlSettings.L3_INCLUDE_HOSTS: False,
+            TomlSettings.L3_INCLUDE_LOOPBACK: False,
+            TomlSettings.L3_SKIP_CIDR_0: False,
+            TomlSettings.L3_SKIP_CIDR_32_128: False,
+            TomlSettings.L3_SKIP_IF: False,
+            TomlSettings.L3_SKIP_IP: False,
+            TomlSettings.L3_SKIP_PUBLIC: False,
             TomlSettings.LAYERS: [],
-            TomlSettings.LOG_FILE: LOG_FILE,
+            TomlSettings.LOG_FILE: LOG_FILE_DEFAULT,
             TomlSettings.LOG_LEVEL: LogLevels.WARNING,
             TomlSettings.LOG_TO_STDOUT: False,
-            TomlSettings.MIN_AGE: 0,
+            TomlSettings.MIN_TOPOLOGY_AGE: 0,
             TomlSettings.OUTPUT_DIRECTORY: None,
-            TomlSettings.PREFIX: None,
             TomlSettings.PRE_FETCH: False,
             TomlSettings.QUIET: False,
-            TomlSettings.REMOVE_DOMAIN: False,
-            TomlSettings.SKIP_L3_CIDR_0: False,
-            TomlSettings.SKIP_L3_CIDR_32_128: False,
-            TomlSettings.SKIP_L3_IF: False,
-            TomlSettings.SKIP_L3_IP: False,
-            TomlSettings.SKIP_L3_PUBLIC: False,
-            TomlSettings.TIME_FORMAT: TIME_FORMAT,
-            TomlSettings.USER_DATA_FILE: f'{OMD_ROOT}/local/bin/nvdct/conf/{USER_DATA_FILE}',
+            TomlSettings.TIME_FORMAT: TIME_FORMAT_DEFAULT,
+            TomlSettings.UPDATE_CONFIG: False,
         }
         # args in the form {'s, __seed_devices': 'CORE01', 'p, __path_in_inventory': None, ... }}
         # we will remove 's, __'
@@ -121,14 +124,10 @@ class Settings:
         )
 
         self.__user_data = get_data_from_toml(
-            file=self.__args.get(TomlSettings.USER_DATA_FILE, self.user_data_file)
+            file=self.__args.get(TomlSettings.CONFIG, self.config),
+            check_only=bool(self.__args.get(TomlSettings.CHECK_CONFIG)),
         )
 
-        if self.__args.get(TomlSettings.CHECK_USER_DATA_ONLY):
-            LOGGER.info(msg=f'Could read/parse the user data from {self.user_data_file}')
-            print(f'Could read/parse the user data from {self.user_data_file}')
-            sys_exit(ExitCodes.OK)
-
         # defaults -> overridden by toml -> overridden by cli
         self.__settings.update(self.__user_data.get(TomlSections.SETTINGS, {}))
         self.__settings.update(self.__args)
@@ -136,20 +135,23 @@ class Settings:
         if self.layers:
             layers = list(set(self.layers))
             if len(layers) != len(self.layers):
-                LOGGER.error(
-                    msg='-l/--layers options must be unique. Don\'t use any layer more than once.'
-                )
+                # logger not initialized here
+                # LOGGER.fatal('-l/--layers options must be unique. Don\'t use any layer more than once.')
                 print('-l/--layers options must be unique. Don\'t use any layer more than once.')
                 sys_exit(ExitCodes.BAD_OPTION_LIST)
 
         self.__api_port: int | None = None
 
         # init user data with defaults
-        self.__customers: List[str] | None = None
         self.__emblems: Emblems | None = None
+        self.__filter_by_customer: List[str] | None = None
+        self.__filter_by_folder: Dict[str, Set[str]] | None = None
+        self.__filter_by_host_label: Dict[str, Set[str]] | None = None
+        self.__filter_by_host_tag: Dict[str, Set[str]] | None = None
+        self.__filter_by_site: List[str] | None = None
         self.__l2_drop_neighbours: List[str] | None = None
-        self.__l2_host_map: Dict[str, str] | None = None
         self.__l2_neighbour_replace_regex: List[Tuple[str, str]] | None = None
+        self.__l2_neighbour_to_host_map: Dict[str, str] | None = None
         self.__l2_seed_devices: List[str] | None = None
         self.__l3_ignore_hosts: List[str] | None = None
         self.__l3_ignore_ip: List[ip_network] | None = None
@@ -158,7 +160,6 @@ class Settings:
         self.__l3v4_ignore_wildcard: List[Wildcard] | None = None
         self.__map_speed_to_thickness: List[Thickness] | None = None
         self.__protected_topologies: List[str] | None = None
-        self.__sites: List[str] | None = None
         self.__static_connections: List[StaticConnection] | None = None
 
     #
@@ -172,7 +173,7 @@ class Settings:
             else:
                 self.__api_port = get_local_cmk_api_port()
             if self.__api_port is None:
-               self.__api_port = API_PORT
+               self.__api_port = API_PORT_DEFAULT
 
         return self.__api_port
 
@@ -185,35 +186,24 @@ class Settings:
         ]:
             return str(self.__settings[TomlSettings.BACKEND])
         else:  # fallback to defaukt -> exit ??
-            LOGGER.warning(
+            LOGGER.error(
                 f'Unknown backend: {self.__settings[TomlSettings.BACKEND]}. Accepted backends are: '
-                f'{Backends.LIVESTATUS}, {Backends.MULTISITE}, {Backends.RESTAPI}. Fall back to {Backends.MULTISITE}.'
+                f'{Backends.LIVESTATUS}, {Backends.MULTISITE}, {Backends.RESTAPI}. Falling back to {Backends.MULTISITE}.'
             )
             return Backends.MULTISITE
 
-    @property  # --case
-    def case(self) -> str | None:
-        if self.__settings[TomlSettings.CASE] in [Case.LOWER, Case.UPPER]:
-            return self.__settings[TomlSettings.CASE]
-        elif self.__settings[TomlSettings.CASE] is not None:
-            LOGGER.warning(
-                    f'Unknown case setting {self.__settings[TomlSettings.CASE]}. '
-                    f'Accepted are {Case.LOWER}|{Case.UPPER}. Fallback to no change.'
-                )
-        return None
+    @property  # --check-config
+    def check_config(self) -> bool:
+        return bool(self.__settings[TomlSettings.CHECK_CONFIG])
 
-    @property  # --check-user-data-only
-    def check_user_data_only(self) -> bool:
-        return bool(self.__settings[TomlSettings.CHECK_USER_DATA_ONLY])
+    @property  # --config
+    def config(self) -> str:
+        return str(self.__settings[TomlSettings.CONFIG])
 
     @property  # -d --default
     def default(self) -> bool:
         return bool(self.__settings[TomlSettings.DEFAULT])
 
-    @property  # --display-l2-neighbours
-    def display_l2_neighbours(self) -> bool:
-        return bool(self.__settings[TomlSettings.DISPLAY_L2_NEIGHBOURS])
-
     @property  # --dont-compare
     def dont_compare(self) -> bool:
         return bool(self.__settings[TomlSettings.DONT_COMPARE])
@@ -240,27 +230,74 @@ class Settings:
             )
         return None
 
-    @property  # --include-l3-hosts
-    def fix_toml(self) -> bool:
-        return bool(self.__settings[TomlSettings.ADJUST_TOML])
+    @property  # --l2-case
+    def l2_case(self) -> str | None:
+        if self.__settings[TomlSettings.L2_CASE] in [Case.LOWER, Case.UPPER, Case.INSENSITIVE, Case.AUTO, Case.OFF]:
+            return self.__settings[TomlSettings.L2_CASE]
+        elif self.__settings[TomlSettings.L2_CASE] is not None:
+            LOGGER.error(
+                    f'Unknown case setting {self.__settings[TomlSettings.L2_CASE]}. '
+                    f'Accepted are {Case.LOWER}|{Case.UPPER}|{Case.INSENSITIVE}|{Case.AUTO}|{Case.OFF}. Falling back to "OFF" (no change).'
+                )
+        return None
+
+    @property  # --l2-display-ports
+    def l2_display_ports(self) -> bool:
+        return bool(self.__settings[TomlSettings.L2_DISPLAY_PORTS])
+
+    @property  # --l2-display-neighbours
+    def l2_display_neighbours(self) -> bool:
+        return bool(self.__settings[TomlSettings.L2_DISPLAY_NEIGHBOURS])
+
+    @property  # --l2-prefix
+    def l2_prefix(self) -> str:
+        if self.__settings[TomlSettings.L2_PREFIX] is not None:
+            return str(self.__settings[TomlSettings.L2_PREFIX])
+        return ''
+
+    @property  # --l2-remove-domain
+    def l2_remove_domain(self) -> str:
+        if self.__settings[TomlSettings.L2_REMOVE_DOMAIN] in [RemoveDomain.ON, RemoveDomain.OFF, RemoveDomain.AUTO]:
+            return self.__settings[TomlSettings.L2_REMOVE_DOMAIN]
+        else:
+            self.__settings[TomlSettings.L2_REMOVE_DOMAIN] = RemoveDomain.OFF
+            return RemoveDomain.OFF
+
+    @property  # --l2-skip-external
+    def l2_skip_external(self) -> bool:
+        return bool(self.__settings[TomlSettings.L2_SKIP_EXTERNAL])
+
+    @property  # --l3-display-devices
+    def l3_display_devices(self) -> bool:
+        return bool(self.__settings[TomlSettings.L3_DISPLAY_DEVICES])
 
     @property  # --include-l3-hosts
-    def include_l3_hosts(self) -> bool:
-        return bool(self.__settings[TomlSettings.INCLUDE_L3_HOSTS])
+    def l3_include_hosts(self) -> bool:
+        return bool(self.__settings[TomlSettings.L3_INCLUDE_HOSTS])
 
-    @property  # --skip-l3-ip
-    def include_l3_loopback(self) -> bool:
-        return bool(self.__settings[TomlSettings.INCLUDE_L3_LOOPBACK])
+    @property  # --l3-skip-ip
+    def l3_include_loopback(self) -> bool:
+        return bool(self.__settings[TomlSettings.L3_INCLUDE_LOOPBACK])
 
-    @property  # --keep
-    def keep(self) -> int | None:
-        if isinstance(self.__settings[TomlSettings.KEEP], int):
-            return max(self.__settings[TomlSettings.KEEP], 0)
-        return None
+    @property  # --l3-skip-cidr-0
+    def l3_skip_cidr_0(self) -> bool:
+        return bool(self.__settings[TomlSettings.L3_SKIP_CIDR_0])
+
+    @property  # --l3-skip-cidr-32-128
+    def l3_skip_cidr_32_128(self) -> bool:
+        return bool(self.__settings[TomlSettings.L3_SKIP_CIDR_32_128])
 
-    @property  # --keep-domain
-    def remove_domain(self) -> bool:
-        return bool(self.__settings[TomlSettings.REMOVE_DOMAIN])
+    @property  # --l3-skip-if
+    def l3_skip_if(self) -> bool:
+        return bool(self.__settings[TomlSettings.L3_SKIP_IF])
+
+    @property  # --l3-skip-ip
+    def l3_skip_ip(self) -> bool:
+        return bool(self.__settings[TomlSettings.L3_SKIP_IP])
+
+    @property  # --l3-skip-public
+    def l3_skip_public(self) -> bool:
+        return bool(self.__settings[TomlSettings.L3_SKIP_PUBLIC])
 
     @property  # --layers
     def layers(self) -> List[str]:
@@ -269,11 +306,11 @@ class Settings:
     @property  # --log-file
     def log_file(self) -> str:
         raw_log_file = str(Path(str(self.__settings[TomlSettings.LOG_FILE])).expanduser())
-        if is_valid_log_file(raw_log_file):
-            return raw_log_file
-        else:
-            LOGGER.error(f'Falling back to {LOG_FILE}')
-            return LOG_FILE
+        if not raw_log_file.startswith(f'{OMD_ROOT}/var/log/'):
+            # logger not ready yet
+            print(f'\nInvalid log file {raw_log_file}. Falling back to {LOG_FILE_DEFAULT}')
+            return LOG_FILE_DEFAULT
+        return raw_log_file
 
     @property  # --log-level
     def loglevel(self) -> int:
@@ -292,10 +329,16 @@ class Settings:
     def log_to_stdtout(self) -> bool:
         return bool(self.__settings[TomlSettings.LOG_TO_STDOUT])
 
-    @property  # --min-age
-    def min_age(self) -> int:
-        if isinstance(self.__settings[TomlSettings.MIN_AGE], int):
-            return max(self.__settings[TomlSettings.MIN_AGE], 0)
+    @property  # --keep-max-topologies
+    def keep_max_topologies(self) -> int | None:
+        if isinstance(self.__settings[TomlSettings.KEEP_MAX_TOPOLOGIES], int):
+            return self.__settings[TomlSettings.KEEP_MAX_TOPOLOGIES]
+        return None
+
+    @property  # --min-topology-age
+    def min_topology_age(self) -> int:
+        if isinstance(self.__settings[TomlSettings.MIN_TOPOLOGY_AGE], int):
+            return max(self.__settings[TomlSettings.MIN_TOPOLOGY_AGE], 0)
         else:
             return 0
 
@@ -304,19 +347,14 @@ class Settings:
         # init output directory with current time if not set
         if not self.__settings[TomlSettings.OUTPUT_DIRECTORY]:
             self.__settings[TomlSettings.OUTPUT_DIRECTORY] = f'{strftime(self.__settings[TomlSettings.TIME_FORMAT])}'
-        if is_valid_output_directory(str(self.__settings[TomlSettings.OUTPUT_DIRECTORY])):
-            return str(self.__settings[TomlSettings.OUTPUT_DIRECTORY])
+        raw_output_directory = str(self.__settings[TomlSettings.OUTPUT_DIRECTORY])
+        if is_valid_output_directory(raw_output_directory):
+            return raw_output_directory
         else:
-            LOGGER.error('Falling back to "nvdct"')
+            LOGGER.error(f'Invalid output directory {raw_output_directory}. Falling back to "nvdct".')
             return 'nvdct'
 
-    @property  # --prefix
-    def prefix(self) -> str | None:
-        if self.__settings[TomlSettings.PREFIX] is not None:
-            return str(self.__settings[TomlSettings.PREFIX])
-        return None
-
-    @property  # --pre-fill-cache
+    @property  # --pre-fetch
     def pre_fetch(self) -> bool:
         return bool(self.__settings[TomlSettings.PRE_FETCH])
 
@@ -324,45 +362,17 @@ class Settings:
     def quiet(self) -> bool:
         return bool(self.__settings[TomlSettings.QUIET])
 
-    @property  # --skip-l3-cidr-0
-    def skip_l3_cidr_0(self) -> bool:
-        return bool(self.__settings[TomlSettings.SKIP_L3_CIDR_0])
-
-    @property  # --skip-l3-cidr-32-128
-    def skip_l3_cidr_32_128(self) -> bool:
-        return bool(self.__settings[TomlSettings.SKIP_L3_CIDR_32_128])
-
-    @property  # --skip-l3-if
-    def skip_l3_if(self) -> bool:
-        return bool(self.__settings[TomlSettings.SKIP_L3_IF])
-
-    @property  # --skip-l3-ip
-    def skip_l3_ip(self) -> bool:
-        return bool(self.__settings[TomlSettings.SKIP_L3_IP])
-
-    @property  # --skip-l3-public
-    def skip_l3_public(self) -> bool:
-        return bool(self.__settings[TomlSettings.SKIP_L3_PUBLIC])
-
     @property  # --time-format
     def time_format(self) -> str:
-        return str(self.__settings[TomlSettings.TIME_FORMAT])
+        return str(self.__settings[TomlSettings.TIME_FORMAT].replace('/', '_').replace('..', '__'))
 
-    @property  # --user-data-file
-    def user_data_file(self) -> str:
-        return str(self.__settings[TomlSettings.USER_DATA_FILE])
+    @property  # --update-config
+    def update_config(self) -> bool:
+        return bool(self.__settings[TomlSettings.UPDATE_CONFIG])
 
     #
     #  user data setting
     #
-    @property
-    def customers(self) -> List[str]:
-        if self.__customers is None:
-            self.__customers = [
-                str(customer) for customer in set(self.__user_data.get(TomlSections.CUSTOMERS, []))
-                if is_valid_customer_name(customer)]
-            LOGGER.info(f'Found {len(self.__customers)} to filter on')
-        return self.__customers
 
     @property
     def emblems(self) -> Emblems:
@@ -378,6 +388,82 @@ class Settings:
             )
         return self.__emblems
 
+    @property
+    def filter_by_customer(self) -> List[str]:
+        if self.__filter_by_customer is None:
+            self.__filter_by_customer = [
+                str(customer) for customer in set(self.__user_data.get(TomlSections.FILTER_BY_CUSTOMER, []))
+                if is_valid_customer_name(customer)]
+            LOGGER.info(f'Found {len(self.__filter_by_customer)} customer(s) to filter on')
+        return self.__filter_by_customer
+
+    @property
+    def filter_by_site(self) -> List[str]:
+        if self.__filter_by_site is None:
+            self.__filter_by_site = [str(site) for site in set(self.__user_data.get(TomlSections.FILTER_BY_SITE, [])) if is_valid_site_name(site)]
+            LOGGER.info(f'Found {len(self.__filter_by_site)} site(s) to filter on')
+        return self.__filter_by_site
+
+    @staticmethod
+    def parse_key_value_section(section: str, data: Mapping[str, str]) -> Dict[str, set[str]]:
+        parsed = {
+            IncludeExclude.INCLUDE: set(),
+            IncludeExclude.EXCLUDE: set()
+        }
+        for key_value, mode in data.items():
+            if mode not in [IncludeExclude.INCLUDE, IncludeExclude.EXCLUDE]:
+                LOGGER.error(
+                    f'Invalid mode in {section} found: {key_value}={mode} -> line ignored'
+                )
+                continue
+            match section:
+                case TomlSections.FILTER_BY_HOST_LABEL:
+                    try:
+                        key, value = key_value.split(':', 1)
+                    except ValueError:
+                        LOGGER.error(
+                            f'Invalid host label found missing ":": {key_value}={mode} -> line ignored'
+                        )
+                        continue
+                    if ':' in value:
+                        LOGGER.error(
+                            f'Invalid host label found can not contain more than one ":": "{key_value}={mode}" -> line ignored'
+                        )
+                        continue
+                    parsed[mode].add(f"'{key}' '{value}'")
+                case TomlSections.FILTER_BY_FOLDER:
+                    parsed[mode].add(f'^/wato/{key_value.strip("/")}/')
+                case _:
+                    parsed[mode].add(key_value)
+        return parsed
+
+    @property
+    def filter_by_folder(self) -> Dict[str, set[str]]:
+        if self.__filter_by_folder is None:
+            self.__filter_by_folder = self.parse_key_value_section(
+                section=TomlSections.FILTER_BY_FOLDER,
+                data=self.__user_data.get(TomlSections.FILTER_BY_FOLDER, {})
+            )
+        return self.__filter_by_folder
+
+    @property
+    def filter_by_host_label(self) -> Dict[str, Set[str]]:
+        if self.__filter_by_host_label is None:
+            self.__filter_by_host_label = self.parse_key_value_section(
+                section=TomlSections.FILTER_BY_HOST_LABEL,
+                data=self.__user_data.get(TomlSections.FILTER_BY_HOST_LABEL, {})
+            )
+        return self.__filter_by_host_label
+
+    @property
+    def filter_by_host_tag(self) -> Dict[str, Set[str]]:
+        if self.__filter_by_host_tag is None:
+            self.__filter_by_host_tag = self.parse_key_value_section(
+                section=TomlSections.FILTER_BY_HOST_TAG,
+                data=self.__user_data.get(TomlSections.FILTER_BY_HOST_TAG, {})
+            )
+        return self.__filter_by_host_tag
+
     @property
     def l2_drop_neighbours(self) -> List[str]:
         if self.__l2_drop_neighbours is None:
@@ -392,14 +478,14 @@ class Settings:
         return self.__l2_seed_devices
 
     @property
-    def l2_host_map(self) -> Dict[str, str]:
-        if self.__l2_host_map is None:
-            self.__l2_host_map = {
-                str(host): str(replace_host) for host, replace_host in self.__user_data.get(
-                    TomlSections.L2_HOST_MAP, {}
-                ).items() if is_valid_hostname(host)
+    def l2_neighbour_to_host_map(self) -> Dict[str, str]:
+        if self.__l2_neighbour_to_host_map is None:
+            self.__l2_neighbour_to_host_map = {
+                str(neighbour): str(host) for neighbour, host in self.__user_data.get(
+                    TomlSections.L2_NEIGHBOUR_TO_HOST_MAP, {}
+                ).items() if is_valid_hostname(neighbour)
             }
-        return self.__l2_host_map
+        return self.__l2_neighbour_to_host_map
 
     @property
     def l2_neighbour_replace_regex(self) -> List[Tuple[str, str]] | None:
@@ -426,7 +512,7 @@ class Settings:
             for raw_ip_network in self.__user_data.get(TomlSections.L3_IGNORE_IP, []):
                 try:
                     self.__l3_ignore_ip.append(ip_network(raw_ip_network, strict=False))
-                except (AddressValueError, NetmaskValueError):
+                except (AddressValueError, NetmaskValueError, ValueError):
                     LOGGER.error(
                         f'Invalid entry in {TomlSections.L3_IGNORE_IP} found: {raw_ip_network} -> ignored'
                     )
@@ -454,6 +540,7 @@ class Settings:
                     inverted_wildcard = '.'.join(
                         [str(255 - int(octet)) for octet in wildcard.split('.')]
                     )
+
                     self.__l3v4_ignore_wildcard.append(Wildcard(
                         int_ip_address=int(ip_address(raw_ip_address)),
                         int_wildcard=int(ip_address(inverted_wildcard)),
@@ -463,7 +550,7 @@ class Settings:
                             ip_address(inverted_wildcard)
                         )
                     ))
-                except (AddressValueError, NetmaskValueError):
+                except (AddressValueError, NetmaskValueError, ValueError):
                     LOGGER.error(
                         f'Invalid entry in {TomlSections.L3V4_IGNORE_WILDCARD} -> {entry} -> ignored'
                     )
@@ -475,24 +562,24 @@ class Settings:
         return self.__l3v4_ignore_wildcard
 
     @property
-    def l3_replace(self) -> Dict[str, str]:
+    def l3_replace_networks(self) -> Dict[str, str]:
         if self.__l3_replace is None:
             self.__l3_replace = {}
-            for raw_ip_network, node in self.__user_data.get(TomlSections.L3_REPLACE, {}).items():
+            for raw_ip_network, node in self.__user_data.get(TomlSections.L3_REPLACE_NETWORKS, {}).items():
                 try:
                     _ip_network = ip_network(raw_ip_network)  # noqa: F841
-                except (AddressValueError, NetmaskValueError):
+                except (AddressValueError, NetmaskValueError, ValueError):
                     LOGGER.error(
-                        f'Invalid entry in {TomlSections.L3_REPLACE} found: {raw_ip_network} -> line ignored'
+                        f'Invalid entry in {TomlSections.L3_REPLACE_NETWORKS} found: "{raw_ip_network}" = "{node}" -> line ignored'
                     )
                     continue
                 if not is_valid_hostname(node):
-                    LOGGER.error(f'Invalid node name found: {node} -> line ignored ')
+                    LOGGER.error(f'Invalid node name found: {node} -> line ignored')
                     continue
                 self.__l3_replace[raw_ip_network] = str(node)
             LOGGER.info(
-                f'Valid entries in {TomlSections.L3_REPLACE} found: {len(self.__l3_replace)}/'
-                f'{len(self.__user_data.get(TomlSections.L3_REPLACE, {}))}'
+                f'Valid entries in {TomlSections.L3_REPLACE_NETWORKS} found: {len(self.__l3_replace)}/'
+                f'{len(self.__user_data.get(TomlSections.L3_REPLACE_NETWORKS, {}))}'
             )
         return self.__l3_replace
 
@@ -503,7 +590,7 @@ class Settings:
             for raw_ip_network in self.__user_data.get(TomlSections.L3_SUMMARIZE, []):
                 try:
                     self.__l3_summarize.append(ip_network(raw_ip_network, strict=False))
-                except (AddressValueError, NetmaskValueError):
+                except (AddressValueError, NetmaskValueError, ValueError):
                     LOGGER.error(
                         f'Invalid entry in {TomlSections.L3_SUMMARIZE} -> {raw_ip_network} -> ignored'
                     )
@@ -555,11 +642,11 @@ class Settings:
                     left_host, left_service, right_service, right_host = connection
                 except ValueError:
                     LOGGER.error(
-                        f'Wrong entry in {TomlSections.STATIC_CONNECTIONS} -> {connection} -> ignored'
+                        f'Invalid entry in {TomlSections.STATIC_CONNECTIONS} needs to be 4 tuples: -> {connection} -> ignored'
                     )
                     continue
                 if not right_host or not left_host:
-                    LOGGER.warning(f'Both hosts must be set, got {connection}')
+                    LOGGER.warning(f'Both hosts must be set, got: {connection}  -> ignored')
                     continue
                 if not is_valid_hostname(right_host) or not is_valid_hostname(left_host):
                     continue
@@ -575,9 +662,4 @@ class Settings:
             )
         return self.__static_connections
 
-    @property
-    def sites(self) -> List[str]:
-        if self.__sites is None:
-            self.__sites = [str(site) for site in set(self.__user_data.get(TomlSections.SITES, [])) if is_valid_site_name(site)]
-            LOGGER.info(f'Found {len(self.__sites)} to filter on')
-        return self.__sites
+
diff --git a/source/bin/nvdct/lib/topologies.py b/source/bin/nvdct/lib/topologies.py
index 8594526..7a4634f 100755
--- a/source/bin/nvdct/lib/topologies.py
+++ b/source/bin/nvdct/lib/topologies.py
@@ -6,11 +6,12 @@
 # Author: thl-cmk[at]outlook[dot]com
 # URL   : https://thl-cmk.hopto.org
 # Date  : 2024-06-09
-# File  : lib/topologies.py
+# File  : nvdct/lib/topologies.py
 
 # 2024-12-22: refactoring topology creation into classes
 #             made L3 topology IP version independent
 # 2024-12-25: refactoring, moved function into classes
+# 2025-01-22: changed: show interface service in L2 and L3 topology instead of Port/Device from HW/SW inventory
 
 from abc import abstractmethod
 from collections.abc import Mapping, MutableMapping, Sequence, MutableSet
@@ -24,12 +25,14 @@ from lib.backends import (
 )
 from lib.constants import (
     CACHE_INTERFACES_DATA,
+    HostFilter,
     HostLabels,
     IPVersion,
     InvPaths,
-    LOGGER,
     L2InvColumns,
     L3InvColumns,
+    LOGGER,
+    LiveStatusOperator,
     TomlSections,
 )
 from lib.settings import (
@@ -57,10 +60,22 @@ class NvObjects:
             self,
             host: str,
             emblem: str | None = None,
-            name: str | None = None,
+            display_name: str | None = None,
     ) -> None:
-        if name and host in self.nv_objects:
-            self.nv_objects[host]['name'] = name
+        """
+        Adds a host object to the topology.
+        - if there is no display name, "host" will be used as display_name
+        - the emblem for the host will only be used if the host is not a Checkmk host object
+        - if there is no "emblem" the Checkmk will use its default emblem for missing objects (white question mark on blue ground)
+        Args:
+            host: the name of the host
+            emblem: the emblem for the object
+            display_name: the name for the host to show in the topology
+        Returns:
+            None
+        """
+        if display_name and host in self.nv_objects:
+            self.nv_objects[host]['name'] = display_name
 
         if host not in self.nv_objects:
             self.host_count += 1
@@ -69,7 +84,7 @@ class NvObjects:
             metadata: Dict = {}
             # LOGGER.debug(f'host: {host}, {host_cache.host_exists(host=host)}')
             if self.host_cache.host_exists(host=host) is True:
-                LOGGER.debug(f'host: {host} exists')
+                LOGGER.debug(f'host exists : {host}')
                 link = {'core': host}
             else:
                 if emblem is not None:
@@ -84,7 +99,7 @@ class NvObjects:
                     }
 
             self.nv_objects[host] = {
-                'name': name if name is not None else host,
+                'name': display_name if display_name is not None else host,
                 'link': link,
                 'metadata': metadata,
             }
@@ -96,12 +111,29 @@ class NvObjects:
             service: str,
             emblem: str | None = None,
             metadata: Dict | None = None,
-            name: str | None = None,
+            display_name: str | None = None,
     ) -> None:
+        """
+        Adds a service object to the topology.
+        - the service object will be added as "service@host" to the topology
+        - service should be the complete service name as in Checkmk, i.e. "Interface X440G2-48p-10G4 Port 52"
+        - if the host is a Checkmk host object it will be linked to the core
+        - if there is no display name, "service" will be used as display_name
+        - the emblem for the service will only be used if the host is not a Checkmk host object
+        - if there is no "emblem" the Checkmk will use its default emblem for missing objects (white question mark on blue ground)
+        Args:
+            host: the name of the host
+            service: the name of the service
+            emblem: the emblem for the object
+            metadata: additional data for the service
+            display_name: the name for the host to show in the topology
+        Returns:
+            None
+        """
         if metadata is None:
             metadata = {}
-        if name is None:
-            name = service
+        if display_name is None:
+            display_name = service
 
         self.add_host(host=host)
         service_object = f'{service}@{host}'
@@ -119,7 +151,7 @@ class NvObjects:
                 })
 
             self.nv_objects[service_object] = {
-                'name': name,
+                'name': display_name,
                 'link': link,
                 'metadata': metadata,
             }
@@ -133,12 +165,32 @@ class NvObjects:
             item: str | None,
             emblem: str | None = None,
             metadata: Dict | None = None,
-            name: str | None = None,
+            display_name: str | None = None,
     ) -> None:
+        """
+        Adds an interface service object to the topology.
+        - the interface service object will be added as "service@host" to the topology
+        - service ist the interface name from the H/W-inventory
+        - "item" should be the interface item as in Checkmk, i.e. "X440G2-48p-10G4 Port 52"
+        - if there is no "item" service will be used as "item"
+        - if the host is a Checkmk host object it will be linked to the core
+        - if there is no display name, "service" will be used as display_name
+        - the "emblem" for the service will only be used if the host is not a Checkmk host object
+        - if there is no "emblem" the Checkmk will use its default emblem for missing objects (white question mark on blue ground)
+        Args:
+            host: the name of the host
+            service: the name of the service
+            item: the interface name as in Checkmk
+            emblem: the emblem for the object
+            metadata: additional data for the service
+            display_name: the name for the host to show in the topology
+        Returns:
+            None
+        """
         if metadata is None:
             metadata = {}
-        if name is None:
-            name = service
+        if display_name is None:
+            display_name = service
         speed = None
 
         self.add_host(host=host)
@@ -167,7 +219,7 @@ class NvObjects:
                 metadata.update({'native_speed': speed})
 
             self.nv_objects[service_object] = {
-                'name': name,
+                'name': display_name,
                 'link': link,
                 'metadata': metadata,
             }
@@ -181,6 +233,19 @@ class NvObjects:
             emblem: str,
             interface: str | None,
     ) -> None:
+        """
+        Adds an ip address object to the topology.
+        - the ip address object will be added as "raw_ip_address@interface@host" to the topology
+        - if interface is None (--l2-skip-if) the object is added as "raw_ip_address@host"
+        Args:
+            host: the name of the Checkmk host
+            raw_ip_address: the ip address as string (i.e. "10.10.10.10")
+            emblem: the emblem for the object
+            interface: the interface of the host where the ip address belongs to
+        Returns:
+            None
+        """
+
         if interface is not None:
             service_object = f'{raw_ip_address}@{interface}@{host}'
         else:
@@ -251,7 +316,20 @@ class NvObjects:
             return operational_data
         return None
 
-    def add_ip_network(self, network: str, emblem: str, ) -> None:
+    def add_ip_network(
+            self,
+            network: str,
+            emblem: str,
+    ) -> None:
+        """
+        Adds an ip network object to the topology.
+        - the ip network object will be added as "network" to the topology
+        Args:
+            network: the network as string (i.e. "10.10.10.0/24")
+            emblem: the emblem for the object
+        Returns:
+            None
+        """
         if network not in self.nv_objects:
             self.nv_objects[network] = {
                 'name': network,
@@ -269,7 +347,7 @@ class NvObjects:
                 }
             }
 
-    def add_tooltip_quickinfo(self, nv_object: str, name: str, value: str) -> Dict:
+    def add_tooltip_quickinfo(self, nv_object: str, display_name: str, value: str) -> Dict:
         metadata = self.nv_objects[nv_object]['metadata']
         if metadata.get('tooltip') is None:
             metadata['tooltip'] = {}
@@ -277,7 +355,7 @@ class NvObjects:
             metadata['tooltip']['quickinfo'] = []
 
         metadata['tooltip']['quickinfo'].append({
-            'name': name,
+            'name': display_name,
             'value': value,
         })
 
@@ -343,8 +421,8 @@ class NvConnections:
                     # metadata = add_tooltip_quickinfo(metadata, right, right_speed_str)
 
                     LOGGER.warning(
-                        f'Connection speed mismatch: {left} (speed: {left_speed_str})'
-                        f'<->{right} (speed: {right_speed_str})'
+                        f'Connection speed mismatch: {left} ({left_speed_str})'
+                        f'<->{right} ({right_speed_str})'
                     )
 
             # for duplex/native vlan it might be a good idea to change left/right
@@ -358,8 +436,8 @@ class NvConnections:
                     )
 
                     LOGGER.warning(
-                        f'Connection duplex mismatch: {left} (duplex: {left_duplex})'
-                        f'<->{right} (duplex: {right_duplex})'
+                        f'Connection duplex mismatch: {left} ({left_duplex})'
+                        f'<->{right} ({right_duplex})'
                     )
             if left_native_vlan and right_native_vlan:
                 if left_native_vlan != '0' and right_native_vlan != '0':  # ignore VLAN 0 (Native VLAN on routed ports)
@@ -372,7 +450,7 @@ class NvConnections:
 
                         LOGGER.warning(
                             f'Connection native vlan mismatch: '
-                            f'{left} (vlan: {left_native_vlan})<->{right} (vlan: {right_native_vlan})'
+                            f'{left} ({left_native_vlan})<->{right} ({right_native_vlan})'
                         )
             if warning:
                 metadata['line_config'].update({
@@ -452,9 +530,9 @@ class Topology:
         # try to find the item for an interface
         def match_entry_with_item(interface_entry: Mapping[str, str], services: Sequence[str]) -> str | None:
             values = [
-                interface_entry.get('name'.strip()),
-                interface_entry.get('description'.strip()),
-                interface_entry.get('alias').strip()
+                interface_entry.get('name', '').strip(),
+                interface_entry.get('description','').strip(),
+                interface_entry.get('alias', '').strip()
             ]
             for value in values:
                 if value in services:
@@ -465,13 +543,13 @@ class Topology:
             # try alias+index
             alias_index = str(interface_entry.get('alias')).strip() + ' ' + index
             if alias_index in services:
-                LOGGER.info(f'{self.topology} match found by alias-index|{interface_entry}| <-> |{alias_index}|')
+                LOGGER.info(f'{self.topology} match found by alias-index |{interface_entry}| <-> |{alias_index}|')
                 return alias_index
 
             # try description+index
             description_index = str(interface_entry.get('description')).strip() + ' ' + index
             if description_index in services:
-                LOGGER.info(f'{self.topology} match found by description-index|{interface_entry}| <-> |{description_index}|')
+                LOGGER.info(f'{self.topology} match found by description-index |{interface_entry}| <-> |{description_index}|')
                 return description_index
 
             # for index try with padding
@@ -489,7 +567,7 @@ class Topology:
                         if f'{value} {index_padded}' in services:
                             return f'{value} {index_padded}'
 
-            LOGGER.warning(f'{self.topology} no match found |{interface_entry}| <-> |{services}|')
+            LOGGER.warning(f'{self.topology} no match found |{interface_entry} | <-> |{services}|')
             return None
 
         # empty host/neighbour should never happen here
@@ -534,7 +612,7 @@ class Topology:
                     entry.get('name')) == str(interface).lower():  # Cisco NXOS
                 return match_entry_with_item(entry, interface_items)
 
-        LOGGER.warning(msg=f'{self.topology} Device: {host}: service for interface |{interface}| not found')
+        LOGGER.warning(f'{self.topology} Service for interface not found: {host}, |{interface}|')
 
 
 class TopologyStatic(Topology):
@@ -553,7 +631,7 @@ class TopologyStatic(Topology):
 
     def create(self):
         for connection in self.connections:
-            LOGGER.debug(msg=f'{self.topology} connection from {TomlSections.STATIC_CONNECTIONS}: {connection}')
+            LOGGER.debug(f'{self.topology} connection from {TomlSections.STATIC_CONNECTIONS}: {connection}')
             self.nv_objects.add_host(
                 host=connection.right_host,
                 emblem=self.emblems.host_node
@@ -609,29 +687,33 @@ class TopologyStatic(Topology):
 class TopologyL2(Topology):
     def __init__(
             self,
+            display_neighbours: bool,
+            display_ports: bool,
+            drop_neighbours: List[str],
             emblems: Emblems,
             host_cache: HostCache,
-            l2_drop_neighbours: List[str],
-            l2_neighbour_replace_regex: List[Tuple[str, str]],
             label: str,
+            neighbour_replace_regex: List[Tuple[str, str]],
             path_in_inventory: str,
             seed_devices: Sequence[str],
-            display_l2_neighbours: bool,
+            skip_external: bool,
     ):
         super().__init__(
             emblems=emblems,
             host_cache=host_cache,
             topology = f'[L2 {label}]',
         )
-        self.l2_drop_neighbours: List[str] = l2_drop_neighbours
+        self.display_neighbours: bool = display_neighbours
+        self.display_ports: bool = display_ports
+        self.drop_neighbours: List[str] = drop_neighbours
+        self.hosts_done: MutableSet[str] = set()
+        self.hosts_to_go: MutableSet[str] = set(seed_devices)
         self.label: str = label
+        self.neighbour_replace_regex: List[Tuple[str, str]] = neighbour_replace_regex
         self.neighbour_to_host: MutableMapping[str, str] = {}
         self.path_in_inventory: str = path_in_inventory
-        self.hosts_to_go: MutableSet[str] = set(seed_devices)
         self.raw_neighbour_to_neighbour: Dict[str, str] = {}
-        self.l2_neighbour_replace_regex: List[Tuple[str, str]] = l2_neighbour_replace_regex
-        self.hosts_done: MutableSet[str] = set()
-        self.display_l2_neighbours: bool = display_l2_neighbours
+        self.skip_external: bool = skip_external
 
     def create(self):
         if not self.hosts_to_go:
@@ -641,6 +723,9 @@ class TopologyL2(Topology):
         while self.hosts_to_go:
             host = self.hosts_to_go.pop()
             self.hosts_done.add(host)
+            if not self.host_cache.is_host_allowed(host):
+                LOGGER.info(f'{self.topology} host dropped by filter: {host}')
+                continue
 
             topo_data: Sequence[Mapping[str, str]] = self.host_cache.get_data(
                 host=host, item=CacheItems.inventory, path=self.path_in_inventory
@@ -651,7 +736,7 @@ class TopologyL2(Topology):
                     inv_data=topo_data,
                 )
 
-            LOGGER.info(msg=f'{self.topology} host done : {host}')
+            LOGGER.info(f'{self.topology} host done: {host}')
 
     def host_from_inventory(
             self,
@@ -661,48 +746,62 @@ class TopologyL2(Topology):
         for topo_neighbour in inv_data:
             # check if required data are not empty
             if not (raw_neighbour := topo_neighbour.get(L2InvColumns.NEIGHBOUR)):
-                LOGGER.warning(f'{self.topology} incomplete data: neighbour missing {topo_neighbour}')
-                continue
-            if not (raw_local_port := topo_neighbour.get(L2InvColumns.LOCALPORT)):
-                LOGGER.warning(f'{self.topology} incomplete data: local port missing {topo_neighbour}')
-                continue
-            if not (raw_neighbour_port := topo_neighbour.get(L2InvColumns.NEIGHBOURPORT)):
-                LOGGER.warning(f'{self.topology} incomplete data: neighbour port missing {topo_neighbour}')
+                LOGGER.warning(f'{self.topology} incomplete data neighbour missing: {topo_neighbour}')
                 continue
 
+            # stop here if neighbour is dropped anyway...
             if not (neighbour := self.adjust_raw_neighbour(raw_neighbour)):
                 continue
 
-            if neighbour_host := self.host_cache.get_host_from_neighbour(neighbour):
-                if neighbour_host not in self.hosts_done:
-                    self.hosts_to_go.add(neighbour_host)
-            else:
-                neighbour_host = neighbour
+            if not (raw_local_port := topo_neighbour.get(L2InvColumns.LOCALPORT)):
+                LOGGER.warning(f'{self.topology} incomplete data local port missing: {topo_neighbour}')
+                continue
+
+            if not (raw_neighbour_port := topo_neighbour.get(L2InvColumns.NEIGHBOURPORT)):
+                LOGGER.warning(f'{self.topology} incomplete data neighbour port missing: {topo_neighbour}')
+                continue
 
-            # getting/checking interfaces
-            local_port = self.get_service_by_interface(host, raw_local_port)
-            if not local_port:
+            # get local interface service
+            if not (local_port := self.get_service_by_interface(host, raw_local_port)):
                 local_port = raw_local_port
-                LOGGER.warning(msg=f'{self.topology} service not found for local_port: {host}, {raw_local_port}')
+                LOGGER.warning(f'{self.topology} service not found for local_port: {host}, {raw_local_port}')
             elif local_port != raw_local_port:
-                # local_port = raw_local_port  # don't reset local_port
                 LOGGER.info(
-                    msg=f'{self.topology} map raw_local_port -> local_port: {host}, {raw_local_port} -> {local_port}'
+                    f'{self.topology} map raw_local_port -> local_port: {host}, {raw_local_port} -> {local_port}'
                 )
 
-            neighbour_port = self.get_service_by_interface(neighbour_host, raw_neighbour_port)
-            if not neighbour_port:
-                neighbour_port = raw_neighbour_port
-                LOGGER.warning(
-                    msg=f'{self.topology} service not found for neighbour port:  {neighbour_host}, {raw_neighbour_port}'
-                )
-            elif neighbour_port != raw_neighbour_port:
-                # neighbour_port = raw_neighbour_port  # don't reset neighbour_port
-                LOGGER.info(
-                    msg=f'{self.topology} map raw_neighbour_port -> neighbour_port: {neighbour_host}, {raw_neighbour_port} '
-                        f'-> {neighbour_port}'
-                )
+            if neighbour_host := self.host_cache.get_host_from_neighbour(
+                    neighbour=neighbour,
+                    neighbour_id=topo_neighbour.get(L2InvColumns.NEIGHBOURID),
+                    raw_neighbour=raw_neighbour,
+            ):
+                if not self.host_cache.is_host_allowed(neighbour_host):
+                    LOGGER.info(f'{self.topology} neighbour dropped by include/exclude filter: {neighbour_host}')
+                    continue
 
+                if neighbour_host not in self.hosts_done:
+                    self.hosts_to_go.add(neighbour_host)
+
+                if not (neighbour_port := self.get_service_by_interface(neighbour_host, raw_neighbour_port)):
+                    neighbour_port = raw_neighbour_port
+                    LOGGER.warning(
+                        f'{self.topology} service not found for neighbour port:  {neighbour_host}, {raw_neighbour_port}'
+                    )
+                elif neighbour_port != raw_neighbour_port:
+                    # neighbour_port = raw_neighbour_port  # don't reset neighbour_port
+                    LOGGER.info(
+                        f'{self.topology} map raw_neighbour_port -> '
+                        f'neighbour_port: {neighbour_host}, {raw_neighbour_port} -> {neighbour_port}'
+                    )
+                neighbour_name = raw_neighbour if self.display_neighbours else None
+            else:
+                # neighbour is external to cmk, use neighbour id if available as neighbour
+                if self.skip_external:
+                    continue
+                if not (neighbour_host := topo_neighbour.get(L2InvColumns.NEIGHBOURID)):
+                    neighbour_host = neighbour
+                neighbour_port = raw_neighbour_port
+                neighbour_name = raw_neighbour if self.display_neighbours else neighbour
             metadata = {
                 'duplex': topo_neighbour.get('duplex'),
                 'native_vlan': topo_neighbour.get('native_vlan'),
@@ -714,21 +813,21 @@ class TopologyL2(Topology):
             )
             self.nv_objects.add_host(
                 host=neighbour_host,
-                name=raw_neighbour if self.display_l2_neighbours else None,
+                display_name=neighbour_name,
                 emblem=self.emblems.host_node,
             )
             self.nv_objects.add_interface(
                 host=str(host),
                 service=str(local_port),
                 metadata=metadata,
-                name=str(raw_local_port),
+                display_name=str(raw_local_port) if self.display_ports else str(local_port),
                 item=str(local_port),
                 emblem=self.emblems.service_node,
             )
             self.nv_objects.add_interface(
                 host=str(neighbour_host),
                 service=str(neighbour_port),
-                name=str(raw_neighbour_port),
+                display_name=str(raw_neighbour_port) if self.display_ports else str(neighbour_port),
                 item=str(neighbour_port),
                 emblem=self.emblems.service_node,
             )
@@ -760,22 +859,28 @@ class TopologyL2(Topology):
         except KeyError:
             pass
 
-        if raw_neighbour in self.l2_drop_neighbours:
-            LOGGER.info(msg=f'{self.topology} drop in {TomlSections.L2_DROP_NEIGHBOURS}: {raw_neighbour}')
+        if raw_neighbour in self.drop_neighbours:
+            LOGGER.info(f'{self.topology} drop in {TomlSections.L2_DROP_NEIGHBOURS}: {raw_neighbour}')
             self.neighbour_to_host[raw_neighbour] = None
             return None
 
         adjusted_neighbour = raw_neighbour
-        if self.l2_neighbour_replace_regex:
-            for re_str, replace_str in self.l2_neighbour_replace_regex:
+        if self.neighbour_replace_regex:
+            for re_str, replace_str in self.neighbour_replace_regex:
                 re_neighbour = re_sub(re_str, replace_str, adjusted_neighbour)
                 if not re_neighbour:
-                    LOGGER.info(f'{self.topology} removed by {TomlSections.L2_NEIGHBOUR_REPLACE_REGEX}: (|{adjusted_neighbour}|, |{re_str}|, |{replace_str}|)')
+                    LOGGER.info(
+                        f'{self.topology} removed by {TomlSections.L2_NEIGHBOUR_REPLACE_REGEX}: '
+                        f'(|{adjusted_neighbour}|, |{re_str}|, |{replace_str}|)'
+                    )
                     self.neighbour_to_host[raw_neighbour] = None
                     return None
 
                 if re_neighbour != adjusted_neighbour:
-                    LOGGER.info(f'{self.topology} changed by {TomlSections.L2_NEIGHBOUR_REPLACE_REGEX} |{adjusted_neighbour}| to |{re_neighbour}|')
+                    LOGGER.info(
+                        f'{self.topology} changed by {TomlSections.L2_NEIGHBOUR_REPLACE_REGEX}: '
+                        f'|{adjusted_neighbour}| to |{re_neighbour}|'
+                    )
                     adjusted_neighbour = re_neighbour
 
         self.neighbour_to_host[raw_neighbour] = adjusted_neighbour
@@ -785,6 +890,7 @@ class TopologyL2(Topology):
 class TopologyL3(Topology):
     def __init__(
             self,
+            display_devices: bool,
             emblems: Emblems,
             host_cache: HostCache,
             ignore_hosts: Sequence[str],
@@ -806,42 +912,58 @@ class TopologyL3(Topology):
             host_cache=host_cache,
             topology=f'[L3 IPv{version}]'
         )
+        self.diplay_devices = display_devices
         self.ignore_hosts: Sequence[str] = ignore_hosts
         self.ignore_ips: Sequence[ip_network] = ignore_ips
         self.ignore_wildcard: Sequence[Wildcard] = ignore_wildcard
         self.include_hosts: bool = include_hosts
         self.replace: Mapping[str, str] = replace
+        self.show_loopback: bool = include_loopback
         self.skip_cidr_0: bool = skip_cidr_0
         self.skip_cidr_32_128: bool = skip_cidr_32_128
         self.skip_if: bool = skip_if
         self.skip_ip: bool = skip_ip
         self.skip_public: bool = skip_public
-        self.show_loopback: bool = include_loopback
         self.summarize: Sequence[ip_network] = summarize
         self.version = version
 
     def create(self):
         match self.version:
             case IPVersion.IPv4:
-                host_list: Sequence[str] = self.host_cache.get_hosts_by_label(HostLabels.L3V4_ROUTER)
+                host_list: Sequence[str] = (
+                    self.host_cache.query_hosts_by_filter(
+                        HostFilter.LABELS, HostLabels.L3V4_ROUTER, LiveStatusOperator.EQUAL
+                    )
+                )
 
                 if self.include_hosts:
-                    host_list += self.host_cache.get_hosts_by_label(HostLabels.L3V4_HOSTS)
+                    host_list += self.host_cache.query_hosts_by_filter(
+                        HostFilter.LABELS, HostLabels.L3V4_HOSTS, LiveStatusOperator.EQUAL
+                    )
 
             case IPVersion.IPv6:
-                host_list: Sequence[str] = self.host_cache.get_hosts_by_label(HostLabels.L3V6_ROUTER)
+                host_list: Sequence[str] = self.host_cache.query_hosts_by_filter(
+                    HostFilter.LABELS, HostLabels.L3V6_ROUTER, LiveStatusOperator.EQUAL
+                )
 
                 if self.include_hosts:
-                    host_list += self.host_cache.get_hosts_by_label(HostLabels.L3V6_HOSTS)
+                    host_list += self.host_cache.query_hosts_by_filter(
+                        HostFilter.LABELS, HostLabels.L3V6_HOSTS, LiveStatusOperator.EQUAL
+                    )
 
             case _:
                 host_list = []
 
-        LOGGER.debug(f'{self.topology} host to work on: {host_list}')
+        # check against filter list (host labels/attributes include/exclude)
+        pre_filter_len = len(host_list)
+        host_list = [host for host in host_list if self.host_cache.is_host_allowed(host)]
+        LOGGER.info(f'{self.topology} # hosts allowed: {len(host_list)}/{pre_filter_len}')
+
+        LOGGER.debug(f'{self.topology} host(s) to work on: {host_list}')
         if not host_list:
             LOGGER.error(
-                msg=f'{self.topology} No (routing capable) host found. Check if "inv_ip_addresses.mkp" '
-                    'added/enabled and inventory and host label discovery has run.'
+                f'{self.topology} No (routing capable) host found. Check if "inv_ip_addresses.mkp" '
+                'added/enabled and inventory and host label discovery has run.'
             )
             return
 
@@ -898,7 +1020,7 @@ class TopologyL3(Topology):
                     continue
 
                 if self.is_ignore_ip(interface_address.ip.compressed):
-                    LOGGER.info(f'{self.topology} rop IP in {TomlSections.L3_IGNORE_IP}: {host}, {interface_address.compressed}')
+                    LOGGER.info(f'{self.topology} drop IP in {TomlSections.L3_IGNORE_IP}: {host}, {interface_address.compressed}')
                     continue
 
                 if self.is_ignore_wildcard(interface_address.ip.compressed):
@@ -914,7 +1036,7 @@ class TopologyL3(Topology):
                     network = f'{interface_address.network.compressed}'
 
                 if network in self.replace.keys():
-                    LOGGER.info(f'{self.topology} Replaced network in {TomlSections.L3_REPLACE}: {network} -> {self.replace[network]}')
+                    LOGGER.info(f'{self.topology} Replaced network in {TomlSections.L3_REPLACE_NETWORKS}: {network} -> {self.replace[network]}')
                     network = self.replace[network]
                     emblem = self.emblems.l3_replace
 
@@ -939,7 +1061,12 @@ class TopologyL3(Topology):
                     self.nv_connections.add_connection(left=f'{host}', right=f'{interface_address.ip.compressed}@{host}')
                     self.nv_connections.add_connection(left=network, right=f'{interface_address.ip.compressed}@{host}')
                 elif self.skip_if is False and self.skip_ip is True:
-                    self.nv_objects.add_interface(host=host, service=device, item=item)
+                    self.nv_objects.add_interface(
+                        display_name=device if self.diplay_devices else item,
+                        host=host,
+                        item=item,
+                        service=device,
+                    )
                     self.nv_objects.add_tooltip_quickinfo(
                         f'{device}@{host}', 'IP-address', interface_address.ip.compressed
                     )
@@ -952,7 +1079,12 @@ class TopologyL3(Topology):
                         raw_ip_address=interface_address.ip.compressed,
                         emblem=self.emblems.ip_address,
                     )
-                    self.nv_objects.add_interface(host=host, service=device, item=item)
+                    self.nv_objects.add_interface(
+                        display_name=device if self.diplay_devices else item,
+                        host=host,
+                        item=item,
+                        service=device,
+                    )
                     self.nv_connections.add_connection(
                         left=host, right=f'{device}@{host}')
                     self.nv_connections.add_connection(
@@ -967,7 +1099,7 @@ class TopologyL3(Topology):
         for network in self.summarize:
             try:
                 if ip_network(raw_ip_address).subnet_of(network):
-                    LOGGER.debug(f'{self.topology} IP address {raw_ip_address} is in subnet -> ({network})')
+                    LOGGER.debug(f'{self.topology} IP address is in subnet: {raw_ip_address} -> ({network})')
                     return network.compressed
             except TypeError:
                 pass
@@ -977,7 +1109,7 @@ class TopologyL3(Topology):
         for ip in self.ignore_ips:
             try:
                 if ip_network(raw_ip_address).subnet_of(ip):
-                    LOGGER.debug(f'{self.topology} IP address {raw_ip_address} is in ignore list -> ({ip})')
+                    LOGGER.debug(f'{self.topology} IP address is in ignore list: {raw_ip_address} -> ({ip})')
                     return True
             except TypeError:
                 continue
@@ -988,8 +1120,8 @@ class TopologyL3(Topology):
         for wildcard in self.ignore_wildcard:
             if int_ip_address & wildcard.int_wildcard == wildcard.bit_pattern:
                 LOGGER.debug(
-                    f'{self.topology} IP address {raw_ip_address} matches ignore wildcard '
-                    f'list ({wildcard.ip_address}/{wildcard.wildcard})'
+                    f'{self.topology} IP address matches ignore wildcard list: '
+                    f'{raw_ip_address} -> ({wildcard.ip_address}/{wildcard.wildcard})'
                 )
                 return True
         return False
diff --git a/source/bin/nvdct/lib/utils.py b/source/bin/nvdct/lib/utils.py
index 9d15149..4eb62ee 100755
--- a/source/bin/nvdct/lib/utils.py
+++ b/source/bin/nvdct/lib/utils.py
@@ -13,7 +13,7 @@ from json import dumps, loads
 from logging import disable as log_off, Formatter, getLogger, StreamHandler
 from logging.handlers import RotatingFileHandler
 from pathlib import Path
-from re import match as re_match, findall as re_findall, sub as re_sub
+from re import match as re_match
 from socket import socket, AF_UNIX, AF_INET, SOCK_STREAM, SHUT_WR
 from sys import stdout, exit as sys_exit
 from time import time as now_time
@@ -21,18 +21,12 @@ from tomllib import loads as toml_loads, TOMLDecodeError
 from typing import Dict, List, TextIO
 
 from lib.constants import (
-    Backends,
     CMK_SITE_CONF,
-    Case,
+    CONFIG_FILE,
     DATAPATH,
-    EmblemValues,
-    EmblemNames,
     ExitCodes,
     LOGGER,
-    LogLevels,
     OMD_ROOT,
-    TomlSections,
-    TomlSettings,
 )
 
 
@@ -64,7 +58,7 @@ def get_data_form_live_status(query: str) -> Dict | List | None:
     return None
 
 
-def get_data_from_toml(file: str) -> Dict:
+def get_data_from_toml(file: str, check_only:bool = False) -> Dict| bool:
     data = {}
     toml_file = Path(file)
     if toml_file.exists():
@@ -72,22 +66,28 @@ def get_data_from_toml(file: str) -> Dict:
             data = toml_loads(toml_file.read_text())
         except TOMLDecodeError as e:
             LOGGER.exception(
-                msg=f'ERROR: data file {toml_file} is not in valid TOML format! ({e}),'
-                    f' (see https://toml.io/en/)'
+                f'Config file {toml_file} is not in valid TOML format! ({e}),'
+                f' (see https://toml.io/en/)'
             )
             sys_exit(ExitCodes.BAD_TOML_FORMAT)
-
+        if check_only:
+            message: str = f'Could read/parse the data from {toml_file}'
+            # will not be logged as logger si not initialized yet
+            # LOGGER.info(message)
+            print(message)
+            sys_exit(ExitCodes.OK)
     else:
-        LOGGER.error(msg=f'WARNING: User data {file} not found.')
-    LOGGER.info(msg=f'TOML file read: {file}')
-    LOGGER.debug(msg=f'Data from toml file: {data}')
+        LOGGER.error(f'\nConfig {file} not found. Falling back to default config file "{CONFIG_FILE}"')
+    # will not be logged as logger ist not initialized.
+    # LOGGER.info(f'Config file read: {file}')
+    # LOGGER.debug(f'Data from config file: {data}')
     return data
 
 
 def rm_tree(root: Path) -> None:
     # safety
     if not str(root).startswith(DATAPATH):
-        LOGGER.warning(msg=f"WARNING: bad path to remove, {str(root)}, don\'t delete it.")
+        LOGGER.warning(f"Bad path to remove, {str(root)}, don\'t delete it.")
         return
     for p in root.iterdir():
         if p.is_dir():
@@ -98,6 +98,8 @@ def rm_tree(root: Path) -> None:
 
 
 def remove_old_data(keep: int, min_age: int, raw_path: str, protected: Sequence[str]) -> None:
+    if not keep:
+        return
     path: Path = Path(raw_path)
     default_topo = path.joinpath('default')
     directories = [str(directory) for directory in list(path.iterdir())]
@@ -119,7 +121,7 @@ def remove_old_data(keep: int, min_age: int, raw_path: str, protected: Sequence[
         except ValueError:
             pass
         else:
-            LOGGER.info(msg=f'Protected topology: {directory}, will not be deleted.')
+            LOGGER.info(f'Protected topology: {directory}, will not be deleted.')
 
     if len(directories) < keep < 1:
         return
@@ -137,8 +139,8 @@ def remove_old_data(keep: int, min_age: int, raw_path: str, protected: Sequence[
         entry = topo_age.pop()
         if min_age * 86400 > now_time() - entry:
             LOGGER.info(
-                msg=f'Topology "{Path(topo_by_age[entry]).name}'
-                    f'" not older then {min_age} day(s). not deleted.'
+                f'Topology "{Path(topo_by_age[entry]).name}'
+                f'" not older then {min_age} day(s). not deleted.'
             )
             return
         LOGGER.info(f'delete old topology: {topo_by_age[entry]}')
@@ -182,26 +184,6 @@ def save_data_to_file(
         Path(f'{DATAPATH}/default').symlink_to(target=Path(path), target_is_directory=True)
 
 
-def is_mac_address(mac_address: str) -> bool:
-    """
-    Checks if mac_address is a valid MAC address.
-    Will only accept MAC address in the form "AA:BB:CC:DD:EE:FF" (lower case is also ok).
-    Args:
-        mac_address: the MAC address to check
-
-    Returns:
-        True if mac_address is a valid MAC address
-        False if mac_address not a valid MAC address
-    """
-    re_mac_pattern = '([0-9A-Z]{2}\\:){5}[0-9A-Z]{2}'
-    if re_match(re_mac_pattern, mac_address.upper()):
-        LOGGER.debug(msg=f'mac: {mac_address}, match')
-        return True
-    else:
-        LOGGER.debug(msg=f'mac: {mac_address}, no match')
-        return False
-
-
 def is_list_of_str_equal(list1: List[str], list2: List[str]) -> bool:
     """
     Compares two list of strings. Before comparing the list will internal sorted.
@@ -228,7 +210,7 @@ def is_valid_hostname(host: str) -> bool:
     if re_match(re_host_pattern, host):
         return True
     else:
-        LOGGER.error(f'Invalid hostname found: {host}')
+        LOGGER.error(f'Invalid hostname found: |{host}|')
         return False
 
 
@@ -237,7 +219,7 @@ def is_valid_site_name(site: str) -> bool:
     if re_match(re_host_pattern, site):
         return True
     else:
-        LOGGER.error(f'Invalid site name found: {site}')
+        LOGGER.error(f'Invalid site name found: |{site}|')
         return False
 
 
@@ -246,7 +228,7 @@ def is_valid_customer_name(customer: str) -> bool:
     if re_match(re_host_pattern, customer):
         return True
     else:
-        LOGGER.error(f'Invalid customer name found: {customer}')
+        LOGGER.error(f'Invalid customer name found: |{customer}|')
         return False
 
 
@@ -255,18 +237,12 @@ def is_valid_output_directory(directory: str) -> bool:
     re_host_pattern = r'^[0-9a-z-A-Z\.\-\_\:]{1,30}$'
     if re_match(re_host_pattern, directory):
         return True
+        return True
     else:
         LOGGER.error(f'Invalid output directory name found: {directory}')
         return False
 
 
-def is_valid_log_file(log_file: str) -> bool:
-    if not log_file.startswith(f'{OMD_ROOT}/var/log/'):
-        LOGGER.error(f'Logg file needs to be under "{OMD_ROOT}/var/log/"! Got {Path(log_file).absolute()}')
-        return False
-    return True
-
-
 def compare_dicts(dict1: Mapping, dict2: Mapping) -> bool:
     # check top level keys
     if not is_list_of_str_equal(list(dict1.keys()), list(dict2.keys())):
@@ -317,24 +293,15 @@ def is_equal_with_default(data: Mapping, file: str) -> bool:
         return compare_dicts(data, default_data)
     return False
 
-def get_attributes_from_inventory(inventory: Dict[str, object], raw_path: str):
-    # print(inventory['Nodes']['networking']['Nodes']['lldp_cache']['Attributes']['Pairs'])
-    path: List[str] = ('Nodes,' + ',Nodes,'.join(raw_path.split(',')) + ',Attributes,Pairs').split(',')
-    try:
-        table = inventory.copy()
-    except AttributeError:
-        return None
-    for m in path:
-        try:
-            table = table[m]
-        except KeyError:
-            LOGGER.info(msg=f'Inventory attributes for {path} not found')
-            return None
-    return dict(table)
 
+def get_data_from_inventory(inventory: Dict[str, object], raw_path: str) -> List | Dict | None:
+    # path: List[str] = ('Nodes,' + ',Nodes,'.join(raw_path.split(',')) + ',Table,Rows').split(',')
+    split_path: List[str] = raw_path.split(',')
+    path: MutableSequence[str] = []
+    for entry in split_path[0:-2]:
+        path += ['Nodes', entry]
+    path += split_path[-2:]
 
-def get_table_from_inventory(inventory: Dict[str, object], raw_path: str) -> List | None:
-    path: List[str] = ('Nodes,' + ',Nodes,'.join(raw_path.split(',')) + ',Table,Rows').split(',')
     try:
         table = inventory.copy()
     except AttributeError:
@@ -343,9 +310,10 @@ def get_table_from_inventory(inventory: Dict[str, object], raw_path: str) -> Lis
         try:
             table = table[m]
         except KeyError:
-            LOGGER.info(msg=f'Inventory table for {path} not found')
+            LOGGER.info(f'Inventory table not found for: {path}')
             return None
-    return list(table)
+
+    return table
 
 
 def configure_logger(log_file: str, log_level: int, log_to_console: bool) -> None:
@@ -396,66 +364,3 @@ class StdoutQuiet:
 
     def flush(self):
         self._org_stdout.flush()
-
-
-def adjust_toml(toml_file: str):
-    fix_options = {
-        'DROP_HOSTS': TomlSections.L2_DROP_NEIGHBOURS,
-        'HOST_MAP': TomlSections.L2_HOST_MAP,
-        'L2_DROP_HOSTS': TomlSections.L2_DROP_NEIGHBOURS,  # needs to be before DROP_HOST
-        'L3V4_IGNORE_HOSTS': TomlSections.L3_IGNORE_HOSTS,
-        'L3V4_IGNORE_IP': TomlSections.L3_IGNORE_IP,
-        'L3V4_IRNORE_WILDCARD': TomlSections.L3V4_IGNORE_WILDCARD,
-        'L3V4_REPLACE': TomlSections.L3_REPLACE,
-        'L3V3_REPLACE': TomlSections.L3_REPLACE,
-        'L3V4_SUMMARIZE': TomlSections.L3_SUMMARIZE,
-        'SEED_DEVICES': TomlSections.L2_SEED_DEVICES,
-        'icon_missinc': EmblemValues.ICON_ALERT_UNREACHABLE,
-        'icon_missing': EmblemValues.ICON_ALERT_UNREACHABLE,
-        'l3v4_replace': EmblemNames.L3_REPLACE,
-        'l3v4_summarize': EmblemNames.L3_SUMMARIZE,
-        'keep_domain = true': f'{TomlSettings.REMOVE_DOMAIN} = false',
-        'keep_domain = false': f'{TomlSettings.REMOVE_DOMAIN} = true',
-    }
-    old_options = {
-        'lowercase': f'{TomlSettings.CASE} = {Case.LOWER}',
-        'uppercase': f'{TomlSettings.CASE} = {Case.UPPER}',
-        f'FILESYSTEM': {Backends.MULTISITE},
-        'debug': f'{TomlSettings.LOG_LEVEL} = {LogLevels.DEBUG}',
-        'keep_domain': f'{TomlSettings.REMOVE_DOMAIN} = true/false'
-    }
-    changed: bool = False
-    org_file = Path(toml_file)
-    if org_file.exists():
-        print(f'Checking file.: {org_file.name}')
-        org_content: str = org_file.read_text()
-        content: str = org_content
-        for old, new in fix_options.items():
-            re_pattern = f'\\b{old}\\b'
-            count = len(re_findall(re_pattern, org_content))
-            if count > 0:
-                changed = True
-                content = re_sub(re_pattern, new, content)
-                print(f'Found value...: "{old}" {count} times, replaced by "{new}"')
-
-        for old, new in old_options.items():
-            re_pattern = f'\\b{old}\\b'
-            count = len(re_findall(re_pattern, org_content))
-            if count > 0:
-                print(f'Obsolete......: "{old}", use "{new}" instead')
-
-        if changed:
-            backup_file = Path(f'{toml_file}.backup')
-            if not backup_file.exists():
-                org_file.rename(backup_file)
-                print(f'Renamed TOML..: {backup_file.name}')
-                new_file = Path(toml_file)
-                new_file.open('w').write(content)
-                print(f'Written fixed.: {new_file.name}')
-            else:
-                print(
-                    f'Can not create backup file {backup_file.name}, file exists. Aborting!\n'
-                    f'Nothing has changed.'
-                )
-        else:
-            print('Finished......: Nothing found to fix.')
diff --git a/source/bin/nvdct/nvdct.py b/source/bin/nvdct/nvdct.py
index 8d93b27..48d4716 100755
--- a/source/bin/nvdct/nvdct.py
+++ b/source/bin/nvdct/nvdct.py
@@ -6,7 +6,7 @@
 # Author: thl-cmk[at]outlook[dot]com
 # URL   : https://thl-cmk.hopto.org
 # Date  : 2023-10-08
-# File  : nvdct_data.py
+# File  : nvdct/nvdct.py
 
 # 2023-10-10: initial release
 # 2023-10-16: added options  --keep-max  and --min-age
@@ -171,8 +171,56 @@
 #             fixed: cleanup -> remove the oldest topologies not the newest
 #             INCOMPATIBLE: removed: CUSTOM_LAYERS
 #             refactoring constants
-#
-
+# 2024-12-30: added support for lldp device id/name and cdp name from global device data to map L2 neighbour to CMK host
+# 2025-01-01: added support for filter by host label/tag
+#             INCOMPATIBLE:
+#               cli options:
+#                   changed:
+#                       "--case" to "--l2-case"
+#                       "--display-l2-neighbours" to "--l2-display-neighbours"
+#                       "--include-l3-hosts" to "--l3-include-hosts"
+#                       "--include-l3-loopback" to "--l3-include-loopback"
+#                       "--keep" to "--keep-max-topologies"
+#                       "--min-age" to "--min-topology-age"
+#                       "--prefix" to "--l2-prefix"
+#                       "--remove-domain" to "--l2-remove-domain"
+#                       "--skip-l3-cidr-0" to "--l3-skip-cidr-0"
+#                       "--skip-l3-cidr-32-128" to "--l3-skip-cidr-32-128"
+#                       "--skip-l3-if" to "--l3-skip-if"
+#                       "--skip-l3-ip" to "--l3-skip-ip"
+#                       "--skip-l3-public" to "--l3-skip-public"
+#                       "--check-user-data-only" ot "--check-config"
+#                       "-u"/"--user-data-file" to "-c"/"-config"
+#                   removed "-p" use "--l2-prefix" instead
+#               TOML file:
+#                   changed:
+#                       "L2_HOST_MAP" to "L2_NEIGHBOUR_TO_HOST_MAP"
+#                       "L3_REPLACE" to "L3_REPLACE_NETWORKS"
+#                       "case" to "l2_case"
+#                       "display_l2_neighbours" to "l2_display_neighbours"
+#                       "include_l3_hosts" to "l3_include_hosts"
+#                       "include_l3_loopback" to "l3_include_loopback"
+#                       "keep" to "keep_max_topologies"
+#                       "min-age" to "min_topology_age"
+#                       "prefix" to "l2_prefix"
+#                       "remove_domain" to "l2_remove_domain"
+#                       "skip_l3_cidr_0" to "l3_skip_cidr_0"
+#                       "skip_l3_cidr_32_128" to "l3_skip_cidr_32_128"
+#                       "skip_l3_if" to "l3_skip_if"
+#                       "skip_l3_ip" to "l3_skip_ip"
+#                       "skip_l3_public" to "l3_skip_public"
+# 2025-01-05: added "AUTO" to --l2-case parameters
+#             INCOMPATIBLE: changed --l2-remove-domain from bool to "OFF" | "ON" | "AUTO"
+#             added support for filter by folder
+# 2024-01-06: added option "--l2-skip-external"
+# 2024-01-07: added option "INSENSITIVE" to --l2-case
+# 2025-01-11: INCOMPATIBLE: changed "--adjust-toml" -> "--update-config"
+# 2025-01-18: INCOMPATIBLE: changed "CUSTOMERS = []" -> "FILTER_BY_CUSTOMER = []"
+#                                   "SITES = []" -> "FILTER_BY_SITE = []"
+# 2025-01-21: added support for Post requests (Werk #17003)
+#             fixed REST API query for interface services
+# 2025-01-24: added option --l2-display-ports, --l3-display-devices
+# 2025-02-05: added option "OFF" to --l2-case
 #
 # creating topology data json from inventory data
 #
@@ -181,12 +229,12 @@
 # https://forum.checkmk.com/t/network-visualization/41680  (from 2023-10-05)
 # https://exchange.checkmk.com/p/network-visualization     (from 2023-10-05)
 #
-# NOTE: the topology_data configuration (layout etc.) is saved under ~/var/check_mk/topology
+# NOTE: the topology_data configuration (layout etc.) is saved under ~/var/check_mk/topology/
 #
 # The inventory data could be created with my CDP/LLDP/IP Address/Interface nane inventory plugins:
 # CDP.....: https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/inventory/inv_cdp_cache
 # LLDP....: https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/inventory/inv_lldp_cache
-# L3v4....: https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/inventory/inv_ip_addresses
+# L3......: https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/inventory/inv_ip_addresses
 #         : https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/inventory/inv_lnx_if_ip
 #         : https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/inventory/inv_win_if_ip
 # IF Name.: https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/inventory/inv_ifname
@@ -277,6 +325,8 @@ from time import strftime, time_ns
 from typing import List
 
 from lib.args import parse_arguments
+
+from lib.update_config import update_config
 from lib.backends import (
     HostCache,
     HostCacheLiveStatus,
@@ -286,15 +336,18 @@ from lib.backends import (
 from lib.constants import (
     Backends,
     DATAPATH,
-    URLs,
+    ExitCodes,
+    HostFilter,
     HostLabels,
     IPVersion,
     InvPaths,
     LOGGER,
     Layers,
+    LiveStatusOperator,
     NVDCT_VERSION,
     TomlSections,
     TomlSettings,
+    URLs,
 )
 from lib.settings import Settings
 from lib.topologies import (
@@ -303,9 +356,7 @@ from lib.topologies import (
     TopologyStatic,
 )
 from lib.utils import (
-    ExitCodes,
     StdoutQuiet,
-    adjust_toml,
     configure_logger,
     remove_old_data,
 )
@@ -323,7 +374,7 @@ def main():
         log_level=settings.loglevel,
     )
     # always logg start and end of a session (except --log-level OFF)
-    LOGGER.critical(msg='Data creation started')
+    LOGGER.critical('Data creation started')
 
     print('')
     print(
@@ -334,8 +385,8 @@ def main():
     print('')
     print(f'Start time....: {strftime(settings.time_format)}')
 
-    if settings.fix_toml:
-        adjust_toml(settings.user_data_file)
+    if settings.update_config:
+        update_config(settings.config)
         print(f'Time taken....: {(time_ns() - start_time) / 1e9}/s')
         print(f'End time......: {strftime(settings.time_format)}')
         print('')
@@ -344,37 +395,30 @@ def main():
         sys.exit()
 
     match settings.backend:
+        case Backends.MULTISITE:
+            host_cache: HostCache = HostCacheMultiSite(
+                customers=settings.filter_by_customer,
+                filter_customers=settings.filter_customers,
+                filter_sites=settings.filter_sites,
+                pre_fetch=settings.pre_fetch,
+                sites=settings.filter_by_site,
+            )
         case Backends.RESTAPI:
             host_cache: HostCache = HostCacheRestApi(
-                pre_fetch=settings.pre_fetch,
                 api_port=settings.api_port,
                 filter_sites=settings.filter_sites,
-                sites=settings.sites,
-            )
-        case Backends.MULTISITE:
-            host_cache: HostCache = HostCacheMultiSite(
                 pre_fetch=settings.pre_fetch,
-                filter_sites=settings.filter_sites,
-                sites=settings.sites,
-                filter_customers=settings.filter_customers,
-                customers=settings.customers,
+                sites=settings.filter_by_site,
             )
         case Backends.LIVESTATUS:
             host_cache: HostCache = HostCacheLiveStatus(
                 pre_fetch=settings.pre_fetch,
             )
         case _:
-            LOGGER.error(msg=f'Backend {settings.backend} not implemented')
+            LOGGER.error(f'Backend {settings.backend} not implemented')
             host_cache: HostCache | None = None  # to keep linter happy
             sys.exit(ExitCodes.BACKEND_NOT_IMPLEMENTED)
 
-    host_cache.init_neighbour_to_host(
-        case=settings.case,
-        l2_host_map=settings.l2_host_map,
-        prefix=settings.prefix,
-        remove_domain=settings.remove_domain,
-    )
-
     jobs: MutableSequence = []
     pre_fetch_layers: List[str] = []
     pre_fetch_host_list: List[str] = []
@@ -390,6 +434,7 @@ def main():
             case Layers.CDP | Layers.LLDP:
                 jobs.append(layer)
                 host_cache.add_inventory_path(InvPaths.CDP if layer == Layers.CDP else InvPaths.LLDP)
+                host_cache.add_inventory_path(InvPaths.CDP_GLOBAL if layer == Layers.CDP else InvPaths.LLDP_GLOBAL)
                 pre_fetch_layers.append(HostLabels.CDP if layer == Layers.CDP else HostLabels.LLDP)
             case _:
                 LOGGER.warning(f'Unknown layer {layer} dropped.')
@@ -398,16 +443,25 @@ def main():
     if not jobs:
         message = (
             f'No layer to work on. Please configura at least one layer (i.e. CLI option "-l {Layers.CDP}")\n'
-            f'See {settings.user_data_file} -> {TomlSections.SETTINGS} -> {TomlSettings.LAYERS}'
+            f'See {settings.config} -> {TomlSections.SETTINGS} -> {TomlSettings.LAYERS}'
         )
         LOGGER.warning(message)
         print(message)
         sys.exit(ExitCodes.NO_LAYER_CONFIGURED)
 
+    # init filter lists before pre-fetch
+    host_cache.init_filter_lists(
+        filter_by_folder=settings.filter_by_folder,
+        filter_by_host_label=settings.filter_by_host_label,
+        filter_by_host_tag=settings.filter_by_host_tag,
+    )
+
     if settings.pre_fetch:
         LOGGER.info('Pre fill cache...')
         for host_label in pre_fetch_layers:
-            if host_list := host_cache.get_hosts_by_label(host_label):
+            if host_list := host_cache.filter_host_list(
+                    host_cache.query_hosts_by_filter(HostFilter.LABELS, host_label, LiveStatusOperator.EQUAL)
+            ):
                 pre_fetch_host_list = list(set(pre_fetch_host_list + list(host_list)))
         LOGGER.info(f'Fetching data for {len(pre_fetch_host_list)} hosts start')
         print(f'Prefetch start: {strftime(settings.time_format)}')
@@ -416,6 +470,14 @@ def main():
         LOGGER.info(f'Fetching data for {len(pre_fetch_host_list)} hosts end')
         print(f'Prefetch end..: {strftime(settings.time_format)}')
 
+    # must not be used before pre-fetch is done
+    host_cache.init_neighbour_to_host_map(
+        case=settings.l2_case,
+        l2_host_map=settings.l2_neighbour_to_host_map,
+        prefix=settings.l2_prefix,
+        remove_domain=settings.l2_remove_domain,
+    )
+
     for job in jobs:
         match job:
             case Layers.STATIC:
@@ -428,21 +490,22 @@ def main():
             case Layers.L3V4:
                 label = job
                 topology = TopologyL3(
+                    display_devices=settings.l3_display_devices,
                     emblems=settings.emblems,
                     host_cache=host_cache,
                     ignore_hosts=settings.l3_ignore_hosts,
                     ignore_ips=settings.l3_ignore_ips,
                     ignore_wildcard=settings.l3v4_ignore_wildcard,
-                    include_hosts=settings.include_l3_hosts,
-                    replace=settings.l3_replace,
-                    skip_cidr_0=settings.skip_l3_cidr_0,
-                    skip_cidr_32_128=settings.skip_l3_cidr_32_128,
-                    skip_if=settings.skip_l3_if,
-                    skip_ip=settings.skip_l3_ip,
-                    skip_public=settings.skip_l3_public,
-                    include_loopback=settings.include_l3_loopback,
+                    include_hosts=settings.l3_include_hosts,
+                    include_loopback=settings.l3_include_loopback,
+                    replace=settings.l3_replace_networks,
+                    skip_cidr_0=settings.l3_skip_cidr_0,
+                    skip_cidr_32_128=settings.l3_skip_cidr_32_128,
+                    skip_if=settings.l3_skip_if,
+                    skip_ip=settings.l3_skip_ip,
+                    skip_public=settings.l3_skip_public,
                     summarize=settings.l3_summarize,
-                    version=IPVersion.IPv4 if job == Layers.L3V4 else IPVersion.IPv6
+                    version=IPVersion.IPv4 if job == Layers.L3V4 else IPVersion.IPv6,
                 )
             case Layers.CDP | Layers.LLDP:
                 label = job
@@ -453,16 +516,20 @@ def main():
                     host_label = HostLabels.LLDP
                     inv_path = InvPaths.LLDP
                 if not (seed_devices := settings.l2_seed_devices):
-                    seed_devices = host_cache.get_hosts_by_label(host_label)
+                    seed_devices = host_cache.query_hosts_by_filter(
+                        HostFilter.LABELS, host_label, LiveStatusOperator.EQUAL
+                    )
                 topology = TopologyL2(
+                    display_neighbours=settings.l2_display_neighbours,
+                    display_ports = settings.l2_display_ports,
+                    drop_neighbours=settings.l2_drop_neighbours,
                     emblems=settings.emblems,
                     host_cache=host_cache,
-                    l2_drop_neighbours=settings.l2_drop_neighbours,
-                    l2_neighbour_replace_regex=settings.l2_neighbour_replace_regex,
                     label=label,
+                    neighbour_replace_regex=settings.l2_neighbour_replace_regex,
                     path_in_inventory=inv_path,
                     seed_devices=seed_devices,
-                    display_l2_neighbours=settings.display_l2_neighbours,
+                    skip_external=settings.l2_skip_external,
                 )
             case _:
                 LOGGER.warning(f'Unknown layer {job}, ignoring.')
@@ -487,13 +554,13 @@ def main():
             f'Devices/Objects/Connections added {topology.nv_objects.host_count}/'
             f'{len(topology.nv_objects.nv_objects)}/{len(topology.nv_connections.nv_connections)}'
         )
-        LOGGER.info(msg=f'{pre_message} {message}')
+        LOGGER.info(f'{pre_message} {message}')
         print(message)
 
-    if settings.keep:
+    if settings.keep_max_topologies:
         remove_old_data(
-            keep=settings.keep,
-            min_age=settings.min_age,
+            keep=settings.keep_max_topologies,
+            min_age=settings.min_topology_age,
             raw_path=DATAPATH,
             protected=settings.protected_topologies,
         )
diff --git a/source/packages/nvdct b/source/packages/nvdct
index 32ad046..d277e55 100644
--- a/source/packages/nvdct
+++ b/source/packages/nvdct
@@ -47,7 +47,7 @@
                    'htdocs/images/icons/location_80.png']},
  'name': 'nvdct',
  'title': 'Network Visualization Data Creation Tool (NVDCT)',
- 'version': '0.9.7-20241230',
+ 'version': '0.9.8-20250205',
  'version.min_required': '2.3.0b1',
  'version.packaged': 'cmk-mkp-tool 0.2.0',
  'version.usable_until': '2.4.0p1'}
-- 
GitLab