From 4ec6d925c12fd8e901e4dac49699e4bb526ea729 Mon Sep 17 00:00:00 2001 From: ji Date: Wed, 13 Nov 2024 18:12:24 +0100 Subject: [PATCH] v0.1.25 --- .docs/_build/doctrees/README.doctree | Bin 31575 -> 31506 bytes .docs/_build/doctrees/environment.pickle | Bin 57957 -> 72686 bytes .docs/_build/doctrees/index.doctree | Bin 5633 -> 5621 bytes .../opnsense_helper.commands.commands.doctree | Bin 8222 -> 8867 bytes ...lper.config_manager.config_manager.doctree | Bin 9208 -> 9544 bytes .../opnsense_helper.opnsense_helper.doctree | Bin 5194 -> 5377 bytes .../opnsense_helper.scripts.scripts.doctree | Bin 22234 -> 28219 bytes .docs/_build/doctrees/python_package.doctree | Bin 82189 -> 340090 bytes .docs/_build/html/README.html | 27 +- .docs/_build/html/_sources/README.md.txt | 31 +- .docs/_build/html/_sources/index.rst.txt | 2 +- .../html/_sources/python_package.rst.txt | 8 + .docs/_build/html/genindex.html | 65 +- .docs/_build/html/index.html | 10 +- .docs/_build/html/objects.inv | Bin 736 -> 855 bytes .../opnsense_helper.commands.commands.html | 21 +- .../opnsense_helper.commands.html | 6 +- ..._helper.config_manager.config_manager.html | 30 +- .../opnsense_helper.config_manager.html | 8 +- .../html/opnsense_helper/opnsense_helper.html | 5 +- .../opnsense_helper.opnsense_helper.html | 12 +- .../opnsense_helper.scripts.html | 5 +- .../opnsense_helper.scripts.scripts.html | 102 +- .../opnsense_helper.utils.baseclass.html | 5 +- .../opnsense_helper.utils.exec_class.html | 5 +- .../opnsense_helper.utils.frontend_utils.html | 5 +- .../opnsense_helper.utils.html | 5 +- .../opnsense_helper.utils.utils.html | 5 +- .docs/_build/html/py-modindex.html | 3 +- .docs/_build/html/python_package.html | 1462 ++++++++++++++++- .docs/_build/html/search.html | 1 + .docs/_build/html/searchindex.js | 2 +- .docs/index.rst | 2 +- .docs/python_package.rst | 8 + README.md | 25 +- python/opnsense_helper/commands/commands.py | 135 +- .../config_manager/config_manager.py | 218 ++- python/opnsense_helper/opnsense_helper.py | 49 +- python/opnsense_helper/scripts/scripts.py | 743 ++++++++- python/setup.py | 2 +- 40 files changed, 2692 insertions(+), 315 deletions(-) diff --git a/.docs/_build/doctrees/README.doctree b/.docs/_build/doctrees/README.doctree index 422cae1fbcb0dda8ea58acfe30f63f840b150e8d..c28e6d26bc77f95f2ebcb5a66845388b3af78b38 100644 GIT binary patch delta 5962 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 J_WC{t`5#R!Ki~iW diff --git a/.docs/_build/doctrees/environment.pickle b/.docs/_build/doctrees/environment.pickle index 88fba6408adbc03600c11e07b0b2a4d2b6feff2f..6ddee0fc9cfe7e582ebafa2d47b1b2c95e09eb44 100644 GIT binary patch literal 72686 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 KFV4YDX8sF3ihdFR diff --git a/.docs/_build/doctrees/index.doctree b/.docs/_build/doctrees/index.doctree index 286620fda609e0b830dc5d87cc7c626a604d4c9c..01a3c19962a9f0a3b1927f8c06825709ffbe9cbc 100644 GIT binary patch delta 58 zcmZqF`Kry*z&iESMwWGqLJC@1AsML(Nr}nXsd*_1i3ORHPca$^$e;;s7GioY3;-spa`a*^`ej8i^@kmDM4F0P_fh|bcYRJbLR_lnpt zCQtq&qQ04twU!0QtmhPCOx`?=Q;Lpnyey22VUvIH$+JX2lt@pu;8$e~ znH(r6Ke?7)f-z~b0#DTB5ByVE0>BD*#h9`JCNC5)!X3IB=uq>? zF~Yl8R)Q6P9J&(h(6c~3)S=73hAsvv06LT@F&Q3DTEHlR#hDTx!eI{~wt*Zb!*~y& z9q6XbGGcp}89g@Nlzha@Cd0(QkR>_!psX-2h}l`3p$H6mpvXg6Z^pjOx^f>G0S^$` A0RR91 delta 445 zcmZ4NI?sW%fpw~|!bVmz7N(wklO0$@7@a4FvM6YBaTVm0rf23Qm*i+9B^IXw1-Q71 zQj_!Z(lXOai&9~N&Xd=O+X3}7imPwVVy$J7aL!3AE>>{P&&^HDODUd`!5%v$L#&6- zdGmixL6*tiSmh@_5MyJL&eCCIVAwp7U!9RLVDe4@dB&j0dZNxOen8R56ZvH(7w||h z#!Ox%YCib@kMd*(0nW)g1bbM#K+=JnVoX_HlM{ptdGdh#3^{Mc4CyTQ$#aFY8C@nT zh|43{QV%gtnJLR<^FLu;M#jaHc|;W%mx3)@h-}$PFUP7RJuWoh+sP$vKI|#R@JN$ptA~TvkR_3L4>#L4KZo?)pZW z3L%*#IjIVzc`2zyIhlE>3MKjZ3dI@uMJ0M%T*w-A;b5{bt5GyW$}_JdwJ0qyIh7o9 z5t`vfgBg?KSq-69hvg*ZZC=W{le3=Mo|uxs9y=vNtcOT*HHi+L&6D^Su+-Bsd|*z; eV}~Qrk&7!5HW!HWvdAGt1I$(=q0NSp$GHG@v%?Ai delta 288 zcmX@%^~0UDfpzLz<&CV(ER2$qJ6TFaor_WvOHzv!5*1Q1k_!~_ld@8iOD6lW8c88! zJo8FYi_#L4Q(@A#SPhXhmgOYoL1i|JvhCzFL6So$$?34_#9_+H4%LEp%*x|7h erX7!x&DTVGS&(fy83%e1z2(Wnq%N>?_x>z+O VgFSXihFA}#Wx=%Y*b&Y)E&z@1Z`=R? delta 158 zcmZqFI;FwVz&h1_Ba0pjW9(#O7Bh9vyv&l!#GK5kRE5Mm1^mgSC=4!4sE&z2GIiUam diff --git a/.docs/_build/doctrees/opnsense_helper/opnsense_helper.scripts.scripts.doctree b/.docs/_build/doctrees/opnsense_helper/opnsense_helper.scripts.scripts.doctree index 15fea03b41133cdb9d99e6ad94a1fbd483d4f19e..4e08d8fcd08c5a98461340618f08e4e99d6ef28c 100644 GIT binary patch literal 28219 zcmdU&35X=udBJLrJ*gAXg06{+sIT!ZFlJS2L8LKnT91b=sbO**- z-&*@Y_YyNOy;vE#UDMFJYtjrW+zM-}7_Xm{ikaqLOB*G>Js-2Z{oKKD)0~QCEvIX2 zXN=ALVBUe*nQwzp@5H~m@b4b{J3}*tu{?{-Z#kM7>XsKr_YB-_XdCZK{8VK0=x*L} zMb4zCp*gn^#CmAnYp$f`*)VU16X51KFWmI3PG;26{3F5vED}zouZ5qX<%8VFT#H^f zL+bRQv{Umw*lK~<>R6y}hPLi$9oN?Up^+Uxnb~Go`{>xk3!iw$^PhO&dHFl0?l#iy zqV4~u5Lkck{`IqZ7`T2o80g;4Lx>4jH##L2I~LMvraF#}XI^Qr#K;o8;LJ`3YFx zUUVh%(`tR(@{Fxe_qwC~B*jg7N`-k>trjM1j&{(dq9)3@pj6y7JHt`MRMJE*lUo%( ztyb6Zb9x=Wky?nQT6>lq7~ZI2Ue;EEbm{Tqc7p1kEsW znM{+cd0fM4Ucwe2a>eQfW_+$iSFHiAT76s?9i9Qo6t3{XikvV}@WWXJl=5x5-JP)m zo%Y--V=HDU5xHIl>A@mWI(^I2hh|1FdUSobv+i#8*Pj)aqxRX7EM!$QeCXMW?TgHl z7tb)OFC%d}j5Xf{*tBirblHC1cLV)MpU&GB0H@m-z2?u!%tnkVPBWV?7iTue(bxc^ zgTtLTnj7w{zyz_8P3g!LUxF{chy=ITd2^U1(#<%^=a>1{NuKHRR3OH!0f`QD%Zbge zM)PbAS5o&m z5>xq>1YSbJ&!FGW<`(nI@^$plJ;~*G#Gl$L?dcl%i>&dA<6Ic;tq+1ftKl_uEL??a zUxaI~Nmt6NK(VhMPZ*YD#pSe2nvUV+7i8bj2X0U`515~q`po|oSX@lH&#ag?i=Dn~ z{$d3t{&(lcXyZ=4qFN)t=~1P}N@}W3V@bs=QYL^S$M? z>fa}ZtXeMeG6TP<1EK-2QmW7$dO548l@e3$N-$MNVGd6HkC9pEt;ti-4CzVH)W4u~ zawR(TpMnmR#ft#73ArYK`o1ziu{ll(oBAJS<{6htv8wD6QwJmwDjVJrjq4-jJj&o{P-dU1-+Etw?m#&I=o2T~;mtL&xDs?2>5Sr-Cx^gs8tBPo;%BFi<&o|v3`7deNf-O-t%>F;w!oBSKR{@>Pi)vlxI{YjF84-P7j$?=t7 zda!}IJeC#?CRm!rpfXfyQB7f1T#ZU|(4kryMG4c*kZVGvJIfL#n&QMj=^(StSSV3d z*7)jBqi?h^24Q^L9!sy|=N_Je+j05YL(Bc*hw&b(9{j#wQIrROT<@(*SZ! z_;hy}pTsOD22oEl1C2!#RnOH_S>kD(Y< zON6TAp&JRDTxp7pf6EPgA7JBOYnyA=v5{sq{F$cmwawcpk&$LKb(Q9Eanp2yi#e_< z1B;VXd4qRd$DuBj3FB*9V962dCu zOH|DBF%+ZXFwbgWJ3N9XNikZOg5uHKu=fEdexSCo)V^qN`*SYIFVY7Y%VCkWRRZJT z8&;kNhfNmac}C`m%nUpnWfWA)H2Tn?ir*r5Y(uUIJlmIY0vBSw4nhfE?oh$=FW>&-E24NWPRC{yqT7&(=2Du7f1A*6Ate3JjrU zN_@O0#YZibd1(9|S%*VomOYQ1;l;O<&aTFb-+>NQxEAr^Pa)TY7k8ENg3L8JsCb>( zXDlkHsvau-fh0I8UK7G9+e=jZDj!2JDh>%$Fw==>87WHv@kVaY`v4H%t8J=X2SoZX zwD{gbIU+ z7*EtTm)aW*ZcS2Ph_6DG14A^d2#oq_^T4=5R^pk(ywH{b#*WhM)pCmvI#eNB1dLBZ zZcl(Ar#Ugec#c_UEHJ359xy&n5*!$g5LQ`V0^<@NLn$z%@kW6mOH*LHk{kFw0LE8p zn`@7Ofp14v0|pw_07gBvd0_krSt$byXv+ZO+e)`r1I8aghbm-?fblnw+cRL$G$#fa zf50p>78q1j4;cSW5*!%c62dC$OJMviA44%P#8;mP4KQAl=T4+(`t{9~6&{YsSD$2) z249kwq9fWppr}-F)(Iq~h5w;TnGg_E};_2M5_W>v_)Hasd7Y%NWQc&=%8OlMy+f@WbU8Q+Y z{5V-ifP(AFfZ_$E$E$(jlhC1x)gn-Q7IJ$63Ng!x0mY9p1C0d*Rn-H!3(Km0|m|RN(}+U{PWs{Zyu& z$~+|gnykYiG0QY%81Y@Dv#T-UFQG#frbUeSXUH{SMCqq8e3BD`h(Blc8H)(2s)vaG zA_u{0Wp-U}3aX{KQRD3cP_@L6 ztGjI1L6tt|DmQ4Vz$&phloD6zbFNJk=b`mZvJ{8bg4CDc*1MIyug0whphM+|B5r*U za!t5Z`kZSr&xt|TDl^eobWv43bUjHD99{PcVU_tMx|aDEiqUm}qf1(^$y+#yZ1!E2 zmY?$FK5nu0JBbQC+<4TGPh@JAGiuAW9^h6OW()gTh&!pnupj+WFdKb#yZX* z>~l(wS3}rOLWgP%6(Q^kkZXdl()V$=#U|%1=$~K)8VfM0st2&Ik^~3X3qn|Bd>~nTd@I8S$rrbJW{*OdeeHqf6j`t547d-F^;&JW?Jcsfvt%W@pj~xj)l!;A z)<2PjQe;6_23h~2^msL7{Uda!R!tGI{u6S0g)Exo#N=Fm#|$(USyWXISu^tqvc4;X zRmPXd`YS$$D#(KE8f9ENK1rte*7eBRHO(oqZXt6NK3SIEI1}&PzxBIsofJc^?zUY= zR{DI9*r~RhS)!p5TImCY_0{IF^#QUH$JV^imf_aJO1D?z*88DD<%1$_or7ExZk0Yz zD5p6w;Ce5!&{%L$Rd(H}LzF%uoY*znRsYa{RB=p;6s zlZP0zZP(f2`(f*OQg>0k{T{hT7{~5@NGdjp%I42<; z^0Ik0-JMvR%&{W&K9;|+;j%bKBW+rA^o1^u+Mp@7(D5{173?4i9NJOr(&Wez^S@0n5Dr(6#Mi(zb>hC~wq!W5{Ac~e8e{xus@MBP- z1>Xp0OMjdLb38fN1>5^$PkyJ1J*GdV+ab;eNlu2D!)ErwFo-wIqu7~w%W$x>b>Mom zO>IP@C&040lXM8}}X(D*C|*3t~8Ipw$Vz>@I2SkOx@|) z1`cAGqoH=N>Ge@=^Nf~RrX`L}=W22)`riQuq9a^yItD3y+h|2jI66jiK-BprOk}f# zv}EfJj_TMlVDT;%)U4wQ}M>lk!%tkcVah)Ef35!@=84BiHbcB|; zmKH?^Ed<(9HyjMLBp6{>R=~br=IEftMs$>wurMd@r$zW;N!WEYEO@j!hV92)-_Wv> zEJ`|ocG>bR9;`>TuI1CQCzyyB<-A-ZSa63i86V4ntvD!M63sMh1A@un=2xQf3#cOW`WD-DqYmYInD0X9Z|;@CaWk)bivXt#8%PaJ)LPOu66 z5YdjY)1ruOF@s>}udl74*k2v)1g7h(y51Jn(n)H`27s0t(q_0PV4O3CD|g-hPT2E^ z*X!ieQ%S1+ZmAy2R;Q4?gp-&o9Hzng01DR3U|^%OMIacfh#eKM5S>O$4zy%(CKJFB zIus$-qGSG!6X@HTX>FM{{vm#62;^~e*vHW{f%aU8Rqex2H?i0JrDnSwU6}TC$HbQZ z9qgIU*Bh+NObF>qH~`@qJAsNFD+4bkIvET@MU5L`Gxpzy=ZTr+Dxv1_|JUyO7X z)TJ09GuSk`Siw6OfoWi$`D@Y14q9E(3~Y37Yd9;5h|5NOoHP@MeZ%f?nq7;g@hl;o zDHSbnnqc`N5^)+6j5>6uE7f#rZmk@wtJMNTh39TAdibx)8;&7;urZ?vblh%tP%@$>^ zOw)-G{kWERK@2jS%4c9j?;zkBIV@;p7+NC$ZFVLI$9L qW6FF)H}5c^Bo zcBPa=NB*`XDXC;gj5Gx7F*Vs4?dcUadg5wK(+ra6jd$ZQF(c8FN*bT2g_O81EJ9vg3#rkln6IT0 z;dUjd818J=qCH*DjFW1&7^h+zdr~poq=r~H8CIgx)+E zZaU<6Cmky4XM@pm6Q5Y%F)D$;OuNMynFV#z$n_KlteJez^%hy9{A}ufdUtwf`u(&w zVOKE>yQiZjQc{hB2eY;3;i|p~(^)pE?V6OjTUR3_|GA8-yI!g&bu0r6Z;pxQ}%1L&n9J8L+hEYc$5VV~jf)>o1<= z+`DFOIe5I(^Fnl&aQ}8U&=S5zcHYbeY$Mdk-1mA?R*hRYHXD?|Q8s`VV^Ew^X}D43 zAKxY50T?Uu;aY4m{k=S!rsUEVc)C)8k{O55r*L|P8y(SUhZyYyrM1DZ_e1;h5@FbpKCaj*KiVl!1*Ll z{+EKkoAGy<`KJY+xHEQ_;-#cf#UxY_$v&5 z*^HlL)9oiYitlUigW8~?d2pe@g{y3OqtBsJt8l=^{LThT;qGgI2^pZ=~>k_P6K8W_f*m5V{D}cK>5IFEm(=gu2VI)x4aRJ-L zVOveGQv$Y?!=A4ET)s-{cykrz|1k~rUK0wga0R!if;;#(PA7rV9|`&;PQPfPkF)FE zUZnK(P|@ZGYnzH*fk>MRSK4)N$reStMKus;u7+%|4A#B956`eOYe}HIM!RlpnVnfH zLwvQ-+MwXcV!Ph}4oVt$$oh=~k1QaL2e?&|8Vu5WksKc^n|R8`llTXlOT znYfz|lH2#*Q&s0tr%s(m-P;zw;=lz5EWkgVm90j(QoE&493QXM$4jl5&eHMv*z{DX z)}Fa{X8r9mx6TZ7mNkpJTlMMYSZSuS0NxlYSE}R9Qf=l2c(?-JZ&#|Vf@ok#t2EZG z)N2WRd2zX~yae7YFY7FBSK8H*@~E@&vPP{{g8zmuE>#<)<_x|HZ!Ws3bPJB5`-MNA z;lt`{6k!nUnew5XWuwWc%L_YwVz8HX7F8>?(#)mPJBywEV!Pd}?3```l8Ds8%6JP9 z=qwql7F#X23D=8j#VLNZbbPu&KXw+!Zfce$@SW+-l6G+tAI+3kmY-NYsC-O$VR=pG z!VTs6RB1z8*>FMqCPqK}G2Ez+mrKR*4RwiRnIXBM0OM_KXzXd1>$TAaG+&%76`CzT z=GaRPoZboeF9Ym~lg(nItWsNP+A1jW2_6K0eG?kpNzIDAQtWErY7C5Hu@MS*IVr^m)2{gp{Ug= zj~1ufW%0AvsHi{3>a_`w40yb5175SgDwBmA`7%`I0or%UUZeBkI;ve6f7wR*c~%-hcC*Ka6SA|L?8*fM+*M+7eJ#K~Lrm_Ou*uD*wJCn) z??PkG2x2yAO!&mUa3nHD(_uEL()CS$4yJP)}cQ^aQk;C{9<~qq~aL>5|@S=djjvqfu|R zM_W_HX8Y#pIvOE&q4a-_4<=nh$!YQyc<~KDq_=XL3?}!%+WH}w_Ri$-Qu$b7LpKPc zAEaYceymHKBBQ0U!?s#07WbRvyU4J=w6?-9-o+M9+CRag57M>SD~Y zpgT{Y|yNe)J@t(a| z<$keq)D)P9l~J@fVdpNk4Db^SX=VyO2xgGjaLSF4(&AH#=zIaU6OmwAU?H*bjyk^xUbm5G1h&C! zAR{+&gl-h#yt=bQyh6r)e6GP>?6Og+jl&jt^cHZ@sr!XhDNTcOZArBP`?X?Kc|CqA zHjTc=)tx>;S`w?vjed3K!$4;d>%4PdV-NZf_6&2F`6}pQM_`se_6)Uxw_{W)9qq&veceQD1d{$7ze$kGk={RKv9$nmo1MH3!cjOX2b=Y3;?nP zFS*<>{3*$nWO>_WUF1Qjg<aOhTr^>+aox{!_Mgb+uRy(PJF_?i2KI){i3^r@1uvn-(@ z7Y*>pfH$;Wn^b5WzXch|)$(FSab-1ldx%)X58>NSjNb(RxRfudyf@&hYorkM0o3am z6bMj135POFsW@m>tkA_m3cq=qWc(x8CkrM8%J>o(-iPGy4i+ohD3d;Agq`pAB70U? zUC_ehZcb%R68nD=%3P2@6utza&~gOr(4*K{4B3Tk0;8Wb6<`CL(f(NV5U zmaFi8dlbw8442}Z_yWpjGAEWLLZ=w1-QmIb0~!>HDxXPisQWc2oPV!@J6W^#Rb5bW zG1b27+B9<=I z7+_1+@||F%m)iDa3xn&+a5G^pR-N-ug-0V1s8-PSSCvZ<-atz}f)Fx9j3M?9fv*;Z zpxW?M?i-?sO0%@PSgrDfJASo7A+GM?2b5c#bCiB^_c9Q_QEk%|XNZqq7{UZyr5_3+ zwFclBVf&elSDC0l_5qm#u+?gdY=qVPnsoJPL0xaaw|ReJ0!{CXKd9g-EDu^dHs&TE z>|XK#6LRN=Ag4;~DnRlaMpUPnYv=@o;;abK3CO^sJ^Iph0z$3!fI1vWkmx#uIu!4) z-9r2XxRk1*bGx*SV1|uX#@aB&&SMu~U>2Kuz!I#rVY7xIUYuO;c7#S0?2apAEZiQA z7u&^xn3>pu0GyhJSu3C!=uM?Pt!V8uM5xz-nLQq@Z9v}2?zNt?^Hy`5xkia%nR|@J z+@^SxP_=Y}WU(d4?zMk1%(laTZFPW$$#$yBvPFo2G!gg@>hSiU4#mvIUOZ>^)ZV8% zsD$ac>-F*5UWYRI_-*$ysf3?QF5!1USukH7LRM9$91cvp8-OzwPD7_46lZ;iPCx;Lu?|1 z;zARmzfjcP@6ng8zYywu9#FdeLZ~$Tbs;(iSazbe?Z9Nls9k5q40j!1GGkP)^|bA{ z!0ZgwMy{?5&7hn}=NF zlRJ-KV4~&eA;_t->}Vk521dw`S%y%Y{2{XpnRbvzU-~RVsHGlI`Yc1Jv{|+}@xG?c zGJK=n6x$HU0A}-Tq5^(u?)1po(vO&3m~$(m19L_t&B?{qn#(a3A_c~3`$QnvC?m7J z+B#;`%|YFX8HIg#&ZxmaqQN{00ioIRf)GU1*>emK?fHyoA+raeIH^Ns4>IVP9)0Pv z2cdR&Kx1;gW&gsbr zszWg7)AF1@n~hGwyD(-@l3gQKq~teA!C!<(fiZj50KwkH$gIzvMMxq!XZa86@WVkJ ziur@Rc+Q{UaH_!+3WOqbz8HdzI*X14;(d-0FJu-W6eoGeEJ8-T!=o>K79rGIJ)rbi zgivX-=qgL1QTB{t@mC%%W^+xePATw!VWzwJ3P;yz664dcG2^W1QeyU5pOGXjT%6xB zJPwHdd&a7OnRsYWhhipTFP<}TQ$D&%#gP}BD7rQTL3M6MK+fX;ILwQLP@D-Na}!zj zUmmkepPLBvD-S4rZXy)T&Gb=r`grVUrgOkB3+AoF{jBETz6IhDA{~a}5D+RmFDaX} zhRUu$mWWUpy{dTrt_tCq43%A;%`26?Yk>?j{`e3;PCDpCU|x6HKZS2UWkDQhtZiHe zJ&jk$p%cHBB(BK7nEnno5j+ini?ccm>dsJ6EA+)~@`S;a`KB!rq2L;N3CeX~|_&FKK^kGI^eHJa)~lpt3|fxP=x*D>b4$y{JXs>zkFX2}4M=TLw*-J?sy z$dYd5Z|e4hpl-z+!M;4_$nk#kf+^zhCJlRI$gtHJvK~lv8Y5N63_&PP;E)-DY+2#a zmp(%fs^0@jpCJfEGbDYK@ue{N;>&Ob!uqFMx40&ebx3W9-0oqS@HEVG5}|fiZ34CJhf}hygiIZZ|s3wz=Om?`TJ0K18hqy z^@vlZA^pdUqxeY)A)dxrTqE+rPrE2w8lsXit4Q zf(#xwH3ZXqz&k$;74MMz(;A9)?tusvJc6X5VzXIc}O5dn1@uY?unWuIf@bvtqC5_U6nEE z>I6mhhky$4`PB*6eE2L6xmls`8~D6c-({vBGoqbtv#3$5G-oSLp zxj8k`s%JVIIb5Q){J^xcZ4{a1*yqEf{FKf`hTx^2&*Q`jPV3n$b%GZ@1-)PW=$>x$ zpt3+zHc@`3NV-fr+X#*@Y5}EZ?(8gvx~R(dO#GK1GPt^Y6h&uE=TB$lP#B#o8}y%< zN5mMf>p$@+b#pRc1){Lb5ts3RypJOu|nR^msR_IbH2W#G@!7N(h zTF(c{uPfhg9n9USgZZ;$F#R+~*gyu9(^(ebGd{d`CIHIK)gAtWMtZb!r1s-j;?)4; za%xN3z6H=(#okrNi1CoFG15&KBl|&PJU!1C3&CR~Q_(b?5uf-Ju$^Vev@%x}iJ=DK zzX37gwbStifQwIszch}=8{tkTa5;@P0Z_RwJ`0=d0|eu<@#+e^!k>4{#3S+%Y9OeV zPKF1p_@bC1u(RC^uSo$B&T6H$GGCSSH829zh>f=^opjRbqT|byx8fqU!5#bE< z)WycEhK51zpUW>WL7`}_O#+zKPamto#tqMI99SO>^kN-D+6rs}$HBhQCy~Mvcf$coHpILfDI%epDbx)Sbfr6T; zJ5p&Ez@Z~C(I9>wl7rb?$_n}*+?=BolxtvIL_0_7mYsqfUs^u}qv)7+UPlp#KjBNR zxsjoUfF^P&mUsg8Zx9k-U5ZobhwB7);Ux`7=_ktZ6IW8(&yTA;HzXk({ zcjML9@#>c3Y7bo96+fH({T%l9bJ^dwvcI3l{(e6GjaUhH8&^E?(AvDJ=Q210WE8wY zv!{42=t=R2;q;((jx(f7B3P=H@T}`@-LoX=lWU&jH|d?93(1j;*H<&Q-uX!a+j;N) zrgsv`@XZ-%oh5>r+%lk(D)sxc9n$Z^c|Q`KXnfF8ZkgC=oU$oe3P3K4QYt^(6fN?2 zSKn|D>*9qtI#XT#2e5YT)#YltK+kYxIRpdiR+fnjOI2y=TsbR9QA!MfQa=(RYgLp3 zis88VqEb5o?UDGL!FgSbCM&y2Q0W0-0)7YzX8u)b!&9ZHI+V$xPns3NN~^NBMo56p zL>Bp0>Z4t}f6qtY)9C_ZEe`R;tICy@umi!>566_((6Co(dBM(L42STulxa8?ztu0|6k;MnC<6lx$ZB>hE_4YmCk?=H@3O*K)JH3a^fSm7Y-G5G zPC_8^H_r-zxWpeKY9zlGgvxKc4I$Wh{B}{uKK|k=Y=JL(_Tw+E4jGip>AM1eTv}P~ zOU3w?XrgS%>C=;oib5_RU4jm}VA@8Aq!IuF`G-#p>;ie-R;)!kOVQeKJQfKnULn<= zAB@ymI|TU_a6rb|U2sB5Ypriu@(ubpt^(2TaTHx+Fq1Guk5Y45R?*!-{1OR&f)9Am zON+5iPgQbM)ckQ!cOv-$`v9*5_6jSE^K^_$XP2syaMy)23x%XiXk-m32@c-r2d2Rr zOmvJ&(pQEMhdF8lBQVk6UPc|=&@|j8vJzf_;_TD+B_@Vc?7Rl+@*qi&3Q*pZbSTq?-GiYyzlU zJvdCwZNbG86TxT|)y^2{TDnV$;pLe~`6Sjy4?P$P%6Z<#_kw7wt0v68_%4&Ld`5-L9LZk|c6rryDdSF9HUp?+e||n{5ef=Y9L5^M

z^xhn9qb?(%8rSZpx5P8@=PYK=B-O<)BKfltvf1(iQhYhr0TAqDvpzWzSKW=S_Xnh1vAngfTrF_ zAxN;2L^*%?Aob)GB*_Kh<3h0GI#;!F>j zCnzd7Pk2I^8&dcZlo>`U-3=*r^wQvvi?$Awy`z5#5QQgSbnuGp=-Ji2+tIV+i}A`V zZs>bYzJTHMu%EYunHhp&|3VpGEJDpXQ?f4bH_d@JIp=_T!_b$qOHaF?4TD648)5lXR&{=O%-lzA4_pg#hH*GpcU1-vblClcze6cA75ew!=G-fEo3n}0j%)ODfCSwdoo1O-={9E*HlsM3B$A-h z|6R-~t4>!F($0j?E=%|T?~&L~jWJbV%S7Apw5RHsXgQ%tvM+W^jUGnce;rGF;(+`C zYo07n)^gAuFmlisOCTi-PTB$boF?tOCXK>xytp*}4e- zuI!v{mxlT=@eC4uSfU$lv*B&=9ggIsB&Q7f=eudx-~HJf4)gKbUWL6se%q^i8!8Fc zHKD>4UyTsle`aMxaf~$qyUQ6U25fflA8` zV-g6y7mygA)G#$MjIDAO|D*1HyjtlV9T)7UNKNatbHhRDTxMS!#2VqA6eLG~z#WWB z-cF!b1$8H!K-dSE@7QaWd-9UmrX?tVYrNZZCxM}mq$Gho6hao}$Pp~SY`ix!y6E<& zY9yfRQhatzf#QryH!T#0r^4~6($y+Rfq8!azzFC)%l<8>J2A_! z56@Y~^1dOzJC*2?4%si#%Ju7zL926MIS}X2m;~KIZ|Y z&jEy@Igma|Ya-;;NX=s}K^0A_q7v21NQ7K>P_m>i=}v@Pk57GzDCBy65 P=a>jd zBe$&Qx>9&cMj~X}!{m#K6OirA?MV57Hq(&AFS9|o2Ag9V0|GU*dOMn^PuJi(y6kLt zETj+z!?S3$v^p`w!>!6p>);K}l*`$}rDse`C5Ypeli&Q5+Eaq0MmIGux$i4~-={m35Izb89=Fyj~ znGxz74=7zTBh+R=6qibKX=ZGL^qU(~5!{jv4rxW`K-s~qdtk`i=ln9fVm@bfweNh+ z?9_h#t@zpVI$zXN!YX|fAT`9*d2D}6^Hh;K_EK=eI8Oe0qTw?qSaFKLK$#+R{q$7o zef1V8k++NaB86AC)!hE_w-(m|m*B0~Hq(!=!ZJ_tAG-m`1EWEJ*v z-|pYSFuHxas!T}lu2as?yMr-1i7IHzHwr1p^zW|O4(C{LrK02%isLjBzh1^$B)MM_ z{n{nh*lDxn?+pmSJ)fUX22IBIj_Lsm7U*8ZBAkMbCJIk~6hpPSJGB=IQU6t4Kwy!= zK|SLMaKDnk7VWAOSwB}^c`+EP^>%%%4t3d0c28=}=)B9f7y6%Ef$@MbJY9=0e|~3i z?561k=xBIZ=F`KkohA5iJPaG^jJU-Nz4N0j3NW-ZHs;4&fE?NG+bk|OJ-3xT5 zN%XC}lV~B#e40eM$z#ktc>;@66reu%OoRnb2h6sWeg)&@w$iWhirGr+ss~$%Mw9z| z)yo^x?NQ7SoIPu)xAGo2o>W^&94PCsH_Y-L+2jC~mJCI*#K97+m+wpaV`*}O#BbHX zE{}!V`dE>`?tjtxgcRIdP;8GmFYj5OkS(CR=Oh3E6DhO6XP z07WcPl~w{ovWV{g@zO+by4r>Sq*i{x%#TdZ{v~R!xOwyG)77KlfzPN$`brQZgb!X2 z7&9#Xp7F#x5c%Dp?nEFG`w(o+3iqEg8x#b8e9!HW*7#)z%BuWgoy5-=d3D#AYA2xE zTZHljQxJ+XC4J3OoaG8Dzu?iAaF#1jf9C-uoaGAC-wL9bq5K-vO7R&uUTVQ7V|b|( zn;G3jcoFPd#SvqyRH{|3PglqB*rrw+d=cZ_QxttBqu83?6pd+6C|Q2mF?89^R-nduD3)6e|Wws~#Lc!WWYF7~ZRRhU+LM9<*l90kmB@e)D{3r=X6h2Pe=u z+gRcMsQBWp*6aW#NAhBg7W@Ow{Vi8Dr{PW!cNOfq+bwjEJ}_>b>Njc9z7klRu2|jf zp_eDHo%io=VL_cnZ0D-Dh}LG)ovol|qljYy$D|&>+>jtKn-;ILVY+kB{XU=7)P3=CVZ61(j*6{5vdS%VmC)<)8D6 z^sAgmnXaE7xEr^-enjJn>j&p=A`t8cO~zlly9jdq@Sb(uuNu@&Iq4_;(#3(r?3qMC z{30Ayt#m1rl@F2#wjZlg{hOPQtPDU>}`iNCZQzKHUZfEu@`JbBn0k3R=Xw<<<}X zK}za5&PndJdUhwUoxj}QtR7Ai)7m-2q^8jM6o~2LZ*Zkgi?MclHGDdXaYZp5UY^h@ zz`}Vc0Qp)tFJ|v577jZ0nO)qqZY_i-a@AtjBo1%OGt4*ht~2e1_qiLluUVpT&o%4g z?kbu|7*DRVm@m)A*bIoeP&ADH*4zs8fapc1HU;y1~71tkf zje3FwTnQ6N#DG9;!5Hh>q!#9`zY-62Yhy~&G=W}zQi%c_T@4o4RfqeePWd@JASX9`(Tq( zso+!9*bdi$_{HfWq=S{3{8JI~Xsg}LN*lf+jz?HQd38UekimDSlbGIo92yDF??Z(r z{vAT_0$VF9vg2zj4}lh3@wG$o1#idjL-2hqkc)!>$hE?te#7{h7(YfDX#CmnwVQ+t zsD*l6!7iB5yUUfaa(8GrBEP;6mu|;CY|$G5j{Qk(MCg3(aXh+LUmuO()t6R*V59s= za&>wT3x#iAum)YM1;$Np-~PEl-3i}5_8|lUbh-1J4D90xb4XtB>d$2t#FwXs5K5gd z=$XHiF~zyQ$atP$B|>plrW+JWJWmmTQ#|?-{(J>$od=Zg=POVr3Zj4%Xr;O5gLOF) z?X1_U_)P*W+jOkn!0-F8MMhgvKn380*yiyFH+Eu}3Hp`}9#-!&u7Hp(QlE4JC{vZD%QLI#6~P>yv;ecguVVub5kg zUF|!!%q6W-d-l9C=l156DZy^I@6z^65v`pqz+*aHtn(N9onr&B$tv$U*4(!NH+Y~2 zKe*TJLRtvylKPNclaly0h*Y}S>2@G}BZ1vt>1-5piQ~_Zh>TplMa6!TkSD-r^a}uT zS@deOlA8F`s}Tgpv1)q=f2>|b@Li~VUq4gd-pqptGI=#B1KA*qUgf)q48 zD_@11bMIMM)jh?8W94+fzT2_Frj_)o9NrDAxK$)ppsBu&2*Rc>)2*`UN+_9<-@9YS zYAGMz6y_Ig_`5#6nHQV{CeI^8n4W1u#U$3hQs5xpiisytwmX)qPPM zhw3@3`I0zt^uW7-h9wFuIU-~-ae{Auf-g$}l}+G>DS18_dMU6SmhCAY<2eU#9Cxv~ zCvjWMry&vPx9252)?HaipY5)=7W;I$E3OY2luQS{27p|OL%J)3=wP-gjX&F6aV@Ta zK3|IjX`71=MLt96|0)nS_Ott2l^(rAg{JyZPK8)(AI*AUPg3a|`2= zw;STcLEQ;A1oi=L2<$b>4Y7UJNGVOAXLz>gJ`2q8LCDvJkVI9M=mmNeBfrc$`fPGRI@ z*9VK?L@Q_pUzgt%4*q0uL`ON|VV2-8cYL(i^K`PuR5V>@$9N;z6N<`-nSKbG8i*N^ zGJT2tpcU&gg4J@z>lS8R1K}q9lT5&L4dndfqgExRpyeF7C*sr4u+n@$xii=O~ zFqb zcxrr|bS7Sx?AuSje)9?K(k&1qTE|GSZtGCA87i#CMq8E1(N?24RvL+tSE6-Wx85L} zX^@i5rG_;9%^)JG`Y4Yl{TvXxi2g)xlzeH0NlHj*$1dsm@Hp7v* zCqZVhK$5#7#4qalbs1g1u6)0>>$_9CrqjL7>2dZ5!KeJgC89y`fV{K^f~FGIG+q*V zk2ujw@zA~#fPB)>Kjd!Qp3)~8S5o?1%kXF2T?D1{@t$c_DxUzVMR7e!slt@Lw{a2> z>6a@Cu#5bC{Gnul{7G#1o*NDOVI1G)W5~>`{JFbvI~zpfo@`inn679IwHd?)-g8bi z^wZMQ#f8P}nPh_aMLamhjf0&BM*@(~R2XzOZs&n$+>-}qySoVD0q;2{57s!B{4TZ} zW_evQ$@+{~_H;MOc9uQOlVzjs#_cQH#z| zV3hTg8i>oFZRRL|R_=?>g3QGD0qBC4%)AuH#(v&06OYJ8XqMs0kaD>PbsY;8m>CH> z>ilA>)EbFK8hb{VH92x4_n6%%MejAZ7jK8LBo@Z1l*Z!Al(*vhu?=p7 z^zkm04qk6`pzGIx_&R8b@0^x4_6&0~STjp-8+IwY1hjZ9wB_0!cluxpG+rN@i6^nu zW$YK)?=$g30NPmsH!${)?9w{RanE#1qgolOv}f*OFYGQhYcqGnQ_w=pm{h|n#<)7R zDpAC7T?{uMg?fK7hk=a9bC=PNQi#d1e~qjIqZ2^x!Alm8EiaeSQSL*g50np)H#*B1 z`y$OO)$Wnj_?T$W%M)701Ev2af!LqueW2!1dcrn1zO)Ugg2iYYHqF9sFV$R>9aF7imej5n1WM}*x1QweM_LefOzZ)_X z8RmQ(fL!y}7&V4Dl~FJ2EZ+&I72Py?^K`vknz?sI4z%-Fp#aqs}i+l zwSEY9MEumPplx4NnQGLVZ7#>G4P@5%7ADu|H((*iwL_6GoY$TP*W7x0&RTW`(%O-= zr=M|F;p~lt(+j7ab@tk!wQ%kV-rTqme;4qd(@tA^>v|C*`JzJh&Z{Syb@)VBqt+_H ze_jU{=p&x9(Mmy`$0OvE1qA2!AZx9i`f8)j5Zn^Q-vYYd6+aGtclx-@%*5Y;U$@Sn zcK-rgff^KZ2qVD1%@*MJ*ElmYD3W0^HiWJmX1tnVRtJtws!(}*9qi-Q-+-ejWA?OE z!_+?N7l%fMgoZ0hH}e#cmUWV^Jv^D@{q(9b(@(?~6xUihNs(rv{vraPteK8VeNEE1 zB=ZU|bY2aFmy=tpGWr+BE~$)*bOwy|!4EB&fJG)N<4|LTUz2ajhhuf9yJ|J+wQ)So zxl)7DoeA*CaqB34qta(5)#Qd-IOznU6aCpYEX9~ ze20Aq0m(`sIDeMN2Wi=}(G($mLkQx`SP?|VkjQn6$Zk0mfi4A#vxa7<=|e&3zF*S_ z$k4M`U%GT2!Pf;Lp|CozPw1Vx)+TAvp(5>P;cg*HJST(_hQzIc0q*7l^gS2ruAuIO z#9<$v64!5#10-h^q?4TX_-*%#Z^)ermx}s%O~`;$Nx*2;eGK^!NkAx0vJgo?LZ2lD z3UlrORhRq`vkZ6|$8(r`1c&z9r}-5>`1 zEQkTPzW*eFkn8)A*7aSVGEcYo)r2;^fW_mYi%PXplOLsco{Y^!!}9pjXiKzK#Oc=J z7NFlcm8>2*p@*%{h!D%35MmRFJRZU3qk$}o0Uqe!tCWuj>P{$S?88$jpB7A2DJ5t6 zLV}JL9MRSLKnQB;oPHvZ@gzn@?>YU71q^yx_fKjYwMBiLm|UY!R8qT5;b84H z97rR8iL%oPb*wuydtl|}j5)2wPp3TI)#DDtu(*n&GYfaFf}3*`ci?_&3_WxXF{N0P z?Yshd?}+39O@L+5&MFg7DcOv^8LF~-g3TL-U93cOP3`jW33A3 zCfmPHHz0RbJmPL;%FLCk&ulbBlJkH-z|SR(4)UuJf;-4xn_%x=j>HMBl2}P2IX>g_ z9_W(i#r+jtvAj5Tl`Ef&>2QgXHk!^-aTbJXeX92|ZBMZI+^ejhii~wh4(LE4Q!qWj z?$ZfDK9dlHUF9MOCD`?_W}m-Zx|w>&k2@=Du}-=)R_Tx&x$&eC%9F!Xwx&pvOT1^c z9J)#a_pss#WER#+?JeuhIDH*_?yOO-Pe1}4dnT99Im)2>SC9eKZ<(Dz_d^51n924G zI_$Fh(GY`SZcY`WQ=V{D~VtF^=P>`rzfo|0#`7Jxu)W+r%TbT@9# zR1u9+riwG&{d{*9LHQ}XXMI^RgGv~y!92TTISI(3?I-36wvbu7m_3trjrc{vcfA`2 zyHR@$A3!D#D(=SZJP?h0@?f{Siy$8Go^$eGg}!ig@d3`IR>MNzomTD;KkjkkW9LW5 zlOGSb8@KaAH15fdceuL<;s@_JCqH`2J^h#)4Lch??8$~Nxf{2$K{W2khHtsM2x0^8 zIWHR&ldWq`^znyMImw?i8-C+P!_J0Zc(P&X5w4}bY-fXL+>;Hf-CYE+f%lx34T`bc z%?AE3VT1TdY}nvN!_J0N0LW*d-r{cD&IZxA!Uo(1NDLOSA2j|;++75*f%lw~4F_x6 zlrBarm5(JNbc2u?cOzqG#*LoLXu2D>Geb14FvC@yaGSe}AZGBMLzv-ySNE=2jcaNk z{yJ>B=E%DyvwBy7F5_9f4+7a@{3)|~@fs~c%<4S=u7$hkU&Kxjfo8?vCR-giPuAPa^Ua(Xe#p=^~=;ns8JYEdFu= zvA?-Tv@G8pn;^${*+90wdx&Bm2%fAIJac{&t&=JCuZo-8H@$v#&F_n_r5 zs61d#xs+7KZ)_F%w@*QjZ8#wWd!N9^d!jO)fnJQpO3gN8W-0pKtYI50}U|`X}_l;O~UUbQ_30*Q%VG-YWOZf@QiUeAHhDP)5)9}+{YhGqc#m1)$^2&RBok;$a$n6D=Pn?b zKTa>pJ8DL`*psb2R8qJtiGLKz>vg(6FS%x`Zqt0ti z+LO!&pg*A(4u&LidRs~K9K?4*^UP1qMuZtW0t?LUBrLKI&O1@&nMW|Warv{Y)=UO1 zIqP~(+gfVM*^rU%nVN}yNf5b23Ys7TW;R~LD5s~OErNv9@#@p4jvkXpnKpAXUj&4J4UlByrlQ@1)OCy*3guLs_7I1Tg46MzjD)e<-L!A?etQr=%ZZB@Re<_XDEBVEU1*b;vH6A!lv4!XT z;0ahK#cD(66$j9s4e<+Pyi$~N13Dtp6oTMQ$02%Qe#t8Rp(`NueebWO<>w2i%w zr+Pr?a*a?V*Xg6Q9_pyrJ!I)C{)>YK2W6>?wI23PSKmvQOp)}8Tc)A16qSw%^5SlPXs0f5#>*k_qPX; zP~W*kK$^EOYI@K8M}xW(b07QgocmAAq7N)`$ECaj-o`Ud*9&6)He|S})Ep0_`2-_P zh&DhdPSEsu22M6v`6^}?vf+&$ed*c&q5jkZO4kMmb-y5rxq~TiADP^A@I88^v1_BO zq9o~0f}NQ=^&!1c+?HlEY0iMGgo)n@bn@(I)&ep>*6^)g-~9dUHXfYn)W0+8nj01mVD5Q-BuWELPRe#Thgo^kptK&T&kKq#))YlDRqp*WTy!iu!Hz@sl+SP|-64=7z&5sHL0 zeUvT(4%I#xLAi@2pGHNC;YE|%WuSu!C(qAWi=k8x8f6(0NhqFt&;C;spy88MKzU3; zNCm9-BFYDx^UhsF`I-cF|E2oe7E-<^p^!CnY|mhOcZRNz)i9VK|FFlqda4&OEq(`% zj+JtddWmP?<{YJZv7|0`hGXY&n>34J$}htRI!gVBwV-mftpTN|QsM?Qx)0IxD60H& z??si6u>pSn7ABC4%ghhR%$R_)%cqJpI3uo!i4mkum4uJoa5Osm$v)-6Ld>|rLf#0^ zDiLL!;Ck*=3|lzepLo?O(v(OAR#3A{Ma8Tc4R^Pi(s?YhJaZR zuS5u*!S%I7M%-qoD6*6y1*Sc3L@V~h=vDS78Ga63^3u~^;1w%9WmmZ>E!hfa9ZiSl zmkn3sLadyBB2v%PN9**{(qw9z*u((2AWw_bg)LAgXe84!B|mha%tF-VPn6_22(K7< z*i|m_%t|5ZmaD5Uv+{qV;q)-O2YWN@oRIR<%MhL_Pr3d`I4vqm+r>$7wF};^Oo$(i zvBs8lr$VtQ^U3awO4E~o$*NByK#F_glOEeDB=a~E`o|8c+dL6q9#Sl*^HL*vp5N; zl=N^LGX;aFsC1?AmUL`%-6v6ets6tT(SIKwUS^@`L+-}y91@Lta_GJ8E`m73d(O$B zrE+!Z;>tpJB00gXiTa;+kKRuG&v;V*U)+t`sV^G$r2c=ny9lB_?>Q&+mn#-Q7X$kE zL&*dAli0BM7=1lAR%bgK4geq@HFcP~aXTAC|VqGj)2oEIv z*)U4A?ss*6meqKp2I6BtpU#m#OO`HP0bTIY#diSN zV*Dvf7x5bFE3kA?8ns}I91FKoT~fW5F238~UY^p$zs5GWt=(O^_z#O+S+&^D>(SJ` zmwJoT_P0fI(%+-Ygld-KSg=;{n+e2zw87TO6d@@k`4|aWM6x_l|02i$gzZ<`UsRU8 zpC;7cTW9S%A|0b-3|j0e4*QMKfRh2TI1GFBk(-}j)M2--;Og8QpvB^UyMPmBG8@-Hao5~mo)+Q&lvxX01W&e)_iG*3pu zCpOH|RcmIecuL675@>V#Z=4swztziIt> zS`hn0N~B;5e(_>E@Y}8bcD!E=+cAgbNHdU1IdkxoTOTVo4 z5|l4Jt(BxvcQv}SlI!fIU=vr_aw zi`+<0(f>R?4qXdBlT8AZuUPm=W6xd-PXjAYoqUaE1wSaihkdp#YSbaNnIvgOTcA>n zm*HlvY8#SvQMf%@H7BTSLr#XJ$qkwTKM5kLzCvLw;SU*o-4pag9~|T${az*n4m>tM zDpXcumDTUFrNdY&5Q^iRJ~_}()Yl56)b~C5(x)&&eb)m@pTY?BZGaNK9L;naVtWxL zuri%kt8fwrf`g<&g*@X(rlbz4O(LT+8Z?A-xdJXlK)XkrJ|l$F##(|!e;WWE7Efof zI?H6wz(6LvJ6@wfUl1~ARXnj+>O3~$kQD}@IG!QmiPRbL=t~z*gc|UG(!~>@(!}#@ zzB$(9GszB7^{CiAuRR3~74N+FOBC|$-9YHKn$a;L*zO!=GP>F`&>Hi0tXw}fYeDcRpfa+pL7lHmV*zsOh8 zP|3=FKLRPIK=hva{z>N?bEm$4G=beOX+5`D@83v>V@m={Nm2S*hDuO#dH&JkT|G)j z{EGhpN5^u@NQ=ZDfSYp^C1mkHp7p-ckYSPV{(I=W!xP%9x$cJ=U`m?1LH2 zH1}<@kmfGewpnS57Vo9Uh3+ptB$x^KFBZUAZe@|4EXf6dS>8yF6z=1oOP<0V;T6kK zW>>j#l$rjvj;6ySZo|7k1uEIwA`U0+8j0I7x(E0PUx9DpwhT^7KN3w7C9ysI3qofm zMA?)Og965A&Op5D$97Wq6sfnb9;LOUID<2KP}#6z#uQo9x5v88apX%!FpgmRLx zHXJ7khnF_0)05!zZ&&4v%m+-kT$zl_s=K=nyJQHUWhHR}TylM#F@B^E)ZqH5`uKFU z^rWcUGUALVBn;>b@OOu^UoH1t+MX$5+$|dt1CBGclZw);^&=1OtH&913)g5N(v+9*niUFUEMCXVovLP{2XY} z!!AEQQ>sGUcuK_t-{S#@+!DAHVECS43eMd;g6j+Nrmo zEi`@2(gY~|nRpV$*ICAXO~GO1Gx2g5MtH^^V-m~QV~k77*qf&c5Tpm$>(b~baQIDu zHOM8No%d5A5pMf*1ve2*1!dkQ9k9+R2PzkFOqi~6VgfNwR~S7b&q$QhvgqQo5f?s1 zy3*%H5UQV%d@E#ywhXHz!{N-dbo**9JBV!)@!(W?4cGmlM_EWNWMSH1ykg7@0oMoP&~! z^1dGkpJgy*o(|k@ITR$Wnen$kH{KOLjvBJBv8P>zFE_;BfnT@IfY8KWfGbc4LT>t? z(WhmLnoOBfph()3l)1|GTBThnRx8iu*MhS6@!D97n_hc z2Z#6DPifzceR2)cPp>MGojoCgl4@o;oA>JYS7_tU3@hJ^=4CVzoe%$vZmYsE5)p{N zRmf}jWJ#?pvYL_I_{kC6$e|$MS5zPavDGT$lNwkha55_MO5oFWX{rJ6c&J64z0QTB zzz(!vbJhs+IZfAlgQzA_Cj>VS0czaM2&kX-wp_tiDiQ4-Nz#Ks-HWr|uun0Ct)XyB z#*2Qo_%r961&U6%ehxd6)Sc5lh+B__a7&#xm1NHY z3;zvz61s4Scu!G;-^ZuE+F2^X?`98PWhkcD(a19=oKg~(e5@xv@c0A^{+tAD5kB%o zK^+%~#2+VOW@dEybZOHWSJdBh(V+|coSa*(S0B0GCswy^~qS4e1SV6GLYdjcDYkmCXMV3f9g|= z`IGHWy~2PnoDg<@Dt6=AeM=V>`cp4VI_SK7-uP1!rZC?)h##Hs;^QS1I@sq`(ruFo zA#B@Ww?mbP7*Aj`rbBg^f)KZrl{++q>5L(`T&ngV%(_eUCC(wHxK#fHfL!`p$|Jf| zU+D3!?ozd4t|->?pv5_GsqThmxl8r!c!fWuOBJtSC1NgBDGz|!Hn{EOQauL^_0~wF zctM|Ms>A7arP?Z#ZYhn8vb|^F$#}^&Gu&5-MVL!OLy8sh=VB0Y1=35U&+e&9-97sl z_9S+OG&p4_?>B~`>$Wf#?falDmxwuvsy_~(;VxRnbmpREg1}s~U2B$uNz5?5m^nS> zt>OilMR9R0B`D_T$o&fPk~?xA#!Hr$<4QCTC-HSD#;*IM9v>GCOVQQk#c)_MSME0w zi1|umFdQvb`hdPKKF)AoB}5bcE56+%3;q{~HlKN!phVtZCB(8VfF^m7=|4sY?&SR$ zT)O5T8s=gbsX+ZibO}VzN(oNfUMrWN)TPDU!028f4I-U(2G*SuJb>iJ-0NK;&C0c0 zr2QmMA>v zuj{jsXZk~o8cU^V)cUtr?{UQodT`2Pv_U$AX-|jGf# z?704g2rPp}&&`$^>H1foNN^N!&-P$H6XjaiDOL~mv+xC9uZI-;>#|W$1s^@wH2zEv z_9HvHJlK_5t6hY0c=k!|R6jO&txK&|vAHL@^2&=Pz|+b87bzsHAI#3YHOwW8e>$kp2kfslHmQX21PK5(eNm!m51QsEF6_$@(X z7cOui4(I}JGY;r3@WT`rxEz|CL)K@s2HX)e9N`kjF^GlI>JnE3^_5P4^giR8q|SUD z9x>>?5C*Ap4;|7oOjw*2rF2LO5kn|0V(AMsT0O!ctqA7L9(@Uiv;sxiK@l3zZs)Zj z)o|x@yJYuCKZ;Zl+KpaSc42=VLN=-0-kXhrN?~-u()cX~uV()37+t(zf%Ni0m~Ol> z)`p<|dF%p0Zm7K=u~NK&N(>T&g|bNnmMc)`hhM`{aT+=wVa6(OMCqM95N)wH z$BHnXT_wz4C|0c?`WqT7!+6{dC7x;THz7`G8`T&-P~RLck2UP=@+V#IZnCz^Uf!g) zTx;21h5Kr&6O}5&nys&jNBY~%sofB>O?h8DR3XZxVl{3xVHZ(+AD^l3xAwHE^+{{<;*s$-#OBl9#z*Sg7{;#_VeZ-n zpFC6{rfWOv)1U`z0P>mo{_fJw$?1yqb@51hyV5L8P8XZ@_ID>wRhVZ_*rgWli}%>F zo@Hk4ILV@jXG;ywmJ(J~u&)9hhSHLJ>K7mblrOyPrFw@WU&Sll^isRr7uia7IPz7o!;o3zuV9CvqQqal^0$4J z>u}_+TG_jviglP}NBOJO;Ycb@AEi1R`KwfTH&L0kw&|%l0o``V}0EhX~5o#HO()O60 zvvOJhCMixLQsi!uh_fy)aqvKs6M~6@P&VS65@6(*1lddk32KIjur)6c@EhnO!9+kP z8xc1z3KzD1H=-*E02Q)$icwt@w;OE$%A4~v*vJv0}k4=M#`l|?aFK|NoHF=T7 zZ(F}I7-@vEA-&#v4`ibLE?D0HJIIIKg8ZZleA(Lfs3T zkp4wpq)&$7|1=nBgt8&MA<(N}5@7j>i9sCP6cOWKY$cCrybjR<;4sseK`|2QrtwK$ z4il3EYl#G&4G`id=0$t}5RZZpM<|;Vtn>5$nW+B}q3+!TA^)Vj$gc-9&I(2zp=`*X z;NjFV5x<-e_W~!RugHt^APoP~V5AYshV(j5ADxN#Bq8qI1R-C^i~K2oMkyG1gt8%j zN`TwZB*62C071Hys0J;UZD^nq!}W@eR+v64Cve&Oaz2V zC4#4u*d)R`3?le8LqvFIULq6#owo%O0ikR}SnuuoHc9XiB0)eSM1YUwCBOzi<-@@Q zK&UhV_&M}V0{oLs0MAB<0AJ5bfKvgLuLKhSp=<;gPU5?^-GA~>4WUvlPW%%hLSREg zi2uw>h|>V2{})UMgt8Ihlz{MyDGN(aN=)IPW{3#=*g_sN_;f(04}inWV1%*}VZC=$ z$t1yXM1p`uhycguCBPYg%9>yTAe4;&8z>lNn-BCfWkPHsG6Xk8#5g-IF*X8P8-s~~ zP&Q(m;2Q;0G{Vz}0AApP^ksRGJ`;w2Nifm~#gR_WWwahNF7gvG$igE|>F}5fDN6bU z6vnA|{L&4jXHa6)^UEx{<<8dBGbj=23m#BXX|cQx&bC~C16GZd`=LcyWJOgtz9s0g3Q^Xw(1^TFyS)#D zix;223a?o4IlJny_`H`I^W=n{*{?BAn(n2>JZWTa)R@29P#xJd=8qZ>#=NrEm}57t z!a!Nmi#6sCCmqbAGQ5N-d$Gp+vk4(=C3fx_b0T8=ci2oh(Icm&@0W`eEoH3o%m@`561F115tt1M@QCwV035q$^)SiR9 zexdfYA=me!FjFJ=)m9v6CH0x@3~H~qZOUs5qSuW(7o zzzeIPvX2*Eh~X-DD^1 z$>O^L9J>cC^usB8;(*2`e6zPz$Ew3tG~I}bepNqM>$9r}A4r^r#t0+u%JM2kI+^i* zEdC_qI}(w#nIWc-*|!^e_u8dY`qj1U@bIZ zpo#yEk1y(~w8XMTqu6pc9L7=NRg+Ly$v&$t>xiNnepMZ7aIjc%SFt(`A3nq?@;dx( zs=6Ee9HQQGg!O>EK)p_La(W7nT$LZX#oPQ3)q{U-OZyYu)xC@baf;CiBze-@%`vh5+f568#i z+6J{^?LBG-!V}7|(R)C2)Z<}WW6eqf&KqvE>SGlsI2-4ct7@lMQyQ^zX+^t0Sm+fh zQzzW44IBtQSs7|-a8r;MXa1w69xcQLKN&#yCKA$|$Eba!h z3X=sacY&}@K_l#@MWX>mr>Q-yQWIGQ4Z(Pt zZIwde(dhW}6nw8bQTEaBmGD_rxYq(>jswFv(#7#&yQqvnJksExwmK0y2`Xn++IYD7 zeRbEKH-135wd2_~eWw+y%!#f`)pMv6W=+RIbsipD-=*Jr)@`U6n5s4PS}vW2Ku&#z zm3TeqvflI0hhv&7*f2rw!q|VNOXcWowRm zA({O~kj(1S1W!)?8lNT~R4fMmi9ySWHO3K**CwmD+{nz9NO>7HookH#XSH`jYL?2r zB!lS&@#1$q@Zxab#cwz-PF+YrjLt$hgh%$tuBp3&)`e`Og}TB%TU~*NHQ)Bk&IZ^jH`iR zPhrAg7!b^hl61*`QJ2pQ>Qc-|?8kFPZtj_cFbjKYgpi#IK~|l4m>m=|;<{&p$ea># zjZmB=Au|sdd67q7`piS9Z5~ki%tI*K%zJXzPBOPGV=*Q*Te7pw=qq+LfZ8 z{3dyLAVeOF`G#q6uVDn&=i4G!{Stef|DX=v5!9iWZP<(FY&#ZiF4-|mxxRC=zfXx;IBjUps}Vr5vJkKnWBmF>~7D_>yYI*(!oJ12VlkF z5z+i@5=jol=6u7a>e7G->jwjH)`mw{ya?6Lp!9<2vn)X`pTL<(os)?=xtk>74CEya zo(gngFmVvdMw|_S{&bTlTZkyZO%XB9$x9489q38H#6Tz;G0w;lCNK$d6%iy?b3~M@ z^AZJ54!RoUppY9d$O7Kt>k%}W|Q^XR@{(jb(LG@E;g5+Rh0M5l+x zV@=Zh3y~&IYebT7<|PT9qV)A(k|30gBp1(WHVlO?;-3?tX0Hz--!Jo$56@%z&tUQ) z6enN0E5hXfK|6klN3WU#zrxdg%aW-7h=Zchkq!MG>H4W)aC(0}sfU|$VF z@tD86FHu=5{6d&1@Q6oW`Unx~ogPrXJ}hxILMAyvQBo?6QdY$#S!Bw0nY+q1x~gJj zS%`3ILO;G$v9fMT)_u^3ysBa^Sud+%pNdzkDwbXCyQSX=ZQK#+HCNs=pw+Hg?K@Fg(fj+F0zyRnR1B5wSM5m~_yYnK$2GR@kx^ zt7Bh~kiu4$;;xP*GRC)KGiG({at$Sslds;R#q6GpVYn(}?ZcS$3fVV1hnP|!`)2^; z@^?w8#0uF5Jl@qSWUZK!7LA_?EzUuO?8~89ULpH&yuzQdLKd&F5(q0~rAz>7&w|^$ zbZ{S5$bMQ3LM}IRS0Vdl?1{^JKNa#RB`E)B3`N&)VHL7pg0@^D=BSYU7J!CV$TFt0 z3RxxyJypogU-b)8ivr_XQc%#bQ13sHo4iK$lX#g~BYP-2g#^<26JvpIvQ~F2Pe@7r zw`g1nur4)b2_e?W{w9H#zYCq$*O-r|0$Nrgq>wJL>gMw%yc)t>-F3cC}$C>xw3%)v1&VlJ{6x2DOE?+PQGK$PP{}VpWd~XohMM9|%2mEsG z-HZczf}8aN%!2vWXS7m27&IJ_{)S@^>e*VYLjKdveEs^dpw4_99x>>V5C*Ap4;_W? zV#4AyF=cAH5HW<}B9^{ZD=u0^FgqT72?wYGm8RXO>fd@mz~3WPgm$A>mCe~dgpf^Y zw=ZU+pi&r9b!q(C3cuqdaCuAT9~6IzFEJa(1NeT6dEKsRvDRA0P77F<)StK3;U}4A z^P|k>XPNPw0oFBOfLmbvGPAOjkFsZlM!CVpfw^x`WDA;h(A^ zF(>>G00stTe^JqEu5db1V&us|Q>fb$}M5I^gi_8ncP52jwm6ceD^;(>dUD00Y;3OA|Z?sp& z0HpVlM&%Xly&Z&upMm@WykgN_cGYvVmj=?IC6LaAR`0FT4umc*80Tn%md{^hk0_&7GG8%^lnyzchhhLx?mF*TA*)Y53%N4gRlI;lPvn zG#nUp_NnkC>skxGWnGHIhzfE@{s1)Q*C!*eg%3@aG&sxIKrsbSjvdClRZ@wQt$ZAM zx{e1@p8)^E(cJPJ?#>c8w5nMtwPtRZ8HgY0gWv^x2xxq|T6$7~)o8g?g%gJg5;;ls z&dL1Et}wg#V!7|q_Dm67`&*PzN};}qL$s3)61&#Jbs>9ce*^r&;@NEOaSQ_l94%m%r?U6xc zOm+Cjq=QapJ}{3&mS+n*FJV*`+Y>#N{UYh7UnS_<^piWifk+qs5}Ps88L&VJrZw%Xhpd%J`4Vdm%=c4Qp0Av!k;p!0k3gEz@!E#34qw&3)zAViu>5_qs}k3 zO0AJ-1QZ>M{Epnn5AwZ{ExiL!6Td{fqHLVbgI-nH(bE-Hr8E{_rbJ-kORxseh&b-G>9>343X2QH3xcUr!z!;8Q=blUaW5v zHYYsY>Jjc{id&C%UoaaD%e~m_G{&U^DD`$A?(D>*h0tJ3`YqWUlQC%pikL3 zz#+nL;<7lv9|9ZVTxOnnM2V~CfW6K*qNm6oc2N7S~eUo+0_}0=dv#b;Ea_< zCg}1I}-xKz{L$8?Foswmye0t)BC*FE|YZe|7QD;`k7 z&8$GB>2nILTkVG}$U&jc=~ZQSa!v?~r9OXB29pdU0y7q9{F)K5;~>~T3`X=#R(T~q zq5>u)Kgb;7F5*IeRIN|)M|hz=nr>Fc;5=@8C@haH)Vx7_O9u^r3Qjr97-OTBR|7XeCIL*3e|0uVw zsle&()5YdEfY7ykfoV94LO0GNJzC1iNFqC03#5uCzOph)Z85HclHt%t`*U7I~M z46TPyS2HMm(K<3s?)f4>@zo|Fm<~hewezBfXJR&j(L<;-^sIB&MDH#gJv$7c_maHm zVXn}h1fz#gHuR23l#WIJm?-`!q3DJqH2*9wns~nA1Hot_lnu?*Tz{H+)I{*TgkTy3 zq4sEA)G&zqo?z4v%7)sJVgcw{2~6ZZOUR|e5PF}_iyp?qKNE}|LRryETg^=L{#8LQ z6^79JUS9N$hJk)N7(Iltp?9=ghxww5YjfHH@n3|X3yM(uU0xKiPX0H+C?ZrUigvrg zhkI+klAk?KS==Ts>xAZ!sW61z&3VzoQ?#an(L*R3H;+lS3YJ}iiQ*lE zq8pCTyfZJFnC9}LU^EfRhUSqI75nbTMDcz?F&&1`dwpK?@U*Je2BU{iHuP3?@8C?t z{*n;0y-g_nRbG_vWT{7jQ9>viN{6$Jr?LMuk@_?tWq+U0`n$Yn;b~Bx3PuZ|tZ3Qx z&n8-rD`;8YC$zqk7cD%|>6^i5A(RcRV`t0l31$EY!V%2t{5s>bb)M(ZemC38y5UiU>75SO-ae>u;DrcU?cUCZpfN z*A78T-=tHSqaj1wG#ZxL#JgaEcA1$R0g5icIYX3TWFWePGpu%O(bAv80F;7ZA}4`J3* z^6qpFF(oDMMF8a5+e$gal)T$K-qlm`te7jxbStzt>nV9{Xq2bqy%n$Ur%cJiYg`>L zB~MBNAa*X?=4nzHDS78$WLQthd!DHRr`wfktAHOx9c7xdKu7sF6Zak#gOF>Ep160m zlDHS%rJmmRUhGK((P(f=5baT8D7r=qOYwUTwB-^o$2mhE0nqRiKgM*H;>QG`r*nqp zqq+roMNx4r9x`6}W#l8z1biD_W@ZA4V;j3vV%L3AjNcFqOR?2uMYW1>R4mM#K+MPG zOwR}0O!6JFtAh2^wXhJ1RU2lyLsehCZ&bADiq{dXxA0hG=uyy@lJsv4$v;vBk^tn3W!)^ ztciE0<&mJ_Y!sODUX1@BhXjpw=e9{VF8}1YGwlhI?MkgyvAHL@^2&?V?7MOJY&x$0 zo*8!6IYu!X0|-f_yDW$!B9%^1dIeDW62=@omCkw=jF1cKGtzc91Pw=|*5Me0-m|9G zDKfmZH~Pwn-fJ_2a4iI3Rm^cwy@?Uux%kRFF~Kr~;w&SB+>s}yF#2MTzC@mw0(F50 zl<){CP-*IilE18ZVy{G63iU&;Dkko$@CDz1pj^OavQbd^f`=#3_$^65TD)aP!*b#X z9JbdR2okA0~&qKp)Uav(zlL>eXGPVQ}~>fZBb%!^u)@ z^rq6DMzPXV0F=A%I-A9rXTe-=;Y`nLnI)d+4DsOxzGG5bAyD^2Vnrw)1|x;sN*~Hy z$pmAiqVjn$jp+ty%twPJvA$xlbNAVN)>ka&sWP7k>QJmn??E7Li(A(u^;DTV=7(6x z#cjR}hgklV5Duuy7{d!+=KRhodw5qer!uBfzL^v-iF%kn5(ml3rO6E<<_|-NDL+#v z7H7<$`*%*yJfA5%#R8fQ74^h>c*dI;U=W33Ka!g*LVA9=@!9+nQ8&L+dMO&eVc3&K7C0e(;3$Q}UvXiFy|Xqm57;?eu+x zHF~T}rQfot6tbJa3CPt^davMLl2tE?68o-4U;1taRUcnU+LZb91njtD-3M8i; zVT(Nk==%|VZ^Y(5U}&=J*!;&02t%mtv3cyqHA`ho6l3#`B^`9`nisKo&PyKUp5s$; zzLwx_i$}P_@NMB7qADcb^P(iQ{m;|Re6?vl*@0rzPH=Bl78;J!O#C^BBr z0wC9Pm+~n_jRAKWwH|PPs#m}rS(Go12>Xk( z=`ADd3KWr?Tpb<}_BW$Mh=s!%VSg*W;OhdDBJ;*<6qvV6j2|QHG=5!V{x`yI9#-@5 zAdZN5g`hM>*#CwxM~_!nqv%2|tj|cj{e94IL^K1(AQYW7nxV*Wuc2{)MX${e!v7kA zuqx)bY(CD2?_6GGgk7)>9Itg{^ zjnS#n)Xq{<4Ssh&q);u3nit2%N2ej`uDF@ytt8Tr75(n~xA|3>Ug$MPQSWX_C@6P7 zBr**~k{}9zyp_->PjLFwAo|R!I6r8NG$`qq@ae5#8>GOxE*>x$IPu-4;V055Zo>kj3(Y zfs`9L0rJF<&*(-rlM-fd_1QRlZbj#6j>O=(A%sxH07F-|Fe3lXdlv_q(APCG5) ze4JEX3Mu5RM4!=0AHR$UkO@l2-kTR$j0xWzj4VQNWYaAvYfMogR@0+b5wTOCxOk@b zs>Bo(p}xVRFWr8_slauCyDxo&s6qHT;fA`O9cwMU6S%sp4;~>xo#X+fizz~_1t{+G zN*8czOi@r2+aN2CFzxg+H`y>6WlA`a-{rout`E^j{~P&gd?Sg{FD)HyG!Cyw;tzoc zJd*e$yke0=cC`;9iR_?(S&bxOi@ih=5q@t(5|?b$Hz(PV#FYkwAuFiRXH}t49*8n3Fz?e+L?F)+31-DI#1WAey~GiH;fc2IOeker-b~tJ9Pwd<;BiDrX8$8b|yIz7Xoj|6w)?%m*UIk8wmAzb-QW8%H$bh%3(w z;fQc83rb@gaXG+4@b1)w^nJ&UHC8C(!upKV+nS)^2*)#yK`6TYE{<3TL0A=YTsBW( z#CI;QGL9%%hESYkWMezxhzg^Zdh{jyPzux{4=52wRG?`3>gtDrgVs3W)ksUBe&|&t zj<^F~2vt8=Y`{>(Vyw=8kQl;T)%uK(!nVtcGyQhc)dgC>-G3@XL44fxVs z*il`Su-QNiR*s3lTgoh-FSt0jJbQBtZ)H#ZqWNS-KzPVTiVFW*!u^7ajY zGh=WSo zq3b?r#%74@ssf2F&l;0qZ7tmK2uPlc%5ZT>J~q-###9j-jC|xPc`HM>ut?EYvXplf z@)JCX6@N349pmlfz9LhD_#KZxff|I3K)5uSP+p>x6=&b^$0~0F*_!Z5p;Mvdx4BIsh_C*TDr#X?^aGTmh?R4k$R zU6DWGGd@o&bV#4<3TN~OOqp^VsJ`|=95-Ks!`dND_l6MERZWK<9CsD+g;_oBGvmgo8$MT|zF}}YFMirrKs7f8K&edefHOPZlo85?@*tv@Ebs=}$$cy8hi^(0Sx75Hg4Ni7?`A_)V_5aP z3V<`*Wb#{CLX9DmjRZp>mW;{&Q-}y*?GPD;^O6BSe6l{63Qfd?^Zil7!swDjgD74;Q`*R-nqD5G0f3XZ+o zSx>=Pbe7X9e5`1Oa32OqS^z*UYfwrZrr`V+&>-twJq5>#IcdW9F=%ntQ*eF^jq((n z69Ksxf65dbyp}0AQUU<6_rq>nMoMpV0Ur~U{ z9a4kWi^il?qf3UVU@#=OBY~J-OO>8}v&k&u2SNm7-{b`+!o~@iY|ENSSWLdDAOugo zku>&CzIiqRa~05)eDmCpp~&Q$TL8#4&81+9QDgEAjarKybuRHL;s*xh%e@)Cl7FHL z&Drc0_-=i5ClHD=7LdFb=rTK*H-d7Qd2@F*v1R6s0!8$uEw@MJ&D&8N#A;#9ym=U3 z@byMXd3h)s1?IRCW5xcGH(vY7edv~ApjJxTw&%7jXx{%rgM>zgVCRoc60M|skvtq z)*&T)klcd^HCudoOztRzkVln8bfXP2GU@8}R2gHInH|Z9U07u06?9HXnjMV1TFevr zz9@tkGW0EE7#*71I6b^V-`ZyrI(|6O%!qHmq4H%n8zZ=nRh%_o@qynlARw1@Th2*aeRc1|EHyv31s5?Ef4z z9N}WeF$hJO&TBn2&GOejEnq$G0nNSOHY+xH9~P#lb!Ey%_{=( zNsqpSr%HkPs0WnrR4Gtt3XlBlR)x2EgcKgVswli8@r6)@cQ^nAtdxi73hxl5TD;tc zlUMV@?r1Jru)y@rgXRl3GgR0=bi8hwcYnGuUTl{}+r^#LlIr2t?o%lti~`E(ILyxa zBzzQC@uzF|jpEw>-`<T7TNir%H?v8KX_{LGe(HZCSjpKlVqQ1|W=Yl&9>YL@8ry?jgqvMR< z`NwikL}X;#h@08fr9b+om#nyP;>Q1+6DQ)vIVWywwo#sK)Hz~K$NZ=wmzalW@C&F6J1tpLNpDGRULXS&5>E1?S-lCMybED(K6TV zsoFCDMO}9Qs*|3zO>vvnReuAfj-cQWPi1PB9utu1@+fvY)FBx#&K3noO=dAT@J87YtnfJ_4M_Bf( zZp0!J<~eq8iMsiw+%U*di%Fw?qyZ<29NQf-+wm3~HQ6c~qtq-GgIXD}9@((%mFI() z^^Eqon%P>ebxtwNs&1}LR`r7BIL;EQT0JNX)2z^X?W@5a?|nsUTUdk0zM*Ry=GClg z6r-ls>gu7WO^NT|jp-D#pren}YdQi-YSJ=9MIUXnUVFQntQDRC&MME)8F5HAq0@3# zGocZ-g|ABiZQ4SF7QI>^4sW=q5E{iNP`yI($vSdF_dVa}NeS|?Bk%5IKtv$B2_NM#L>6Tvq&&S&`fn zGvnWjG6P3&Jd(@|Q2Cg#E@y@0f?suBHV5RAGT6s~Dt7nRwEQ?|V;mgkN|faX zl@ILUec#btn8z{901*Q`p(sEcpRqm}AgFu*H<+6V#?)>W`)2m@7;bbE4E}XML{2f?k9s5RKnKVYj`QFo;N0eT9*1?zk^{% zHo(B&RTTJo0)AUEa8UWAy-7gSJBhBqb-54xM;UxVBg}x06=lG&1m*T*27t=P04cb< zw{v%#f1bfdHo(BYP!#xa1mT`!;GpsWKSoGCwB6u>zMnxy5HsL^C<^#^>iTz+0fWj1 z_*mYsHuRheeMtyE0hdz3^@7*{2O z2bBchKTyh>NNDW)y)5DdStSx0RB~6f|5K5EsJ=|8sIV719`P(YJPTCPw-bJr-9vbU zq_-_hW%m$1y3>+-2&yd+RQ8T5s7@p**wc_%^d>DTj-m|?LSv*|8F0T@##|YBi*_J> zPwJu$@#iq`*wjUx{GucIxi3+viytAym#K@lf)2fYpv;Bl~y(B?7ofp z$&0ep0n!&&>z*w{0^{c$dzPQT_;(J4&RC7$H|0_K{1buJBv($E$L3MWo zmC80WsCf9==7&=Vy%X0HE@bn=RgG-JHK>qkehwj`gxL!R0doKI4g%b^!;{fNi8x8I z>S|rW`GzOq;Js>PbP~zdol32NMT*L4ER9vEq~0#IXX!pI_d1i+>|VRmLe)xRtTbM) zP*!&T%*BaTWz77p=j@Cqq0B>h>YtHKWb8=GU{a?NWmS#tFe`}yyApEFwzOxrw~TsO zS#Q;zT4t}>)zMj6x$QP9sUo{2ie&`rjC!iXsaAC|;##7>tu)=-X)>%;MS)wX)*ydY zy>33Ts> z6IDAcHY=mn#6mSU&G0afQ5w`D4apqoD0(RmWUAyDBAv}*NW!r~R3pa(jg&H5V>FYM zWnz-`da5YnLw6%A53*$P%nmYG!Zrsr!UhL5dQ~|qGcdKZSuucRW!cz3dtp0+8vTuo zI@Hy8hHYUenLK!OdbYmG74EB10#6CO zN|*_SuUix*QExpglHRbupOeCW^UdZ7B<<^@wDVFN_jgkq1Cv2;9tchW5!S!2;m@`C zWE4Z@9yYVDrN_;_MjkhNtxHx0`;IC(t4LJoYt?bnYtRF>JlBi|EqGt!onyCiO?Hm8 z&N0+M8y#hy1D3g#zk<1Pe1sw9Q8LtD z7KMuZt)C}D1(gr#+R(zn1-kMgwy;4uhPetPvAe0Jg+->;aw5*zRn^&h%EE%m2lI&j z3nUlhdIlMyVu;5Tg@}BtQZhtP^?}%Lndd@0$AZ|GiXom?6e2RO&Ps*|Dj&q$#i|SR z8U~tA%W$tP3Kuz7Ta)2}st?@o!rO&AVZqIzWw^DXaFJqo)R-cb}9vZdaZ3=LF1={>36*1rqx zb_UmLmUB66R;oeU9FK8S1lySpyDhZ$Z!3WoNJqR^1F^s{7Wpz=Xm8`_$70WNzA zFX)1F408oYiY(}mm$a0K(-w4~^1(bhyu0oKUB^K4C>iQ8MWG@K>8NC=pz=Xo8*&l2 zK+j~LVLFC+c2SroQio4Zh6yS!%-(%S_Hwu|uQFf;=@{nKMPZ&q9ljzNCa8QckLd5H zaY2qT$Pg7n94`uSGj(=*GDJ}MAg<5%G`Ub;#!zFZ8S;%qA#b71?@opcDj(!0^>dlI zKyPKB{b?B9d{KBOQ%B#D3=dR3cyS&-W7p?H3^SjW;eMnj+*7FIA54Y|DhaoLpqn>a z)Y#A69kCUoaxo1mxhLNLsYbS_qQXx4Sj4mJ@H$XQH(B^ub`RlPldjY-mC6=XJ%j`H zl;j?Q>ZAxNdnX-Kn}`Z4kw zxlqo}6y4xZIIbmsrYIg0%Jb1lddL(#GJC)x`C6Ek=Yr&ko}Z!jWypl{M459_=b;%l zPjsn9BK?p0PoCMHne*^aj;R0PL(dU?aj=V%{v6R45>dziYxP5p=(dQuc8;hQIormm zHPqtJb40JCMrDrZtMChd>Ksx0);XeD4+z+ks94TKdof4!b*dA33K>?8C|v4OR%+;* z@g$YO!wGFh2>n}~uGoewEj9EOYAd8*fz;6V5NUd9s1UkH4Hb$oP-^Jn_|&DgO)|?9 zdJ>v2bfVoI5TYb`4StH1Rwyx49i~A~>(rGSc#S%F1JQ{nWPA8Y)uh%|TSeS-MP#IW zBBNMzlb4+o+Vi1G$^StMZQZl(%I%xLA%GgHazQ7v(MrJgjlrW+F88K)$ zsi2C5NCo|IKG$_Bs6oYy=RJUkRL}#Tmg&q2Zz||=REXVd){3))h!PYBsi53{TX7ys z1$9$FHzY|!MQAHcK`Q96LO6DWwm02V*%I$PHgabqbw|Z-qYuj5{m!L=UXlV>#M3C| zV+y@kFh4le>QqpJN{WnE-GNk4L(rv&XQ{m^gKBLAl}ZIQsCf9==7;k;y{VuL@KUW~ zxvH^9nL&kA^Ha}v8M{`@7^K?l08)flX>2s^guNk;3=WWz)a@3 zX%Q%eUN|m_zVJl+u@v5$_7A>>JEWv@5_!6_I$MEqQVg9p^wucFNQf8&1@&L|L`%b$ zH}Uxtmgu|*HLJmI^I6H2B6$v(Io> zf-H6!WJ(vA5(g4-&fA!d;a1WGm4w@W&FS^Z8rt#o2qr6^r$Hr0T>qyUURgzjae8OO zv+R{FsHDvcKg;eRETx|3Pl6Q(Pw>yAu_F!&hA^fwibgWvwfA$Ryg`?np|12I8+HThl zGWcg-mOY^R=0$OmFH2-zxjbKyoda3QQwqH{%CE>%&3iKr_%^M>K3eA9)O*m3>!V#^ zqEWB?MiF=7!#SM@`D*>07`m_aGr=zQ@zs8sh(c4bRz>(~|7S#9+gIyF&Ng#uC$%_q zU+wMGsPxtT0Kf33_SNFI_SI^AAYeC8v7DdwqObO!R44S*vgdrYKf{xhvU`2C4?A74 zjar(o_NUZVNWlWW+W#QZbYHCyy71KsMQH9vjhyJ(rP&UhWVWSxLv^$)PG#L<@Bu5j z%ZO_7sKsee|`&jhhF7Ck=P zvs1dFU2&%qQOF=@-BrDY4_B)Td1iin=sx>o%Z+QJ4)tYm74zese%)xRHq&WuNoh!r zn_|RL+JyXSV%Bi{xr&|e=U$O70qxH^&t$!jA+bcF_QqcZfB{EP+k@3nr;LkM#{i=v(sa-CE>g5qs%Aadc z@$j|H4?AhS{@lL@FO~V>s)j%JE2xlae!iR!!ITU9x!nI;f9|#=gZguMrlJ?^YO8{C zLfd=(aNka=GCp1#Eq9tThKIo@&0LY(Q2zX+c8gX`bRNZE}|jZ5E-tRc%yyD$By&%Jo`f z$E>LtXh9bGE8|OyhRAev6gvbIMnK8<+pTWvGFvBR>~op%r(I!s#*8tbzqUxno)L^N zT^X%4#+ydhREgaZ9B$<{of!%RH!54@9+JkG$s|#miaZtRsbkyShDD*La;4SNDizrk zYvZFG^9X72x6SIY$V^pcOww?xO zva-x*P`!XT9VvxvDc#RCq%x`_-ioUFIVM!qim;!mW{&ZsQ$B;1bEKqgq`^*IWSLDm zvoqahDk94!8V~BVrlvY8+i2CdtYwinuZNRX)%GhSJjOp23vBxpG8pIe+oT7omqa`z z^pa@CdA(tg*nhpTv}pRnFX{(BBjHubd_O`I!zv=CH}#x&uWb+RPa9%#Kki(L<SSk68*y(G4o$=qXH#Uemoc{c&u%tt+{7klm7F&QSt(~ala+F2 zGOm;}iBZM8`HQOfr>?-Pl`~-_%vHJ5RMZTWJ2?gBrKl+>bv7zC5rHaa#qQ2Q+5~88 z=!>;HKE)jASWC6zglMc|q(v$eYZ+sgWZyZ~@;vlcZdDx@MbDP^f)uEx+*NZzV$pl@GF%QD6U+3-=+0n@7n|e_j+S&i#Hc87ioJP@mM#3Fm@5@X~C#*q?^s zEl2eti$t9Fy@ZI<7Kxzp!CS8#hQ>J6MaqZh0zI06#!xflQc=h_|9fpRWKj7aZ%%TC zy6Def^ob2IBhD(y2%HN3$wh$w>pE20R(o(;Q)p!(+sD%&3d zs{f+`5JI-zo8zuHiZ(b#n2~m6=GA77DKqlid$Y27a@}>_mx^Rf$Ke-)`3FRj0jAA6WeFAI>&UiiEg)cDQ+h?wROeF~v(A>B zT#DcVGorRYOKv%|+BV^POqW zWwT$7C#eh{Ba54r{R*cmwiQduX1^3H-)^^HW>*EW*`H0M>DlZ;=pvh4D8fM5?2FCB zlCDBcLX(9~2A+Tn<-z0U<0rk1hZEUXszi2a&!u`;Wvm}BQ4Q*KPJij5H^`Q2WE6{S z+Ojj(&+f@w7waFvnv*EWf3n)Dbtnq1G~Q}Xmw51Ok*|(xy2Tq{_P#1(mT#If2agu^ zD?uU89M_zV&sTp7h}E?B>~>|MY8a(kQ@Wy4jsK8{LPOr^HS*QD*LJG$RdM<15K^&J z^^>pDsp@fUXgMd-N%0cY4-t2Ulc27ojs*4pk?*qAfBk8{X1!&VxN8VYt#5e2(TprvW_$WZ5a zK%u@lwekwYleH^+){05Uf@db#s(I1%lhx8pg&gOdQgi!Mb+l8Ww46?*)_{FzROxuI zcBQqublr8A;RY2Tr?fOyr7Coi$_mMtESRx-;b84)J`dUqbL|0#Yhf#oPai0*A*20! zLPS9lMU0Xs3@JT2z`pD~(b^5C1I%tTuGN z5Fkkp5ApPg8Hs9z)bC)6C6@Ya+eCvdCBZ_>V`ggeuygA+(`mA1YGsFphj@!Zc?%<9 ztVY3Hon~t{acg!40qN!HPCGHuEJn`M=2ClRY^++ZTG`84nW|FpRMBO!iu6mmDV0X4 zSyH zvqAjYd(MW_8r9Bty}2vN-LS#ke{~vt(O}!k=xK4)yGbnQZCHtGL+s!$T z4>=zEj~}|@;g5q|?BjTN9}$H%iL?g7@$jyQy0+uNi<~Xw)N`rDp*tRKp+=?S;j{RK zKegilzp?EF$Ai`c0`@#AmOHb1(eZGv>V%$1_MGG4D|k}K@$faLE4D35b3FV#wG~pZ zfaBqQB29NZ2%!tdgHVKl91n}n(2}-7Q9_f3-q(E)Qk1TTd+;;Y^&q|o-=;k?G5zZo zL#LEY;=ij#wZht3;_i>b{qSf;vG}Gi+W}#G_Oyf@6vdN-V{->+fmwXuGo{?(Sd6{D zrJ#^52+eKtRk*u+WJEX@yk`y`2I4+0h{IF5qIc$=NJOE*uT@p`8ZHR#_0-qQ4ee94 zhd_xU-_M{GxB#V;0QKf10x%pefr1Dx0Yfylf);ka|0zYJdA^Wl{C+>`r){_@CSkvy znPhFac7N~p`(NmAEo@eeF^`@5Z9+sr68#>ncnvD%wV{v;vftlH0jsUbi~yOt-;ZjA z)DQdp%o0oewrxw>@7MFjLhtzN=lb^ivA0df4wYx&WHxrPt#aC}F3DGT+AtM&=xoh4 zx!ndUL|4t&6OBcrKkwDNcek*=Ch9MUQ*nwF&M4 zE%Bjinw~}%M`w6#^cH7$2aTR^5h#m?ox`p)A(`8E+t>131e(8(jVSC*|1OOv@+Ukd zU1?I;){s?qWM~*O=Z-~Xn?-}jzG2a5<7m{@MOHn_E*h1sa;7li2L7n1F@Ws0ecyQjLdmIq@4V_=%VjU2A^Gd%Xa&Mv>5cd%0qyyq} z_$3?=;@4huK+qi4Xwwp5ql$%W7;L*dMa zeg^~|6WUAC+rMx?d?$OrBH6vkB)T9Dh+k*$(;X1Zx~X5GnH~p321Pj_^xwSPS$2i} zHe-u7ocZ8?{Lmc`M+dvu#{qE!5rwvhv8L>&`v-5X8ok>Ob5T-2c22;k(c8pV6^mup|Vj zSS^Yc6R z<-C2aOi)i#=ipZVPr>})?5Dq+r$h!SDKcIQ`*uCl$rOg5zZ>x^buxuP#iPO=)tm(4 z9o6f>6*a24sxhjMOJSQH)g>ZIm=W*|JnnzqH}JY&bCP24^{s?$d_B$6e;4fQceK?~ ze#vaLwcFTo%gSiGO-wPl=ImY<_<*Q~oft!MD3wM^8L$vo$8;{!(DY=>1#GjPk)(^Z z3B?&IPZeruo6z8wW3EejpfZwpO2|l>Y4#}|AD`Y1MS_~;i=#6v?y(f^n-&|V0#&4} zbG{kVJuypKDX6!aUNh16wTay+K=juplx@ekNV5`4)Ym4Q<7A`8BF`98Z$?+;e2_nm zSyXRLL1>B!pNqIfzza{V>QE)c6i~e>hst{#mO=l99C~_dai(-URf-~IN{0Yj5zn%x zbWlkZ4nIpBhox{q@uiUqQ`w6Sn8pi}dkCs?BdBc2f$A)xk~Q0&?Uhx z_Hhk8jfg@oYFY!~8oDr|uI(D~B4-OZ^&7HzLw60GM%|RIp>h1epV~Eq-!S>eP0^Y_ z!2TqGg7!UWe4ad`-?p{Q2$TfgNRildZ5Ccxhy9u3Deq`0`8 z8vSZM-=zH8{GPz^PscwIw3Lxoh=(2w&Wh@Ax0|5X9qDF^&OV$*UM&Y;}w z+r{LKTD3JrB5EsDaT>OKInmPyJ%v8GtPc_GdOc1`FTD5I&K;T59ktPlJ}8U#JLio$ zDFv{p=E$7cB$ywZaP`)e5*etZ$awi3@J1PeUK;T%wRL4s?H57yE3)o9VVMgAYr-P) z!;UGh59@mHQkfsFYAk43`!(7}l1os_3EZhEuaTjD@3VOxbys8jdQaG_JXNx(!(&#al#W0aE14D1sB zMoRKVwY9T0S}oIAV4YUe%!%^d_C3%&qD|0=Li=$$YIRCDqC*eqltaVCWft26qxBjc z{#NF5%nYiYdz83kKvTCpN1n+xvy;!%H$Czup>10{ikIj{bhU0MPN)?tv?oQPA1S#S zWiZXX#nK!pu~HcXTxeD`6w0V;lxAWaP{$N`go2HfOf@okWfmLV>QNtqq~~6yTJ%+Q zOMQBYT)isduHBVncI`C=JQ;0af$cR$&|Ukwqz9^11fCLFMGWGu-6JDKzjLIzYxhiN zcn$aV6z-c-ClZ9-CS{%DuH80c<`AZvh&bz@2fKTSrehd%we)!>${a*nf^0Cy&o3EC z^t|GZe{HO_GWy)!A4yRN?e^c0k*|EKif5quwB*?n&G)indG<5dz=b{re47~%Mb4nV zTNLyW)ctQJg9eok^l`>$HBcL+G>f`o{~4o>qi5I;7lnN!0r+V$Y*6`NpCth%+a#A0 zORvrbK;b5tIm^&m>sZ@ZN z>LFgnW~?6GcECQg5~qI@M|CXy|9JZUhAgIP%I+%2>lV46%3jTYszy}OaoK+zPdP*2guFwHWt#)Ui5(^%I;4G}v?o)%(DTRiW$%Q0^R)3u+Km*)I{P94Z9l^ zVz&mh;@p`JLF^)`{^QgD?!T=#kA;T02NnDvNg~Q^t2m8=3ho!evE8=b@Gqqo-g|81 z9!%E3)wapL5dwD~{Hav^X4_7rp!#1Hps`)vdh!W;49B;t=&kYTGC+(~a z78<5jBMTWB)}CzcD$C~@yX;137PCSzTO%c$LcOc9+l=ngMI4}G)-3O;bVli$)JEfq zK;P*@N8j)5SW#q}Ox|#)=mVS*h4GxMO#4Z4W2|1~kzhiMl}CJgD8$N;j{(>y&7>A7 zvaFhxiqWiQypkkyo{6G`H9}t(j*yfawV;QUUYhw1nRsTBMX;woWF}uCjDtP>1*4X> zB|T70aCk~+f-|F*ZdwGor#ZeT`ofEgS_<#Yxfb4uaVhDXn5FL5bgaw@I$|QmOlISu zuX)UBL=H2qA2Y%V4lWH-i`o|w^okUI>yRL|48V~HFO@7W5)yP=4DV&%ISyzZ{gtmT zarCTOZ%=`0sunzZw+fu$Wt?)CDei#k&5}D-@XtCuul8EvHcYUK_#=!smz2TYQ4}m< zmOhjW7F0g4o1HnP7qcEPKInHd^u&gk5uYo{2*fdcCYceS@-gB>&4`Hc?rOkym<>tI zFcZF4lnIDx`c^U%K;>gX+!}%3C#D`^D($WxvQ`$#!va4?%TN1eI;0 zL3KV+$&L2@*4-P@pg4*)_+^rjcI7twX8H19L^Rmj^F3ch&>R1}IUF0%pj~2mf1mpj z70__ywK|~TD*O@w4dT~c3}|RiRw$b)>ExT-fCky-AOQ`sg@p)c7$-@XyCL}j4Go9F zQEGoc10ECdPic=b0vfhw4_G8VAuZ1Z323+}L+|tOgaaCwb5pNDGd%$f8i`!rE>(Z> zy!O_dhldWm@jra%0SzAvcCk-D!=DgQX!}m99|9WQA5qs1Xz(Iu+c@_!%3}aGjX< z^~Qx-F{whfhySIT)Y@vRi0f{LU*$hCibXehgzJ_pyj%rA1Nsp9*)KcIC{!HT6aN-*j zM-ks}TD}l;e1k#7a^MYti1>!BP!lz;c;g$cL4{NY|F#}I7T@5; zH#CwYq8vPm(}-`F62h?^Jl+@vr5E0NZ1Y~8)E(viK_8U$`<;t#xFrR!spgnb-z1nH zoOyM8gAy62q{w&?9*A!+1U(V)EVXfEP>n`VsrUwiiifXlemH2s8{hC(;H5G@T-Atg z_#`T%nxDVShhWMD@eSPn-1vq~9`880`V*X#YH6{M@eQ*x^u6qAxl`F*w?5Bpl^f@_ z&^L??=L7w9vT>8X%sRcBK49AO)oS|&Ip0UbOq&~UaF~asgYk0@1squHG8kl)>jPl= z@sRwJi3}_f2k`Brkz-F2*rdHr3?h4)2*v^YbJ7FVM1ZG+CW4}I0G=T>6qV2`$CNmL zJ=PbVg#Rst_onTG)8P>*>AX0Awsw$H9Dt?oeaFE_$uk57*M8j(EeoHc{-Enq<_%l> z;l5c$^xl<;XL@kX?l?Y*IkNNYsb7x{%b75;0ha!8DPT?M!|7fUNW&`<kU-^wbYiNd=GD*CHH8D_$@MVSD%`qpG7 zfJ!o<|4P&Am^Jj`h=`{ux2HiRM_d1=8je{-g)zD$;#v0k7F5#Og`Z{j5H?cIhG8n@ zm{mQ*9QsvZS;J$??jflDE`rJ)k)ZltY6=ML-*_FfMt^w~%t*CkR?cF*j@hhyLzX`; zGR^e{CU0`ZI%f6OzTQRSzC<}@Zzr{ondW!kmvGFAUwh9ndrEt=Fe;u4L3-Jfl6F^XQOE*8k|CyJQc1wymv>?tQA_I!f*!ecGRh zLWWoC9$d1&B_8C}wOz7aUEB-AFi3fKJW`N zip4f<+3wi0-A~htSs}q2Tf6~g?{vm2-!$j47tYu^D5Nu1b2{D`dn1U|wC8?%?^=BLjVK zq*Q5)l}dDW?|5xuRv!LeYF9g*T4SQUr6k?_1}kY0w5#-yfv3@b$`{w^`2JF@L5Ts4 z(dvpl*AK0td+F(H$G(uF2BvJ`?7b&n9UP4Y6>EechWrm%d>=hk8twJwJ&*!L+rjVU zLoiXpo5%h4Y}Z;REpI!~^F4u7Zyw(57t)zWV`Zk<>X_+ux`?wlW8^k<;Y%N~2+=YRdxD5>_GCpLu{ssgnGeu0{>T zch&oe3avGU9CQ)0gt9*KwP>W2uR^Gkqdz01pz_e|7|j_e1=VJxq!q-#t&1|9K^AF* z!i<#iw55picAw{@O#RAzrKWUj2LDFb%mnA{>ZAuM=PjP1md##Ynd!WJ&ED64J)B3_*_u0}H4Ri{V^7s(i3mm_D{ZFWIF6J3?dw_H-z-z!r9n)(Yj z@?`>DaN|RtjjwnDs%J=^SkC98f|HaBwZc$y2pQ;TQJ`=xmy>~l$_I39Xrgs--oQA+ zbPV&QMPb5yyfYalsC+Pw46GGgly7E~K{5vU)}kPfr0(953=&j6kcahI?YIE{lmYf7 zVQ_y|6dW?MKadO#R6cO)a@J}t$iHQfxugvCuA*S^#fiU31`8@5*u(m)I9-tUG046o z4DOpn!Qq<{UrPoDDj&F`_2SsSUUo75m@(#2GSr_Ig^KS*{A)5)Q2C&)%USfhSoeD_ z&zX6o4E6v}#qR8xb0)qEu@4dF>GyFaWZ}@Nt=H1js(2N_$v`p)kobcClxqxT5PvvwV6wT!Cz|fh;v((4(0?O^wQEribtgf9hx^{MOM-S{De| z8>v{%NP97w=~2}QJ)P{iXr|xdNy>NUjb_?+o8*V?ifzu)qM7~^EHC!OjFr@l^k^m_ zbP>%Y6k(ufrbTFLNnN2Sp@~CBiyQ?p%7CVyE9IgXmzA4dJ%8i;#>pjAarECN<|J1UC)T2J`vMjBCE>&*d}X zo6eB3Kfqvle00nA5U1#7^xB~asgyHeE)$!E6X>KEi9n|p$C#7?%EA%oDe|HxY-$Fw^;m5p%o)Woz`r@GRngL2?F-{OLC|)vKZSB4s zp?~#yb?jQAH$$L2cYX9vtyf!}E1PD7lAzS(4$U-TU+9D)1B@;*k3^`~+8uQ0s^04E zE$0EatuWzm27#(-MJe^E;b})>Fxdj?th|RNaLR*-v8>l!Hk-vxb7QUMjDD2CFSFR8 zD3@!ETBlq#YU?7@6ccD}bi?aCnw=7D;!|r|r$sz%`3aR+_qLR5y#w z>RLICs=6rC*CO#=UrL%URJ@m3*w^J3~qi1+$d(gRhz7oMVCBXZ-tE*xwG>ZS9b z&G72t|4Tt^&a-f!{FCHwUYymhd*ZD67>#&)QPfnVScx11k!~9XH47;g!D|fS?EuAv zMb26yB|X$rM!%j&v_ku0#QrlyHFb=b8vScXJAWm$vq+5C;eA(Xvcnu5cIfl6HE)23 z$J-N8KlY{09B+tvdpr^6>?W3ijDMd~6d8 z{y&O>M=0OFC4&c*5B$2AWxKK5Ir#b6LN}L`!5#vtBFo+N=r$3jEq4W#U=c@7L`6Fq z%jK03ELO3E29JqB+R=DL`K5S8v10}t7p0CRG^pfjclcR$5Am`tH$%cyD%w#! z3tK36C-)FkcScaDXh-8&e?wFXn@Q*L3xtJhy#3=i#9^NB_fz9_RRlU>EzuIo?A=p=npEAL1PEil}SH zIeL+^ZJfH1S{!iD@ zzr>T2>)spZ`0q|vY(th7=lBS<6;iOkscrkcph%pf5W0wS6pAoVoa4=lBc`0KEGaAW zBs5{@n6W1i_f(YQzbbi#qa4*YvusOf7qUG(8m&jK^%3ECWJa;*CNDd_@tmIcM!6Ca zL7*p*cXhJbs^Orf(s-*mUE&e6MNc0Cw7?wRobk&y-8nA~!g+9#lsIA=HM8Sm8!rHH zpV-EWQo5o;dCnuE(7@Mvs(OvsM((v8oVGPCwo!iG~%bw^Yq_up2W$D$e+ zMO5P(Qq;hdE#ebz%2x+Rqd~PyXj?oi>NJX>-ZB(Q^i1B7?x?+a|W`$M& zVsoNgX^l?ScABTSQ3JZv45X|#M|U)5JLO3_h>^k}jqPyfjzvUHk#c2brj9Q*)|w61 zF=E~05_@wsK}-67Sl9AM&?+eHlN_C2&=(J$riOiW#3Rb zFhU76`3g`Z*6uS&R!qg(sr3T3d_OG=T0*Sd-zPm##oFO1v=xiz=EmCHvf_ec`BVCt{&@pZaW4Fk#O&GDo z2CL?oRvlkx+#@OS)1h%{up%Vvp?>4Xv$Al$<8`z)z=i~L5!qzfYj%jQcwx4B%A;iU zJP1Uw?+B)PBI0Wq5$C*Q>L5AA5l}4=RCbVD?!@APJ%+*N(=yy+i^4@5*Sci5pz^_8 zZww?uxJIeWJmA&y)Y%L;hMFOtTNE-Px6Vw43@RVwk$7X_%KB=Cp3(}l;8{gkfT*sk zl34&M9}7-JR8P{%#pT2}bD}_F%#g{V3_)bqSTaLEYW1o>AwLGT65l1&i3MTa&?pN`hriQ&e1tF+=PZ@l+Lc zVNl7Lw*OO&xDaE0{;8OsZ6AxXy{qFw3@W)W3qQ;5A(nG;;U1<^aUtqiSnmIIat}fE zR}oYyF2s1&Ul5h_#r5ZiH!j3zgEvnYY4#02vDe}GhMz9qV1L6;`%cu02zgXuV?wl7 zQ9JawFHtce50MJWn2?9@OT>hTUwbzuq*I+9dQ1qlIbci(wXhH|AuC>FuT}G7LJn~# z9HsWhgy1nD7oT>@BPL{N_JGCXRul4EkeHAS8G4_SEF2TUoSQlU&G=$M1QNL)jF=Go z$@AJ7IS&uTg!msm^q7zOySS8PL; z787y zCd9CW91~*O!~d?D)Y@vRh_j@fL`=y0Gm1qwdF9815cq@>uY55fkuC6y^YM&dzUj{8 zFwVRB7%1fT{4}%UV?sU)Vrdk5&bzuNr7PMIdKVFe2ENu))oa9raIbCGesoL-F?sNq zkf=uFUdfuS850smC?!m2ze@}nPE3emA!0(lna_3YK{u$F@w@>L5fk!r=!Kd{yeC~f zf(o(wy;^Y|%7-AfhE@L&6TgRj!ad%2d-BG?m^g)?B zudmS1;R^@juSPLwGYt6D6u_pMV+!3Om>-;Kr_?@>5*etZ$avN5?vEKeRfeEfMm$TM z*KSZ97(w+bI?9k|D<~BYUqf{H-(P$lc&W?}$NCuS*cYHes`+_tJ_J)P2;kxVd)#c6 z>uKB9J%=}u9ZK_PV=b|e0X&^nZDOL@D$7rpmPaX$W}-S)uI{Whtmu|ty)3SS;Et3C z!VE%WX00z78l~BX=|pvUA0O@1jV*gwV7EyJrFCY_Xd79;K^d~CcD+1Wo1ws7t16e; zjka22<(YA`RHeha#+ybT+*=Oe_~;p@oqnczR=GV}>saTvwS4!tjJ9_g0a2~$bd#RD zy)wGPI$6gmckneT)75shwbRCDlsfehqGa<{mDq49qn+B$T4%R`Ul&nDVJPz^!fhY& zDF0xZzlXw;f{JW#AXqQsUy8QV(Q7%XPBrrMoT{)o8{IWZjn27hY!JA&Oe&xHnxww_ ztls!&UXpH)lnfO%o@11b--9{w4)y?|KJC{Dd~Y~5rwCO7E$KMEE9vA-05j3gH(fG6n$ad zzbA$F=DGnMx_3%R=LKAS-wwD68XZHIgY__yBkfYqB!#U}q#+_>kT%N_fzP}c;Jx>S z`AM-nu|_Ay-V`!5G2vn#OVK_ZE~b|22p79uDqxY5)GiCEmm!H@J#A-s-9L3NFv(T@ z@Sec~_KPVjGY2e!$v!9e6@CFzZf?b8P<>W%+4_FxamG^DWEL-rX#p&49>?5$pIH+} z&#)gT3L8OX-%ExKDj)14`mb(Wu#Yg<5EVoGWl@L-BKvtVL{Rx49vN7kxfoZLGhGjo zG00V*h<&9s)fZu6%ZWH=OIAlvD)j}G59D#iQqoXgqtr99*E8BUdWL;mQP>CuD<#7Q zl@Ioow0X)E_c_df)RveP=M`lI0>jQqW(BBxtT@Lz!y3Bay^k5#<;pe8l>r)M23=c} zK?o+>n#>?j`53e*`US^j$OJPap%G?4ttbOKL0ig0R;CSQJ%+L#?6fbNp_*XFa z$OahrtBL~0L1HgY1`aA8@U@|rPZ#fPj5kcjFyB!WCXNhyTQW>g`CuNcUzQEw>LQQz zzny{RQ8Ltz7KMrf!#R1Kn%VGjLQ=-zNrqrbeB z&q$4MQd+fp!%1cN20NTohh4Cu}QL_$9(g#jm{=PI}sG zV|#P9F_sihx*_kYs;bce0!mk@CKe*1bUg{fT)XE-l#VzQj$ZpCO7WObfRGO9LqzG( z*#o-WMRER3CeZ~6DZMCz?~Abshm zR#;n0+^9!{w|p|ASbS3$t@rXGLb~U>N02F2MZ}wyWB?j(o_EhJ-k5wbT{BPsEce7Hso9VQ-M7N^#tN^7{F9N)z zGOW;9#YKe9?n{k^bO&0eV%H2R=DjftDRzzHVZDb>9Yqh*(Ed8nukr6-7SJV%Je zj_U9R$11Jx-edcEMN)TE%m@0QENx!k>;(g{SC8=lTVc2_Oo3~vI93SH6RZ!eBy`xU z5*VnY!1%o)5H@Rw`OJuCsU10k>a++d+w?%Cc<9=uhht&ArsuWbrCO2TVUm%-gS%>-$5v4x&>f2+JW7 z@lS@6jz=q0YSzKuPMz&Ir3J^7m>{{39x4n;Zd~bCJlZiV)hHgkX6uEi6t+f; zl=9UfPZz}ffFdC1DNYN-VO&8KpkSx4{Ha1%_#-72vY{ees3q@A&>U^4>=zgCRPn(i z>t#>W@FM()Fl6>b9SpzxtE302sTfZQO~q#T<+~TJF6)VYF?W$wBrFJ0}O5K#R@a>EKZd=kRa%z0M) ztqm{O3w=oej@V!|;wN3J;NU zMi2gNV`cjcbYBC3voXq<_ z6&tZC(#Ti>+!ygId*=lU2f3;XKg;eR)`IdKAxvfW5N{U$ExCuFdT#`kJx7A-T|_0F zbNv?&-dHHbQMAGPGmNw=J$IY6Kaan-=(zNHKDemeh3x8yjfT>mbnQmr-b6)1JxHoA z57+w{eu-!(@oV5{DDFu2K((kfqQi`$4OU>-J?JjFqJNVjBd%pYlvF7M(r&e!op)VgbI@QM5e0S+{ zD)rfFd;V?nn_exRyp+DGFf-ek-#YoYt=;A77}aKrT3S9v3DdO1oSvaSR<@eW4)wV@ zUb|twyYI>K-IcO0WqR%os<1-+Ioqn!U#mLJQIYeEyVR8>)cMx#`R*a()J#JsM9Z6R z?5=88M>~z#>GBQucEtP}yQ|xjax_E9>eFIRjvA!g3Y1jqRZ&vLeqdSnr3nl@PZ88B z?Y1a5s8yY5wmRiHWw;Yx_v@~htkknVmvkRry`e_z6lu7+(vt3yvFi5O32JZku1c#x zT%;PUYGs;AmXlmYt4QNU|7s25S-IL6ZH`f``R?-B&iLlDH@9o_d#!!5Ty3?Qt+M)X z1xWy1rA*N7<0R*eR+flX=DSBZcbq1sjvu7}h}E)P?t`XYYu|8)y9WaUDPen(SX3uW z^W9Z-It^g9GC?1^XpYg}`&AqB-9uZ|(I(~eja6qzbLcTO!Pup8?-d-WQChuJh{>hh z)s@*!v&==^{c5CctHx%hXUdsM5@SU{jkK}VsMI04$B7c5^^iUn&9tktW6kmn(@>~` zt2fLv+sNN%N}(mAo$}6FyGGliXsbL{Yge|{0i-cb!r0n9P>&fJe+LnmHVqCj0O{T; zeH_Agtxn)xG~Zo=U-S^lzQ?{Y$p)>Bk;qrJD!Z~juh_Tyc#JpFoW=wxV!bw9>&#zu z@P_W{nd(fLm{O(TIx000C2)e3F*LeQn(TCD+UK5pG8JPZxHH*oY-zS8Xhb~`HAM%g zW*OOKWGYA;%cy+w@u!?l{9H{b8BX&k;pR`!&C}qmKxKDSTU%l)#aDW(x`3|qYf2+%RkGsJ6ut$J&Mkpp#G5f%3emq)+#M{;SxU^|=-TkYLo%35~w|AFGYf^=d9b9XS)o5&V z%Ct{EQSAu5c|3KBG^<1Amf9Grj>D{X53x!`)&z>Fwejqar5Hh!jZU+`)F+niv(Jm> zr|zZyRHmlai9c?n|5T>_`b6=^m*_v0+3ta$)=Hy3lS$8sB*5dNcQ|WRWEzj_K? zeu>NTE~d+KaQVwi>GCmL&b^#2XW{b6XVT>pxIAzrUGB%_FRrG`pX2hBYw2hLj?0Vb%ZgJk!sQ=dNS7bt@|!YUeuc}A0O0|;%yyT9!=2qTq#Fn z1;rIQ`!If85AA(EF8AZ|LtKO&f8#LvH4lw`H!f?Bq{}h52;Kg{TKaY8(RBG7E<($n z2BpVfn|dQIFT>?#T!iKySE64}#Td8*7cmANz^^~UXt)CxF&Zv8fquP^ggjNjMU0DI zaqTeyg^GIJ9By6|MWT#SntHTUD!Ut#=w3Kubct~-r>{TD{k z{-@JLjHLJC*QYS1?!rZkscmP`uRAfi?!iTjt{0v|zdns|_E}thiE;M)3+S@usdPCK z7ct^KfnPf@_FjgI7<*56I{o^}rF8iQE@BimE~8)1y__yD#6^t9NAc^vXVT?6xQLP2 zx`KZF24i&JtLP%e=)3Xje_u_P`*0DXb^03m^&*Vh?YM|>`ycrAx7X9^RWeFBIx}G%pW{IpvO9+}Nr?)rj*<>oJKEWx>YA?51T&|xghz_yZr?qOU zGfyjzC%SE^JM`{A%db&N(V#h&*5{+mdRW%(azN}g&2mOl8{7>#!t%<2A zJBN37dQ#pIkJ!!lDs_dbqhs`XJV$>8-wKyxFD3_Injrx__o>%zy?pDXSXbdqj$T>x zo1<)vo-v2UDUAz{68rVy4!waQNcBy0Q}bdE%Ibfj4++i!`mg{|xUyO!W%cyQ(U~zr zLRvumZf8=tm)cB_3c5g2`8rJ}Nm7~m3nKIgL^Q+#!8D}=f;%w+krJ9*)6E|tB=H~S zz{M`XW~%oORL^Dg;Ugp0R$o%0(O#KukBoS@9jK8p{K1?asC*DT3}i=mbH$Ck>bm-G zae6hYzRBr@Q}X3wTA*>E&*TOpiao>m!>Hv1RYyk#R`ostAPvJh%vSZ)3n!M-lEree zM=VE6+jBXl$#HtSHAV!c3T=b(s%1mtDQN;imqqgVXtsEJWphQC~D7>~Q zLE+BA3hItzL4DXGC^bB;lZhRckhFk8RWiZ+O`2fP6%x#=>3w!6BSPy)f7 zg%!welLhh{k3bHQCapxT+LLOWXkqwnWrA2$OlOSqm4M`B9Ar=I7L6snIr>`J5uBUt0 zvm4}Qq&>6hSc$wmkG7H|8%7Ur45QQKAAwm3)UjY0c zPsabz0r4-T?RKC1u_3SIk17i!|1T%w|I!fgoAU3)@0b4`+0_1*WH$VxV1vD>t(NKZ zsffnTnMP3Uj$Wt?v^@?bS4HG;-N-=S#uWjBVf;o6xKRkB5 zvWh6&^@>`mUpHB;*RyGIOEy*XyPXN-XlgS-Am{=Kq)zUyp$J4Y!~%g^O$h{d7FHnV zBn#wBk3iPS1!Q}cqN^zO#TqKvre3&GCY(#tgoEyoaLyn<CX#ohi3FV>e&M9Xp@>8@#3IqhKB`A7tVlkQERv6VMDip#N>pIBM<)F% zWJ0++O(^IFaqNpP9+FT*LoAd}QoG7@;f@qawR=bw?x_O}MErrrht01hnaA|`myyF9 z0S7xuG6Ju(e!4j}Td!_*&zh9m)MfhI3q?4zw%C8JFV)&53Pq;0Y89v>j2BxIcp*P~LtKzQ!uWq3 ziT~eH@lQQM6#VM;28y)OuP({xuZ%{oLi@L<|Aw(YVIsUV)3Sd&)eEIQW^e9V_73*1 ppXkM2?XJv*hln^`S!;~eXU7oXI)NbN6=R6ZRzb`dU9;P_{Qq<pkpQ@#-pE6-8X#`~4C5 zXJlq)MOIc-*Ocdaq$V;m;*b9oe|+&rWcF=C?_IfU`7-*Sx7MjyrOLi(qgbp|i>A}` zRu!v-#=Kc^oA)+P{DbDL%}H;xZtQicje5asddpCwV3o?nx>;%7h=&tY-z}A$X`jG| zV;0;}wGy=netF5D~gRG;yi?&(Sd2^dx z+V=A5ZlOQAn5tEamT45XRar<&2)S(<{5soe3$9hIr%$d`52f`e4*^0&t4XC1U<<+CAfv#s7Ji}gRwp)t*Yamv#VM$x8@wj${P*W@-K~ADSTO|R%YQD zc)WQVmBmkKZhGH*dGpqsYtGmFr_<3Vd(8Sw)iKYx$S6DLq*N)nd1_yp%g-AXxLN(2 zYw8X2MByLA{aV4jQK?j2BQ|cmQ?5VzI2Yq^M{cuF{N{7F=FVnm^A3E?+>r~a??yt%azCcSZtf+^@xaN?~Y#)KP)Ycp&g>kZZ| zG}LZYdbZw(I3h(F6(Ysu3FZnv;4%A)?kjHJqvLiJaVuF}MIE;0^6o?~HwZ2%P|usL zSud9mz(eZ4V^9XWKPEX+1q_Fo9SxWbY76sGT+7D|v63pY_T`0A`KMZ|+*@8*JW3yh zZa*d*dA-mlbyx)wFx+ZA%;EBLxJQ&!caQ11t1j;DOMZ8w-f*cR@IlCAD=9Q{H}XRD z^mm$`psQJ+__Uaw%G-|bK-OW4mM%}%+Q6&(vpS=GYl9|>KiigQVsCYk= z^svNa7~E?j^M`M=PX>=y@ne=o-B1$!xV1#q=NE-jGSt)H;mbiMEll!j4cV_PB1;A# zvfm8I>a1Xo0J>`Ee=kBmMfK>mBh^Rd>cy(($ARjD8-eQWoL-DX?b}D8Q5y!Zr}e}W zZNS9aG{f2YF0z=x@3|989HoOviwrXw)Mm?s?kLk-s~0WVG_PvYGzC5JWYhS>m1#CC z28AqA(jz6?&HC;$nN4!liP;WogcJ@I6d`NWmU zCN@WqI*Sv(tz>#o-)$ySNp3nZ)tbnHVv&7D{KqU#aINI{vR;m4+kBx-+x)Gbc(QGL z;>tEdVztCF_*Y7Tf7W-B$r7^fPAqX)tF_!Bqa2)2lZ8usrbPXPUesh`J=>-1e@GL`JPZBywhdWX*g9+vWiII(i~mkhWBkQPwzKENP< z!O07u5XDy-lzc(j3h21$jnTrTI%6Z#){LyGyy5w3u|cVm7snDJ@dOF7U2=hL4STC* z8|AXV_}0DNSl+D`h(p(D+Ak$_-g|o!88*7>*CFJ3HS#`Uy#}_gBs>OOvdoi9QB1RL zID@D%fYSlSJA9{stlN&<4lIKOBRg)A0^Q`h^G0vPucE-deO}DG)Md@A6tRTP??Z|% zyk6MQf;5tABjpm-YeqR#-F_vJrrhI3Z`fC@a;qz2{YH}ylio_vxwoRWKzW3PDXH3s zX`nQF7m+#Az7+rH@@n2NL_uz#=}kmags_8nGH;YBP5XL~@kVgNU8uq0zCFEh+%fmn z%B4aHt|cn$HR=@b+UCYQKkSA8Fr3|H$+;Evo|lXR5`8MO@#ZAHV~=7N*k)Ihp<6XHxUx2c9rsJ)wf>)L-7;?7fv8-IFI zHt9L_i!2~%`H%`8@Hft;2@i}<~C6g`>A!)r7OoN`|g5OS7~;kg}*SH95Gh~ zHa9?A{BT(zQ|4WjWPl+*G9M(B?6fRke; z(@W)hz*I{QpeHd85c5E0i4=!%lIix@(p&qS5u}=u>_ZQ$b$?c1OU;(bK02I?StX#w z!!j|E^}3iE+p@~5TUv#l#H_M~=BBV!G;%0g#0jO_;_BYo;!Ho$M3%T@Hb*%CY=c7- zvDC`#EtSi;8n#m1T+y6q%;j*9!@+L1lQ^XYSt@^k1lLO7x5hNrmLcBU(h&3{W{9b5 zVkp+)w9;+y#@^cE@`U`7&Ms8lUsMT;MUEpR8YnSKOMAM*8j)F7mfYbU6*wo>JLT#qPGe~#(8SWd_s;4a~5bgIlhcck>; zt>v}glsxf-J!XG3l=LWcLTd~ex`U((i<4S(zHB_Y_L;QS@eLgBtIp%35KhPOawE8* z9C&nPbW~^u86A|fhl3V^iM3!tEx1m*!0*$K=2&DqxrJ&YSHO~&7EYL63vOAg4r=Ab zT&aR9=C8BHN|r+d&a#|$TNjcEvYgBM%XH9fWz8tno8E~sDR)+|+~*T(todm!G`&e# zcZ$8Cb=Ammwvhh)6Kx%FY+dTTG_Y0c!+6YYz&DQLZAs;Lnkh$5;w@$!M>qUL<)S20 ztD}lIk?25k%{#myq&Z8)rhN*`PU+2^Wd5VQJ9=`)iv`D-JUNI`Pk99uxj;uiDL=^r z2F`Uw)VU_DYxssYae?2_j9F7R6a#NbVc?If4<#~ylXWc^_-MeuO7d;lxjO$8 zy7|eZnj1Y?L$UY)EsMzqp6n(X>Go<^kvO1bzq^gP_DKLSyy2HDxtjFt^FUGV{Mp;k z&fbonh#KwFaK}3~)vcQL>8NN8+h-7&w}GsECY3%%CAz%5Y46}iG!U`B7kC3iyAo9h zLo{aWvr&x|&Ix=qm3OwU_eGyP;~x8xP$yoiojm)BP%ZnbM5EOP-e1Q$hz6ob`*ony z?)KeRKuhNm!SvM@dtdze6{m0TA9p=G8@X zayZ(a`!}+Zoc=yfMY%nv0jm=i(Ayiicz}xERx#iipDa6C)DPmjZvJqv8i}l@$F}53z}7ZxNEo@jc)tWEZNFj%w;P>CXFa=V#^k_*|=qkoNjbi--pk^Bu!?CG&7#;2gP&C73t(ckMqWh;#z@9d`Vzg$ zGCd~rOnav*aUvtUZR$_oengMY<)>s)@ph^mxWFh>w^eAHwtPkYu!NoI$YJyGkOmm` zMD8h9+?k1$^QgA$-{%Nk2sV^uLOZ$AaP~t8_|X|RvVZB_u;c~`1(@rXurt>5lJ=o2 zcS9D-i3!Rdd`PL-uMr-koj>HBbKrN>>e`f6-mpAl(DasXq4$tvrY=I_suU8`>PnI< z(n>nKf|i^-3cbr`Z;7;`VY0c6zK&niil@v-d>!AiApS zBYwTm-tS%XG;2h?6OA%N)YXtzah^KpLN6%E_F(<_n?x!m*_9@Hlm?4qI}1j5J3}pPCrwqQjzir=7-=(G!tnvu2bF#{Gv!F zxus0|21^j0RaGOA%kd9LiX(VQ`Ct1N$n)AylC;lJ>04C#Wl;JRO8f2S#qY1h?+fDh zH{$m{#P4tEm*!zVPb+gYB30`hjw5%N_wsc34l`xJEaqnxg(rjtDlTw$CNaFeV*OqxX@1B9ly@Z&hmzQ<9v$a9pek9NSFZ?YW z!qSsxzmBzt%{9r%GwMb+pg3Pa$+MpX9So=e#oM5L$ev9MM{N_;v_5;LlxVLZGV$!0 zID8?tFBsi~?kFsBR0<2a{8=&!JL=FhI85?ylsjLI}cQ2vbl0iylLA8LvDvk3cnn~QXu@B@*KTQrSoj3jWq%-Pxhhx|fjyvRJd$W2~FiHuN_YkW28 z#3xwiBPe#lfCwf?HCW^eCMo#oF7znz)7?}O_(_zyz)!wJG+TEJe}c?auIKP4ApQFQ ze?rWr!k?gSKj6>Dd{<@ko(_NhYpg{yMp()4hq{S7bvPTy(+$C&2Z9a;2>t{{5&OUx z5`BO_PXvlcN~Y@Jk5Z!j6(X|;{)7aQGs394qu|fqr?8-dKglfg9RB=B%VG=o^8+-} zMQ8R$f)oj1udP2Y2eTCXqp}V2%8J|BmAH%_;Vlmg@iw39$n;S z_!DZf+E=4a{1E(MCj|VFM6$!5i_oKlKd+#YfIp(t4g3k1=@|Z;8Q!1E27uBJl1+z* zw}e3cp)$=$rS%~){Qw*{`OeWk;ILvXqNB0};Gk}FZp#6l0LKkM2R(YHlhdqS95;(v zSI*vjfE>35s>lvGlp5_f5}7#UzzfhavqvA}^W$*jo`N0kOkqO@JCfPxIqbMk%VG=I z@gX$QMMd^Xf*l`hqiz&-BuXBF9oK>462Ojkfvkib|AR_&$*_aU3_I8-AlhqjyCv+H z>{Ht137-iLL`#=;`6l(G!yQk?ScyisY`Ej=prvy$4et0Jnr6ox!sr5b2uJ7!cMNd& zh1Zh9=%y4#LO&&IO7QeJ6;n@3trt1MTtawEc%p*j^FDEQ;HcMxSJ%WDotb(rXfe2& z#l84>(LM&*{LKhpCgOO?H$)fBc6=gs;g9U ze5a}zf-4@ISvAy3sYXp}@vZ5^o%4`-c@Q9n6S#+$+p^_adM|Oyma*w+Y?BwwshM)M zuzSajocz>_(9vY%Y=k!&G-3@72_m6{`UP2Z#7E9vCUk=%e22DTMj3(XhW)d(UN}7Y zs-S~vAyv>7mGI_R-bI;v+Hoi|IzOIepY}uG|&OK3{ zAPcGZ^v>w8YCCPNRAt0erH_}C()U$GCvI*OHqkb*ucrk@tS>aW))fr1yyN z<&UeczL-uu^4Z3siSK}v9XjJU#)}R40TBO%K4+Rh}#MpFC;?l|AJ>?~Qqnc8G;1*fsE;_Xte{xJWk!x7h_rs#c&hW`Ix#Hnh74wA}0^Uj|urO6xS`{M`@5M zbu|9gM|2S#fA>W`6N1kk`TC&2hPOT&_zX1un1q7?gD+!<&*soyKG?jcY*d`h;)wBP zy!3C@XYnn1XYb4IG{o-0_7&n(h$I8q8I_72CK^GpU2gA$0BK%m} zBsNZ|l^UlC)p+aW8OIRf54~|MA4XPEM(8B1MMr&kO{Qk*c27ET+JYBcimSHv=zj_& z$RzbMR1!&QQR*T|Ewglrk!XkCqm*cpwqkGns<6eoI<@+!`CPM zR7P;EeB^hTe1uk{zX@81*Ut}&fbz-LidW4m@^y2gUTt9TpSeAF9Xsc9O2htMKu5gd z1@lv`miE`2~`QUo|@73)4bb7Fx$uoM3?)S9!F8hBc8qz!YMQ`PtUx;-OjXl|RaXt%LI)~8g;(P^7v+v>vBZ*xc zVYpWBvJcv9EI!FXmes{?&U}8G3?w&ZK0!sfF{2W5E1&)FyWh)EQC=wP{)^v&-6v|@ z_-+ej4^L0(P7S+w-kM;6e;}AgLO>*PB1V7+IbK_TR!hEC^&D7`OO9VsNh~=;siP%_ z00=DCF8kX!BaQc@{z?8r$i_YcKi#}8b=r0Kp7LD7s24M#%oEd4W3=#Gkt=cKTp17yi8^c;{~t!1$VAiDyMbn`F!Apx?>+Nc`^WQmfi znZaHKiaiHpF9T5t$Q&xsB?B@lGazGcfMDae-4c+6GOtVda@0Pb2aY>Y)Yi+FquxS2 z=}zgqEyhYTpk>2%w}F<7r#vl}9A$CGwAxR{Hh1yEIehNKG6!d8-2^1ts-Jqa=na+}bNT_Y2KT(hg=e#5h z=Mcr#5NH2DKpe?q8oC*yYW)Bu5pf>78kxZiGAtdt{wAynd^D7XQNaiFkY||wp87ec9VIP2CkK^`%Ml#!D z)>}Fxvx9olA(^vdtVA6t8QZK{6MiX?7$dY%Y+D@Pn?9%wLjUNF+n%(FJHm zGNC3@z8ZDnqZuO^c0wQ-NhCXxnM02f$yihpNJf;pK{5d|ogasM>3Mf zR3t;y`hjHL;=3bH59vte&RC0R9BA=o6?LNwjG9M0P&yHl2 z-t0dnGK-Lml*30d;zA*rM^if1A(`ZkdyZrt*Rt3mC-Y@A(uG=fJVG*GXrpcv$s|f1 zLNfb6vFAwUQy?ml%)e5JE*Z&CnUM_p00g@Qw+}Ru`JvAP2b!frGC!l9bV%kGF;=1x zEE|$}7PNH!r9m>kL(}X?M%Y{+8Q}+AA(=OjA|#R_^XLLJBbiW>f1}pz=VaIkfn+3+ z>`3Ots~O4Us3ee#D0PEm0%kf!GSfwzX>I+cc5KHR2b_*eOUZy>YaG*?zi#1k)$!pb z!D1SW8AYvrpqL$S2XaLYF6k&{XRJju2DCsi)QxVC@MJhk%@ztgM{D7=q}0%!k8)D>L5&Vw>^h2w`f^x0b%x{k#34%uOkTKv{5$- zVG<<|L745J*mDRo2cisyej0S(4Vc;bfoRsQk#T|z5>>DM zqk2kn_5_iMBbs4$KpF4(QsJ7TQ##k-n&i%Vj%!ZUve*LGY(pd6yuPB%* zqU0f5^Fugv&vDI*!KTDDS5t{D8P`ymaSi(c1pBc+t`g5L_vyIIYrtA~^|W-jW`=sw z;hJKMmDoVb^ey3=NaLM-OSpoj*>R1qxxh8T54yrNKO(=7xQ5K53)75iLQSsq)uTIb#zt_NDy2_HDnM_AEl4@&g z^R5BGHq2)lx|v`_^#k90+;>l&PSf$tXJRd)aiImip>A{ohbI`qHxC9Kcmrp;ejuE$ z2J&am*(lxFPY{_n!db<>#ixSR4TW<4A%y`Q%1LIR=P2jLS{7TNoTt%97lzph3FZ82 z8+D^7CsFbc%J~Q=_8jGW8$>0_8J|FjE*a%enNbe=0tCAsw-0poW*vC#56U@$deWhs zqhhQ?qgl4>&0(OW^Dj;I<^(j&j&g*}1dzo2P$TqA5QaEdx(Gbqr7SW&c8I6=Y43J z9rFmA3(O<@pexL?ffOMz51B_7vKjM)n%qjQ+s8cYgupzKNOsKgS@b9|&*!Klayg>Z z4dw}$=@|2DFV!5gkO}S_p9*(~WNV~z=U^Zm$z&SH85jEL2flgIcS#O1>G5>5rl^M{m7eKJD;r4+BG-t-Fw{%IIoz#;K(7ZgxO4O0E0h;qbOXpu2K(h-?vjZAo za{*|CA9Mw1{)YTQ0va-pE=Dt;2{n1CuST8tXvRly*a-n>B$4cZrivaVps7(w02)#1 z2G9h|bPQ-tshI9;xw_xU_ic*tPIx=X!B4Yes*Z1RA)a(Wa7|fBoIYL{oq=`n?FnG zUI%ZIyYD%?`I?r+7Wta5qLD8CvIi2pdAyCfQFxOmc?jOz28un0H=hMj32%N(CAwsI zLuH0H>IU8f z%ybTKsx`CXIN83*mkMuuqJs`^0yb0OjbF1L@Mb66g9080n{;?{X{<#wCbWPz)QxV? z@Z>`9=G>rz0f9HlaQ%Qc`9S>a@J6Z5em#*{1aAUSe0amx3f|18bgzRq$=&xH-n>=I zVheclW;D{pU-m$PH@CD=HwteOB@e-yGeNQE@TLl)65iZTCAwsILuH0H>+H zz?(z0`qjT3u2g4lBr=KcMu-xEH*~Gw&G9MS>)=gt_dSO-Q_lMSjd_VjquohlWEgihE zsV5!0DaTlehO%t%#v+Y(_I~&tG|diggv|xK5q{7Wy!jdVg@iX`9$lnncoS-JqpwDt z_-MwPQ|yF*HRaSfFj*RUTzut#wFK&NdE1F!wTHJhj>9j$ARWz>58JwHiv&~XiOnTl&<#eU!#+jqiH(+>TAzU*XbTA;eCNNt+aLqdc>9gY+r8)a{BC`nBup~aN@h=sw zd2dSRI$V?7dCzgpBU%<);F^cfNEdk7{Rr3G-$vaiu1Se?r+fnRG!N2WXPJ z?m3_-XjyClX!2;Jn{e3o2+&;LM%^f&Nt8SUXbuC#o&%aIKvV*nw^E5N8PHIf0S$Wr z1UnM94>X{;!{=e?0nI(slMc`{W2{7jST;cOZqU;Cmj=-MF`8xvG{WWr&13eX%u zijaVY%%h9Z3}`}4-cGIC2Q=)205p-BN2e|pJ@17iR(!tGBu@=#|&;s00H@bnt6AXcy ze+)VpkPj5IJ(@mTc+-i0(7d%dX$^U~wC@q{d)Da{70CEiAb)mzqjYEglE}pI%_=2} zjQ#3{VqoYH8M4?Xq86UCbSNj8fu5tBBeg8HKskq^k!}`ZCnS_}a2s`_C?`?!5X$)` z?9p?SGXgdx$~lWlbjc`(%8YW@7a-WTeD6At$8636YvC2u(xq)KrJi&s=an&5V!oc~ z&oEp}8t?3w&8yKgJIWC@7br*gL02f}Nk3e`MaVq5V9h8e)Z`prjXLqsjE~u{69VN( zBAHRnd!IXRnfRZ*7hOxZvyVyw?ub%1a3|oZW4Lp&v*47gbD6+S>UkSUvo+KijPo|k zWg5^~Ei3i|=G^1^Cr@eVFz3I-T0{dw3z$RQ=td6DFa&e%3OX2+eJSCveF8b33Z%~t za+K!mPZF6p$Qfn_l!2cw6~OsoO6NL&liYdF0nWFyEVckR-#{Z>1ZMXmfb+FB>P7)h zqU0ffa|bB)9N>HzL?yuaIhE*=0S=WJ;IJP+uy^A2fllK*@AJUnX6XRVij64%XC+k# zy(1cpW7*O;%Ro!#Uz#+|1e#_CIKt)v;0QnH3gFyHHj!x@GLJ4yGr$Qoc`op(r~<9y zZ){qd@!yFzHbVf1oe%&=63Gm3>}}AP#5LQgByf!=b%SdHX1c;Pt~uXxTmzcF2e?LX z*$%Ej#eU$LOW_*f(VdQKu8OsYxpU2kd9+nPOr>sg1BNFS!ZjBK9Sjbxk!I`1=G0su zeRf=SMZL4HB3%F zXi)UUbqe{Dre^< z-U~WLt6%*K*GhHv(L^Q=Cq@EMWSkAI6`aHffn9Mj-s117~9-U zR+Mn4NJW8dLeBj2ODZL|WRy#9z{}9CW#;@Sm@AYG$Jv@IRV=fP8kM=+Y`r?4yKtXb z$X`IurgN9f=8W8o;h4GVjBOU&+*rxU)#}wfrJ`9xQ`gBEu3In7h&Qe2Z*do@wUSxP zxmD3sp;51!6&HOtu2CtN)46LbP{5y-VV3GSqwG|3jtNqY8hR_13T~-dG3pDs>MY33 z&l|ufg=%HCG}ovb^vuc4R_nPvM!i&RfEdezFVFbTr}-4Z|Ch3>+3bD3KQO$B4)A#1 zV`?BF2S!4oTi9}?1^crZWX5eqp`h(jk)Jruthd@y+lnfW6es77MqMC1y>9MKYY}uHDUCJd# z+Jy=c5Fj zLz8)EG_Zogx0I~Ub}H*QxR%|v=9=8Az6^u#v*$#iXDGsP1C3D-%eChmtS)!pPub$8ma1f{y^xYfFu zFU};?R1cXZ&bG`YGAJK#wSr|9b|(-N_qpo=<{~qx65Z!YGv#VwH-*b2an(c7v2cb- z`{a<4z44HS*;yj@l(&o@CV^vHK*8RQUXQXg}V36o*Ju9XwULe02Pmq zC5~l0-m;j&Q@4F!k@q}x+XtsZTL;Hw5!nJZ;s`ncd0#B#)xxP*+@)4WNi9XDs!i$s z92fmeCHe{R)=F?54>7TKbSm~*2yl94v8h!nu~SB9T#1e3J%lP|Rt2?6QpHQ! ziKlTNxK*iOa#4FFeh`skqQALQ(KkSdTQZAIt&&9Fls-qq1#c?BPl&fxa*HRJK4N0O zuT!xPh5+|u7Mofni=8^_#l?OkEOxAQNNj)BjEVhdr((Yd0(>m9*wiXX?5%354vket zH|k;HnEy+qfbFVkh>p^Ndc1hd|IbcUaR`KZEVC-8l~hG*3@MPW{R{zIpvfrorOEQ} zHhR4>0z{~l1R||_rM|XAS`mC%*+yT-FKVUmuD-vs2-d6YGh?i1qe*W?ZK3H6iKJZ9 zjI0rUaS-y`&x1#~i}-6QiCsid>S7mBZpJA_yb-ykcbXzwPfUbwZ4OR-SyY!=MwWitzF(~zEdt$%;qj{6_SXQ#&nwA`eL=va7$%p8Y#_+YgUR)Z}`=e z+;N)sHs#918^-q`YYn%#%UZk38=W&Nls>H1L20yzbOr#_d<_@l^=j1xtLAKJU(;K@ zt?7+R7V_wKH!6(zmyLQES8LpAAwOdfaC2Ylw)pfI0t-JZM zxyOV$S9oKVQ4TJLy!Gb3lA9-Z*l@VgkT+B`XBu;$H?h~KQ<4ic>ZUP|JEJg1!6cE0 zzfuJ{%bS%#wTN0xZ?xggPMtaBlu$k@`Y>(zSRLVB%i)?oo+wcs|rwNmpTDno)w z(>pr$Bu2vCb~u*H6OyjtfmHMRo{l*qbmKZZ)rNc`HhAw>8Dad@UciBor%d zh`+O5LCzaw)`~mA*~$9q7XDmrqgc)Fn&D*T z^0MXLdKz!=oXQ*=v0R!jxy{ShZT2Q=W-X6Vf?O*y{Upq4x1urrBkB;UDHuS_JkEH*JTAZ2#K_YfMg9aPkHKq@ z%kDPo(-9vLEbf@)SsByzdk5ho{!JWp@m9%bg5e8K zELDmnj14#M8udBs*pT0>2UGAY7vUB(ismdub8kcBE+yH;ZBUyHE`}+j7De#4Pg%Ka z*^N#6<@m?2PZ$*!XW$>B;f+uOu6GbB*vshWdE}q$=ji7N%*^)V^z#&5exH6;AWqo- zNz=MFO6}c+n&4p4CJOsS>;0nTe$i^bXtBRx9gC?*bYQKvUTIw`dl;q;iADQL`Z%UX!Y3lzc^z-3i{5(KE*Nx!k2Ko^^Jx3+t$^H$oNK)E6>E|^1xsZNVjp1j4 zejcNrC+KJ2IDXzlKigO1=XCn{A^rT6em=4WKM&H+YbNmXdiq(1ziw)8q#yb-yy7oQ z+w>Rf#Ghic>5sdJzglF|chJQL{%!hlsQ4bPP2buQUy!ruBNgJiD>l8zY17-DHofs6 z-s7?9jG#FAYSZSvIJMI7)`W+VF!-8634@V51ICtjC|Yto=|SN;vL5^j!)|`2S`J30 zsQRMV&7k&ZgyJaoDjIEw(8t7Acbf=fhs0_5Pk=uV%L_Ofg#nAnzECZfU`^sy~lH0u-> zu)l&0Q44c;u#c&$qOy4Jhtr3T7HIQRNNF8uH_@Uo1Af<(OOVzm=fx@_#ICnVYz60N zN|al$=Gav1e{$8fv8}3-TXg*px^^@N2K*B#_5&o8Ft)Eo+6&q^Beu-J9y zU$yI!U9X_1O*j#Nh~cQDh84j*vJV=dQh(wJVC@PdHSfj4Xf9jjWV}PKtzdeFdtmnq z(GieMcza;Dmd};d9;eP*zONEq2oMk^t38Gmx?LZSm2i;fD`DuutFF2N&Ki-N6*5YT zqLwOH)dv-PKeGzHmsJJ!KcPjk{A;xGukM5VFJzYgS3Q*fV6FTc`XK)yM`RrW8_*)t z5Qy2Ic0-f=pV#y%-Jk0JXYFiYf^c(UV;k!QM{I**9YaTFu)NrfCl&K7#P=XlG_b&; zSP_JYu4t%GvOTjNr`pxy3O|(CU!-y5hthF1ph%%1Kk|1Fy$!+YhNnU?Lu!MnoFT1^ z-mZ%g!D&j|os)nww`smwY?RHZ_*Y2y{KqsGHqjvtthO<4`3sUFZ~h>FUJj{sa}NX& zf%ejNwX>W9?aE7bU35h#&Rzj*$l6-Y`bim4f&8y*TYf)nGwuJYm)>eqdNZr^_6%Am rmpC7YHbHjN8xNd9EGx!Kl|s2uq-D$;ttiHdwEXs0sx&h;W~ToiqPython API-Dokumentation

@@ -118,8 +119,14 @@

about opnsense-helper Static Badge + + + Static Badge + + +

The backend api for opnsense

+
+
+

install

@@ -143,13 +156,6 @@

usage
  • you can run the provided snippets directly by pulling the example file

  • -
  • Please also have a look at:

    - -
@@ -220,8 +226,8 @@

assign the config

    -
  • you can run every script fron /usr/local/opnsense/scripts/

  • -
  • you can use every pluginctl and configctl commands

  • +
  • you can run every script from /usr/local/opnsense/scripts/

  • +
  • you can use every pluginctl and configctl command

  • use <command: str> <argument:str> <flags:arr>

  • besides command, argument may be required based on the method

@@ -357,9 +363,6 @@

motivation + + + Static Badge + + - + +***The backend api for opnsense*** + - create, assign and enable lan / phy interfaces and all the other stuff that is ***Not enabled*** in the opnsense api - use the config_manager to apply all your configs in runtime at once - uses the opnsense backend via shh @@ -22,18 +29,23 @@ - around 80 opnsense scripts you can call - automatically configures your Vlan and Phy Interfaces after applying configuration +--- + +- ***[pypy project](https://pypi.org/project/opnsense-helper)*** +- ***[Api Docs](https://ji-podhead.github.io/opnsense-helper/.docs/_build/html/index.html)*** + +--- ## install -### pip + +### pip ```bash pip install opnsense-helper ``` + ## usage + > - you can run the provided snippets directly by pulling the [example file](https://github.com/the-pod-shop/opnsense-helper/blob/main/python/examples/add_vlans.py) -> - Please also have a look at: -> - the [Api Docs](https://ji-podhead.github.io/opnsense-helper/.docs/_build/html/index.html) -> - the corresponding [pip package](https://pypi.org/project/opnsense-helper/) -> - and soon also the Ansible collection. ### required variables * import the package and define the needed variables for the main class @@ -86,8 +98,8 @@ helper.save(temp_path) #helper.remove_items() ``` ### scripts and commands -> - you can run every script fron `/usr/local/opnsense/scripts/` -> - you can use every `pluginctl` and `configctl` commands +> - you can run every script from `/usr/local/opnsense/scripts/` +> - you can use every `pluginctl` and `configctl` command > - use ` ` > - besides command, argument may be required based on the method #### Example @@ -203,6 +215,3 @@ python setup.py bdist_wheel \ - my opnsense runs in a vm, so it really doesnt matter for me - i just add the interfaces via libvirt and all i need to do is to enable them, given the /conf/config.xml method xml has the answer. -- however for phisical interfaces its the god damn conf.rc -- my opnsense runs in a vm, so it really doesnt matter for me -- i just add the interfaces via libvirt and all i need to do is to enable them, given the /conf/config.xml method diff --git a/.docs/_build/html/_sources/index.rst.txt b/.docs/_build/html/_sources/index.rst.txt index bc25301..a195cab 100644 --- a/.docs/_build/html/_sources/index.rst.txt +++ b/.docs/_build/html/_sources/index.rst.txt @@ -1,7 +1,7 @@ Opnsense-Helper ========================================== -**The backend framework for opnsense** +**The backend api for opnsense** * create, assign and enable lan / phy interfaces and all the other stuff that is ***Not enabled*** in the opnsense api diff --git a/.docs/_build/html/_sources/python_package.rst.txt b/.docs/_build/html/_sources/python_package.rst.txt index a1249fc..9751ad9 100644 --- a/.docs/_build/html/_sources/python_package.rst.txt +++ b/.docs/_build/html/_sources/python_package.rst.txt @@ -9,7 +9,15 @@ Api Docs for Opnsense_Helper pip package. :undoc-members: :show-inheritance: +coonfig_manager +========================================== +Config_Manager class lets you assign configurations, by passing arrays of the corresponding Instances. +Config_Manager will parse those objects to xml and automatically reconfigure your devices. +.. automodule:: opnsense_helper.config_manager.config_manager + :members: + :undoc-members: + :show-inheritance: commands ========================================== diff --git a/.docs/_build/html/genindex.html b/.docs/_build/html/genindex.html index df4efaa..243531e 100644 --- a/.docs/_build/html/genindex.html +++ b/.docs/_build/html/genindex.html @@ -49,6 +49,7 @@

Python API-Dokumentation

@@ -84,6 +85,7 @@

Index

| C | D | F + | G | H | I | M @@ -93,6 +95,7 @@

Index

| R | S | U + | V | W

@@ -107,10 +110,14 @@

A

C

@@ -123,6 +130,8 @@

D

@@ -140,6 +149,22 @@

F

+

G

+ + + +
+

H

@@ -333,14 +376,18 @@

R

S

+

V

+ + +
+

W

diff --git a/.docs/_build/html/python_package.html b/.docs/_build/html/python_package.html index cfea8a6..2c51e6b 100644 --- a/.docs/_build/html/python_package.html +++ b/.docs/_build/html/python_package.html @@ -19,7 +19,7 @@ - + @@ -43,7 +43,10 @@ - - - +
    +
  • auth : Execute configctl auth command

  • +
  • captiveportal : Execute configctl captiveportal command

  • +
  • configd : Execute configctl configd command

  • +
  • cron : Execute configctl cron command

  • +
  • dhcpd : Execute configctl dhcpd command

  • +
  • dhcpd6 : Execute configctl dhcpd6 command

  • +
  • dns : Execute configctl dns command

  • +
  • filter : Execute configctl filter command

  • +
  • firmware : Execute configctl firmware command

  • +
  • health : Execute configctl health command

  • +
  • ids : Execute configctl ids command

  • +
  • interface : Execute configctl interface command

  • +
  • ipfw : Execute configctl ipfw command

  • +
  • ipsec : Execute configctl ipsec command

  • +
  • kea : Execute configctl kea command

  • +
  • monit : Execute configctl monit command

  • +
  • netflow : Execute configctl netflow command

  • +
  • openssh : Execute configctl openssh command

  • +
  • openvpn : Execute configctl openvpn command

  • +
  • syslog : Execute configctl syslog command

  • +
  • system : Execute configctl system command

  • +
  • template : Execute configctl template command

  • +
  • unbound : Execute configctl unbound command

  • +
  • webgui : Execute configctl webgui command

  • +
  • wireguard : Execute configctl wireguard command

  • +
  • zfs : Execute configctl zfs command

  • +
+ + + + -
-

scripts

-

You can call around 80 opnsense scripts.

-
-
-class opnsense_helper.scripts.scripts.Scripts(base)
-

Bases: object

-

🐕

-
+
+class opnsense_helper.commands.commands.pluginctl(base)
+

Bases: Exec_Class

+
+

class pluginctl

+

Initializes the pluginctl class, inheriting from Exec_Class. If a base object +is provided, its attributes are copied to the current instance. This +initializer also sets up a dictionary of command configurations for various +pluginctl operations.

+

Usage

+
+
from opnsense_helper.opnsense_helper import Onsense_Helper
+Commands.pluginctl.run(<command>,<argument>,<flags>)
+
+
+
+

Attributes:

+
+
+
commandsdict
+
A dictionary where each key is a command name and the value is another dictionary containing:
    +
  • +
    argumentstr

    The argument to be passed to the command. This is currently always None.

    +
    +
    +
  • +
  • +
    flagslist

    A list of flags for the command. Currently, these are empty.

    +
    +
    +
  • +
  • +
    commandstr

    The path to the script associated with the command.

    +
    +
    +
  • +
+
+
The commands are as follows:
    +
  • ipv4: pluginctl -4, returns primary address of interface

  • +
  • config: pluginctl -c, executes plugin [_configure] hook

  • +
  • ifconfig: pluginctl -D, lists available devices

  • +
  • device_info: pluginctl -d, lists registered devices

  • +
  • flush: pluginctl -f, flushes config property (raw, e.g. system.firmware.plugins)

  • +
  • get: pluginctl -g, get config property (raw, e.g. system.firmware.plugins)

  • +
  • info: pluginctl -I, lists registered device statistics

  • +
  • if_reg: pluginctl -i, invokes dynamic interface registration

  • +
  • run: pluginctl -r, runs a command (e.g. myservice restart)

  • +
  • service_dump: pluginctl -S, dumps service metadata

  • +
  • service: pluginctl -s, executes service command (e.g. myservice restart)

  • +
+
+
+
+
+
+
+
+ +
+
+class opnsense_helper.commands.commands.reconfigure(base)
+

Bases: Exec_Class

+
+

class reconfigure

+
    +
  • Initialize the reconfigure class.

  • +
  • Inherits from the Exec_Class class.

  • +
+

Usage

+
+
from opnsense_helper.opnsense_helper import Onsense_Helper
+Commands.reconfigure.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
baseBase_Class instance

The parent object containing necessary SSH connection details and +configuration settings. If provided, its attributes will be copied +to this instance.

+
+
+

Attributes

+
+
+
commandsdict

A dictionary of command configurations with keys for ‘vlans’ and ‘interfaces’, +each containing command details such as the command string, arguments, +and flags.

+
    +
  • vlans : Execute reconfigure_vlans.php

  • +
  • interfaces : Execute configctl interface reconfigure

  • +
+
+
+
+
+
+ +
+
+

scripts

+

You can call around 80 opnsense scripts.

+
+
+class opnsense_helper.scripts.scripts.Scripts(base)
+

Bases: object

+
+

class Scripts

+

Initialize the Scripts class. +* its just a wrapper for all the opnsense scripts classes. see attributes below.

+

Usage

+
+
Onsense_Helper.scripts.<attribute>.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
+
baseBase_Class
    +
  • The parent object containing the needed ssh connection and the temp path to the config.xml.

  • +
+
+
+
+

Attributes

+
+
    +
  • unbound : unbound instance

  • +
  • system : system instance

  • +
  • syslog : syslog instance

  • +
  • suricata : suricata instance

  • +
  • shell : shell instance

  • +
  • shaper : shaper instance

  • +
  • routes : routes instance

  • +
  • openvpn : openvpn instance

  • +
  • openssh : openssh instance

  • +
  • netflow : netflow instance

  • +
  • ipsec : ipsec instance

  • +
  • interfaces : interfaces instance

  • +
  • health : health instance

  • +
  • firmware : firmware instance

  • +
  • filter : filter instance

  • +
  • dns : dns instance

  • +
  • dhcp : dhcp instance

  • +
  • auth : auth instance

  • +
  • Wireguard : Wireguard instance

  • +
+
+
+
+ +
+
class opnsense_helper.scripts.scripts.Wireguard(base)

Bases: Exec_Class

+
+

class Wireguard

+

Initializes the Wireguard class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.Wireguard.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
+
baseBase_Class instance

The parent object containing necessary SSH connection details.

+
+
+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for Wireguard operations, +each containing command details such as the command string and flags.

    +
    +
    scripts:
      +
    • wg_show: executes wg_show.py

    • +
    • wg-service-control: executes wg-service-control.php

    • +
    • reresolve-dns: executes reresolve-dns.py

    • +
    • gen_keypair: executes gen_keypair.py

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.auth(base)

Bases: Exec_Class

+
+

class auth

+

Initializes the auth class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.auth.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
+
baseBase_Class instance

The parent object containing necessary SSH connection details.

+
+
+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for auth operations, +each containing command details such as the command string and flags.

    +
    +
    scripts:
      +
    • list_group_members: executes list_group_members.php

    • +
    • add_user: executes add_user.php

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.dhcp(base)

Bases: Exec_Class

+
+

class dhcp

+

Initializes the dhcp class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.dhcp.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
+
baseBase_Class instance

The parent object containing necessary SSH connection details.

+
+
+
+

Attributes

+
    +
  • commands : dict

    +
    +
    +
    scripts:
      +
    • unbound_watche: executes unbound_watcher.py*

    • +
    • prefixe: executes prefixes.sh*

    • +
    • prefixes: executes prefixes.php*

    • +
    • get_leases: executes get_leases6.py*

    • +
    • get_lease: executes get_leases.py*

    • +
    • get_kea_lease: executes get_kea_leases.py*

    • +
    • dnsmasq_watche: executes dnsmasq_watcher.py*

    • +
    • cleanup_leases6: executes cleanup_leases6.php*

    • +
    • cleanup_leases4: executes cleanup_leases4.php*

    • +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.dns(base)

Bases: Exec_Class

+
+

class dns

+

Initializes the dns class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.dns.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
+
baseBase_Class instance

The parent object containing necessary SSH connection details.

+
+
+
+

Attributes

+
    +
  • commands : dict

    +
    +
    +
    scripts:
      +
    • query_dns: executes query_dns.py

    • +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.filter(base)

Bases: Exec_Class

+
+

class filter

+

Initializes the filter class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.filter.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
+
baseBase_Class instance

The parent object containing necessary SSH connection details and configuration settings.

+
+
+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for filter operations

    +
    +
    scripts:
      +
    • update_tables: executes update_tables.py*

    • +
    • update_bogons: executes update_bogons.sh*

    • +
    • run_unittests: executes run_unittests.py*

    • +
    • rule_stats: executes rule_stats.py*

    • +
    • rollback_timer.: executes rollback_timer.php*

    • +
    • rollback_cancel.: executes rollback_cancel.php*

    • +
    • read_log: executes read_log.py*

    • +
    • pftop: executes pftop.py*

    • +
    • pftablecount: executes pftablecount.py*

    • +
    • pfstatistics: executes pfstatistics.py*

    • +
    • list_tables: executes list_tables.py*

    • +
    • list_table: executes list_table.py*

    • +
    • list_states: executes list_states.py*

    • +
    • list_rule_ids: executes list_rule_ids.py*

    • +
    • list_pfsync: executes list_pfsync.py*

    • +
    • list_osfp: executes list_osfp.py*

    • +
    • kill_table: executes kill_table.py*

    • +
    • kill_states: executes kill_states.py*

    • +
    • find_table_references: executes find_table_references.py*

    • +
    • download_geoip: executes download_geoip.py*

    • +
    • delete_table: executes delete_table.py*

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.firmware(base)

Bases: Exec_Class

+
+

clas firmware

+

Initializes the firmware class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.firmware.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
+
baseBase_Class instance

The parent object containing necessary SSH connection details.

+
+
+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for firmware operations, +each containing command details such as the command string and flags.

    +
    +
    scripts:
      +
    • upgrade: executes upgrade.sh

    • +
    • update: executes update.sh

    • +
    • unlock: executes unlock.sh

    • +
    • sync.subr: executes sync.subr.sh

    • +
    • sync: executes sync.sh

    • +
    • security: executes security.sh

    • +
    • running: executes running.sh

    • +
    • resync: executes resync.sh

    • +
    • remove: executes remove.sh

    • +
    • reinstall: executes reinstall.sh

    • +
    • register.: executes register.php

    • +
    • reboot: executes reboot.sh

    • +
    • read: executes read.sh

    • +
    • query: executes query.sh

    • +
    • product.: executes product.php

    • +
    • plugin: executes plugin.sh

    • +
    • lock: executes lock.sh

    • +
    • license: executes license.sh

    • +
    • launcher: executes launcher.sh

    • +
    • latest.: executes latest.php

    • +
    • install: executes install.sh

    • +
    • health: executes health.sh

    • +
    • connection: executes connection.sh

    • +
    • check: executes check.sh

    • +
    • changelog: executes changelog.sh

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.health(base)

Bases: Exec_Class

+
+

class heatlh

+

Initializes the health class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.health.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
+
baseBase_Class instance

The parent object containing necessary SSH connection details.

+
+
+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for health operations, +each containing command details such as the command string and flags.

    +
    +
    scripts:
      +
    • listReports: executes listReports.py

    • +
    • flush_rrd: executes flush_rrd.py

    • +
    • fetchData: executes fetchData.py

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.interfaces(base)

Bases: Exec_Class

+
+

class interfaces

+

Initializes the interfaces class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.interfaces.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
+
baseBase_Class instance

The parent object containing necessary SSH connection details.

+
+
+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for interfaces operations, +each containing command details such as the command string and flags.

    +
    +
    scripts:
      +
    • traffic_top: executes traffic_top.py

    • +
    • traffic_stats: executes traffic_stats.php

    • +
    • traceroute: executes traceroute.py

    • +
    • rtsold_resolvconf: executes rtsold_resolvconf.sh

    • +
    • reconfigure_vlans: executes reconfigure_vlans.php

    • +
    • reconfigure_vips: executes reconfigure_vips.php

    • +
    • reconfigure_neighbors: executes reconfigure_neighbors.php

    • +
    • reconfigure_laggs: executes reconfigure_laggs.php

    • +
    • ppp-uptime: executes ppp-uptime.sh

    • +
    • ppp-rename: executes ppp-rename.sh

    • +
    • ppp-linkup: executes ppp-linkup.sh

    • +
    • ppp-linkdown: executes ppp-linkdown.sh

    • +
    • portprobe: executes portprobe.py

    • +
    • ping: executes ping.py

    • +
    • mpd: executes mpd.script

    • +
    • macinfo: executes macinfo.py

    • +
    • list_sockstat: executes list_sockstat.py

    • +
    • list_ndp: executes list_ndp.py

    • +
    • list_macdb: executes list_macdb.py

    • +
    • list_arp: executes list_arp.py

    • +
    • ifctl: executes ifctl.sh

    • +
    • dhclient: executes dhclient-script

    • +
    • carp_set_status: executes carp_set_status.php

    • +
    • carp_global_status: executes carp_global_status.php

    • +
    • capture: executes capture.py

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.ipsec(base)

Bases: Exec_Class

+
+

class ipsec

+

Initializes the ipsec class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.ipsec.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters: +base : Base_Class instance

+
+

The parent object containing necessary SSH connection details.

+
+

Attributes: +- commands : dict

+
+

A dictionary of command configurations for ipsec operations, +each containing command details such as the command string and flags.

+

scripts: +- lib: executes the ipsec library directory +- updown_event: executes updown_event.py +- spddelete: executes spddelete.py +- saddelete: executes saddelete.py +- list_status: executes list_status.py +- list_spd: executes list_spd.py +- list_sad: executes list_sad.py +- list_leases: executes list_leases.py +- get_legacy_vti: executes get_legacy_vti.php +- disconnect: executes disconnect.py +- connect: executes connect.py

+
+
class opnsense_helper.scripts.scripts.netflow(base)

Bases: Exec_Class

+
+

class netflow

+

Initialize the netflow class, inheriting from Exec_Class.

+

Usage

+
Scripts.netflow.run(<command>,<argument>,<flags>)
+
+
+

Parameters: +base : Base_Class instance

+
+

The parent object containing necessary SSH connection details.

+
+

Attributes: +- commands : dict

+
+

A dictionary of command configurations for netflow operations, +each containing command details such as the command string and flags.

+

scripts: +- lib: executes the netflow library directory +- get_top_usage: executes get_top_usage.py script +- get_timeseries: executes get_timeseries.py script +- flush_all: executes flush_all.sh script +- flowd_aggregate_metadata: executes flowd_aggregate_metadata.py script +- flowd_aggregate: executes flowd_aggregate.py script +- export_details: executes export_details.py script +- dump_log: executes dump_log.py script +- flowctl_stats: executes flowctl_stats.py script

+
+
class opnsense_helper.scripts.scripts.openssh(base)

Bases: Exec_Class

+
+

class openssh

+

Initializes the openssh class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.openssh.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
baseBase_Class instance

The parent object containing necessary SSH connection details.

+
+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for openssh operations, +each containing command details such as the command string and flags.

    +
    +
    scripts:
      +
    • ssh_query: executes ssh_query.py

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.openvpn(base)

Bases: Exec_Class

+
+

class openvpn

+

Initializes the openvpn class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.openvpn.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
+
baseBase_Class instance

The parent object containing necessary SSH connection details.

+
+
+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for openvpn operations, +each containing command details such as the command string and flags.

    +
    +
    scripts:
      +
    • user_pass_verify: executes user_pass_verify.php

    • +
    • tls_verify: executes tls_verify.php

    • +
    • ovpn_status: executes ovpn_status.py

    • +
    • ovpn_service_control: executes ovpn_service_control.php

    • +
    • ovpn_event: executes ovpn_event.py

    • +
    • kill_session: executes kill_session.py

    • +
    • client_disconnect: executes client_disconnect.sh

    • +
    • client_connect: executes client_connect.php

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.routes(base)

Bases: Exec_Class

+
+

class routes

+

Initializes the routes class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.routes.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
+
baseBase_Class instance

The parent object containing necessary SSH connection details.

+
+
+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for routes operations, +each containing command details such as the command string and flags.

    +
    +
    scripts:
      +
    • show_routes: executes show_routes.py

    • +
    • gateways: executes gateways.php

    • +
    • gateway_watcher: executes gateway_watcher.php

    • +
    • gateway_status: executes gateway_status.php

    • +
    • del_route: executes del_route.py

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.shaper(base)

Bases: Exec_Class

+
+

class Shaper

+

Initializes the shaper class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.suricata.shaper(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
+
baseBase_Class instance

The parent object containing necessary SSH connection details.

+
+
+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for shaper operations, +each containing command details such as the command string and flags.

    +
    +
    scripts:
      +
    • update_tables: executes update_tables

    • +
    • lib: executes lib/

    • +
    • dummynet_stats: executes dummynet_stats.py*

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.shell(base)

Bases: Exec_Class

+
+

class Shell

+

Initializes the shell class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.shell.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+

base : Base_Class instance +The parent object containing necessary SSH connection details and configuration settings.

+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for shell operations, +each containing command details such as the command string and flags.

    +
    +
    scripts:
      +
    • setports: executes setports.php

    • +
    • setaddr: executes setaddr.php

    • +
    • restore: executes restore.sh

    • +
    • reboot: executes reboot.php

    • +
    • ping: executes ping.php

    • +
    • password: executes password.php

    • +
    • halt: executes halt.php

    • +
    • firmware: executes firmware.sh

    • +
    • defaults: executes defaults.php

    • +
    • banner: executes banner.php

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.suricata(base)

Bases: Exec_Class

+
+

class suricata

+

Initializes the suricata class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.suricata.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+

base : Base_Class instance +The parent object containing necessary SSH connection details and configuration settings.

+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for suricata operations

    +
    +
    scripts:
      +
    • setup: executes setup.sh*

    • +
    • rule-updater: executes rule-updater.py*

    • +
    • queryInstalledRules: executes queryInstalledRules.py*

    • +
    • queryAlertLog: executes queryAlertLog.py*

    • +
    • metadata: executes metadata/

    • +
    • listRuleMetadata: executes listRuleMetadata.py*

    • +
    • listInstallableRulesets: executes listInstallableRulesets.py*

    • +
    • listAlertLogs: executes listAlertLogs.py*

    • +
    • lib: executes lib/

    • +
    • installRules: executes installRules.py*

    • +
    • dropAlertLog: executes dropAlertLog.py*

    • +
    • __init__: executes __init__.py*

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.syslog(base)

Bases: Exec_Class

+
+

class syslog

+

Initializes the syslog class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.syslog.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+

base : Base_Class instance +The parent object containing necessary SSH connection details and configuration settings.

+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for syslog operations, +each containing command details such as the command string and flags.

    +
    +
    scripts:
      +
    • queryLog: executes queryLog.py

    • +
    • logformats: executes logformats/

    • +
    • log_archive: executes log_archive*

    • +
    • lockout_handler: executes lockout_handler*

    • +
    • list_applications: executes list_applications.php

    • +
    • generate_certs: executes generate_certs*

    • +
    • clearlog: executes clearlog.php

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.system(base)

Bases: Exec_Class

+
+

class system

+

Initializes the system class, inheriting from Exec_Class.

+

Usage

+
+
Scripts.system.run(<command>,<argument>,<flags>)
+
+
+
+

Parameters

+
+
+
baseBase_Class instance

The parent object containing necessary SSH connection details.

+
+
+
+

Attributes

+
    +
  • +
    commandsdict

    A dictionary of command configurations for system operations, +each containing command details such as the command string and flags.

    +
    +
    scripts:
      +
    • trigger_config_changed_events: executes trigger_config_changed_events.py

    • +
    • temperature: executes temperature.sh

    • +
    • sysctl: executes sysctl.py

    • +
    • status: executes status.php

    • +
    • ssl_ciphers: executes ssl_ciphers.py

    • +
    • rrd_pfstate_info: executes rrd_pfstate_info.py

    • +
    • rfc5246_cipher_suites: executes rfc5246_cipher_suites.csv

    • +
    • remote_backup: executes remote_backup.php

    • +
    • nameservers: executes nameservers.php

    • +
    • certctl: executes certctl.py

    • +
    • activity: executes activity.py

    • +
    +
    +
    +
    +
    +
  • +
+
class opnsense_helper.scripts.scripts.unbound(base)

Bases: Exec_Class

-

Initializes the unbound class, inheriting from Exec_Class. If a base object -is provided, its attributes are copied to the current instance. This -initializer also sets up a dictionary of command configurations for various -unbound operations.

-

Attributes: -commands (dict): A dictionary where each key is a command name and the -value is another dictionary containing:

-
-
    -
  • command (str): The path to the script associated with the command.

  • -
  • flags (list): A list of flags for the command. Currently, these

  • +
    +

    class unbound

    +
      +
    • Initializes the unbound class, inheriting from Exec_Class.

    -

    are empty.

    +

    Usage

    +
    +
    Scripts.unbound.run(<command>,<argument>,<flags>)
    +
    +
    +

    Parameters

    +
    +
    +
    baseBase_Class instance

    The parent object containing necessary SSH connection details

    +
    +
    +
    +

    Attributes

    +
      +
    • +
      commandsdict

      A dictionary of command configurations with keys for configctl options, +each containing command details such as the command string, arguments, +and flags.

      -
      Attributes:
        -
      • wrapper: Executes ‘ubound/wrapper.py’.

      • -
      • stats: Executes ‘ubound/stats.py’.

      • -
      • start: Executes ‘ubound/start.sh’.

      • -
      • restore_db: Executes ‘ubound/restore_db.py’.

      • -
      • logger: Executes ‘ubound/logger.py’.

      • -
      • check: Executes ‘ubound/check.sh’.

      • -
      • cache: Executes ‘ubound/cache.sh’.

      • -
      • blocklists: Executes ‘ubound/blocklists.py’.

      • +
        scripts:
          +
        • wrapper: executes wrapper.py

        • +
        • stats: executes stats.py

        • +
        • start: executes start.sh

        • +
        • restore_db: executes restore_db.py

        • +
        • logger: executes logger.py

        • +
        • check: executes check.sh*

        • +
        • cache: executes cache.sh*

        • +
        • blocklists: executes blocklists.py

      +
      +
      +
    • +
    +
@@ -360,7 +1668,7 @@

scripts

diff --git a/.docs/_build/html/search.html b/.docs/_build/html/search.html index f2dd05c..0aaa2b9 100644 --- a/.docs/_build/html/search.html +++ b/.docs/_build/html/search.html @@ -52,6 +52,7 @@

Python API-Dokumentation

diff --git a/.docs/_build/html/searchindex.js b/.docs/_build/html/searchindex.js index fca92e5..3ae9bb4 100644 --- a/.docs/_build/html/searchindex.js +++ b/.docs/_build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"About:": [[1, null]], "Class Commands": [[15, "class-commands"]], "Class reconfigure": [[15, "class-reconfigure"]], "Example": [[0, "example"]], "Frontend Api": [[0, "frontend-api"]], "Opnsense-Helper": [[1, null], [15, null]], "Python API-Dokumentation": [[1, null]], "Result": [[0, "result"]], "about opnsense-helper": [[0, null]], "assign the config": [[0, "assign-the-config"]], "commands": [[15, "commands"]], "config_manager": [[0, "config-manager"]], "config_manager manual usage": [[0, "config-manager-manual-usage"]], "contribute": [[0, "contribute"]], "create the module objects": [[0, "create-the-module-objects"]], "install": [[0, "install"]], "motivation": [[0, "motivation"]], "opnsense_helper": [[2, null]], "opnsense_helper.commands": [[3, null]], "opnsense_helper.commands.commands": [[4, null]], "opnsense_helper.config_manager": [[5, null]], "opnsense_helper.config_manager.config_manager": [[6, null]], "opnsense_helper.opnsense_helper": [[7, null]], "opnsense_helper.scripts": [[8, null]], "opnsense_helper.scripts.scripts": [[9, null]], "opnsense_helper.utils": [[10, null]], "opnsense_helper.utils.baseclass": [[11, null]], "opnsense_helper.utils.exec_class": [[12, null]], "opnsense_helper.utils.frontend_utils": [[13, null]], "opnsense_helper.utils.utils": [[14, null]], "pip": [[0, "pip"]], "required variables": [[0, "required-variables"]], "scripts": [[15, "scripts"]], "scripts and commands": [[0, "scripts-and-commands"]], "usage": [[0, "usage"]]}, "docnames": ["README", "index", "opnsense_helper/opnsense_helper", "opnsense_helper/opnsense_helper.commands", "opnsense_helper/opnsense_helper.commands.commands", "opnsense_helper/opnsense_helper.config_manager", "opnsense_helper/opnsense_helper.config_manager.config_manager", "opnsense_helper/opnsense_helper.opnsense_helper", "opnsense_helper/opnsense_helper.scripts", "opnsense_helper/opnsense_helper.scripts.scripts", "opnsense_helper/opnsense_helper.utils", "opnsense_helper/opnsense_helper.utils.baseclass", "opnsense_helper/opnsense_helper.utils.exec_class", "opnsense_helper/opnsense_helper.utils.frontend_utils", "opnsense_helper/opnsense_helper.utils.utils", "python_package"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["README.md", "index.rst", "opnsense_helper/opnsense_helper.rst", "opnsense_helper/opnsense_helper.commands.rst", "opnsense_helper/opnsense_helper.commands.commands.rst", "opnsense_helper/opnsense_helper.config_manager.rst", "opnsense_helper/opnsense_helper.config_manager.config_manager.rst", "opnsense_helper/opnsense_helper.opnsense_helper.rst", "opnsense_helper/opnsense_helper.scripts.rst", "opnsense_helper/opnsense_helper.scripts.scripts.rst", "opnsense_helper/opnsense_helper.utils.rst", "opnsense_helper/opnsense_helper.utils.baseclass.rst", "opnsense_helper/opnsense_helper.utils.exec_class.rst", "opnsense_helper/opnsense_helper.utils.frontend_utils.rst", "opnsense_helper/opnsense_helper.utils.utils.rst", "python_package.rst"], "indexentries": {}, "objects": {"": [[2, 0, 0, "-", "opnsense_helper"]], "opnsense_helper": [[3, 0, 0, "-", "commands"], [5, 0, 0, "-", "config_manager"], [7, 0, 0, "-", "opnsense_helper"], [8, 0, 0, "-", "scripts"], [10, 0, 0, "-", "utils"]], "opnsense_helper.commands": [[15, 0, 0, "-", "commands"]], "opnsense_helper.commands.commands": [[15, 1, 1, "", "Commands"], [15, 1, 1, "", "configctl"], [15, 1, 1, "", "pluginctl"], [15, 1, 1, "", "reconfigure"]], "opnsense_helper.config_manager": [[6, 0, 0, "-", "config_manager"]], "opnsense_helper.opnsense_helper": [[15, 1, 1, "", "Opnsense_Helper"]], "opnsense_helper.scripts": [[15, 0, 0, "-", "scripts"]], "opnsense_helper.scripts.scripts": [[15, 1, 1, "", "Scripts"], [15, 1, 1, "", "Wireguard"], [15, 1, 1, "", "auth"], [15, 1, 1, "", "dhcp"], [15, 1, 1, "", "dns"], [15, 1, 1, "", "filter"], [15, 1, 1, "", "firmware"], [15, 1, 1, "", "health"], [15, 1, 1, "", "interfaces"], [15, 1, 1, "", "ipsec"], [15, 1, 1, "", "netflow"], [15, 1, 1, "", "openssh"], [15, 1, 1, "", "openvpn"], [15, 1, 1, "", "routes"], [15, 1, 1, "", "shaper"], [15, 1, 1, "", "shell"], [15, 1, 1, "", "suricata"], [15, 1, 1, "", "syslog"], [15, 1, 1, "", "system"], [15, 1, 1, "", "unbound"]], "opnsense_helper.utils": [[11, 0, 0, "-", "baseclass"], [12, 0, 0, "-", "exec_class"], [13, 0, 0, "-", "frontend_utils"], [14, 0, 0, "-", "utils"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"]}, "objtypes": {"0": "py:module", "1": "py:class"}, "terms": {"": 0, "0": 0, "00": 0, "01": 0, "02": 0, "03": 0, "0tnitdbolveqvd9fu8wfedo3d3dy5wrutf": 0, "1": 0, "10": [], "100": 0, "103": 0, "1500": 0, "16072": 0, "16384": 0, "168": 0, "1731025409": 0, "192": 0, "2": 0, "200": 0, "24": 0, "25": 0, "3": 0, "4": 0, "5": 0, "5jvvgoatpbaaa": 0, "6": 0, "64": 0, "7": 0, "80": [0, 1, 15], "A": 15, "If": 15, "No": 0, "Not": [0, 1], "The": [0, 1, 15], "There": 0, "_to": 0, "add": 0, "add_item": 0, "add_vlan": [], "address": [0, 15], "after": [0, 1], "all": [0, 1], "also": [0, 15], "an": 15, "ani": 0, "anoth": 15, "ansibl": 0, "answer": 0, "api": 15, "api_auth": [0, 15], "api_kei": [0, 15], "api_secret": [0, 15], "appli": [0, 1], "ar": 15, "argument": [0, 15], "arnl": 0, "around": [0, 1, 15], "arr": 0, "assign": 1, "associ": 15, "attribut": 15, "auth": [0, 1, 15], "authent": 15, "automat": [0, 1], "back": 0, "backend": [0, 1], "base": [0, 15], "base_class": 15, "bdist_wheel": 0, "befor": [], "besid": 0, "bit": 0, "block": 15, "blocklist": 15, "bool": [], "build": 0, "c": 0, "cach": 15, "call": [0, 1, 15], "can": [0, 1, 15], "cd": 0, "chang": 0, "check": 15, "class": [0, 4, 6, 7, 9, 11, 12], "clone": 0, "close_con": 0, "code": 15, "collect": 0, "com": 0, "command": 1, "conf": [0, 15], "conf_path": [0, 15], "config": [1, 15], "config_manag": [1, 15], "configctl": [0, 1, 15], "configur": [0, 1, 15], "connect": 15, "contain": 15, "contribut": 1, "control": 0, "copi": [0, 15], "core": 0, "correspond": 0, "couldnt": 0, "crash_report": 0, "crashreport": 0, "creat": 1, "current": [0, 15], "damn": 0, "data": 15, "ddnsdomainalgorithm": [], "debug": [0, 15], "decid": 0, "def": 0, "default": 0, "defin": 0, "descr": 0, "descript": [], "destin": 0, "detail": 15, "detect": 0, "devic": 0, "dhcp": [0, 1, 15], "dhcpd": 0, "dhcpv4": 0, "diagnost": 0, "dict": 15, "dictionari": 15, "directli": 0, "dn": [1, 15], "do": 0, "doc": [0, 15], "doe": 0, "doesnt": 0, "done": 0, "download": 0, "each": 15, "ejl4fiu9yfnk": 0, "element": [], "empti": 15, "enabl": [0, 1], "error": 0, "everi": 0, "exampl": [], "exec_class": 15, "execut": 15, "exist": 0, "expir": 0, "fals": [0, 15], "fe80": 0, "ffrom": [], "file": [0, 15], "filepath": 0, "filter": [1, 15], "find": 0, "firewal": [0, 15], "firmwar": [1, 15], "first": [], "flag": [0, 15], "forc": 0, "fork": 0, "found": 0, "fozldqy92": 0, "framework": 1, "from": [0, 15], "fron": 0, "frontend": 1, "function": [13, 14], "gaqmpk": 0, "gatewai": 0, "get_conf": 0, "git": 0, "github": 0, "given": 0, "god": 0, "ha": 0, "have": 0, "health": [1, 15], "hmac": [], "host": [0, 15], "hostnam": 15, "how": [], "howev": 0, "i": [0, 1, 15], "id": [], "import": 0, "inherit": 15, "init": 0, "init_config_manag": 15, "initi": [0, 15], "instal": 1, "instanc": 15, "int": [], "interfac": [0, 1, 15], "ip": 15, "ipaddr": [], "ipsec": [1, 15], "ipv4": 0, "ipv6": 0, "item": 0, "its": [0, 15], "just": [0, 15], "kei": 15, "lan": [0, 1], "let": [0, 15], "libvirt": 0, "line": 0, "link": 0, "list": 15, "lo0": 0, "load": 0, "local": 0, "localhost": 0, "log": 0, "logger": 15, "loginterfac": 0, "logloc": 0, "look": 0, "m": 0, "made": 0, "mai": 0, "main": 0, "make": 0, "matter": 0, "md": [], "me": 0, "messag": [0, 15], "method": 0, "modul": [2, 3, 5, 8, 10], "motiv": 1, "mself": 0, "mtu": 0, "must": 15, "my": 0, "n": 0, "name": 15, "necessari": 15, "need": [0, 15], "netflow": [1, 15], "netif": 0, "network": 0, "nhop": 0, "none": [0, 15], "null": 0, "object": 15, "onc": [0, 1], "one": 0, "openssh": [1, 15], "openvpn": [1, 15], "oper": 15, "opnsense_help": [0, 1, 15], "opt1": 0, "opt2": 0, "opt3": 0, "opt4": 0, "other": [0, 1], "out": [], "output": 0, "packag": [0, 15], "paramet": 15, "parent": 15, "pars": 0, "passw": [0, 15], "path": 15, "pcp": 0, "phisic": 0, "php": 0, "phy": [0, 1], "pid": 0, "pip": [1, 15], "pleas": 0, "pluginctl": [0, 1, 15], "pod": 0, "print": 15, "problem": 0, "proto": 0, "provid": [0, 15], "pull": 0, "put_fil": 0, "py": [0, 15], "python": [0, 15], "python3": 0, "question": 0, "rang": [], "rc": 0, "read": 0, "realli": 0, "reconfigur": [0, 1], "remove_item": 0, "repli": 0, "repo": 0, "request": 0, "requir": 1, "restore_db": 15, "return": 15, "right": 0, "rn": [], "root": 0, "rout": [0, 1, 15], "router": 0, "rqia15f1yx1snikgciel2qnojwhbekrawie0anryceh9hey5ifgzlf3da4yj": 0, "rule": 0, "run": [0, 15], "runtim": [0, 1], "save": 0, "script": 1, "servic": 0, "set": [0, 15], "set_vlan": [], "setup": 0, "sh": [0, 15], "shaper": [1, 15], "shell": [1, 15], "shh": [0, 1], "shop": 0, "show_rout": 0, "singl": 0, "snippet": 0, "so": 0, "soon": 0, "spoofmac": [], "ssh": [0, 15], "ssh_auth": [0, 15], "ssl": [0, 15], "start": [0, 15], "stat": 15, "statu": 0, "statuscod": 0, "str": [0, 15], "string": 15, "stuff": [0, 1], "subnet": [], "support": 0, "sure": 0, "suricata": [1, 15], "syntax": 0, "syslog": [1, 15], "system": [0, 1, 15], "t6srlmkd1": 0, "tag": 0, "temp": 15, "temp_path": [0, 15], "temporari": 15, "terraform": 0, "them": 0, "thi": [0, 15], "timestamp": 0, "tmp": 0, "todo": [], "true": [0, 15], "try": [], "type": [], "u": 0, "ubound": 15, "ug": 0, "uh": 0, "ui": 0, "unbound": [1, 15], "up": 15, "upgrad": 0, "us": [0, 1], "usag": [1, 15], "user": [0, 15], "using_api": 0, "usr": 0, "valu": 15, "var": [], "variabl": 1, "variou": 15, "verbos": [0, 15], "verifi": [0, 15], "via": [0, 1], "virtual": [], "vlan": [0, 1, 15], "vlan0": 0, "vlan1": 0, "vlan2": 0, "vlan3": 0, "vlanif": 0, "vlans_api": 0, "vm": 0, "vtnet0": 0, "vtnet1": 0, "want": 0, "well": 0, "were": 0, "when": 0, "where": 15, "wireguard": [1, 15], "wrapper": 15, "xml": [0, 15], "yi8ka9": 0, "you": [0, 1, 15], "your": [0, 1]}, "titles": ["about opnsense-helper", "Opnsense-Helper", "opnsense_helper", "opnsense_helper.commands", "opnsense_helper.commands.commands", "opnsense_helper.config_manager", "opnsense_helper.config_manager.config_manager", "opnsense_helper.opnsense_helper", "opnsense_helper.scripts", "opnsense_helper.scripts.scripts", "opnsense_helper.utils", "opnsense_helper.utils.baseclass", "opnsense_helper.utils.exec_class", "opnsense_helper.utils.frontend_utils", "opnsense_helper.utils.utils", "Opnsense-Helper"], "titleterms": {"about": [0, 1], "api": [0, 1], "arg": [], "assign": 0, "baseclass": 11, "class": 15, "command": [0, 3, 4, 15], "config": 0, "config_manag": [0, 5, 6], "contribut": 0, "creat": 0, "dokument": 1, "exampl": 0, "exec_class": 12, "frontend": 0, "frontend_util": 13, "helper": [0, 1, 15], "instal": 0, "manual": 0, "modul": 0, "motiv": 0, "object": 0, "opnsens": [0, 1, 15], "opnsense_help": [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "pip": 0, "python": 1, "reconfigur": 15, "requir": 0, "result": 0, "script": [0, 8, 9, 15], "usag": 0, "util": [10, 11, 12, 13, 14], "variabl": 0}}) \ No newline at end of file +Search.setIndex({"alltitles": {"About:": [[1, null]], "Defaults": [[15, "defaults"]], "Example": [[0, "example"]], "Frontend Api": [[0, "frontend-api"]], "Opnsense-Helper": [[1, null], [15, null]], "Python API-Dokumentation": [[1, null]], "Req params": [[15, "req-params"]], "Result": [[0, "result"]], "about opnsense-helper": [[0, null]], "assign the config": [[0, "assign-the-config"]], "clas firmware": [[15, "clas-firmware"]], "class auth": [[15, "class-auth"]], "class Commands": [[15, "class-commands"]], "class Dhcpd": [[15, "class-dhcpd"]], "class Interface": [[15, "class-interface"]], "class Opnsense_Helper": [[15, "class-opnsense-helper"]], "class Scripts": [[15, "class-scripts"]], "class Shaper": [[15, "class-shaper"]], "class Shell": [[15, "class-shell"]], "class Vlan": [[15, "class-vlan"]], "class Wireguard": [[15, "class-wireguard"]], "class configctl": [[15, "class-configctl"]], "class dhcp": [[15, "class-dhcp"]], "class dns": [[15, "class-dns"]], "class filter": [[15, "class-filter"]], "class heatlh": [[15, "class-heatlh"]], "class interfaces": [[15, "class-interfaces"]], "class ipsec": [[15, "class-ipsec"]], "class netflow": [[15, "class-netflow"]], "class openssh": [[15, "class-openssh"]], "class openvpn": [[15, "class-openvpn"]], "class pluginctl": [[15, "class-pluginctl"]], "class reconfigure": [[15, "class-reconfigure"]], "class routes": [[15, "class-routes"]], "class suricata": [[15, "class-suricata"]], "class syslog": [[15, "class-syslog"]], "class system": [[15, "class-system"]], "class unbound": [[15, "class-unbound"]], "commands": [[15, "commands"]], "config_manager": [[0, "config-manager"]], "config_manager manual usage": [[0, "config-manager-manual-usage"]], "contribute": [[0, "contribute"]], "coonfig_manager": [[15, "coonfig-manager"]], "create the module objects": [[0, "create-the-module-objects"]], "install": [[0, "install"]], "motivation": [[0, "motivation"]], "opnsense_helper": [[2, null]], "opnsense_helper.commands": [[3, null]], "opnsense_helper.commands.commands": [[4, null]], "opnsense_helper.config_manager": [[5, null]], "opnsense_helper.config_manager.config_manager": [[6, null]], "opnsense_helper.opnsense_helper": [[7, null]], "opnsense_helper.scripts": [[8, null]], "opnsense_helper.scripts.scripts": [[9, null]], "opnsense_helper.utils": [[10, null]], "opnsense_helper.utils.baseclass": [[11, null]], "opnsense_helper.utils.exec_class": [[12, null]], "opnsense_helper.utils.frontend_utils": [[13, null]], "opnsense_helper.utils.utils": [[14, null]], "pip": [[0, "pip"]], "required variables": [[0, "required-variables"]], "scripts": [[15, "scripts"]], "scripts and commands": [[0, "scripts-and-commands"]], "usage": [[0, "usage"]]}, "docnames": ["README", "index", "opnsense_helper/opnsense_helper", "opnsense_helper/opnsense_helper.commands", "opnsense_helper/opnsense_helper.commands.commands", "opnsense_helper/opnsense_helper.config_manager", "opnsense_helper/opnsense_helper.config_manager.config_manager", "opnsense_helper/opnsense_helper.opnsense_helper", "opnsense_helper/opnsense_helper.scripts", "opnsense_helper/opnsense_helper.scripts.scripts", "opnsense_helper/opnsense_helper.utils", "opnsense_helper/opnsense_helper.utils.baseclass", "opnsense_helper/opnsense_helper.utils.exec_class", "opnsense_helper/opnsense_helper.utils.frontend_utils", "opnsense_helper/opnsense_helper.utils.utils", "python_package"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["README.md", "index.rst", "opnsense_helper/opnsense_helper.rst", "opnsense_helper/opnsense_helper.commands.rst", "opnsense_helper/opnsense_helper.commands.commands.rst", "opnsense_helper/opnsense_helper.config_manager.rst", "opnsense_helper/opnsense_helper.config_manager.config_manager.rst", "opnsense_helper/opnsense_helper.opnsense_helper.rst", "opnsense_helper/opnsense_helper.scripts.rst", "opnsense_helper/opnsense_helper.scripts.scripts.rst", "opnsense_helper/opnsense_helper.utils.rst", "opnsense_helper/opnsense_helper.utils.baseclass.rst", "opnsense_helper/opnsense_helper.utils.exec_class.rst", "opnsense_helper/opnsense_helper.utils.frontend_utils.rst", "opnsense_helper/opnsense_helper.utils.utils.rst", "python_package.rst"], "indexentries": {}, "objects": {"": [[2, 0, 0, "-", "opnsense_helper"]], "opnsense_helper": [[3, 0, 0, "-", "commands"], [5, 0, 0, "-", "config_manager"], [7, 0, 0, "-", "opnsense_helper"], [8, 0, 0, "-", "scripts"], [10, 0, 0, "-", "utils"]], "opnsense_helper.commands": [[15, 0, 0, "-", "commands"]], "opnsense_helper.commands.commands": [[15, 1, 1, "", "Commands"], [15, 1, 1, "", "configctl"], [15, 1, 1, "", "pluginctl"], [15, 1, 1, "", "reconfigure"]], "opnsense_helper.config_manager": [[15, 0, 0, "-", "config_manager"]], "opnsense_helper.config_manager.config_manager": [[15, 1, 1, "", "Config_Manager"], [15, 1, 1, "", "Dhcpd"], [15, 1, 1, "", "Interface"], [15, 1, 1, "", "Vlan"]], "opnsense_helper.config_manager.config_manager.Config_Manager": [[15, 2, 1, "", "close_con"], [15, 2, 1, "", "get_all"], [15, 2, 1, "", "get_conf"], [15, 2, 1, "", "get_dif"], [15, 2, 1, "", "get_item"], [15, 2, 1, "", "initialize"], [15, 2, 1, "", "put_file"], [15, 2, 1, "", "remove_items"], [15, 2, 1, "", "save"], [15, 2, 1, "", "set"]], "opnsense_helper.config_manager.config_manager.Dhcpd": [[15, 2, 1, "", "initialize"]], "opnsense_helper.config_manager.config_manager.Interface": [[15, 2, 1, "", "initialize"]], "opnsense_helper.config_manager.config_manager.Vlan": [[15, 2, 1, "", "initialize"]], "opnsense_helper.opnsense_helper": [[15, 1, 1, "", "Opnsense_Helper"]], "opnsense_helper.scripts": [[15, 0, 0, "-", "scripts"]], "opnsense_helper.scripts.scripts": [[15, 1, 1, "", "Scripts"], [15, 1, 1, "", "Wireguard"], [15, 1, 1, "", "auth"], [15, 1, 1, "", "dhcp"], [15, 1, 1, "", "dns"], [15, 1, 1, "", "filter"], [15, 1, 1, "", "firmware"], [15, 1, 1, "", "health"], [15, 1, 1, "", "interfaces"], [15, 1, 1, "", "ipsec"], [15, 1, 1, "", "netflow"], [15, 1, 1, "", "openssh"], [15, 1, 1, "", "openvpn"], [15, 1, 1, "", "routes"], [15, 1, 1, "", "shaper"], [15, 1, 1, "", "shell"], [15, 1, 1, "", "suricata"], [15, 1, 1, "", "syslog"], [15, 1, 1, "", "system"], [15, 1, 1, "", "unbound"]], "opnsense_helper.utils": [[11, 0, 0, "-", "baseclass"], [12, 0, 0, "-", "exec_class"], [13, 0, 0, "-", "frontend_utils"], [14, 0, 0, "-", "utils"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "method", "Python method"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:method"}, "terms": {"": [0, 15], "0": [0, 15], "00": [0, 15], "01": 0, "02": 0, "03": 0, "0tnitdbolveqvd9fu8wfedo3d3dy5wrutf": 0, "1": [0, 15], "10": [], "100": 0, "101": 15, "103": 0, "111": 15, "1500": 0, "16072": 0, "16384": 0, "168": 0, "1731025409": 0, "178": 15, "192": 0, "2": [0, 15], "200": 0, "237": 15, "24": 0, "244": 15, "25": 0, "3": 0, "32": 15, "38": 15, "4": [0, 15], "44": 15, "5": 0, "5jvvgoatpbaaa": 0, "6": 0, "64": 0, "7": 0, "76": 15, "80": [0, 1, 15], "84": 15, "A": 15, "If": 15, "It": 15, "No": 0, "Not": [0, 1], "The": [0, 1, 15], "There": 0, "__init__": 15, "_configur": 15, "_from": 15, "_to": [0, 15], "activ": 15, "add": [0, 15], "add_item": 0, "add_us": 15, "add_vlan": [], "address": [0, 15], "after": [0, 1, 15], "algorithm": 15, "all": [0, 1, 15], "also": [0, 15], "alwai": 15, "an": 15, "ani": 0, "anoth": 15, "ansibl": [], "answer": 0, "api": 15, "api_auth": [0, 15], "api_kei": [0, 15], "api_secret": [0, 15], "appli": [0, 1, 15], "ar": 15, "arg": 15, "argument": [0, 15], "arnl": 0, "around": [0, 1, 15], "arr": 0, "arrai": 15, "assign": [1, 15], "associ": 15, "attr": 15, "attribut": 15, "auth": [0, 1], "authent": 15, "automat": [0, 1, 15], "avail": 15, "back": 0, "backend": [0, 1], "banner": 15, "base": [0, 15], "base_class": 15, "bdist_wheel": 0, "becaus": 15, "befor": 15, "below": 15, "besid": 0, "bit": 0, "block": [], "blocklist": 15, "bool": 15, "build": 0, "c": [0, 15], "cach": 15, "call": [0, 1, 15], "can": [0, 1, 15], "captiveport": 15, "captur": 15, "carp_global_statu": 15, "carp_set_statu": 15, "case": 15, "cd": 0, "certain": 15, "certctl": 15, "chang": 0, "changelog": 15, "check": 15, "cidr": 15, "class": [0, 4, 6, 7, 9, 11, 12], "cleanup_leases4": 15, "cleanup_leases6": 15, "clearlog": 15, "client_connect": 15, "client_disconnect": 15, "clone": 0, "close_con": [0, 15], "code": 15, "collect": 0, "com": 0, "command": 1, "conf": [0, 15], "conf_path": [0, 15], "config": [1, 15], "config_manag": [1, 15], "configctl": [0, 1], "configd": 15, "configur": [0, 1, 15], "connect": 15, "contain": 15, "contribut": 1, "control": [0, 15], "convent": 15, "coonfig_manag": 1, "copi": [0, 15], "core": 0, "correspond": 15, "couldnt": 0, "crash_report": 0, "crashreport": 0, "creat": [1, 15], "cron": 15, "csv": 15, "current": [0, 15], "d": 15, "damn": 0, "data": 15, "ddnsdomainalgorithm": 15, "debug": [0, 15], "decid": 0, "def": 0, "default": 0, "defin": 0, "del_rout": 15, "delete_t": 15, "descr": [0, 15], "descript": 15, "destin": [0, 15], "detail": 15, "detect": 0, "devic": [0, 15], "device_info": 15, "dhclient": 15, "dhcp": [0, 1], "dhcpd": [0, 1], "dhcpd6": 15, "dhcpv4": 0, "diagnost": 0, "dict": 15, "dictionari": 15, "directli": 0, "directori": 15, "disabl": 15, "disconnect": 15, "dn": 1, "dnsmasq_watch": 15, "do": 0, "doc": [0, 15], "doe": 0, "doesnt": 0, "domain": 15, "done": 0, "download": 0, "download_geoip": 15, "dropalertlog": 15, "dummynet_stat": 15, "dump": 15, "dump_log": 15, "dynam": 15, "e": 15, "each": 15, "either": 15, "ejl4fiu9yfnk": 0, "element": 15, "empti": 15, "enabl": [0, 1, 15], "error": 0, "everi": 0, "exampl": 15, "exec_class": 15, "execut": 15, "exist": 0, "expir": 0, "export_detail": 15, "f": 15, "fals": [0, 15], "fe80": 0, "fetchdata": 15, "ffrom": [], "file": [0, 15], "filepath": 0, "filter": 1, "find": [0, 15], "find_table_refer": 15, "firewal": [0, 15], "firmwar": 1, "first": [], "flag": [0, 15], "flowctl_stat": 15, "flowd_aggreg": 15, "flowd_aggregate_metadata": 15, "flush": 15, "flush_al": 15, "flush_rrd": 15, "follow": 15, "forc": 0, "fork": 0, "format": 15, "found": [0, 15], "fozldqy92": 0, "framework": [], "from": [0, 15], "fron": [], "frontend": 1, "function": [13, 14, 15], "g": 15, "gaqmpk": 0, "gatewai": [0, 15], "gateway_statu": 15, "gateway_watch": 15, "gen_keypair": 15, "gener": 15, "generate_cert": 15, "get": 15, "get_al": 15, "get_backup": 15, "get_conf": [0, 15], "get_dif": 15, "get_item": 15, "get_kea_leas": 15, "get_leas": 15, "get_leases6": 15, "get_legacy_vti": 15, "get_timeseri": 15, "get_top_usag": 15, "git": 0, "github": 0, "given": [0, 15], "god": 0, "ha": 0, "halt": 15, "have": [0, 15], "health": [1, 15], "hmac": 15, "hook": 15, "host": [0, 15], "hostnam": 15, "how": [], "howev": 0, "i": [0, 1, 15], "id": 15, "identifi": 15, "if_reg": 15, "ifconfig": 15, "ifctl": 15, "im": 15, "import": [0, 15], "info": 15, "inherit": 15, "init": [0, 15], "init_config_manag": 15, "initi": [0, 15], "instal": [1, 15], "installrul": 15, "instanc": 15, "instanti": 15, "int": [], "interfac": [0, 1], "invok": 15, "ip": 15, "ipaddr": 15, "ipfw": 15, "ipsec": 1, "ipv4": [0, 15], "ipv6": 0, "item": [0, 15], "its": [0, 15], "just": [0, 15], "kea": 15, "kei": 15, "kill_sess": 15, "kill_stat": 15, "kill_tabl": 15, "lan": [0, 1], "latest": 15, "launcher": 15, "let": [0, 15], "lib": 15, "librari": 15, "libvirt": 0, "licens": 15, "line": 0, "link": 0, "linkdown": 15, "linkup": 15, "list": 15, "list_appl": 15, "list_arp": 15, "list_group_memb": 15, "list_leas": 15, "list_macdb": 15, "list_ndp": 15, "list_osfp": 15, "list_pfsync": 15, "list_rule_id": 15, "list_sad": 15, "list_sockstat": 15, "list_spd": 15, "list_stat": 15, "list_statu": 15, "list_tabl": 15, "listalertlog": 15, "listinstallableruleset": 15, "listreport": 15, "listrulemetadata": 15, "lo0": 0, "load": 0, "local": [0, 15], "localhost": 0, "locat": 15, "lock": 15, "lockout_handl": 15, "log": 0, "log_arch": 15, "logformat": 15, "logger": 15, "loginterfac": 0, "logloc": 0, "look": [], "m": 0, "mac": 15, "macinfo": 15, "made": 0, "mai": 0, "main": 0, "make": 0, "matter": 0, "md": 15, "me": 0, "memori": 15, "messag": [0, 15], "metadata": 15, "method": [0, 15], "modul": [2, 3, 5, 8, 10], "monit": 15, "motiv": 1, "mpd": 15, "mself": 0, "mtu": 0, "must": 15, "my": 0, "myservic": 15, "n": 0, "name": 15, "nameserv": 15, "necessari": 15, "need": [0, 15], "netflow": 1, "netif": 0, "network": 0, "nhop": 0, "none": [0, 15], "notat": 15, "null": 0, "object": 15, "onc": [0, 1], "one": 0, "onsense_help": 15, "openssh": 1, "openvpn": 1, "oper": 15, "opnsense_help": [0, 1], "opt": 15, "opt1": 0, "opt2": 0, "opt3": 0, "opt4": 0, "option": 15, "other": [0, 1], "out": 15, "output": [0, 15], "ovpn_ev": 15, "ovpn_service_control": 15, "ovpn_statu": 15, "packag": [0, 15], "param": [], "paramet": 15, "parent": 15, "parentinterfac": 15, "pars": [0, 15], "pass": 15, "passw": [0, 15], "password": 15, "path": 15, "pcp": [0, 15], "pfstatist": 15, "pftablecount": 15, "pftop": 15, "phisic": 0, "php": [0, 15], "phy": [0, 1], "pid": 0, "ping": 15, "pip": [1, 15], "pleas": [], "plugin": 15, "pluginctl": [0, 1], "pod": 0, "point": 15, "portprob": 15, "ppp": 15, "prefix": 15, "primari": 15, "print": 15, "prioriti": 15, "problem": 0, "product": 15, "project": 0, "properti": 15, "proto": 0, "protocol": 15, "provid": [0, 15], "pull": 0, "put": 15, "put_fil": [0, 15], "py": [0, 15], "pypi": 0, "python": 0, "python3": 0, "queri": 15, "query_dn": 15, "queryalertlog": 15, "queryinstalledrul": 15, "querylog": 15, "question": 0, "r": 15, "rang": 15, "raw": 15, "rc": 0, "read": [0, 15], "read_log": 15, "realli": 0, "reboot": 15, "reconfigur": [0, 1], "reconfigure_lagg": 15, "reconfigure_neighbor": 15, "reconfigure_vip": 15, "reconfigure_vlan": 15, "regist": 15, "registr": 15, "reinstal": 15, "remain": 15, "remot": 15, "remote_backup": 15, "remov": 15, "remove_item": [0, 15], "renam": 15, "repli": 0, "repo": 0, "request": 0, "requir": [1, 15], "reresolv": 15, "restart": 15, "restor": 15, "restore_db": 15, "resync": 15, "retriev": 15, "return": 15, "rfc5246_cipher_suit": 15, "right": 0, "rn": 15, "rollback_cancel": 15, "rollback_tim": 15, "root": 0, "rout": [0, 1], "router": 0, "rqia15f1yx1snikgciel2qnojwhbekrawie0anryceh9hey5ifgzlf3da4yj": 0, "rrd_pfstate_info": 15, "rtsold_resolvconf": 15, "rule": [0, 15], "rule_stat": 15, "run": [0, 15], "run_unittest": 15, "runtim": [0, 1], "saddelet": 15, "same": 15, "save": [0, 15], "script": 1, "secur": 15, "see": 15, "self": 15, "servic": [0, 15], "service_dump": 15, "set": [0, 15], "set_vlan": [], "setaddr": 15, "setport": 15, "setup": [0, 15], "sh": [0, 15], "shaper": 1, "shell": 1, "shh": [0, 1], "shop": 0, "should": 15, "show_rout": [0, 15], "sinc": 15, "singl": 0, "snippet": 0, "so": [0, 15], "soon": 0, "spddelet": 15, "specif": 15, "specifi": 15, "spoof": 15, "spoofmac": 15, "ssh": [0, 15], "ssh_auth": [0, 15], "ssh_queri": 15, "ssl": [0, 15], "ssl_cipher": 15, "start": [0, 15], "stat": 15, "statist": 15, "statu": [0, 15], "statuscod": 0, "storag": 15, "store": 15, "str": [0, 15], "string": 15, "stuff": [0, 1], "subel": 15, "subnet": 15, "subr": 15, "support": 0, "sure": [0, 15], "suricata": 1, "sync": 15, "syntax": 0, "sysctl": 15, "syslog": 1, "system": [0, 1], "t6srlmkd1": 0, "tag": [0, 15], "take": 15, "target": 15, "technic": 15, "temp": 15, "temp_path": [0, 15], "temperatur": 15, "templat": 15, "temporari": 15, "terraform": 0, "thei": 15, "them": 0, "thi": [0, 15], "those": 15, "timestamp": 0, "tls_verifi": 15, "tmp": 0, "todo": [], "tracerout": 15, "traffic_stat": 15, "traffic_top": 15, "transfer": 15, "tree": 15, "trigger_config_changed_ev": 15, "true": [0, 15], "try": [], "type": 15, "u": 0, "ubound": [], "ug": 0, "uh": 0, "ui": 0, "unbound": 1, "unbound_watch": 15, "unlock": 15, "up": 15, "updat": 15, "update_bogon": 15, "update_t": 15, "updown_ev": 15, "upgrad": [0, 15], "uptim": 15, "us": [0, 1, 15], "usag": [1, 15], "user": [0, 15], "user_pass_verifi": 15, "using_api": 0, "usr": 0, "valu": 15, "var": [], "variabl": 1, "variou": 15, "verbos": [0, 15], "verifi": [0, 15], "via": [0, 1, 15], "virtual": [], "vlan": [0, 1], "vlan0": [0, 15], "vlan1": 0, "vlan2": 0, "vlan3": 0, "vlanif": [0, 15], "vlans_api": 0, "vm": 0, "vtnet0": 0, "vtnet1": 0, "want": 0, "webgui": 15, "well": 0, "were": 0, "wg": 15, "wg_show": 15, "when": [0, 15], "where": 15, "which": 15, "wireguard": 1, "wrapper": 15, "written": 15, "xml": [0, 15], "yi8ka9": 0, "you": [0, 1, 15], "your": [0, 1, 15], "zf": 15}, "titles": ["about opnsense-helper", "Opnsense-Helper", "opnsense_helper", "opnsense_helper.commands", "opnsense_helper.commands.commands", "opnsense_helper.config_manager", "opnsense_helper.config_manager.config_manager", "opnsense_helper.opnsense_helper", "opnsense_helper.scripts", "opnsense_helper.scripts.scripts", "opnsense_helper.utils", "opnsense_helper.utils.baseclass", "opnsense_helper.utils.exec_class", "opnsense_helper.utils.frontend_utils", "opnsense_helper.utils.utils", "Opnsense-Helper"], "titleterms": {"about": [0, 1], "api": [0, 1], "arg": [], "assign": 0, "auth": 15, "baseclass": 11, "cla": 15, "class": 15, "command": [0, 3, 4, 15], "config": 0, "config_manag": [0, 5, 6], "configctl": 15, "contribut": 0, "coonfig_manag": 15, "creat": 0, "default": 15, "dhcp": 15, "dhcpd": 15, "dn": 15, "dokument": 1, "exampl": 0, "exec_class": 12, "filter": 15, "firmwar": 15, "frontend": 0, "frontend_util": 13, "heatlh": 15, "helper": [0, 1, 15], "instal": 0, "interfac": 15, "ipsec": 15, "manual": 0, "modul": 0, "motiv": 0, "netflow": 15, "object": 0, "openssh": 15, "openvpn": 15, "opnsens": [0, 1, 15], "opnsense_help": [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], "param": 15, "pip": 0, "pluginctl": 15, "python": 1, "reconfigur": 15, "req": 15, "requir": 0, "result": 0, "rout": 15, "script": [0, 8, 9, 15], "shaper": 15, "shell": 15, "suricata": 15, "syslog": 15, "system": 15, "unbound": 15, "usag": 0, "util": [10, 11, 12, 13, 14], "variabl": 0, "vlan": 15, "wireguard": 15}}) \ No newline at end of file diff --git a/.docs/index.rst b/.docs/index.rst index bc25301..a195cab 100644 --- a/.docs/index.rst +++ b/.docs/index.rst @@ -1,7 +1,7 @@ Opnsense-Helper ========================================== -**The backend framework for opnsense** +**The backend api for opnsense** * create, assign and enable lan / phy interfaces and all the other stuff that is ***Not enabled*** in the opnsense api diff --git a/.docs/python_package.rst b/.docs/python_package.rst index a1249fc..9751ad9 100644 --- a/.docs/python_package.rst +++ b/.docs/python_package.rst @@ -9,7 +9,15 @@ Api Docs for Opnsense_Helper pip package. :undoc-members: :show-inheritance: +coonfig_manager +========================================== +Config_Manager class lets you assign configurations, by passing arrays of the corresponding Instances. +Config_Manager will parse those objects to xml and automatically reconfigure your devices. +.. automodule:: opnsense_helper.config_manager.config_manager + :members: + :undoc-members: + :show-inheritance: commands ========================================== diff --git a/README.md b/README.md index 506d799..36ce7c5 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,15 @@

+
    diff --git a/.docs/_build/html/index.html b/.docs/_build/html/index.html index f071f00..977ba55 100644 --- a/.docs/_build/html/index.html +++ b/.docs/_build/html/index.html @@ -51,6 +51,7 @@

    Python API-Dokumentation

    @@ -81,7 +82,7 @@

    Opnsense-Helper

    -

    The backend framework for opnsense

    +

    The backend api for opnsense

    • create, assign and enable lan / phy interfaces and all the other stuff that is *Not enabled* in the opnsense api

    • use the config_manager to apply all your configs in runtime at once

    • @@ -116,6 +117,13 @@

      Opnsense-HelperOpnsense_Helper

    +
  • coonfig_manager +
  • commands
    • Commands
    • configctl
    • diff --git a/.docs/_build/html/objects.inv b/.docs/_build/html/objects.inv index 95ec5623a8c70675c6e47394b2eb4557b7bb7db9..1645428ed84b1f28b4a55361e1951f53a3d317c9 100644 GIT binary patch delta 742 zcmVuV@BRLNFy*~iDq*Pmqy^=QYNQVLrOYZ$(N`q9V_$mL>%x}&laF3~ zMysShPGnhPkr@!QNeZS9%cz4bq(~Jee{oLXu=V#Am2hkfx_=vlhS7)=JC+m6L41WGsz-}+pmz#^uyd}EMUlT4}ri-a( zM4dvmlvGY_iaEcH&@tjRN*TM65VN#gUWo~28RqOCU0zF7Hr+9P10_vMxu^9Kn&s6D z-rrzOhS~*09)Aj=viQeNja_zgJqb(o4ig&R{TaRN41RMg;+sEG=lYg2S~qAb*T4SR zT>m(0Ll?*p&6I^Tho_uTyG`7X2Cy^eq1G=KdC#5rdPtLm>)B zL|G$OitMcY)gtNg6;JDM`>cXu9%s2(bBaY-Ev5`5F@HdU+8vj#F;G&uQB%Z1?Ujgu z(z3E~*4z|W#o3|8-RDt2m1YE69EEi-T;`ETr*3&JsS;aRiAl$1Wf_OnoH}E)O!%lR zcK?61Be=pjH&BbdQIbv-yCesy?X+|!mB`Ym3)lq&s!kOf*7)`K?P2qXjLq&dNzm#J z`8#zW!hdd0*)xCTc}Y$M6B&K0vpC*Te#~p``pl=}*W?m*g0~`?c4_6?rNo&UpsK^F z8SrTvKWon)nR#0|E-r^I8XQW1br7!IEum;6 zD1nwrxS?K|MPpIPlv)b6HBgIDNMr)2zHnEgX1s@1l^iLb&vj4Ot)ksNv2AjogM3%F Y4aJ%iU0Hw6$G`aTX9r0B1I;W`37u7JiU0rr delta 622 zcmV-!0+IdK2H*vdeSfW&&2HQv6ovOZ1yZy1wAp4?wUOFI)F@eWX)zevs2C7GP3G;( zn0Ur^oSJJRMVc7y{mwB4E+{Q63x8$Hgk}co(_N=3FBtxU)wJ*P?D^2TPJD{d#SJdd zyq~JBBeJp}=#Z35UzX7)*D6_RLjRD4!C{AQBPtcdT70_*O@E_NC2QVLC-UI5@C}#Q zaE3*H-mq=Jb8;D2A+&sU z)$S0tqyb!%Gt}Yviro0dm*3yqu*MTp<$w|I+8ms!r8)~oW*MWJ3%6^2jYy_@A=@^W zIjdzv>Z~Eo(g8fBpV&kFBfjDt|?Baf@S1ABahqQ|`hqA`dl8 za#^w0K1+}z6X(34HD>-i|BJ3+Z}3sXa9k?laR~)e3sik7hK`u_5Z^lTSKfTAoN{NH z2cgtE5kzQmkHR1ICQI-0kY)zrYI5)gS$cQS$N!H(yRH&DyuqzPo$r{(Df9XOo#(@y z0pMeNEoZ?Wbf)U=(NFP_>?iKM02kLy7cX`pU>$^OPfI9X2}00P2{$w=vv@5EZK
    - opnsense_helper.config_manager.config_manager + opnsense_helper.config_manager.config_manager
Static Badge + + Static Badge + +
-***The backend framework for opnsense*** +***The backend api for opnsense*** - create, assign and enable lan / phy interfaces and all the other stuff that is ***Not enabled*** in the opnsense api - use the config_manager to apply all your configs in runtime at once @@ -24,18 +29,23 @@ - around 80 opnsense scripts you can call - automatically configures your Vlan and Phy Interfaces after applying configuration +--- + +- ***[pypy project](https://pypi.org/project/opnsense-helper)*** +- ***[Api Docs](https://ji-podhead.github.io/opnsense-helper/.docs/_build/html/index.html)*** + +--- ## install -### pip + +### pip ```bash pip install opnsense-helper ``` + ## usage + > - you can run the provided snippets directly by pulling the [example file](https://github.com/the-pod-shop/opnsense-helper/blob/main/python/examples/add_vlans.py) -> - Please also have a look at: -> - the [Api Docs](https://ji-podhead.github.io/opnsense-helper/.docs/_build/html/index.html) -> - the corresponding [pip package](https://pypi.org/project/opnsense-helper/) -> - and soon also the Ansible collection. ### required variables * import the package and define the needed variables for the main class @@ -205,6 +215,3 @@ python setup.py bdist_wheel \ - my opnsense runs in a vm, so it really doesnt matter for me - i just add the interfaces via libvirt and all i need to do is to enable them, given the /conf/config.xml method xml has the answer. -- however for phisical interfaces its the god damn conf.rc -- my opnsense runs in a vm, so it really doesnt matter for me -- i just add the interfaces via libvirt and all i need to do is to enable them, given the /conf/config.xml method diff --git a/python/opnsense_helper/commands/commands.py b/python/opnsense_helper/commands/commands.py index 8b2ed84..a5fa275 100644 --- a/python/opnsense_helper/commands/commands.py +++ b/python/opnsense_helper/commands/commands.py @@ -1,8 +1,16 @@ from opnsense_helper.utils.exec_class import Exec_Class class Commands(): """ - Class Commands + + class Commands -------------- + + **Usage** + + .. code-block:: python + + from opnsense_helper.opnsense_helper import Onsense_Helper + Onsense_Helper.commands..run(,,) Initialize the Commands class. * its just a wrapper for the pluginctl, configctl and reconfigure classes. @@ -23,27 +31,37 @@ def __init__(self,base): self.pluginctl=pluginctl(base) class reconfigure(Exec_Class): """ - Class reconfigure + + class reconfigure ---------------- * Initialize the reconfigure class. * Inherits from the Exec_Class class. **Usage** - .. code-block:: python - Commands.reconfigure.run(,,) + + .. code-block:: python + + from opnsense_helper.opnsense_helper import Onsense_Helper + Commands.reconfigure.run(,,) + **Parameters** + base : Base_Class instance The parent object containing necessary SSH connection details and configuration settings. If provided, its attributes will be copied to this instance. **Attributes** - commands : dict - A dictionary of command configurations with keys for 'vlans' and 'interfaces', - each containing command details such as the command string, arguments, - and flags. + + commands : dict + A dictionary of command configurations with keys for 'vlans' and 'interfaces', + each containing command details such as the command string, arguments, + and flags. + + - vlans : Execute reconfigure_vlans.php + - interfaces : Execute configctl interface reconfigure """ def __init__(self, base): @@ -59,7 +77,65 @@ def __init__(self, base): } class configctl(Exec_Class): + """ + + class configctl + ---------- + + * Initialize the configctl class. + * Inherits from the Exec_Class class. + + **Usage** + + .. code-block:: python + + from opnsense_helper.opnsense_helper import Onsense_Helper + Commands.configctl.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details and + configuration settings. If provided, its attributes will be copied + to this instance. + + **Attributes** + + - commands : dict + A dictionary of command configurations with keys for configctl options, + each containing command details such as the command string, arguments, + and flags. + + - auth : Execute configctl auth command + - captiveportal : Execute configctl captiveportal command + - configd : Execute configctl configd command + - cron : Execute configctl cron command + - dhcpd : Execute configctl dhcpd command + - dhcpd6 : Execute configctl dhcpd6 command + - dns : Execute configctl dns command + - filter : Execute configctl filter command + - firmware : Execute configctl firmware command + - health : Execute configctl health command + - ids : Execute configctl ids command + - interface : Execute configctl interface command + - ipfw : Execute configctl ipfw command + - ipsec : Execute configctl ipsec command + - kea : Execute configctl kea command + - monit : Execute configctl monit command + - netflow : Execute configctl netflow command + - openssh : Execute configctl openssh command + - openvpn : Execute configctl openvpn command + - syslog : Execute configctl syslog command + - system : Execute configctl system command + - template : Execute configctl template command + - unbound : Execute configctl unbound command + - webgui : Execute configctl webgui command + - wireguard : Execute configctl wireguard command + - zfs : Execute configctl zfs command + """ def __init__(self, base): + + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -100,8 +176,51 @@ def __init__(self, base): "wireguard":{"command":"configctl wireguard", "argument":None,"flags":[]}, "zfs":{"command":"configctl zfs", "argument":None,"flags":[]}, } + class pluginctl(Exec_Class): + """ + class pluginctl + --------------- + + Initializes the pluginctl class, inheriting from Exec_Class. If a base object + is provided, its attributes are copied to the current instance. This + initializer also sets up a dictionary of command configurations for various + pluginctl operations. + + **Usage** + + .. code-block:: python + + from opnsense_helper.opnsense_helper import Onsense_Helper + Commands.pluginctl.run(,,) + + **Attributes:** + + commands : dict + A dictionary where each key is a command name and the value is another dictionary containing: + - argument : str + The argument to be passed to the command. This is currently always None. + - flags : list + A list of flags for the command. Currently, these are empty. + - command : str + The path to the script associated with the command. + + The commands are as follows: + - ipv4: pluginctl -4, returns primary address of interface + - config: pluginctl -c, executes plugin [_configure] hook + - ifconfig: pluginctl -D, lists available devices + - device_info: pluginctl -d, lists registered devices + - flush: pluginctl -f, flushes config property (raw, e.g. system.firmware.plugins) + - get: pluginctl -g, get config property (raw, e.g. system.firmware.plugins) + - info: pluginctl -I, lists registered device statistics + - if_reg: pluginctl -i, invokes dynamic interface registration + - run: pluginctl -r, runs a command (e.g. myservice restart) + - service_dump: pluginctl -S, dumps service metadata + - service: pluginctl -s, executes service command (e.g. myservice restart) + """ def __init__(self, base): + + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) diff --git a/python/opnsense_helper/config_manager/config_manager.py b/python/opnsense_helper/config_manager/config_manager.py index e04a39a..487a7a3 100644 --- a/python/opnsense_helper/config_manager/config_manager.py +++ b/python/opnsense_helper/config_manager/config_manager.py @@ -3,35 +3,47 @@ from xmldiff import main, formatting from opnsense_helper.utils.utils import parseChild, update_xml_file class Interface: - """Creates a Interface object""" - def __init__(self, id=None, descr =None, interface=None, enable=None, ipaddr=None, subnet="32", spoofmac=None): - """Creates a Interface object - The ID is the technical identifier. - This is required and you need to find out which "opt" to use, - because rn there is no function that provides that. - The interface key in the xml are the identifiers, in this case the class id`s. - Im not sure if they have a certain naming convention + """ + class Interface + -------------- + + Creates a Interface object + The ID is the technical identifier. + This is required and you need to find out which "opt" to use, + because rn there is no function that provides that. + The interface key in the xml are the identifiers, in this case the class id`s. + Im not sure if they have a certain naming convention + + **Usage** + + .. code-block:: python - Required Args - ---------- - id : str - the technical identifier - interface : str - target interface - enable : str - 1, or 0 for disable or disable - descr : str - description, or name - - Defaults - ---------- - subnet : str - CIDR notation, default="32" - ipaddr : str - the ipv4 address, default=None - spoofmac: str - the spoofed mac, default=None - """ + + from opnsense_helper.config_manager.config_manager import Interface + interface=Interface(id="vlan", descr="vlan", interface="vlan", enable="1", ipaddr="38.0.101.76", subnet="32", spoofmac="00:00:00:00:00:00") + + **Required Args** + + - id : str + the technical identifier + - interface : str + target interface + - enable : str + 1, or 0 for disable or disable + - descr : str + description, or name + + **Defaults** + + - subnet : str + CIDR notation, default="32" + - ipaddr : str + the ipv4 address, default=None + - spoofmac: str + the spoofed mac, default=None + """ + def __init__(self, id=None, descr =None, interface=None, enable=None, ipaddr=None, subnet="32", spoofmac=None): + self.id=id self.interface=interface self.enable=enable @@ -57,26 +69,39 @@ def initialize(self, parent): self.spoofmac=parseChild(parent, "spoofmac") class Vlan: - """Creates a vlan object""" - - def __init__(self, id=None, parentinterface=None, tag=None, vlanif= None, pcp= '0', descr=None ): - """Creates a vlan object - Required Args: + """ + class Vlan ---------- - id: str + + Creates a vlan object + **Usage** + + .. code-block:: python + + + from opnsense_helper.config_manager.config_manager import Vlan + vlan=Vlan(id="vlan", tag="1", pcp="1", descr="vlan", vlanif="vlan0.1") + + + + **Required Args** + + - id: str the identifier needed for storage, since the technical identifier is always the same ("vlan") - tag: str + - tag: str the tag of the vlan - Defaults: - ---------- - pcp: str + **Defaults** + + - pcp: str the priority code point, default = 0 - descr: str + - descr: str the description, default = $id - vlanif: str + - vlanif: str the interface, default = "vlan0."+tag """ + def __init__(self, id=None, parentinterface=None, tag=None, vlanif= None, pcp= '0', descr=None ): + self.id=id self.parentinterface=parentinterface self.pcp = pcp @@ -95,25 +120,37 @@ def initialize(self,parent): class Dhcpd: - """Creates a dhcp object""" - def __init__(self,id=None,enable=None,range=None,ddnsdomainalgorithm="hmac-md"): - """Creates a dhcp object - Req params - ---------- + """ + class Dhcpd + ---------- - id : str - the id of the object - enable : str - "1" or "0" - range: dict{_from:str,_to:str} - the range of ip addresses - - Defaults - --------- + Creates a dhcp object + + **Usage** + + .. code-block:: python + + from opnsense_helper.config_manager.config_manager import Dhcpd + dhcpd=Dhcpd(id="vlan", enable="1", range={"_from":"237.84.2.178","_to":"244.178.44.111"}) + + **Req params** + ---------- + + id : str + the id of the object and identifier of the corresponding interface + enable : str + "1" or "0" + range: dict{_from:str,_to:str} + the range of ip addresses + + Defaults + --------- + + ddnsdomainalgorithm : str + Domain Generation Algorithm - default = "hmac-md" + """ + def __init__(self,id=None,enable=None,range=None,ddnsdomainalgorithm="hmac-md"): - ddnsdomainalgorithm : str - Domain Generation Algorithm - default = "hmac-md" - """ self.id=id self.enable=enable self._range=range @@ -137,11 +174,12 @@ class Config_Manager(): """Creates a Config_Manager object""" def __init__(self, base, init): """Creates a Config_Manager object - Params - ------ - base : Base_Class instance + + **Params** + + - base : Base_Class instance includes the need objects for ssh and stores the objects - init : bool + - init : bool if True, the xml will get parsed when the Config_Manager object is created """ @@ -179,12 +217,12 @@ def save(self,output,put=True): file specified by the output parameter. The file is written in XML format. - Parameters - ---------- - output : str + **Parameters** + + - output : str The path to the file to which the configuration should be written. default: self.temp_path - put: bool + - put: bool Automatically copies the configuration to the firewall """ output= output if output is not None else self.temp_path @@ -213,15 +251,15 @@ def get_all(self,element): of the elements, and the values are dictionaries containing the attributes and subelements of the elements. - Parameters - ---------- - element : str + **Parameters** + + - element : str The type of the objects to retrieve. Can be "dhcpd", "interfaces", or "vlans". - Returns - ------- - dict + **Returns** + + - dict A dictionary containing all objects of the given type. """ print(f''' ----------------------------- @@ -261,12 +299,12 @@ def put_file(self, _from=None,_to=None): Apply the configuration. Transfer a file from the local host to the remote host. - Parameters - ---------- - _from : str + **Parameters** + + - _from : str The path to the file on the local host. default: self.temp_path - _to : str + - _to : str The destination path on the remote host where the file should be stored. default: self.conf_path """ @@ -299,16 +337,16 @@ def get_item(self,type,item): This method retrieves an item of a specified type from the in-memory XML data, removes its 'attr' attribute, and returns the remaining attributes of the item. - Parameters - ---------- - type : str + **Parameters** + + - type : str The type of the item to retrieve. Can be "dhcpd", "interfaces", or "vlans". - item : str + - item : str The tag of the item to retrieve. - Returns - ------- - dict + **Returns** + + - dict A dictionary containing the remaining attributes of the item after removing the 'attr' attribute. If the item is not found, prints a message and returns None. """ @@ -328,19 +366,19 @@ def set(self,type, data): This method adds all objects given in the data parameter to the in-memory xml data. The objects are stored in the self.objects dictionary. - Parameters - ---------- - type : str + **Parameters** + + - type : str The type of the objects to add. Can be "dhcpd", "interfaces", or "vlans". - data : dict + - data : dict A dictionary containing the objects to add. The keys are the tags of the elements, and the values are dictionaries containing the attributes and subelements of the elements. - Returns - ------- + **Returns** + None """ for value in data: @@ -353,8 +391,8 @@ def get_conf(self,_from= None,_to=None): """ Get the config file from the remote host and save it to a file. - Parameters - ---------- + **Parameters** + _from : str The path to the file on the remote host. _to : str diff --git a/python/opnsense_helper/opnsense_helper.py b/python/opnsense_helper/opnsense_helper.py index 90ccccc..ee4d1c0 100644 --- a/python/opnsense_helper/opnsense_helper.py +++ b/python/opnsense_helper/opnsense_helper.py @@ -2,18 +2,49 @@ from opnsense_helper.utils.baseclass import Base_Class from opnsense_helper.utils import utils class Opnsense_Helper(): + """ + class Opnsense_Helper + ----------- + Initialize an Opnsense_Helper instance. - :param host: The hostname or ip address of the opnsense firewall. - :param ssh_auth: A dictionary containing the ssh authentication data. - The dictionary must contain the keys "user" and "passw". - :param api_auth: A dictionary containing the api authentication data. - The dictionary must contain the keys "api_key", "api_secret", "ssl" and "verify". - :param conf_path: The path to the config.xml on the opnsense firewall. - :param temp_path: The path to the temporary config.xml file. - :param verbose: If True, the class will print debug messages. - :param init_config_manager: If True, the class will initialize the config_manager. + **Usage** + see example.py + + .. code-block:: python + + from opnsense_helper.opnsense_helper import Opnsense_Helper + from opnsense_helper.config_manager.config_manager import Vlan, Dhcpd, Interface + helper=Opnsense_Helper(host=host,ssh_auth=auth,temp_path=temp_path, init_config_manager=True) + + **Parameters** + + - host : str + The hostname or ip address of the opnsense firewall. + - ssh_auth : dict + A dictionary containing the ssh authentication data. + The dictionary must contain the keys "user" and "passw". + - api_auth : dict + A dictionary containing the api authentication data. + The dictionary must contain the keys "api_key", "api_secret", "ssl" and "verify". + - conf_path : str + The path to the config.xml on the opnsense firewall. + - temp_path : str + The path to the temporary config.xml file. + - verbose : bool + If True, the class will print debug messages. + - init_config_manager : bool + If True, the class will initialize the config_manager. + + **Attributes** + + - config_manager : Config_Manager + An instance of the Config_Manager class. + - commands : Commands + An instance of the Commands class. + - scripts : Scripts + An instance of the Scripts class. """ def __init__(self, host=None, ssh_auth=None, api_auth=None, conf_path="/conf/config.xml", temp_path="./config.xml", verbose=False, init_config_manager=True): diff --git a/python/opnsense_helper/scripts/scripts.py b/python/opnsense_helper/scripts/scripts.py index 4bcd359..06a7f1a 100644 --- a/python/opnsense_helper/scripts/scripts.py +++ b/python/opnsense_helper/scripts/scripts.py @@ -4,27 +4,49 @@ scripts_folder="/usr/local/opnsense/scripts/" class Scripts(): - """🐕""" + """ + class Scripts + ------------ + + Initialize the Scripts class. + * its just a wrapper for all the opnsense scripts classes. see attributes below. + + **Usage** + + .. code-block:: python + + Onsense_Helper.scripts..run(,,) + + **Parameters** + + base : Base_Class + * The parent object containing the needed ssh connection and the temp path to the config.xml. + + **Attributes** + + * unbound : unbound instance + * system : system instance + * syslog : syslog instance + * suricata : suricata instance + * shell : shell instance + * shaper : shaper instance + * routes : routes instance + * openvpn : openvpn instance + * openssh : openssh instance + * netflow : netflow instance + * ipsec : ipsec instance + * interfaces : interfaces instance + * health : health instance + * firmware : firmware instance + * filter : filter instance + * dns : dns instance + * dhcp : dhcp instance + * auth : auth instance + * Wireguard : Wireguard instance + """ def __init__(self,base): - """ - A class to represent a person. - - ... - - Attributes - ---------- - name : str - first name of the person - surname : str - family name of the person - age : int - age of the person - - Methods - ------- - info(additional=""): - Prints the person's name and age. - """ + + self.unbound=unbound(base) self.system=system(base) self.syslog=syslog(base) @@ -47,27 +69,38 @@ def __init__(self,base): class unbound(Exec_Class): """ - Initializes the unbound class, inheriting from Exec_Class. If a base object - is provided, its attributes are copied to the current instance. This - initializer also sets up a dictionary of command configurations for various - unbound operations. - - Attributes: - commands (dict): A dictionary where each key is a command name and the - value is another dictionary containing: - - command (str): The path to the script associated with the command. - - flags (list): A list of flags for the command. Currently, these - are empty. - - Attributes: - - wrapper: Executes 'ubound/wrapper.py'. - - stats: Executes 'ubound/stats.py'. - - start: Executes 'ubound/start.sh'. - - restore_db: Executes 'ubound/restore_db.py'. - - logger: Executes 'ubound/logger.py'. - - check: Executes 'ubound/check.sh'. - - cache: Executes 'ubound/cache.sh'. - - blocklists: Executes 'ubound/blocklists.py'. + class unbound + ---------- + + * Initializes the unbound class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.unbound.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details + + **Attributes** + + - commands : dict + A dictionary of command configurations with keys for configctl options, + each containing command details such as the command string, arguments, + and flags. + + scripts: + * wrapper: executes wrapper.py + * stats: executes stats.py + * start: executes start.sh + * restore_db: executes restore_db.py + * logger: executes logger.py + * check: executes check.sh* + * cache: executes cache.sh* + * blocklists: executes blocklists.py """ def __init__(self, base): @@ -87,7 +120,44 @@ def __init__(self, base): "blocklists":{ "command":scripts_folder+"ubound/blocklists/","flags":[]}, } class system(Exec_Class): + """ + class system + ---------- + + Initializes the system class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.system.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes** + + - commands : dict + A dictionary of command configurations for system operations, + each containing command details such as the command string and flags. + + scripts: + * trigger_config_changed_events: executes trigger_config_changed_events.py + * temperature: executes temperature.sh + * sysctl: executes sysctl.py + * status: executes status.php + * ssl_ciphers: executes ssl_ciphers.py + * rrd_pfstate_info: executes rrd_pfstate_info.py + * rfc5246_cipher_suites: executes rfc5246_cipher_suites.csv + * remote_backup: executes remote_backup.php + * nameservers: executes nameservers.php + * certctl: executes certctl.py + * activity: executes activity.py + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -106,7 +176,41 @@ def __init__(self, base): "activity":{"command":scripts_folder+"system/activity.py*", "flags":[]}, } class syslog(Exec_Class): + """ + class syslog + ---------- + + Initializes the syslog class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.syslog.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details and configuration settings. + + **Attributes** + + - commands : dict + A dictionary of command configurations for syslog operations, + each containing command details such as the command string and flags. + + scripts: + * queryLog: executes queryLog.py + * logformats: executes logformats/ + * log_archive: executes log_archive* + * lockout_handler: executes lockout_handler* + * list_applications: executes list_applications.php + * generate_certs: executes generate_certs* + * clearlog: executes clearlog.php + """ + def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -121,7 +225,44 @@ def __init__(self, base): "clearlog":{"command":scripts_folder+"syslog/ clearlog.php*", "flags":[]}, } class suricata(Exec_Class): + """ + class suricata + ------------- + + Initializes the suricata class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.suricata.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details and configuration settings. + + **Attributes** + + - commands : dict + A dictionary of command configurations for suricata operations + + scripts: + * setup: executes setup.sh* + * rule-updater: executes rule-updater.py* + * queryInstalledRules: executes queryInstalledRules.py* + * queryAlertLog: executes queryAlertLog.py* + * metadata: executes metadata/ + * listRuleMetadata: executes listRuleMetadata.py* + * listInstallableRulesets: executes listInstallableRulesets.py* + * listAlertLogs: executes listAlertLogs.py* + * lib: executes lib/ + * installRules: executes installRules.py* + * dropAlertLog: executes dropAlertLog.py* + * __init__: executes __init__.py* + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -141,7 +282,43 @@ def __init__(self, base): "__init__":{"command":scripts_folder+"suricata/__init__.py*", "flags":[]}, } class shell(Exec_Class): + """ + class Shell + .......... + + Initializes the shell class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.shell.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details and configuration settings. + + **Attributes** + + - commands : dict + A dictionary of command configurations for shell operations, + each containing command details such as the command string and flags. + + scripts: + * setports: executes setports.php + * setaddr: executes setaddr.php + * restore: executes restore.sh + * reboot: executes reboot.php + * ping: executes ping.php + * password: executes password.php + * halt: executes halt.php + * firmware: executes firmware.sh + * defaults: executes defaults.php + * banner: executes banner.php + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -159,7 +336,36 @@ def __init__(self, base): "banner":{"command":scripts_folder+"shell/banner.php*","flags":[]}, } class shaper(Exec_Class): + """ + class Shaper + ------------ + + Initializes the shaper class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.suricata.shaper(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes** + + - commands : dict + A dictionary of command configurations for shaper operations, + each containing command details such as the command string and flags. + + scripts: + * update_tables: executes update_tables + * lib: executes lib/ + * dummynet_stats: executes dummynet_stats.py* + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -170,7 +376,38 @@ def __init__(self, base): "dummynet_stats":{"command":scripts_folder+"shaper/dummynet_stats.py*","flags":[]}, } class routes(Exec_Class): + """ + class routes + ------------ + + Initializes the routes class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.routes.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes** + + - commands : dict + A dictionary of command configurations for routes operations, + each containing command details such as the command string and flags. + + scripts: + * show_routes: executes show_routes.py + * gateways: executes gateways.php + * gateway_watcher: executes gateway_watcher.php + * gateway_status: executes gateway_status.php + * del_route: executes del_route.py + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -183,7 +420,42 @@ def __init__(self, base): "del_route":{"command":scripts_folder+"routes/del_route.py*","flags":[]}, } class openvpn(Exec_Class): + """ + class openvpn + ------------ + + Initializes the openvpn class, inheriting from Exec_Class. + + + **Usage** + + .. code-block:: python + + Scripts.openvpn.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes** + + - commands : dict + A dictionary of command configurations for openvpn operations, + each containing command details such as the command string and flags. + + scripts: + * user_pass_verify: executes user_pass_verify.php + * tls_verify: executes tls_verify.php + * ovpn_status: executes ovpn_status.py + * ovpn_service_control: executes ovpn_service_control.php + * ovpn_event: executes ovpn_event.py + * kill_session: executes kill_session.py + * client_disconnect: executes client_disconnect.sh + * client_connect: executes client_connect.php + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -199,7 +471,34 @@ def __init__(self, base): "client_connect":{"command":scripts_folder+"openvpn/client_connect.php*", "flags":[]}, } class openssh(Exec_Class): + """ + class openssh + ------------- + + Initializes the openssh class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.openssh.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes** + + - commands : dict + A dictionary of command configurations for openssh operations, + each containing command details such as the command string and flags. + + scripts: + * ssh_query: executes ssh_query.py + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -209,7 +508,40 @@ def __init__(self, base): } class netflow(Exec_Class): + """ + class netflow + ------------- + + Initialize the netflow class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.netflow.run(,,) + + **Parameters:** + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes:** + - commands : dict + A dictionary of command configurations for netflow operations, + each containing command details such as the command string and flags. + + scripts: + - lib: executes the netflow library directory + - get_top_usage: executes get_top_usage.py script + - get_timeseries: executes get_timeseries.py script + - flush_all: executes flush_all.sh script + - flowd_aggregate_metadata: executes flowd_aggregate_metadata.py script + - flowd_aggregate: executes flowd_aggregate.py script + - export_details: executes export_details.py script + - dump_log: executes dump_log.py script + - flowctl_stats: executes flowctl_stats.py script + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -226,7 +558,42 @@ def __init__(self, base): "dump_log":{"command":scripts_folder+"netflow/dump_log.py*","flags":[]}, } class ipsec(Exec_Class): + """ + class ipsec + ------------- + + Initializes the ipsec class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.ipsec.run(,,) + + **Parameters:** + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes:** + - commands : dict + A dictionary of command configurations for ipsec operations, + each containing command details such as the command string and flags. + + scripts: + - lib: executes the ipsec library directory + - updown_event: executes updown_event.py + - spddelete: executes spddelete.py + - saddelete: executes saddelete.py + - list_status: executes list_status.py + - list_spd: executes list_spd.py + - list_sad: executes list_sad.py + - list_leases: executes list_leases.py + - get_legacy_vti: executes get_legacy_vti.php + - disconnect: executes disconnect.py + - connect: executes connect.py + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -245,7 +612,58 @@ def __init__(self, base): "connect":{"command":scripts_folder+"ipsec/connect.py*","flags":[]}, } class interfaces(Exec_Class): + """ + class interfaces + ---------------- + + Initializes the interfaces class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.interfaces.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes** + + - commands : dict + A dictionary of command configurations for interfaces operations, + each containing command details such as the command string and flags. + + scripts: + * traffic_top: executes traffic_top.py + * traffic_stats: executes traffic_stats.php + * traceroute: executes traceroute.py + * rtsold_resolvconf: executes rtsold_resolvconf.sh + * reconfigure_vlans: executes reconfigure_vlans.php + * reconfigure_vips: executes reconfigure_vips.php + * reconfigure_neighbors: executes reconfigure_neighbors.php + * reconfigure_laggs: executes reconfigure_laggs.php + * ppp-uptime: executes ppp-uptime.sh + * ppp-rename: executes ppp-rename.sh + * ppp-linkup: executes ppp-linkup.sh + * ppp-linkdown: executes ppp-linkdown.sh + * portprobe: executes portprobe.py + * ping: executes ping.py + * mpd: executes mpd.script + * macinfo: executes macinfo.py + * list_sockstat: executes list_sockstat.py + * list_ndp: executes list_ndp.py + * list_macdb: executes list_macdb.py + * list_arp: executes list_arp.py + * ifctl: executes ifctl.sh + * dhclient: executes dhclient-script + * carp_set_status: executes carp_set_status.php + * carp_global_status: executes carp_global_status.php + * capture: executes capture.py + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -278,7 +696,36 @@ def __init__(self, base): "capture":{"command": scripts_folder+"interfaces/capture.py*", "flags":[]}, } class health(Exec_Class): + """ + class heatlh + ............. + + Initializes the health class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.health.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes** + + - commands : dict + A dictionary of command configurations for health operations, + each containing command details such as the command string and flags. + + scripts: + * listReports: executes listReports.py + * flush_rrd: executes flush_rrd.py + * fetchData: executes fetchData.py + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -289,7 +736,58 @@ def __init__(self, base): "fetchData":{"command":scripts_folder+"health/fetchData.py*","flags":[]}, } class firmware(Exec_Class): + """ + clas firmware + ............. + + Initializes the firmware class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.firmware.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes** + + - commands : dict + A dictionary of command configurations for firmware operations, + each containing command details such as the command string and flags. + + scripts: + * upgrade: executes upgrade.sh + * update: executes update.sh + * unlock: executes unlock.sh + * sync.subr: executes sync.subr.sh + * sync: executes sync.sh + * security: executes security.sh + * running: executes running.sh + * resync: executes resync.sh + * remove: executes remove.sh + * reinstall: executes reinstall.sh + * register.: executes register.php + * reboot: executes reboot.sh + * read: executes read.sh + * query: executes query.sh + * product.: executes product.php + * plugin: executes plugin.sh + * lock: executes lock.sh + * license: executes license.sh + * launcher: executes launcher.sh + * latest.: executes latest.php + * install: executes install.sh + * health: executes health.sh + * connection: executes connection.sh + * check: executes check.sh + * changelog: executes changelog.sh + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -322,7 +820,53 @@ def __init__(self, base): "changelog":{"command":scripts_folder+"firmware/changelog.sh*","flags":[]}, } class filter(Exec_Class): + """ + class filter + ............. + Initializes the filter class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.filter.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details and configuration settings. + + **Attributes** + + - commands : dict + A dictionary of command configurations for filter operations + + scripts: + * update_tables: executes update_tables.py* + * update_bogons: executes update_bogons.sh* + * run_unittests: executes run_unittests.py* + * rule_stats: executes rule_stats.py* + * rollback_timer.: executes rollback_timer.php* + * rollback_cancel.: executes rollback_cancel.php* + * read_log: executes read_log.py* + * pftop: executes pftop.py* + * pftablecount: executes pftablecount.py* + * pfstatistics: executes pfstatistics.py* + * list_tables: executes list_tables.py* + * list_table: executes list_table.py* + * list_states: executes list_states.py* + * list_rule_ids: executes list_rule_ids.py* + * list_pfsync: executes list_pfsync.py* + * list_osfp: executes list_osfp.py* + * kill_table: executes kill_table.py* + * kill_states: executes kill_states.py* + * find_table_references: executes find_table_references.py* + * download_geoip: executes download_geoip.py* + * delete_table: executes delete_table.py* + """ def __init__(self, base): + + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -351,7 +895,32 @@ def __init__(self, base): "delete_table":{"commands":scripts_folder+"filter/delete_table.py*","flags":[]}, } class dns(Exec_Class): + """ + class dns + ........ + + Initializes the dns class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.dns.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes** + + - commands : dict + + scripts: + * query_dns: executes query_dns.py + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -361,7 +930,40 @@ def __init__(self, base): } class dhcp(Exec_Class): + """ + class dhcp + ........ + + Initializes the dhcp class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.dhcp.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes** + + - commands : dict + + scripts: + * unbound_watche: executes unbound_watcher.py* + * prefixe: executes prefixes.sh* + * prefixes: executes prefixes.php* + * get_leases: executes get_leases6.py* + * get_lease: executes get_leases.py* + * get_kea_lease: executes get_kea_leases.py* + * dnsmasq_watche: executes dnsmasq_watcher.py* + * cleanup_leases6: executes cleanup_leases6.php* + * cleanup_leases4: executes cleanup_leases4.php* + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -378,7 +980,35 @@ def __init__(self, base): "cleanup_leases4":{"command":scripts_folder+"dhcp/cleanup_leases4.php*","flags":[]}, } class auth(Exec_Class): + """ + class auth + ------ + + Initializes the auth class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.auth.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes** + + - commands : dict + A dictionary of command configurations for auth operations, + each containing command details such as the command string and flags. + + scripts: + * list_group_members: executes list_group_members.php + * add_user: executes add_user.php + """ def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) @@ -388,7 +1018,38 @@ def __init__(self, base): "add_user":{"command":scripts_folder+"auth/add_user.php*","flags":[]}, } class Wireguard(Exec_Class): + """ + class Wireguard + ------ + + Initializes the Wireguard class, inheriting from Exec_Class. + + **Usage** + + .. code-block:: python + + Scripts.Wireguard.run(,,) + + **Parameters** + + base : Base_Class instance + The parent object containing necessary SSH connection details. + + **Attributes** + + - commands : dict + A dictionary of command configurations for Wireguard operations, + each containing command details such as the command string and flags. + + scripts: + * wg_show: executes wg_show.py + * wg-service-control: executes wg-service-control.php + * reresolve-dns: executes reresolve-dns.py + * gen_keypair: executes gen_keypair.py + """ + def __init__(self, base): + super(Exec_Class).__init__() if base is not None: self.__dict__.update(base.__dict__) diff --git a/python/setup.py b/python/setup.py index 7b071fb..c308d66 100644 --- a/python/setup.py +++ b/python/setup.py @@ -8,7 +8,7 @@ setup( name='opnsense_helper', - version='0.1.24a', + version='0.1.25', description='backend api for opnsense. assign lan interfaces, create vlans, vlan-interfaces and setup dhcp in a single script.', long_description=long_description, long_description_content_type='text/markdown',