From 2fa991b4d95bf0c3053361afbfdaf36fd1948fa2 Mon Sep 17 00:00:00 2001 From: Leonardo Rossetti Date: Sun, 25 Sep 2022 23:19:06 +0200 Subject: [PATCH] OVS Traffic Mirroring Proposal (#227) * add traffic mirroring proposal Signed-off-by: Leonardo Rossetti * Add pros/cons + implementation details + question sections Signed-off-by: Stefano Cappa * update proposal based on the discussion Signed-off-by: Stefano Cappa * fix doc titles Signed-off-by: Stefano Cappa * feat: merge ovs-mirror repo in ovs-cni with mirrorUuid fix + golang generics Signed-off-by: Stefano Cappa * fix: invert ingress and egress boolean passed to CheckMirrorProducerWithPorts function Signed-off-by: Stefano Cappa * feat: upgrade go to 1.18 also in hack folder to be able to use it while testing Signed-off-by: Stefano Cappa * fix: restore cleanPorts when Netns is empty to pass tests without errors Signed-off-by: Stefano Cappa * fix: small improvement to traffic-mirroring doc Signed-off-by: Stefano Cappa * feat: add unit tests for mirror-producer Signed-off-by: Stefano Cappa * feat: huge improvements in mirror-producer unit tests Signed-off-by: Stefano Cappa * fix: add public ^Cthods documentation Signed-off-by: Stefano Cappa * fix: refactor variable names to pass 'go lint' Signed-off-by: Stefano Cappa * fix: use unique names for container and bridge to prevent errors when you run all tests together Signed-off-by: Stefano Cappa * fix: remove bad test Signed-off-by: Stefano Cappa * feat: add a test to verify cmdDel behaviour when a mirror has also output_port Signed-off-by: Stefano Cappa * feat: add test in mirror-producer when both ingress and egress are false Signed-off-by: Stefano Cappa * fix: the logic to check if a mirror has output_port == the current portUUID was totally wrong Signed-off-by: Stefano Cappa * chore: add comment Signed-off-by: Stefano Cappa * refactor: add comment to all utility functions and remove number from test steps Signed-off-by: Stefano Cappa * refactor: improve errore message Signed-off-by: Stefano Cappa * refactor: fix typo Signed-off-by: Stefano Cappa * feat: add mirror-consumer tests Signed-off-by: Stefano Cappa * fix: use different mirror names for producer and consumer Signed-off-by: Stefano Cappa * refactor: use the right plugin names in tests Signed-off-by: Stefano Cappa * chore: update gitignore to ignore also producer and consumer files used in build Signed-off-by: Stefano Cappa * feat: add ovs-mirror plugins to ovs-cni.yml.in and define new env vars Signed-off-by: Stefano Cappa * refactor: rename ovs-cni-mirror in ovs-mirror everywhere Signed-off-by: Stefano Cappa * feat: experimental impl of e2e tests for mirrors Signed-off-by: Stefano Cappa * refactor: rename again all mirror plugins from ovs-mirror-* to ovs-cni-mirror-* Signed-off-by: Stefano Cappa * refactor: move mirror cmds into a subfolder called mirror Signed-off-by: Stefano Cappa * fix: remove 'apk update' and fix net interface in tcpdump command Signed-off-by: Stefano Cappa * fix: write tcpdump output as text, not in binary format Signed-off-by: Stefano Cappa * fix: fix paths in gitignore Signed-off-by: Stefano Cappa * fix: start tcpdump as pod command Signed-off-by: Stefano Cappa * fix: re-enable all tests + cleanup Signed-off-by: Stefano Cappa * fix: testFunction name of mirror_test Signed-off-by: Stefano Cappa * fix: fix typos Signed-off-by: Stefano Cappa * tests: add a sleep before reading tcpdump log Signed-off-by: Leonardo Rossetti * fix: prevResult is not available in cmdDel if cniversion < 0.4.0, so we need to save and read from cache prevResult Signed-off-by: Stefano Cappa * fix: start consumer pod before the 2 producers in e2e tests. Also, run tcpdump without -c. Signed-off-by: Stefano Cappa * fix: use the right plugin names Signed-off-by: Stefano Cappa * fix: rename 'ovs-cni-mirror-*' in 'ovs-mirror-*' because '-cni' is redundant Signed-off-by: Stefano Cappa * fix: change function 'ReadTCPDumpFromPod' to be more generic (ReadFileFromPod) passing the filename as argument Signed-off-by: Stefano Cappa * fix: typo in MirrorNetConf comment Signed-off-by: Stefano Cappa * fix: copy and paste error in test 'Describe' Signed-off-by: Stefano Cappa * fix: copy and paste errors in suite_test files for both producer and consumer Signed-off-by: Stefano Cappa * refactor: extract IsMirrorUsed from DeleteMirror method to make the behaviour cleaner Signed-off-by: Stefano Cappa * fix: remove useless calls to 'checkPortsInMirrors', because that function is already called in testAdd Signed-off-by: Stefano Cappa * refactor: move duplicated code in a helper function Signed-off-by: Stefano Cappa * refactor: move common mirror tests function into a dedicated package Signed-off-by: Stefano Cappa * fix: add missing comments to exported types in testhelpers Signed-off-by: Stefano Cappa * fix: golint error Signed-off-by: Stefano Cappa * fix: add -cni- in hack/build-manifests.sh because in makefile 'docker-build-%' appends 'ovs-cni-' Signed-off-by: Stefano Cappa * fix: update examples/ovs-cni.yml Signed-off-by: Stefano Cappa * feat: add cleanEmptyMirrors Signed-off-by: Stefano Cappa * feat: add cleanEmptyMirrors unit tests Signed-off-by: Stefano Cappa * fix: ovs-vsctl command to add mirror requires 'add' and not 'set', otherwise all existing mirrors are removed Signed-off-by: Stefano Cappa * feat: add cleanEmptyMirrors test to cover cmdDel case Signed-off-by: Stefano Cappa * feat: add external_ids to Mirror table Signed-off-by: Stefano Cappa * feat: add tests to check the behaviour of cleanMirrors with owner in external_ids Signed-off-by: Stefano Cappa * refactor: move duplicated code in testhelpers Signed-off-by: Stefano Cappa * fix: It texts Signed-off-by: Stefano Cappa * fix: It texts again Signed-off-by: Stefano Cappa Signed-off-by: Leonardo Rossetti Signed-off-by: Stefano Cappa Co-authored-by: Stefano Cappa --- .gitignore | 4 + cmd/mirror/consumer/Dockerfile | 3 + cmd/mirror/consumer/main.go | 28 + cmd/mirror/producer/Dockerfile | 3 + cmd/mirror/producer/main.go | 28 + docs/images/ovs-cni-mirror-1A.png | Bin 0 -> 75238 bytes docs/images/ovs-cni-mirror-2A.png | Bin 0 -> 119867 bytes docs/images/ovs-cni-mirror-3A.png | Bin 0 -> 105939 bytes docs/traffic-mirroring.md | 579 ++++++++++++++ examples/mirror-consumer.yaml | 64 ++ examples/mirror-producer.yaml | 66 ++ examples/ovs-cni.yml | 26 + go.mod | 2 +- hack/build-manifests.sh | 10 + hack/docker-builder/Dockerfile | 2 +- hack/install-go.sh | 2 +- manifests/ovs-cni.yml.in | 26 + pkg/config/config.go | 68 +- pkg/mirror-consumer/consumer.go | 251 ++++++ pkg/mirror-consumer/consumer_suite_test.go | 27 + pkg/mirror-consumer/consumer_test.go | 749 ++++++++++++++++++ pkg/mirror-producer/producer.go | 242 ++++++ pkg/mirror-producer/producer_suite_test.go | 27 + pkg/mirror-producer/producer_test.go | 867 +++++++++++++++++++++ pkg/ovsdb/ovsdb.go | 524 ++++++++++++- pkg/plugin/plugin.go | 15 +- pkg/testhelpers/mirror.go | 308 ++++++++ pkg/types/types.go | 41 +- tests/cluster/cluster.go | 14 +- tests/mirror_test.go | 108 +++ tests/ovs_test.go | 4 +- 31 files changed, 4070 insertions(+), 18 deletions(-) create mode 100644 cmd/mirror/consumer/Dockerfile create mode 100644 cmd/mirror/consumer/main.go create mode 100644 cmd/mirror/producer/Dockerfile create mode 100644 cmd/mirror/producer/main.go create mode 100644 docs/images/ovs-cni-mirror-1A.png create mode 100644 docs/images/ovs-cni-mirror-2A.png create mode 100644 docs/images/ovs-cni-mirror-3A.png create mode 100644 docs/traffic-mirroring.md create mode 100644 examples/mirror-consumer.yaml create mode 100644 examples/mirror-producer.yaml create mode 100644 pkg/mirror-consumer/consumer.go create mode 100644 pkg/mirror-consumer/consumer_suite_test.go create mode 100644 pkg/mirror-consumer/consumer_test.go create mode 100644 pkg/mirror-producer/producer.go create mode 100644 pkg/mirror-producer/producer_suite_test.go create mode 100644 pkg/mirror-producer/producer_test.go create mode 100644 pkg/testhelpers/mirror.go create mode 100644 tests/mirror_test.go diff --git a/.gitignore b/.gitignore index 0135e4959..16e8412fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # built binaries cmd/marker/marker cmd/plugin/plugin +cmd/mirror/consumer/consumer +cmd/mirror/producer/producer # Temporary build files build/_output @@ -8,6 +10,8 @@ build/_output # used in build cmd/marker/.version cmd/plugin/.version +cmd/mirror/consumer/.version +cmd/mirror/producer/.version # local cluster _kubevirtci/ diff --git a/cmd/mirror/consumer/Dockerfile b/cmd/mirror/consumer/Dockerfile new file mode 100644 index 000000000..365a146ef --- /dev/null +++ b/cmd/mirror/consumer/Dockerfile @@ -0,0 +1,3 @@ +FROM registry.access.redhat.com/ubi8/ubi-minimal +COPY consumer /ovs-mirror-consumer +COPY .version /.version \ No newline at end of file diff --git a/cmd/mirror/consumer/main.go b/cmd/mirror/consumer/main.go new file mode 100644 index 000000000..3edd4918b --- /dev/null +++ b/cmd/mirror/consumer/main.go @@ -0,0 +1,28 @@ +// Copyright 2018-2019 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/containernetworking/cni/pkg/skel" + "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/utils/buildversion" + + plugin "github.com/k8snetworkplumbingwg/ovs-cni/pkg/mirror-consumer" +) + +// mirror-consumer +func main() { + skel.PluginMain(plugin.CmdAdd, plugin.CmdCheck, plugin.CmdDel, version.PluginSupports("0.3.0", "0.3.1", "0.4.0"), buildversion.BuildString("OVS mirror consumer")) +} diff --git a/cmd/mirror/producer/Dockerfile b/cmd/mirror/producer/Dockerfile new file mode 100644 index 000000000..4631ed6d7 --- /dev/null +++ b/cmd/mirror/producer/Dockerfile @@ -0,0 +1,3 @@ +FROM registry.access.redhat.com/ubi8/ubi-minimal +COPY producer /ovs-mirror-producer +COPY .version /.version \ No newline at end of file diff --git a/cmd/mirror/producer/main.go b/cmd/mirror/producer/main.go new file mode 100644 index 000000000..7a52c3ab9 --- /dev/null +++ b/cmd/mirror/producer/main.go @@ -0,0 +1,28 @@ +// Copyright 2018-2019 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/containernetworking/cni/pkg/skel" + "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/utils/buildversion" + + plugin "github.com/k8snetworkplumbingwg/ovs-cni/pkg/mirror-producer" +) + +// ovs-mirror-producer +func main() { + skel.PluginMain(plugin.CmdAdd, plugin.CmdCheck, plugin.CmdDel, version.PluginSupports("0.3.0", "0.3.1", "0.4.0"), buildversion.BuildString("OVS mirror producer")) +} diff --git a/docs/images/ovs-cni-mirror-1A.png b/docs/images/ovs-cni-mirror-1A.png new file mode 100644 index 0000000000000000000000000000000000000000..486e00412c642f80aa1cd6b8898e419b42ecd249 GIT binary patch literal 75238 zcmYhjH_r506CQYH0znX<4NQIkDbNpk6Vm&pw~0VcHf__B`2^aEcAzyV)+&@}33K>e zVP=4S+t8kKSmvpxs^0%%eWZW?@BXiU_t#&4{d+~`oxlG2KmXfbfBhf-^WXj(u;su1 zumAgh{PkD#UpFDzhO27(@~^*9?w@aeBTzG~+utbvH-ZR#nQzN9g0EovFcodpmc>8& z5C}p36NLRI81-QEZF`~SP$JN2Vn))uV8pH=yPFZ}K)x3vCeb7QMFY#ByZvdy;hecz>@e->i9=klLp zit=c4FcRc%Q~*m}v}wJh1Gr54zgN|>3F0wDt6d31*<0;!Iq#ptAN`5e-=lRcihauR zLTJ7f993~ex4oRTGd3};EaY?R5&HAyB8akVF%AM=To_+ZBQW{K8c*Ykfu$EQ1-$K* zL8_tI^ZinRpQp;N;DgEc_ub-sHj_^We2?=3Etmy-&e_Ys=p4IvibL5X4|Jv0v<0b5pd@SfF;UHJv_Bo>SFO)C=p4XTG%NOM}fuwqY)X+~I&~6;gDo)T0&x&2rlKBhtsWf4 z@6Rz+K$_TX-7Aa$izvqXJ3aHs-uqYYbnpp?kVxv-NLE>NRA}&Sjlj@ZBx9MJ+cD;q zyub=m6S3|(SWQC*&n)!3MC9BA70lG4Jjw zvESjd4?S|b3U&?L_&!zC925&|mxox#EhN{7kv`gSjKQ|{mcjcCc8dr8{eq7hEF23v z@0~tiFz@h0xZOSrBsB%eIywl?!5QI~5DFYjl$|M4M}ZE$H#E;IfVr41SmYZg?E83T{nuK% zpinxk=U@YiDu3QC2i~JcoJr$dx%eGeibE)Af1J^b`1(RQ$~_#7GCL70 zm}q@!?5I(ct}FRu-^1BcC2=zK5}=e!2ZqA7?WLn^GpbhJ6nhoBlrY(^Jm?-n;xpEY zCn|69;eIZ#aZj)j1E0R@Ue%p?JULVSbLWNq zL&iC6Vop*qanYsD(q+MX_ar#GCkx z5HJH|Q*~C0Q~^VMEW=77fgq08kcyX(jDoW{n?KXra`xY`s9R*Ety?kX$oy=sbHG8; zj;Z~G9lw6jDo#ho#46K|C+y30WQm*G^Cd*H2J;n1F{V2yb7oTlvnE4Hr^9mvg2HmO z|H)GBDyAtzgIA`8e!@&XhX%`MjNDCU;nfu}M1yF%5C(jAL^=9Rv>TR-hqc!gm|Msl zw^TG7@58CV?xokPuh`VI$yN8OH}G01__!+)7=Vbz`nN~2{m2T#cc_IN5I0ITKZg!* zEW7)*?!S6ViIc zTrhxDVCVR3SOpHt@R{f+sF%YMW-a-B3w#zETzFN4(hdx`9u~!abg?%ztkNAd66sDR zk9$4>xTxcsn0}_%9|Z!3UQaqrowYUozG%|=%3ypL+yP`x+$oal5Y}U8pUqJEO-OH}b0Z$qieoDic`p1`y;Xaf@ukG0(#+&dy=_C~w&^J{0C+nLm|e1Ius z86P>`dySi<5}imA2*vJ4YeE;3#sbxkzZ+)1K|%NEN;aXUlKnd(-m$(PJ5;g<;pByx z&kSQLp^M1OF0JI$O>u)f*;97O@KFdGH^FT>GQxIx{`ustSP?ch~$JT}7tw>dlj7etLlFCe1JUY^sopWb#S1sAJ|<>%y4{Pz8e#L( zPjHg=PSuh^=yWg)Y8=?mFQhIVj6gbcR@)(*wkG?YXZXa|`eGv%9>@dDur{YMEM9fK zzcGZTf-pzOLI{!MGMXDg7Oqg)fH>(w{BmiveYDIw(fF*8+&5<%`ClLn42Zc+)XW4tr8;OwJv z%l!5F*;OrdXj9Hd!>^|x+#i~smn;kEb9GmQAM^B@X0~RCR&7l-mRKM!{c$xij)G?* zQhTp!_~t+a4vej+3xc%&X3;iejj@^FK6Dz5dT2RFt_dcWz~c*(WGWxUnc6*!+9hq9 z!3~_N@vf2aF9$iwJOEc_zuyWXs__9Zfi<2X{S|jk0Ci>|ogPy~4Cm7S zC>NaD3rSc7d*_`SR`J<_Q&TQQVruu@_B?a5>xtxdH$}1GJn*E%GE^xzEbz3fxl|p` zl(Tmk2oAlTWvd{qWDAV(RYwv%0T(^Un2tF~l8pRqVeeL9sv$7L9*Rx2Bm3-OlS@<<^mF15s!;5whD$jIZwS;3>Q6`8e7kXKQtG)TK_bY^7^@<;ON0?L={;HH19I)qL zRQQ3%HviJf39+SddR|7!tWo77BF**HcZhr36$M76jhgnP5hXws%jLRief-)kJ~ZhU zjt&6AFG80)I``-Ms|sQ>i7xrXc|$ElPN#N8V{~$k9FfFlpzs>;K9Q8y@JK-c@)4sG zIT|0JH{E~8lb95ILLKcOWqu6Il^+%09y4_deflPDrpksg?NA?CHfOg}G*7v4l zV6Eiern6jpRZYMzu#cp}rK(71V&couI~|iP60+oND4R0>(>8032JECShF4smJd~Ex zi?%|n=k(^93623KERG*=Db!RQ--;YYQFCLk;K6K_9`bfMqJJtMgP06pJ@EI+8GAFC zm4P4NJIpSboEz+YQ@#yUgReNP9Bn!@HVq z2tb+Wk<4j3_Y*kJxKbiD5=^iRK2@iZg(Z7hgY*ky5tGSOzWLQU^d$ zL}qpOAP~dSTEe(^x2%WZBN%T0yv*|Hs}642Kx(N)4E%jwUt{2DB{O!R;km_H-?NsV zSxJD`y;yY|R9CcwEo94Ap^glmx_eAQn;8` z1xBwaWc@5j`oz5b-cA@@zkB^viQk%MlR9jIQ@{(HDUys#*`u)VGb|zwA!Oq$&OoRg ze&xY){2Z7RhPR%C=H~9y^Iqn&C{4W2l<=`W#vZNFr|`0lU*z&b;CtTfdwiaX79)-} zm=pOyi{xZPE+pi{VA#NI&M%7VIsF2TZ+d`%=mtV#UmM^s1x*|%QA1G?F)Dhdu%g}h z*Hh|c+X@c?BM@lJ^p30gfDq45KuAyj3_lbIESt{y3cr$Tp7^LIPa4G2611-33={XF zwV)*TIZvD?tu@k}alOg6E$2cpNNf=3+I1kfr9W zs|}4Q&z=PIBGY|2Vhl(o4GrEC;GL!>Oh2pvHYkx?LKrnO(pb8C)_lJr+16bevu&=X zPM+F)fW@_ntZ6}}u56%Q>NVuK?Y-d%)*XZa{pHoHB#@rmVvYRdxm9pN!tj?os(Ur^ z6{^InO~u8z@n(YftHkQ!Udm$CSER@CL)c}O49U&mmp^PDaAst$3oWT{7IwciG<~Rt zR12)+Z1|5npbBkA$)5U0-w*BCTewIAS^f?zT=~3JowNp(Ra{S;+Ls8+j}X0Xk{1}o1@0Y%6Hb94*p%fUB`;2uu5dSn|71z z^&N+|AXqj)vm(~^QO54JeG1neCk+k^Cu|;+&~Oz$(WC@LOQ!d)B<&lU=zs7n3nH%X zxY1NaRzDSYEZo`AD_V#C1;{3Ow_S)+R;_G^E4e1|iYme2Dq3eEc?o1+^k|oy=9f3y z9@+wDsT!Ap<25-^fDP#@OzRMmOLZl#f=)$B-nD#VsXUY-a{tuTZnvl-EFJxOapz#aB@T+>Xa{z*9S!Sty zm99`ax~)G0?c&|?r6{soRC3M@u7 z@Z2SUe_<WOZ%bSheGVKZ5d3e}{M z;*P)soMx^2!bFYt{OpefzZW2UguhkjoM~?Iv{YkoHZYa^J5&Wr0HOl#tQy&8`p7#u zcS`6R0wza{zjrcb7i^JSiA52NmNk6u?n2kcrz>wYK@}l+o{yMuOjLeo*DL85Z@ud0 z8C+_eMly0rE~3=|l zMugE#Jd-pTO8wIbPlVLbV{~w5Q4=~4H;P(#5iRCh*|Emba`P>bwV%eiHMglCIb}Q- zHWPr}{XEt_%qBOTdw={mBKYg<9b9vVVH-prI#zv2N%NHP;;@-fC(W;D@cbPfY~9#Q z56?D;TB1``3F@M@ns9|$vzV=+dzMTua&JXN5w9UvwTGK>S8N>bRY1;o;(k|(XtK_{ zo|@Fx0dkJ^6rmD!I3MSQKqX5J#jAo=pxwA@vo7gAzD8nsguRMUR!Zst=8p82D=!kl zxS9Ptt5Lpm$=M94yI9cs2^A<-DOi zkl~$Zm=5BIZ@paB4~yI#-CIVq>~N~Sv2|aO@$0Taa_-w(XiUm49dh@m(FT_RUNq?2 zB6SeVlxq{PJ@qHV&pc+RTv|Z4Ka8=7Q%|fYVA&F_oyMmt0xB zPr{78v3vvc=j+lN7bbo=cJhcz9Xttf`_v6Xc{4N3eEHJQVDhwhpi{ue?!2gQm1K;& zXbQ|vo{5LoG-ig8L6J@lsKZiep(kwJ@p+fO7?CCo>7d+soTNJVgAK%R>0}AJM0e?_ z9~G7}8JRkDcHNk=rXL{m@(M_LDxNI3>HtduE@~|sZiBW z7>Zb?Q40*f-=VR#+KaonQQp4SOC_zb*HDDEdTceh-r0F2gI-%tnk5cdkUS($TwISO z?E6&GFyn$#tboc=2;v_2!!vtMO4TrsWtVTJ=B{*+-(hIN`?dC8B5N3dr#`X=5))kz znDNI~-e}43s}liaW~Z^^y!l8D_$pXVk;jBTbNImQ=C-XBH+1_fUKU^$cZzLV_7#j2 zbR|je$TM4XXW6K~TLXUSAS*;Z%@>=8{{A2&=CWnRYe1PTYoQd_h zaL3r`J+6XI}w})12#gvSBMRuG!$z8zi+3hgFT|G0@nZ(!7Iv zs`gzX#I}X;hW9}VWpc=e&fUBKt>heurqckT-MP8GN6}wYC@q} zm}D`Ct-y({U+eOhK7G=9ZVO=4UJ$cNF#vq>;0GK3J%1KRRTK#(Ie9|Oc z3sv!H%`!W%yKLK)S<1>|nJ&{ANd(?VM0+FdtsPun{IdPWr-oikCpw_LUzWP8-iDCF zTs8_RWL}l(S!Agkr6`v(Ie@hL?|LLk5e7|3B%D0RgXm~D2X-}TUWFOHm|;@V=E|?< z8wdMxm=<3r^(2DQY0kSp@c5O$YYWXr45ES>B$|7wkR$3jjDD~lCW z=UZsXJE91)w%Nwk9Ah0f9wVNiWf&rU{@J~QrmPQ^E*5#d9zrJObpP`%Km3#HuDXY97ux(({`)kRD0x^*3YLe+T@4PTdZ?omwUE{SxP z#nsL%7|;Uro;?|+@5tW}O2!SfVF2#3r@vNYDz#zH?qk{5&!PY(q}~3aKVbK<&#%L_ zfgu#1qgP3^u7?WHi9K!NU>g%e(>8}@qjhcx3}a0M$%SXfj;acn1Upacv>MeA4uBE? z)T5KX?umxV09Zov(=YksP5|aFnoh*UbBtvv6e=)3Vo55ssULWA4u?cV2S0hC# z9z|7z!B+`fGDXV{P|z5Xi}TqHutuo>Zel+`&ZDQa!0z$W*5*##(B|@kn-3o?e18wF z;uTqaZqjUi)v9SS?~YUJoiy6wj!CI&0V($-9tF(G4WH?qyqZyIXzb&EXK)I5{d6HD zfK;)z!?Wt6X|}R-&s^=~myb~t<@%{Qx@shE==vFG21E_wAk2oSY}I5%TuIVZKi2Aq z8aA=ZHq)O{WqWj7-&8^!Tv>b4p`YnenSp_QKcP-S&jF`x&vGE;injLR4wc4};ZsVC2LE3v!a&@qnq(;Yey`C#OQgiuww3({gJqC6_6eGxK z7fBu~8mZ3L73Tq6rgE;n^iT&Y$q3G9(VZqga< zq|zj(WVAg4$h%AotAYeg?Tg7L5~~{h2JhwBeQRYduV23w{pi}L$T7~RYSuD5@@mrk z15U~65)e4C=$F>_R;d}yZqFA{Knkq?IO^%fkca%h^)%LLUSoCD=_!%ZrxWw#3G!xy z_-dhOVf0qPoe%|33b&Aj4o4Un{Jq#vl{D>D(5bJn@Ig0YWM|Q5wf{{+#bnsot<} zQqBNMnuf?jtE2-uCIV=c0FbrK-)*~X7^Hp|qnf&ufEkbn#6#;f2{dqOqWp$C`jKz(EFzWgGX6$TcVsV(s|6EC%oMqHqySwQ ziU7UK4I7gXOflOxi!&v$F(cBlk53ms6j^6~OZM1Lv!q%KIS`(Lk`E7TnYNC%GU6MZ zk%fg!AoI0KhL>Dt-M|_e+(NCIu|Gvki$-Ps4JCMHJC^RZVWCUKitea?Zb7I=FomZf#;pp58a-q zk(7n+0k9;7+;(`K1{#@^Z@+izky8wE3#fP-SU-|V({sLu-!wW zoDZU79r*S`ORhUv%jUd5C?ITcg?|qy{wU|?#@pATrchdAQJz}S0>b=@-PhTnJ%pG+ zwywD)7?AC`nEcvkqb*C`I6AR|##l-aCq)FtDXpqgYSpBJ?qUui~a`4r3ISQZ(fx8JZrtntp&s z(X2goOo@o+Q`QUgb<$8{k=<}MCm%PT4im=N59<}RrVfT-rFy5(m>l)0V46xSPKNPX2A^|Q2tv<51~XQ*$7&N~rh zic%ypQ=L9*!`3x~p+KtjN+`Y{>3WM!3jkT(w=><7_69xGk&z-t!;7*)pBeX_^mKy$ z^m7n&@^;6cGBc~W7UKm~5Eh#^&W;V0{9Q&AE`G{I#~8fTte2Po^00s-P|$Q9HSiG3 z*Z;o!)Y`2upwQSvtTxm$A9ucD{9RrBsTssaISarIJA|h7Is_~!Jt;=*zbB7*mh2l>vQtwIe(%0W`rfpAK9$d$2 z?@p>%RHR~j=adDi!V~fUFmJELF-b5t378H8z6e?+Ce8ma9{aO?>>f#=FxL4s^q4WM*Pp)z0?wOs$H_Lww~_dk z*A9lD7~y`RYZ4?_2WlTE6uC{Is(2`~q)2@&5<7x;mdD!!B(V;XcY#O~Lt|{vVozT1 z9x-mrnWk~QGxA_fm*`%S$e%H`ybn|)oH`N-b9RclU=&bvxqa>MK< zBKaT}hr(F^8f3L!X{*)c&`(KdZI73Un;Cu_&>FmMx)-x>%y&>p{zwJ!gIP~xwV*m(^?IB-A%Vg{qwhSu@7 zm3<|kTIt$mn_&)wKH)k;V1R=W&mWV|MfZx}U?^cK?pXNky*s?9$3X1p_5M1jlolF~ z*S7$JEl*`{7^)Pt@!~GHY8sqsGpMwq!vgDn3aoMvFp<+`M2cKFHB-5A+&)^}6 zd1C`xB1T$~SO?BLw|&#y*h8hvVc)?@gcM}cXc2^S!YXi~884u$ZEtyJMv`c-biU*- zgY=I&Q)cBd>z zwLN8|;a)K60L3Hodfk0ZZPIrhIhsiY7XOH8Kr^t`n48~U1N?=4L?Ef4;L^~*9oLiM zUMxY6JMeVElN&*C9B#5P{SmI^$o1Iw!n=_uPhfxgiIOiJZigm5O5WKB{4o`PV-`B} zNMc)nwr)E0%5I7U@wRTmKEnE>T6W6eN&s|~?~Y9^xUM7Li1X#V&OQdzd^#9+{r;8cFn;dvl|gcOiD#Eh6t$X0y|{%j&>#03}zt z{HYNl&WvaK4Ll>|#0gdN>D>dwsto`6F>>zeBXWYmh=;5``{fP41(f1-9>yUD0a%PT zdVXbOUX%h9I%$XE+_mmDjlc-mt1Yie%|3C&pc)FSp%{XcTfl5)d$E|l7ioe;zuWEA z^p}Kg9Vl$@ZHEurW!dDW5^Oky8**)yEGz{WHsbmY(W+zgX)x<@?|g zi_h3Y+>L(sj+X>G986=F{c_I3Bri(^~R#_D~CrEF`JRT znV^I+RpuW57NF`ZouA{V+}d6;u;5gbfFJ`_{7f6l8cSR*W(h0oH5NA8|1i4IE0Ej5 z=1&SbLLBsyf>c$Rkb>+-7YN$f{w&Pn0wuq$0djqIowIOni7EFjSF5c`S0{>b{SGqp z2q4sWbKsjerPm?T3Y*F6UgdT8>3|)Th~E80 zc|BUqD4yG(!UPmk-7?ABj*@PRd%MJ*%_7Zw)YUE}dK4Om?g+B@+ir;N1G3L#!LHnF z#D}G2-|D(nVAok@0R~@mgTV4fviYowequJ*^0DnBtOf{aQ~ApdZf2a*BVoHrp3ovD zW-MDuNQpA;`)-x6kuQy|fa2Eq{y@0;QOX8!V7Zw`G14TX(n1k*$&wU?2BgAz<2-D)i-#a-gC->Lh8v#}+I%sR4 zKBiQo-ItYlV?;9S99wUJK%GZ3lLIf%)r$hIj?V}+bUar9)eB%lDGQ3ZeHJ|Nt}g$v zSQ)+E(?T&Hw#yTW3*lA?VkxJ!?i;i;twCShnZN*DZf!Ai=J7yu#J9=2kpqkbBwXQd zS>KU03xxG1gsR7MdW|%Y6mW5nD{xhPf9*05&M|rw-6ek>sPvv{OFS|KSQl~bv8LV$ zT+l2p{)FGA?YNxgd0(jLS}}XejvKQ}jHcR4v!^Ahh#GP&xuutR*d@&oSAn-lJvJTB z;E4qGRQzW~oogqt4@(PcH1AQNP`R}l(+Y7QWE6j(xdFH&kUNdW7bd^TpW6CqG-nNp zOMVx5a&u#&Y0l;1Ci>(db$j{kIF27dPLyTn-GO7Th}~URv-9g&jr`O{s0rB(t%sV( zB-`vhT^38bCuci=l75hfguW>Km_?l=zpxuYd4kr$AhC;Kk8)0UM3%ot;tj3x9|p@a z=U1qS4sbZgy`4H2|M-+|HXE^3mHj{_E-!CH4@N#??#*ye1qXSiiTNX6?+!AWt!wIp z*;u#YrlyaL$?j#vBiPAyFyKVx@-Z#jGtZ4PvZ+mljqyQ&aX;G%0WNU&AHDt=$6hxB z>$Prx9aLJn#3i4owo4|EP9Pm7$Zeg;&1V)4`V_`bnB!mNCkb25lKyB*g8@pwOx2%W zrdt4|32QbD@YRDpg}TaTv|bFop$}%)4H|>+Og#phQQo zF5`&HUdU!}=?9c7VZIIw7$3TmqB}-%&lU1pjm9o%$6u|V6lwwJ!J$S_mN%$+mpqt( z?w$4XKupR;_>+OsQQ(%v5&T(Ry_ch}GS3H849N9@HUaPCjLSEm$fyRlWCLThjEN13 z!0mahYJ6;OX3p|%_zlQldf!hX4QIUqWFFW6UM<3OXB6d8p1k(~RQr{Nwl#Gvz+WAt zpPF@U#nMn?OkT*aNIvKSdV3Yc*butxz>$VCVHw~fwqB1mC? zc1Cn!<13QukLGy#wU@SEKB;>{QWdeEUA-tkcL}sPV(Ch5-6GotwgmM>*x$1mPHhFX z{56g}qyszE>4p(2YaH{dwJ%{w$@dgNcU;t4nkuW-SG}xD3Rs4v>tgx}wmG^waNTqS zPb`|g%$rEW9Qe3v0Sqd87$igNS_4fb6yf(I0CMSa;iCH5d+<7n6Czzvf{y9y!Ki)`zJL$&)h6(&LG^y1&5Y*dsf?<%$4)OB0@Pm=RY05D+;j84B}ELS zJt3Ug!ud3<_zm0ox#Dykr_^Cgr&)x|~`1HllkRhjhVm7jby zW?=v>%g0G3yx0*|YEbxIBUX8gphg(^(h}R8(TBs|==-MgG8c4RmL~Ss2Muw5;3yLB zfI@gsFDau@CEnn;k46&GiR`6e5@yDbEbs?Pjg|z>IZyqKDl$W5#|@wn6AcX5F(8#J zsBGojiY>fvc$GI_ETbmpZEm7FXuJmZCW-_hm{L;^wCCi@e6D9a0moG?Z(#F+mw3tG zm%^CHnmdCFl-yH}Y^F7{FkBCDu%C~YsPLcw9Ea_?-t=#}F+|z!JE%CU8l>xqTC4B| zlRZ4B8OW{&xc?50fk3`rs&xGHbHV>8YfH=K_aaa?SORs~?X|V)w^0`~OMX&N!p+^V z^26+R+|&nXs3_t612Vo+mVxSx@C|Vu3;Sd%MS&ddlocJf?y2?cjd*nUjZz$-CoElm zi3zin)pOWWmugyyhz?NxUAHt6Q}jyow@yZ*FnIYFqd;O3#9Djkn?F!p!GqQ)0xMz6 zLZ?;l)_{FMWB@u};`B%m09mOlpvfxbkrP~wl@<~Tb)G?n#7*9B(w z&3*(DQNP<0w#rH^KEB79U8v?q!fex%2l5M+q-Y6P3y;81%F^TjwD~SC?)~2QP%@w; z28rJV0WX{oNpcs)(m?5>cs?16bJK}ZAgZ3QL|KduJFiV4$@pNRAO{asbo-2bF#d}D z2&UW=A>>_M=8w`=fJ4Vf+`iMe!44A^&>;15G_OGwaIc=Kg_;jjS}kyJ)&hZwH{q9v z@+%K0sD`SHYEk-IE2-Dj-1OSi=A|!^+`H3Ey`NOjbUKETwhiy(A@a!?uo-Ye$f3njaLV1UO{s_n3Fb$ zKvF^8^%OegY6|Ozr_S_2(0!q-AVQ;SLXc7!)2L(fwk;<^2qL08ZIJilKTm$*#1teS zAWH0p0l&e%0G>?~TKKR6Y2G`Y(jGiFdNQ^6fs<7P%H#w~D-QCvo>p=IDpS=Mg9iNp zP8owV!}^sS(n-EN8`OIQ?0x#8vm(sgcc5cm)+~8d7HHgxL3OpRWxwTtujKVvn^c4X z&icy)O3FS#^BQ7YOhh;Zj3m))fM;!L(3c1fz5d9ZtYPL}kp-CxfJ9+0PVG2fd=+>A z#g^d1)PqYQ-k6tE_0jpI_3_2?AGm-u#p5+cfZBdV0|(GZqz~PMDZB+$&7e6r|1xCw zyurc^tfLr!GR%-=&dM=0&;+a!y}jr^$ESj2!}sK~cK6(l&FO&?M$s(0tO4afrA~o9 z0rKGV_KOqNrLz3|2)oI7xx0Q!RK&5KyvB2P?vwTW1qD4c2}tZ_VQGTqVIQG!Ow2*V z>2NJjOW(QI$&)I9-aB#h{T2qTFB(I4R&l%Rf#neB`?1ToV5|_klV@v9)FxgMOqu5< z3WYSs$rTV5$cOc?$POU?W8Sq-E}H^eg;+|9OUy)DABm%i1dsItk&^}IXWgABH|CHv z^4ablqVicicGt$VThm9H?@Pj2%@;yZ&H?Hyzp`PJMe|hzo8ZN`&CU+AlIG|4$jeh& zfVXG!%=oll#0}cRfc5zzULWL>F-DEeW&Pj2fsShsIdcdwz>bQ( zOY>uHRKGqHj7$KGuT7?p3r>$pt%zE3-J#mgkj)i>0>|3M$6NN^j|;$mphxRyhLP0R=*!}kK37fGc(cCYvH)*$FA zln<^zs)-t)F4aTNp8@jE01KnFYZG|144_3;JYojapqOL;#uiVsdhi8H>4&GfGb&Mg zaAIXwS7;qWeS`TC*MJhji6_ASU(td*=Gzq?mpc)g8dz2epjt_LS8^tQpQb9ltcjPZ zL{I1`!{I`EGeLoT^xNesx+&;_@o~Vlr3#7yZX=FtsN%oyvwdWJ_yU%{USqg~Sh&DJ zdn5pp8K5Cs_tjw|^`3{I2F&Cntbfg4)X23<685?MFbdg0+r)5V6<2~Ty=)9rU_eB5 zdJpuySX%I7AU}HvFIi|0x=EQD{nHx1gj0O+GandaaT0-5ljY`VeqVCGFZx?WB)D}y zU{CuxW6=NWZ*;|rV*#p2ATs^YU2OcELVhFj{`W(VgmhkDb2#S!tbu5A<_6;0o0o9n zSLXbT4xkl2u*_8PNq<6gNbsE+-i%9dVWdj(%0=%s)E^#Pu+u2m0&q=&=S z2-<5d_~kl(*?0-~06JPUln?s&RbquxN(%hX5A`DE(00K;_pn(Vi!bfxE&XpodGr^_ z35gGbe0pD1MZIHR70?-7Es^Vkp5F5A3qVF|u@FFqBq=lCz(tuJH8UVdu7GNDtG={A zZY~XfE4Vh=*Lu1jfvG^fft3?RfA%Lim4+{6igbCT4B+s}I3+E*B99YB_{OwI z_!3C$&x8F+JmKTRt{A*5vC|g`r-(=ky_(1N3+aA;_Jv;Jb5qgx^>n3$MmqBq@^0bk z$!?xf3gkuc!UqF;!M{lX`K&drR5Zia{cNsEs<8qTf6r}nL-BhEbG>*ELUuJgec)qu zPAG1s;)W7*K^55q{!IoR0+J16HC+@XXQp%TaqCT@F$C&>%b+``cD47I->z`_!EY5L z3rG6}dRPmHvvbg$6Y@)Eyx#B_VwmouFSagX>MvqPRVJa5pcvN#9pik`t2|y^tRhJM=Utqa7K7v(LptlRycsU@hxo~X%`z#%FD1fB|$Ig*- zH%rt;if27tb7kZCEwsvEL8}n7Wr_GT(v8q#4j@9KUdKQ$jA}|v*11U ztGq*d;VNt&pNj<24VoW#g62C*Anw*w>MyaKTc}L%>o=nN21PCwsEyYge(=Wshq^Zp zhw^>@hf7K-lBJS;-y#MxgR;zGp9RApWX3v!88c=qQ`(d)At6GtCVP^ygd$5SWJy_) zEEQ3T7NX~x-hF=W-}iaG-{*M#d7k5Vj^ln$@0q!;`@ZM8&g(p1=j&YH@CdRuFgqck zT)}aH5<-zcmx2HF_eNU#hlYak2xEE1Se42c>H_2D>h5Zrb+2vDY=4>T=zVFT=8 zY?>L4>gx?+l;C|JE|{W=*R|L8#reU4VK{3ue@jqUO^0}s!d-A=0~D3O(Shl?l9}2z z1UkjcD$u}@tIudm3W2-XgXp?!V03jDj(SjSybT9X+$ff|Ba#vp!617DA(^56I`p6* zZ5E46v!&3T{k@oWLAv^&dW9P3O~g3sA~*rgD26u39MEOaLUk-0ticqZX=E2KC%Xuj z0C+In$Jd%owh0C>gS*L4uIApl;ZVR9fx9#9Sxz{bFWx%B0gENz4FWhJ=4>vK11IW+ zQ(S=6a?;Va2Kb0<0g4#?As~+n7it!P^Wkc=zyLjM7Yqvq5=|IZ46Het(54m_?U$`)tu>t(zdcv-GgZu(5LAG#+ zSs;|@97wc;I+0Occo&uh!yoG63ix>vKt41WF4V$@6JgHLHv<-8Ln7n6$i5+=I!FRYO@xM7q4X?+I3&1@69wW=^fm}` zCiyydfC{9`r#>XIN29tuZ!mH zNrr8Z6-t{E;>v;}8K6WJ?F4ED2^0jI%rJmQgoJ=Ze(NAt8deu%xLcYtK#o43+VM~) z`*3|I#fceiV{I1};pNPAL2M`v);d0D(9QtwK+*Q0J4G0P$Rjbxn#Lg08FZ8j7UBw; zM+MjrabZCY&Jo^pT39&FDZl{^k`!1L{;qVkIo3)$#4^Ymu-PTBr= z4`CuHF19W@T)-EEA+b>oPNYC6!yM{oVb8YlwDB0EpSXgskTquQ`Om5D!4R_?4R~_)fLUkE{@$~lf!G>wu zBT&$Y04qJ~P$D4C;NEygx=S!3gu=mwGhDz1#oKd156dD53Q`V0cNa?(#XCGC#2Few zM7UazX#sdTk`xl6Ywrt-AO~~83>=BL2&{`gg6RYNPIC({NLU~(0_`0H1tBLLuloqd9UNiDu?i&<^ekY1g0p79u9kSZ z8ECi0!XhxBVlN^LH1+_4Ng&zVCjiulLVYmdEF=|f9mJw=%x$PPFf_wI45~+=qCkWM z66WK`wBcA75Lh};OM7cFl5XZ;26Du3!T#EG0vwbDfIM9m4!GwSGc?JTYwny0AvT;K z5Jo|hb-=9<9A6v>t)~sx1JE&oZS5BpMkeT6S-9Gf1IXt36mM`pa8@|g&OjT+3Z;W$ ze-hbJ7s0l)_w_L#z-?T;K%R3@gf$h8rR#+T>iff-?d zd2Ir45g<{AOAcw9dHG|#Kwl1c1je7C6GDPe=qP;#h$~`sT&%h90|SAQnYKU+>ap-T zL^B$g6GvZ~0WLyMhXKAOQ;|4|gF&F43yTGEi^BDZ+CCw+fo6nK6VV0 zUx-zh9|%PetU#pA84d0Mh-^fNexNf4spqN}=+8}CL^u)kKnYRB9}avQbI^ap+KOvdA#ijU!2&dnKy#fNA8!~eR3GYNPt`F959OZ70(BY?z#_vy z_<`$J>w*{%_e5t$mp`rJ!kmdry-=9GE%i@(JUS=P3Yq22?mmD<>kk zmq&sM3o9;Fib3JM?Oc6{coNOR(KbZ)PcJ$%M_)EU7ZdJh%QgpdVGiRW2AEE;j;$S* zYq-b=5N}0rO$_)P98j*&^0#pUMGM@i0xY39l?Hm^fpZbQVPQ5HE(rw%oCl1>(gPe6 z6;1*lkX(>hmX~D&3q*TJEJrBOfC;*&ARVD3s*9bK!=J1!Z~=n=v~{?juOBPaMIUQJ z0~{_w7sh>bP@WIAA%bL!3DGrhG_VLHyE=sW=m$X{4p=;qPNuSTsICCFnCWX{Tv=>K z;04*RZRs{)7;AjX$fctj8oIN+fm;4Y!y!u~d17(bdXnyBl= z4)uZQSO7I_j@I{rT3UIT>sf+&Pk(O&!P(B%j=(~q5e|p|2+~VC&=tW91wL80Ip|*y ztZfZ<0sVOZJ&hwEZS1s>fX7F`bkPp_Bxk_5QaFxK{~(SlDDk9thuFi-ec4E;4c^Y1 zyRu#(pj(ckt*Bgm8w?c|j3D71FmN2!Nf%`GVA<$U3lxRy0@t=i zTXD%Ky#NPdFx!ub3$y}pBycU@v3VJgu^|RTFJGpPkFA4w1m1#V8BPmz&;d;ZC~y`q ztiVK5oqgaopf=SDplusV@DT?}#D!WiwILiThYh8&oc(mY!ilyPfmjD}xE?W-qibnS z!rI$%C{$aZ(|kdIEdub2L7_N2N!x*ovG5_7)pmjeK>!Z1cS1Om z11-#G6ej}9)h-0i4)DbR{lxUs@wVlvo)9yTBBSRR!Z!0^(ol8?YhN=g$enNk=|BWs zGm3pUA`IiefH`@wC^Y9FZ7c~N5(Gd$cp9cFR@;x|Zx`-uhOvUc{qPQ;DgYA3X4rVU z(9ytSBm4VWN9cJWvAWhQdx8aMbA?8EbMcHXAO`{vB!Vr~0%hmy49A*>(84Vp$q}}c z09{}?U0p!MD+&t7=u?9TK~^M=jgK8ypAZAB0Of)5_P6$naK?aGpPyAYG|(GvPr=%- zyRSvWa_8V&`a3rnu1qrgC+JCzEWgF0~)jWz*~gZblh@F;t>6P!d3 z48}tIz3eT*9UL)8@I4*pN?`*Xgn&aiOhgchX@%mjK?yGuu7|e>CxdW}rE`d-ZeT<> z5*=p7A^_5!VTN{bvBKN9c^>G9}Bm9FRydh3b0XkGmZ!bVSvcQF1 z0zs(Q2k(#M>SdIRtquW6g7~8J;EXVRYZj=|10UOlfzozAsvmdvS%p}TElHpn2@N!& zdAO?{#nIB6;25kE%7(E3OhN~dEp6cpP($yH(}qM4AUK4!J}n4Lk)I2118iDf{b9AalE}Lp}tmTq5edS zEl6j=*gNVt+eVN;&bBQJ%&5z!W?- z2E)TF5O6&tG>`&p6oSjCaDM^#hY^N=`}*4j5d4uKTa5+_wb%1Q_?!JHbO}bIEKn#s z4hPT)2Nv#!fcn5e+5%U*>Y>88&2hO0cdh=@6ZcPpT=4#HTVh@G>~gjArcJV&tWaiz zaF37I_y~l@FV5fd6wFOL8@Jguqt8w!DdMT&Ytr{ZQPTTjzH zBIX2Be)(;@zliQKzwVr74V+o@9d`Ycz#?7?nJb%XXe{fk$eLXH{%Inw^|Bmto0w~X zny~EVe-C`N={9zZo7lW>`|-ccx^h;Jz7v^4DZ(_V{FvD%VJ^!DdXX%jqM5Ul;zP__3<@5`ozDI2Vb+0MUf^~(2`_y845AdFUd+<1(W{96w?@X$&I?c<>2!&}rx8n;QT1^9N@QdV9ExuYfjw=~cgdg|R**8%)BT z6_4pJM=O~k=PImBC;0v6qPByou!(Ko3Uobvf1Ca0T9Nt$MSC#ne51k0hY>ZbGyzx5E2A08YOZHpE_R(Uk_72{;|f<_3!_dywQ_|Gp& z%D9}1jH!;iqxWd=utiDgi*mJvRYL;<(ue*cp_8SKWzpw%?z3K~^k~ZTUWb3aUgh4_ z81eN|1=aB!Z7e2ms8sIL`{&y;AzLS;k05fA6_V+5gcJ3QEuTIv*!^hvEp#_RxA{n9 z-22m;WWCI-eklKH%#Dh=BOx*Oeme^N@TMOwEhbTKzw~G8(B{pbV+_fum2)&|A((c z#Oj?po7SNWdE31QFIC9}B%2z_<(9<9-1t?PLK;}3b+}KB+zpCvyRh49qB&C4<+fre zq2z4c@VTyMS2T~FXlNZQaZuVUNt_!ji4xcK%>{C^R256$t45BMIH-Kkx9eF?Oq(Xmwh=;W`Ms<1`bqYESb0j=*+Zu{8%`WEC};%s+lcKfivOtjrQv{uWE z@^4L|Z9?Br+0x^2sq1d;q_GwgbS3lpzPm4wJedtK{@qE^f_4ob?X$b24u@;Ixw}6v zb<}VM?IpV|$=Q2U{+OsjEiR5XD#>ZsOPuo;qX>-NqgyPD)b2_DBKz@0)iD{HD+eE* z;}vr62t2SCWP9{VpJ2_w<>-L_xkciuS%c zO?72fz|(V+97kkhy`00g+Vt+a5s@i&m!WXhuh;x>F-|G@sQ*`Ail3A9na?X}bK9%4 ziV$1Hmo%BDYn;lWCU$X0MN3902GS{iX_5I6gg>yGI>Nk4jVUq$rbc>6o(HF%@4S`n)3uls( zQ=Mda>262ls4c6jt%2{bB|W{T;-Tm7(k&|Q1{Hl*=)K+esr*p+Rc`)LSSVul7>T+C+ zYJIV4^H=FkBwHUlVx!8esHjj{Qaf2Msq1?&HGNGZSp)U?=ht^WsLE^Cq;A+=m2Yxt z;;`LXPD;IfeFfsv?b(?q^?tk&7QI_05gIgNDGwv9qvY?MI+e=#Y0q&HuD=R-$okMv zh#z|=Akli$L1kJTYI}Q6L#U>_pv;v}@AF-njNitPbTx&Uv5u4L>O+q`{bP2y+_o$c zIQHo^dNOitd5&-%KI;ZcT^pM}|96c#FuUX2W{gz>D_Z_4s&%Q7<)NB$^29>bm~j`o zL@6WXg*kNvW_w~~og8K?SUHqVtnx?z|5+Ke<@>u%N0&RF*=-co8rUoCebCK$r2W}Y z-j*-BWVAGxr!sjaa!fkCLPA81!-ZTn!i3M?B>R(j+73r7lUuoVW&a8SC{Eg!2_`UK zYo~A3Ziw^^qEQYfmrp(Fx7a;}6%sb8D|4#2+`j`A@pV=J%nUCh<*HE8s~SIx@dkF$ ziIF;q-b_7;e34p=aC)UDoqmY_sGV`->N&HhZKUB!_a*Ii0m|ELHHC93idxrBWwl~y z$!!;wjwsLm9uWJuu!sG-IrXw^9GopTIq{r6@fKa<+mG?ij}ejBA!DR0wMBVhdiAGL zy^RFpv#iJZr)>@AboM&eY>S8%9Ey+qN5k#pio$P^`j(8HV~_vO(m?JdU}W|5M5~Uq zM4vx@Uh&Gnokx#k=RP=mczMef9k)pwQb>W1yyaREg}fU1^a-ZJ#+9sKDlcncb*e*G zlOvzVbVvv0O#kl!4dL^|0R9P*eE{UpuFoa8%_{vDnvnS_gn7MGSYeZq)xY`oT4~v}c>++|u$2<-jw$E9R;# zm8h9Cz_(qz3ecBP{anTt(fYYu>ntvAL7FpHKc?B>mt65SX+Ai+PX_4zNIPW&r8tP}A4#sKl5E4yd8FZg~T z@U50`U+Ry+R2g^4EgiNzikI}bqo3{iA4iA%-{GRoi_@iEg81puDD|MroA}@F+Kh?C z0n8=-c~>fUGBl4H%pSG;WCu`vOI`usw@ayTEnWV}=62L%+;5j3ylzN;Zz0ri_d^pc zrz~=ySULSjRh z@4jTR(FL`GU(&(w8>REX2afJY*!5`O&GGH=yHLfYP*qPi#RP@?7G4hMg6Y^R#NBBgCS<jJP^4G6Bni&(ESF-&|Xw{esF+L9=A!w~z)PfPX6J^0)e zY8q7{(2{4>*7P+Sd0kv}09LWLzPeiHb2xKH*p^BKMacH*39r--WlneWyF6p=8`$C> zmLHf)^%vZ-1z8ajZ+6L)qr{!F`zIyc{POLeuX57zFO7`}m+ZWEtd8U#_38tAwR%GF zvklLeN{L4qdYdc6>dp3SetD{GPsgjTZHG@8Oh2ZT!eAcfPVRVA<9Y1dsU7Bi0TLP0 zkf~paK)w{XJE3dJVl2bw6j^oT=x&_Cfy}Vu?>nfrLA6d_6F(&LvQD2qJRdE{Qh0_K zv`*Ft4IXZIW?p?_#%`<-k@?W>p4ac(#_)|r>A?Bk>BPzg(tt6)B9LM*FTY}H=?_cx zfF@MV4dRFVs`NAZ`{l-$)1{O)qqld}&{WBXnudm~lb>YYS%wO{Iwovs^v~m501Gs` z`AEuXsPHAf@xG3_S|#NQ(fR;)zlWv%?$gigOpn4#G!rj%-TNFdDLA^VW^gtt8ld>g zf8^`%NmZtS19&UU9W7gZtm>0^b3960Y;OdgQavZM^FqG+P#z~aDofbu%_K?7d)3ck;T1&L-agFJ_+GfkOsl z_sfqw;FAqpl2SRN$PXq$$o&iOKx#ch@zd0YvfAfZe+()2VmC%9UBGigS03PQ;sd$r z)XVZjJ90UWr&K{U=?CFFEaI1{Jku-R%QM67yNq(1!XtuCqd)Q(_pU+cc)Dhh!h znkaQR@ydd$Y{1>}rG!w!1=NecSAEqM7rX`Rrn@^cYWr0-N4!W_wlZ(_Z~z}zRn)37 zhvL#Ztlp$^^=EQtk~+F_UYz*!dh@2@svP^ZUE6o>v{(SWA}6oxwB3>MV7POi^uEKl z#!hbCwoB%8^*{$uC6S`ES_%T(S}#eW|Dq!P1N(kMzm z1KRqaZ%`P; zZ?zzo#;wW8*WZK<@4iPHLyViVb=qkV=4?0rZ;Oo#U53_)Pd{4o3%4piyBgCyq%qap z8wsy;PKt9>fBXJSfp$;O#rgEA0#<#0@W`ig4i|8(^Ktts-J27?jgL4?s~cFnI=s%6 zJIfZ9{bD^f-YxR6RY4afBLDU_{+sdttiBa?I&LsZ`32^AfF~}s%vsuI_Mll;qmb1! zE%YT1E(~`u^YD&zH!af4*|g6mg2j*DTWr6(GCEmzT&mk8&1UaJc5KM4uz6$KEEBO= zrKdUpt%1u2UhZ^X3pM+CKoj2(NZBQ=zg@ag`eieT59@9aTa3%e$Bj1IuWumN3NE>r z?V5w`*ML@-t}CxxQgcv#Jn_XG^!BlBUhu+1e~s zVzp#t-LYg1ce9u=;vt?}C%-4vG_)svZVp=04*s#}#ea^DDFFVPxT)uJhrJXAh}G#fFfG|E9SE4$R4g1 zmo|AEJ7(3UBA1@L=f(&w*>rrVIcib!I6#q~F|r-6E~Y=Kc&Wb-Fu-?YV-aj~6FFx4 zGt%#;nX<2%7uXUDox_(-Ia z{i{fyPrTtrBDcg?TpCt&bQoDmh)u}ra`f%5*5vQ9;;JUB!-w0MJuz;PJzOzMzkVdt z)n+?z`sI_ZPoox_M!)y9Oieg9vBj*@G|!(3{Qb}ehO?SZd=Im4n0ylYR0su<--{(B z;f*q8XC7uiIS1V6?muQC8ptX{$=@>{IopAg`RL`tDSV+b>8ZQnzS<`*9{v!Y0*b)G ztvOtLYFpBU;t8LzFAYixOodhj{J`zKf{g|?Y9fsDA9lDx9*al!{88esdML8N*Y?z} zW~Z1Zmz}wa-KTg700wu|^ioQ8S3*KU+M{=`y!?th^r){bM{337zh`#pTW#u`c?SJm zcJ+>i#4>cd)bEeH3SJ75-{Dt2{@9s(e5GIi@bNFUr3y6rqcxGWEYX*X&l+{;x375{ zeeqJ9)yj$&93O34D}_Zb-$}p`g%(iiQI=O@jLdECLut3_mddwx*h*b1Kb4d6BuSZH z5eMco>etCR-;cWN)>Pja*jGIQMlBntB*+ubrzG95G&KX}mbfDmLvWWi zQM;BR@foto<+it(amoqEHxu&{kAQ}-dvt&MZJ*w~zjNScW*w$g4xd^*{P=Uoba(8+ z{5`9m&3C5)_pBQ8Zhf|+dr++L4t?T2I+=|2mR`P;YR1UEDl`-)8s~=gucmPnfVd^U z&QR??cJ0;Up?BQfh`w=f{_<%m7Yc%PPDRnE9IYI4<-`r^Qc*2xbXj_(wfuKtV@1i5 z23;b~EyAkWgQLj3jC6h1sNa89LE{t4=ZG-=$Um!4AefvbtP{2`j1pGIxi+Wf==fyq zAEd)Ua0Lz5PRQy+Hv)YmFd1e z7KGfQC#H+8ykYsf6@0UI7Z1|%-hWP{HC&7S2w7;kbHpTb{6G8va_@xLK_C3Y_#k#LDg0IC|qvsWHf(}hGDSch0V<#u0^8|e7 zBagi@6Sfop+9zS~C3bfZ*XXCJffu3uh=#vFZ%jt2^Q+J)f)2g#yVu+Gr(4^;RKz4$h|K>fEZ}z7~Ze48J>%{m| zmv-lDoj8wK7w_`UaG|d~PE0-x;hlDer7q?52G!b6k4VYHg*sk9@D_hUProrZZs1AP z;EVjEzGlJWYQ=?d=wJ5*PQ`BVNbI`SCc5|MlX=IvvzB(@2TtrJC7*du7}~b=V!Z#% zeNVh%t^lv73+#@ZM^L<(271wT*yWbG>%NE((LSh$dXkFb7tIqMb!~Sa9!wj(VFngI z^^YraQ>MD$c>C{zCwMC5^dOdpnwR4btwb<09(0!*QuoqZrN&Z=RRRh_c;YH0K1dSt z7XymAe6Rgvj0|gE@5Fdi(E3$vZSBFzH~R(n3q`nF zy!&lms*KN+_i^C7EXZ-a$m%|Tfg40c_ncmbNs_XUm^}TOj?DRZgHydlwi)+-LD>#8 z(O1B0lO5v9-SgDzgeXrWxK@P%_gZq#UBR`KLRm+0vRlVb&u@?2h4}b={|u*WYlQ%0 zzdT(~grHp(ZK#wP!bjZMaZav~{*U8|Tb0Iq%i&#)-vns24u>+}9J>E;u!JqI{=;jh zFq2%OfVW8e!+RJ0_dN9fySz96;B_hgv*Y}rmp8w;{XzSh812VaIrztv*OGFXCSjq@ z={jZw>gSUU9J=34g^c4DYyKy{{r-;C?Zs2y@v_%-y3-GCAGmL@5^e3-WifXA*EagZ zc`wtlFRFoG=9?LSWBDH<10S+azkGPDWolcw_0@-^1hb~1$*=DPPDpR{WFDG7w)mb2 zhVJ<9Y5xI(GY-V-QxScBdZixLpRDTKj-Mkdzv{bhUhTcs4OW+uJ0zcuJM2IU1`;4D zCnP17FiP9~`SYyqiST`!y}NQPLl(-{){QQHnL&K$p1nz!5Ej?HTO(jW>rn7_iW^z4YmE(DuO&OKe5HM z+0-b?%A5(?oCmL25We_TJTJyaZn`Jkra9UWukq2H*TXnG;d7Ww-<@8WF25@l%2af_ zAGNVO=K8(KLyQMc_SA=Dw921~n0`#kbiL;~tfdDL!bN?OI|vcFRyvYYp@WAWx zQ+@7-PlfHiyW-dSKz63_;D9mNJU5B=k>FzUDV+7&yUyb4>#ZwehfIYV-8*V8y?XA5 zI-#z+@nv<7QC%V}#51RbCoaobZOXoEbd~St8<)@SF9)wblya|-R(rN}pU9fjlvAUZ z^RQuNNM=XQi;pW0z9g>zFGf!gJkI#PiueXCAj#!lufFJrna*Vv`oa9iBljb}2|BS4 zt~5^=OYg=%I4n~NRg;+@Mzj~-4xV0VTfw>X2UeBw>S;2!cpT}xmNoo5A5%}y-g^4ZBSi_P^$`DJ2A`H9wd=Zyj-p7sxEY15=s zZXJKlNUd^lt+E%rEFqlS5d5hd$@=~7cMG9g!#GXKH)0)L*Mlm`U-=^uq4Mm>Z#!YT zg0zpi+z+hc<35Iak7U&Nay9(a3aRu%ErlSC?6k$y;%{_e^f~nhs6}11M#iEGrGVL! z97{Flh8=^yF&_*%3932Hto@0+ZB$&Z+o!pzkFO63(hq0GYXt6P4|HF1(~>3X`{<3U1x8x8tlB_NIGPb!(p*Q_TDZehc6a zLXuwPdetWHV58DCWbQW$4;(d`WA5AEm?#{R5ZfNDw)Q-q{sm_I-hcSAz}mDE?B&6a zrD^iSv7}pX2DjfSGqjz!r7|k>hsU^l?90Hj)4%fzO2q2z?2aw@3D~4+9o>AlK)G#v zW!GJrb8HAv`ps)8x~&9^cOTt2<#1z;(V2P&i>Zux&A`!H!be|JJ?a?mT>AJJc~QJy z#GNC6TI*@5COsqX-@pF>>B%W)2z>_v`T8O41Ltw{4}fQRS9cvqJac3uH!}V8`&Sct zBfiW!gzx|R>*Czb2bUB>(3PCJeroqW;?~ltc|FU2s2tP%P|0CxxjseJ%VlqSTt;#C zHZ-mDR!PMkbmd6jT=wK(+*rI;vCb_Ejj9{34rp&cuVh|?5%W<7Sy`>-n#YWEiZl<9kNO`3VEom?QYgsEDn&?#XoQhVJKiW zApbpkUi@i}$(F_9s>00huip}M$X)LVJALBD2n9qNWSm>A=a3b58js(t=)9Xy#(4Yo z+K-cAYh}`z_ZC%)v=ql49|@b=KHDpKXl8QbAal6F?R0?_Oi7};x?0siRrj0J_XqVU z7eGwGUuI=R10bqXnRb9i+0T8EEgTR^-BX{mE<>8jw)(u(6m@y8l8#ruq%*N{Th-Cw z$y+4->=%J)H(%F9NJ-o?B+va2f3K6f)8jrTaWBR-FYI13{6wqxs~VrqWRV_K$9%on zUbk~^_bS~A`tEz~!UY?$Q))#cbaLPwF6NhLtmFI@-GscB|KtJ`qP z#xjxC$z|>-*W`(f9SWKSRux@MC)=f>j<27W*!}upU!Rcty(a6D`@tiTJTeNcI*jRw z&-d3{=SD({hwS4J_VQtqA7hs&0vk!|eZP`+*f%we!rrml7(*UL6II2U+&;I6>S*}9z@y*Zwbc#pC^4T?yOhCVbb*0&cNi+0Sa z_AHS?v?6Z#_q1FVSPGjiPI^BPBD*U1Ar`yPkQ;XIWLK6-RqGbpGOao5rB*UiJj_m99zqI1>* zm6A0!#%sUJvgUQ+j^$TVveZF2NPbtEMB1vl{d8%PrsnTVQ!1dQigvya7}(N0EG=h| zn0+~gIb=iHC`W7e_+%G$iK*@29}s)ASRquOL^HOSeR_3ibs=Do>Lzy5W5ap!mL3rK zu#po$icX+%K`nt@(~wc3+dC!=*m+jJpoE zjl!x5hj;GNzu@DdVKu!GU?PyHvCp$T`bdeP`9*o+Rq+(&(YUcx@scLM^Ns8oFoW>g zYcRtWM-!hJLEnz;xflLK3JBU2%xLa8G{1QA@s1hMS*>iPKRZeaTHacMe5WKK&)3u1 zy{o4&adP>m14KS-vGr=q>fWt!ZtGdV&sc@sC|O8`$P<735|WO-N9Vy8W^BFf^F&L; zJh<=ZbWh5@s}P?n5$-W>4jnVLX>7=Pp_#%A-r`}5oXp?bf5?V`F2_AQL-(}L#ph39 z+S(A8bknO-)${xTpOKnDUIChHs_4qdKQ1Rz$P(-i&eV?8-3tOTxRxK1bzk(Yz1CLU zV`5?+yi_ZAv#_XuzU5P#lA3=hwX60RM8&vb^6@~Y|6oHUfj-b*@?i}lNOm)c;E;;J}^~=4yuC(hzhXUdx z+r9Xb3Va&(c4cY?oEX1`HA>b$3T1tKF8Au2XubLY-b}j;AVf6QGK^5}j%~PF9?-d1 z$NDKBomlfws&_4Xc{(rE{uX^+?t|}bt;o@-`QfU|;bK4Qr-~CE>JMkZ8LJtomABaA z*uk4lHrLYKk}-UVQ zJ*;rw*r7(NPeUb3e7~e$r=w_|eMj%Sf63cnZoXePkVpAh=IL^m+ixSgj6OU%SM-&b zADUqDc@?wCa5*pvc7|R!951RB2Pt}DEg$y$%TMPTl9J?=O5BhRS+u@1%Y<(VgLK~YkNA5nT6ZcA1vbo}IMf->1RRsAW|IY|s@s^JI1jhOocB&Tf z0Cw$K@Z4vsaQCIw+9$)$n~xrkb6e^0a>XeD7*iSiQ||4S*Y_h6^3ic}M_&X&?08#t zB4|^GyHeFsp3Wn9PpyC1`fj|($|7EBguSZ@7|K00`LWD5XU|4?QpuUBd|C$&@2Y3c zT0076SH`(Tx`PRD_&GV$<||qs%jX!Q$51?AGq)WuElpFAd~K_{I_E0Ngg$@K_f5FI zwRl}ZuAg`H&ce>G!n5y9?wF`MR>Xt~jM&K6W}tPrOgpk;msu9JB1K8h@2%v=@rK<4 zv$(X6PcyY8nznsI3^%(24`nC7DhHx01kPS4hxWlmwVX2KVeGFd6@#{>+>I0dXX7M&JW_H|{QT%#KA?nFlH`e#YUTF? z?l?}zb#9X}sM}dF!p5+WS+}DeUUT!|-)Ly+i5VNPz#+GHVED2<-3Jcfq>asTCceCB zui1C`<>3zzJZQ~0^3`$X*!@tEQNiEvnAczPRbKPnM_*W6IHkrs*0lY>>Ug64HIW{b zYWk@0E=l7}&?C`rMyATTl65aEd#qg6*A7^+&DaY`TUz_t%VzWdByV(v;%>l0QDdSP zE5jer--WuT&7V74!M2;8`?_k8T=s_d#njx0IFJ@_z=$V-zN zWy>Ji@p1Dcrz1L%ldO--k@E`sc6W}>tq|#YLF1w`8fhs@Hpv<=n$w^>_t6G+L}>v* z@Bo4^Fqe`Wd2&dO39E2dAJ+~3y{I{MpRsl2fPN>dai;6)lkGU^@y_ztHpchFd&@s2 zNd`qN0!dh3*@7kqF1OZ z|9z!wxc|)3)f(?Bp?>c$ciV1SDUj^C5$1uF>tUKFA{V`euB6#t`1HCV{$ACVCBG*f zk1vbL|A(L+GWc%^YPCZ*%u6!+6X~{ryMPLMr(6+mdJ|f!@We69^!>+EDp2UQo!A7^ z#?m%#^JCYCf%XzO(Z_yt-Vn9;G`8*5@iGP9o@4v~)Mpq!+#9(~(BgYD^hZs!l688; zrct|;$wkh#qptm?;JBgkO3pk^nC75fLawWEB#Btjp zbR}fjo>rzSPn>RTXsq*k+|b@HKw2O9Zc)>cnQwa@0wI5Wy>R^f#1b98=&Hj1#>{E7U@&~+$*UD1i0{)FK;7qAR<~v}vb3n_6;VsN9eG|!iFGHIARPkIyKyZ~ z>SowB9duMUFV*!$D+%V8dgq|{r}z&$)NqIwAe;##3tidtwK~dw;I@0d#7JsUQM^(8 z>irG7X?3H5mExub)MD8_+~l3LbG+8s7aILWIlB&Q?B4dx|9n;wvPdN_*8VRN-?~HW z3Tn{>XChjo%6~)S{i5W6c=DR@k0XY#g|iP}XY3o!!rMKc-5m=5@`nE}-;GncYV5l$ z`@5k{L4Ph~%fa!_pAG>U+^C*-$i1ybrMD0|Q{GK__HC%&&a)Br=}KtA`QEOik=zgA(XoB z9o8BN6MTK5~!#I^VKd9Rq3hTQ?Q+S#K*=-Oq+v@e(BE!&6 z_{51%xrJz}rx|*DFVe~;U#~Yh&(uk#X*fO1E8rsqW=jw&kD0&fpSd+akvaKfEvvhs zB3_>ON3_#0LDyh~T7S^)$19cTKR!-;0LHP{Yo?=V|JeMYvGC7Y8zl*j)-!3LC(t88 z=*k|PO34;OC8k5XTa4FI$T9n(U^`v6CYGacealD?rh*M)5}Y>frut8m8Qd70%;`kR^>zM-3D^+_%b2cvgOZ-pi8e0e8QVGI}X_VIwq`>|rF zxU78_Fu(Iu2{*MlyXT@;G@yBQ>n0-CYRh)*bQh~HS*W>NbxO3pM9O77pjazeBk{N4 zENACch;M7x!*To_XU5Az(Dibh739ObJjuN zYo+glJCYsi)@!rxGw`Wni4kSp!DA<#4R-{-^W}S*bJ)K7al)_to7a3K`bv(U3U^<5 z03i9)fEdasx%*Az!tL&)Jpg-r(z^iw$g%oz5Yav#n>J`_>Ml<_ydF4WQd1a1fBg;7 zC|#jLU*!oLujPN2Sl9l&(5tGY?>0rkIjt(@ktO=(ydS?YnoMuVzT6C2j^=hM^1r@iC8Prb}^G z+D;LTk~61)j=1;U|6u0VD$%8!Ek@P;?L6h~cQ>zJ_!ga1eC&jv+s^|<)sgF3YV;iW zefC|@@;M$xoFa+nur;Jlb&SHJb6rxfA*>J7k>bCSW8s)zF#r?JeNbs zm|aiqFAS`I&)fk_5&D^-K&pPxmv3j}WF|QaayM2gz%(!$Om+d6jlW#p7tCc|dqb&W zu2%B#pL0q-OunWgD)2o!vR(wHB=U^jJ+6DBH%oqII<8oVe{!@48RquAIl39i=_uBS z36)Qq?07b>Zo+kz(sV5&j)IgV-9eR|#g)dLUvqo?2~DZSY>mgw|B|>mAYhdMMOS7% zP$yrJC+=}AFSF*E6RtO}zo^q4dJut_D8QTPUcJ<=wUBkMs%YFjxpA#bctcLI!@Q&P zBLo15I0uRsh+8WRYHYEaAZwrM6mU+1L^-OxnmW8vQed3)`oTN9P0HVIo6p)t1E=SJ z8(^sZ=BD&RYnt~h!R`DG@!1B=5NnJDvRj&lcj5FI7vBW)Z!x@hdZ}A zA3WBqyyY^eX$-~80849e@xpp+oaiL<=DW2ee;c3pvc`Cp;<1W3_Mhb>eUkr& zqbyEyigVkO8Z~cAPYpKg z7uez=dZqS1(v9jEKR}vfR?L4TXx%=sa%;cq?h}1-RFZA8{u@o?YxRxcp#G(33CE+- zq4Hmr!cX(e{>0Z>MSx5xx8HXdv)VWAD9U2pW5j+JPqoV;x2djf0S-MgsuECtk_9y{ zf}n9T8cA=J#Zu?<_TKxpyCMGFr^SbNH*ELsSG=B5(C+T(&oPxXR6nq`aU@p$%6WYG z#lVenY1X0UoxSTkpT9f|V2Ka&0BV2GHF-@_dB?xB^oIeVuH|-@ zEx)(!-Dznm&I1IrZ=fHjr}VS(Jhy&FD_pF3cOhbf*_AR*pr5SVzAhT$_po{UZ_$fA zFQD3wi&|O{^&cL;$dv!AB`fN8*ZnNRSi$OJsXE)tVd-~wlBJ!{Tj;NGKD~vnd|$3U zmY%HySwQk9Uq`n*+I+}jx`(tQ{@2V3IJ;2wut4*LTV1BCQfd@W2l@^vQ+u0mpEWmm z&h^z9Jwa=YFy>T7jFfwlN^vmkjUhrlN@(qfasor`(`(r%xi7QYW?O5hHy!c~!h+K* ztaqx*%Z9S(jYl4|W%YiZbU*iFN4fkHivR3d*#4O(=lj#1*_AXGJr$(65kMxI>YT;L z_Z~|oJFlM(*q*SfKkLxX{}Kt*`{mzPOA`V)fqe-c6fJ9zO_$kbisJ^d_+um#n>*)D z3c79ElDy@6KsEOlLQWPg{g5vEnxco?7A4w=d}s7n@Y3lW{Hisyo7%gs=>worgY0|& zLRADaZmbjY({8KC2&k>;69mbNsS^KVTBRuu_xmhCiWW0%=XGvHiKS@-kGz!WxKhy_ ze)d{%B#82jE9S9xGGPq2&ZKKJ74y=H*C{Q z+1c5_-#)tB=dA1iq_EIi&B_zMH{4hKat=q}TTVtUe;D1Dra`rdcdaX0 z?mLof(m$`v5C6z`ONQ#`n6+%I>-hJ{dUvNv+xDI=i?$9!1Tp%#MzFt*@>yUun8G=s^t)@+_0(nM3LKgwd7v z3!RhxeP%t=2kq@!z08w3F6@@EU%+g?53>GAgYTc0+)W;d7Ww^?Z|&QH!_E5odV_C^ zqkPqkWu=JZGWE-MkpI?oSqlr5@PD1{aqRC-nkDnqS*0ikJ2yIMe%NHSHDYd1?WLgj z<jRl_s6r7Q0m}zY!j!Ito! z!u#CZghWIX9B|8wca`op9f}iqq*JN@T%3|=F2b&4CcdtCbQq}n< zKvh@ro)YrAZzC)7puEdVKIJX^&dJENeDo9GtVSB1%OLsyzd{K!BLAwKcp~)FU9lh)h}0IXWSEv0`06%1^jFX3I>+iMWCvTw47+&OlMeUUxMu3q_n zC1(E>b4&d*6-V!pUp&1ES!ma)d~$y0j)mY=KI1d%d9N~F(hgXQsV2}Tn)6%+ZiKHc zmt0&}6*%De^=j^=y1}^K0Kdg?{#JPE_O1hL- zm2)%b%-OBgo)39Gbm%(HjCOAUY2DJ+<^P*#Q)T<|kbGmy->y_2^jHh~(2siEd~z-B z{mb`?1>t*sNv4D*A(h*n?jN8Yo^yWDem7`T+iR62PEy1A*S(w9Xqr9oUZ~@uc#5Ux zr_mM@t7>)iIKIO%d`A=yw!YX(B#LS{?TX}lk8W%{epx5(g7L2-o|{~2saxf447mfk z2$S*cUr~`2=b3qu)&`2>-cE=$);%GXn>L0n2-iBZyp6R?EL}}E-0A}sr2XBigYpS6 z=Q>%j2dIDW>R=xu;@ST$jwzZKd)-(d>tU1+iq?GM zXYhK*Zn-t7iR#6t)a(W}&wybMqE=Y{^2$NlVTl?x{3NXEteTXstWC#3`*EEDi?&y< zTyBg%FpDZ2AFaFn;^F3<`?OMH+dv#nT3T9red1LuP|qDVlNCh9;h|lx#9MxE9KX$w zGGtkDgw9>RK5>X{DDrNTk&N-h%Stw-F4`ovtYS3D?RWL5}`xjZPqe0%3y#pvC~Tl=34 zi2>8mwlvk5BG%{wB=>oNg&gl)b>NYztM+gESBt1xz5$xOKf#SZX-GKhoJFs68+F)c z`>u41oMq2_pS;_*J~eNT-WJFN!%mytusj`X;Q(wDw7_p|)aQBMb7s!@=X>UyGxM8q)R|rDv*M2HzV2%sUyPS(YF+HN zVe>c3B!U0GQQw|Go0f-y>a;BDSCs!4hGMR0Fj|x<;Ah>7*w4xE=+MNjMd5HmCJa^*~M{bWL8?W#d3ZI3A0{3#ShO~oAC;_#a6W( zuWiV+01_r?>Sl_cglnkkD>1-mq-=$C@cj8>L-nOWm2U3L5Ys*reH(nv@5ZH^gZ~O**Bi$o zntD`uEo>^t?8)v|+>VDW9$^hP!(CzD-uQW}PJhmpVCW)u*;*P*VPSXR`t5sZh^(xv z-qrlLbk4|OjL_GsB+0Dk>gf>`zyOVpwg)Fi9m5&i$}N%@@~Nzp+VuJMDW9^;T?4Bv zhQlv?eZvFg^Y8wjXJ7a&>Mw)h`(48Tc;zu)-f3{|%i`x2eHKFTOO=pGK74T3XvrUr z$M4!Z|6+v_YsDC=ACI4(|HWJd<@9Oroy7{LN3gClRm-(793I!5)S+mHoM%3)UY|0} z>`*D^TJ`zE^%|s-@c!e_hN9g1#RMMPI`3e#scOrZj+l15m?ONHp9=0`8SRuiWV{f> z+h3|AVf>2S>V2JQmK2W1xN;Ep==FkbtxeKPG3^c$V`I3K9Eb;QYn%BGi=lKbIa)=4 ziGJxafA`N?Qy(zGie)yhoCRJC&5La?|RTzV*b0i-^Ka6PQDNL%LT~DAi3UKB&Ygtyg}t^Fk9=u zB1onn;J&;;Hbd|^M{Z*fjqMljYcj1k6IPQhlt>aG1L#9UWMsJ*lI!y{YEmtp|;6l&@6oAdAzcC|GakNQ_f8R zSta^B@QMPSKk3yu#cjj_kuX$X!K*a`Bw=PxL;JH;*uP%sYij#vq4Po9=%yzAo){A3 z!)jj}InYzex}4+3Y8AU<>Wf!NmmTSP&<-c#2!4t%tLddoWrO=2iO`eXgdvt|_0{6h zLK&>k=jJlbfOGs&ljhEUMsP0J*i?+Qe4sPxct4&7TM9&cCklH9K|+;Yj7JU;1SvOt zm2XJ}j!%1Y{H|Z;OGK=^DlMI3L&@X!o?>4h;x=Yl+0i$qTe|{wm|b*) zN+S03!(w99VBMTA_H@ z{=W(o!VD;VjF9eW5Wf!feCM#FaUvljdnEYEZBKwdv63kX?ehTF>!_%YXO*T-Ah@|h zWTah3`t-$1(bnb$RZj2II&NB}oTxzL8!-T~wUvPyKb4}!-;@pvaThdE^r^I=ovA$ zU9`=Fx@u(S&7oM?Vm*=TkBwE+P5!dk3L;!OMd>Os3Hxv!?SOwbZB2~70F~RX%{Y{( z$XBBwF0c72Ot)#v7Hi0unK$A_z6hI=y?m(^JUHoKylp{O;N9wR{(y|{G-M-54nT=< zW~P6q6(AH;@JC^KrAOw6izB1->djX9eQmuxxv~Y}S|6=MAdrxdsFkZ|d-`Q{f;?mHo;j18 ztY1@Orc|J0RDqw6_fqJnr9NwgA3X zT&+Y07wgv^omZyDJ~Zn~p`1e|Z!>{r^WM@v9Kyk@gGa@5KWbLvX-{mx)t5pSxkLt4 zxZ~~dZYbHTv3dhvPjrPy_hM9r4@@bHI?_s(7 z;W9~$wI!y>SNC@onlaZ)CpuD(~W9F#^}xu{sB0+IA<4l_)qj- zm!`}sk2ey}3B1os)rQ-dtHeOY%<0OzYFeESBNEgohEr)y*?fHm_e$(p``|XxJq(Pu zb@<76tbPlm{gYG5k79YH?3pTZBDmq+KYwrF!)Y@EA8o5?I_9XG$>W2m`QD4t!Tybz z#Gb#e&-1B{`_-1$*grsHlE_l2CHuyj%Gwbw=zsqkOY;d+6ub3S2F!yvx5o$Js$FWYZ=756^vPSt{C;@wvgw?928M?t%ZRPGFw-WRO* z!_sejDZ&;JFD%a~+96~#8%;)UZy@jKH?!vVrA`$~t@MH)>R!KmfJ!j*A>&C4ohyWd z%OO;EVe;T2ScvcCUNEC3%j$H3)aFPv0UANI@O5(o4j#rm@;T()rk?>fqrED#!RR{$ zS#y2_EYJHl3!Ad^;t-D32MNA^CnMsx_WSf&){54g`YHaRscD=DZtK_tDlyO%`0ZGp zC;B&ob%=_3{So6m31ILM_jnw&&&&`5&QGMT+-k{ANMD=Y<6sdyJRIoB;M z_DkJ^DfF69c-a(Yn&}_M#}W-_ct)|52%=(Q*u*Dr-A{&a-BvH%yf2RKPFG*u^rJ5? z8`tkF2Slb-=eq9lu@|u;w)!B9!r$y)0__)8_KP@GQrs(ym>hxN0-##z7xC(5)RJDm zvF7JO%IBQr<>i3ShOO;0jl_{rkd^P%8{oGyfBG6N>3T;*ix_ioNGQ{38x~3IEwp%z zlxOUaxzp0pZjP4{?CkCq^sZd#&CSm%zc&z+Pv=Dau|fCgoRy0!3Go2rEb8!Qo!NpY z+AYN2QsSRIVt!0FA|m2G`ks6Ulhj}2WQXB(;J*ifQ%)EsEa(F&QS>T1i+Jr%~CZ|4^%#X^>@ zuPzC>Y!ZJOQl?Ip>xKeAPX_3PJO!XA7C=rV_U0N!-Wv*q&6^s(0n$ZNZGJ-dOj`ax zG9(Yky=XN+XwdK&s5`a7=G>e&sBFhe!W>;Clf&_p9q0MXil4(7XK=61juwSsqTB&$ zZP#fWI6M~jOhm;1Tj<1DWdF}8%;khr57+yEzG1BM<(~OKqB0-!r-?9&xP2C%%kw5z zJ8Qa00CU{W;SL+W4T;7cAfc%n)V zXMIJFyX1I8DKI}b_Z%o4xyLnmo!Ydfj{}172qyGktl9Df(^p- z;#4RQw*hcf;kv6MpYasKZniE7@f~)vdWl*}65AWmS;OS3U>=u9!b|UugYNLU&U`cMOCy6H|1ozuo_`DiOl7q^phViIA zfFc4cQ>Ow74Hn(d(!OE56&@ZQt^4&(RGOJMbVv5VUur8Vg!dO(XqK78_xx_IkeyaD z`X=$dqfx1gk}A-u-~$Us_XrYLs3_4-r}TC*|DjM%f=YoA5z*^d^FcsT6mpx6{}apB zq~DRSpTUd)2L~kQ`2fZN?f1p1Mc6nv0YKJEt_j+9zrh5VM+p!wd@2(dRWQ2F?Z8M- zbSPg0J3cXN6~H9pqmD|(Zph2agOvb4uJ@MD)zvkg5Z|MT%8+|m5!%=P1Yo%kPW#AQ znFQ?PO`Py--at23*9Q&O{5RjT{)}Wy5~MqJR_p9#qg31t&Y+2Crz04>fD?fdBJpWA zjl+_fLbNYD0ej_B|9qVt$;HVoBX9_QtM<&e+}RS6k|t*d%Trb65!5dM0LWB8PYU)0 zz*G_xwH;r+jBGQu1DWer)O)BAeE|GuSQPRN;<=fi&}4of=y88~YwRPxmzVf_Ta4)J zhryb2&r{}KyeMU$hubm*MJ)u8_9HF2-3{SYYoBD%i#T%Ky)3o&xnhg;`}F zK@o5Rkl+FV{kT?&PxE-i*wg3l=4-NQe=7d*@_fSnRNQvRufbpEqyK(E_U#u009`@d z>wn%$om&JnEJj_8fQ3pHYWXLJ+k6ZcK37Ag1^!xB`fIm@;XP$o z*?Lhdv;Nd4cBtROPFFuXHPPy9@MMALUd=z3k`#9~ez^D&?mCHF*l#C66tr|b@k{b3>Qg6@&EgP`D|Hs@@dr5A zSJd0tu!7CTd-i=;S+3YQgdXBz-$%%b8Vj`GgyS;m5EQ^)ztq2hUJ-M8mrpQ$7zPmn zh%lt-ZofX)MQ5$9WR&=Gwm<}(R{l%W8mn1SLFAL5xXQQ=GbJE@p(Rc4`DX}4U+Wx-S%Lw9QYW2PRU~@` z9{zd_4{l6oNdf2(mVpO5P813#VBt_t+>3yLeJ{}bN>7saux}q{hRX^IG%2sG7$de| zyqbT&?{3iiib=aV_uO!p&Hh5mY&aROns!Li(TH{HX-UMeE%zIr4<}PK7PwSGlKCQ| zNLmdZG$0yZ5xL@cNyRj%yVCI;-F@$dv<3ONCpQj(^3H|OQ7;hnSoZ2Az^8e_YW+!E z4QfCXdP3S!N&tuAi5mv=C0C+P$z+1Kt>+O3xtTkm3QbEc-RP6dhAF>P*RC%*DL(Z_ z@ryDkuFYT)M$4EM$zp^?Re!8hm8vivx8SrJBlW`C>j){22Av6%4C-fO3B3wpfTiR3 zBbxi$lk~flKfL-!$-1A&w*M$WwqI-sUisF|W-*2My`>&9xIwE?u8UcEgjc!IuBcXX zL5ARo)sw$|;(qb#@x>`|7|zo-jvEVhWd?2V+nO&rTE)B$j>gfOF+3w8Lqg&Y6wXdJ z^Hy0cm*r86<-yjDpt!XU{*7R|OCrj=U5{ng!e0iWiqT5LJ8$QeTP;=yW(#;zyDyu>|KTat=G+Eq!mf4QYZCZ~gZnjL)Ij9w@d&MOJ2j?0N zH&mhHy880@*DC7tNvnXb=5nS7??et9kK0c$_#L+fWOf#v?l2ORiD!kle6f7)FxTcq zK0DzMy1M5=Z>i4g8`OhI9NN*1rn*+hYUxJ<5%P@`p1dY=nw&)D=iZa(>P5LnB>23_ zO22{Fey*AMYW6q?*#N0I5sWT;s-ea0hMC#0g?jg14AC>VVPftWKf||_KS`(Xo}|}^ zX5ibg`7AsL;|xqo_$KXQ!IB!l$h_dbazp=2-#BR%jY+%V&ha*<|8Dzoz0;47p+(Ub z5i6$$&(dohy0Y}nCm1JgtISzcFgwv%0Em@9I_16u<#P$`W>*}v2MqoTc9*$5%9=EO4LaY;SdEWNu{s__Y^$J=c)qTw!$V-RzF@O2tF2_!%+_sqprN zotpKlu;N5C9v(jN*ZY?Pc;b+4uF_DAES(bGkW;c~&WC$_8A4byTsASCDOevXqN%jo zy1pEWIc|qwB@Y{h=+n7T$;4B;H|c%E_LPM>`JpV(H9!h<2h?Qw3Z`fIm)a+85|_ej zpgPU?66ge-a}w38kPaZIKYzEol?h7CE!G&B%I?aChEE?Elf+0!efn#pw8Xydb8dDC zlksrXu{#hDW%v6QyqH`jW@RPK*{@&o;@S1Ai)gpO+tcCCA^e4F!~|X^x6>*|E!;oO zZG$Qb`GMaT@wOj`EiLHL>$g4#PhtpZo3IzZfnJR?DAB##92ZfxUwnsf@?tU2Wp>i! z`R9+a5D9m(EVA)aA&msact&#+b4JPR`dY_tRHRe2K;LPT<6fot;H7@ zuiyjKXg%hJDz|hK=jsJ__7n7Q=7xwkI{W*H%;l0hCkq(c8SVJIm5(@RT2rDIg{#c^ z9e;Hxk6Edyo12o1+m=wD4y%MJ$?RpYBw{?|z4P?x1mPSvdqPH&^i$9ZB8~b!FGspO zQAVERw0i5uiKVRlT=rc9AaExPpDgSjU+R!}KWL5E2C^vrn4M8Yw?HF_zMtvze6rr} z^qK1~2mVIReee@&y9vtXs5$X-pM3b<=wy}H5EqXC$W*K|8DpeUhK{t}Q8)@0gnca4 zc-?bJ&W=`##E4h!?{6T`_uTX7UB;zQZgEfWP7L66z*9(^o2TMdv7_q zq4e9zOhM^ctA+DVqOZCwtS9Qwl}*8V0vqm*aH=45xJdVCX-#%+$gA3>F=A#1U zkxe#kT-#@Q_-tMzGZLPLQqacpJ72$y%o31oanv}^V9=`arrcX-i!l6lRYL2f3&Eg4 zT_5xgylz2$%=VP?{2|R`Jz*umZRFK%3!kXxlM{kS*$~Q1rp8t4z&))}raQQ&T-%sd zW!4c{z!A$US9mJBm!B5FW_*UoH6)XRJ0h+|%^mE6rYGQ~>~k|qoW$mNVKqfv`*A>m z@ldrbu7p39Y0H^r<_ApAmaolM&#IL2%!wY%Kpv}mY2xI3<4ARrg>5TUX6 z!dklQa!oeMZt>yO;xeu7%VV-R58&}?PxS@OnHv7)E?fHp-dsp2njhT+a@RW^Fnwl#wYzYrYv_XOX5PBpMoKFOoqM#aR8_LLb; z^K+3407DYcK!WdNwECmiqHz@^wf) z4G6uz-IFxHX`82SI2@eKw0N_YuF6?|`+j!?aG>@_!B?lWSyZb+xWufT5R@TLn9cNH zaUB&U%&>d`JK)^*DL;)SMSi^6PEE^g?WVj8+$GyD)~DaNjh_7gogQUSCbp;uu@ zTyLl+OL{59wR911LnIB@UJ8^c=16tzqZ=iCct90$b!QApeBJ`7_1{Vgu(#v%fTwRr zH>&9qp>Mz^dddtFjKRUqUE3mA3wqRWr%YBIc}q<=~LMzm}Ml z7^mnHJc8$)4}0w;KVrhp7G2w?Q8m)g;k4P|9$g}Ab0dIwVWmg8ed44=xyoh~9fxSN z<$o zuQ_a&y2Eycs(y<`spF+2^4yRWSUyA0U zUWj-1hgA4xdTl+xdJ3#MYZQrk5%)+4fcuI?L3s{2#(=zv*VAi4Ej3xB zS~+{F6_x%9<&-l{dxF~rQvSBfXvs<|t0fFY$xUm%g%qRZejvrk*Ou?60qhnXT%l`k>PN8c!Qb{$FsWubsYHg$XPE74~rg z`J*vmQ&)7cf)TVzcW5+mBk_4rXeOyTdn91H0Uov+0yCGW2~Hojmk9H!BjwhedL}7z z?IhT8*=5h@!(pC;(d*sO(2()(?8SDnne?I|n4P4eayvPdlxY!2W)o!1^;$a>>ogiK zq^t1oTHCO}!(sChFv>!#VpelNDkXWgrHj*f32&ESgYP2?wV;1uPazBK4vs{JonGqO z)62Ik17#D)Ib)=hu=&s@M*Q$u^sg3;-Mv8RN`(Z5i!k=d+Kf1s92r4qNK||&r93)G zc)YDK+$sblbc)zgm~ z((`KS1Hcg_U}sJRM>OvCh+2WS{Nl>UiZ3OSjUWk&w{@aieFgh4D(u4nh5!4*|9iqn z{$JcA)yZ?YUp6kjR#r@V`EcMoj_1{yDv2`OD_dZU+RyCsbWQ!j8aL18kOB*|hjPF< zUnEQ`U)zcZuKowZixr*&7I(FW1*)kES(S~Q{P&f1FPJU%VFI4?Xk+J};vi}w?2Fn} zAjj*Ur@?9X4OZT#zdaKL;7sKF`|Z@vZ=)YM5d8>Q0y*yAWi;vB+w+|a&Zgg|zknol ztnjVTWunnEyw3a!0ZUT^+=qEfE!a`y{RgKMhKEIZ+!e5(%h~nM*HQ!^%EH16qsRXb z4OqhsM3*Q3j^Yk#YP7Jpc+hvGjXrXk+@>}5GjGvrCmZa*2j8466LACFNZ7kndmf!bSX96+K+ zteh@h9!lfr1HA#^1T4|0xU{(d!oH9U`j7QSfvtC^6;6Bxho$1rooQS^Khk9P0(}eM zZVM@ygSGwyHk&beL`1|~PjJthx`2e&bsxZa*g{&IBkbu7Ed5f_h;Rp^)cKloqd3jr-f1n_;`N9~k4Zg#4a? zvP{v%;Hh|IL4U2Hq9Ow>)782cL;+kDqi)zQ&TTt!?|5sx^T)>LB!L)mA%o$Ml7EZz zv5v6Q5>+lC1C`zeRnkOpKoR>;g5-Ab^SgVso(MZ=sNfVyVb(ubjS(_g?g%F2w8EB* zc^JjgdL}9Z4Lf3K`}OL5>jj$E>7II;W-(my{nzTH8jmTH+1@H@B91>FcwVN(rdF&X z(O+xkY(8{xyuI2UfRM`V@DSWpQfSufvOOW|YyRT=0&t|qbZCBf?apx3H#NNhg~$uZ z?0>gJDliw5Z{3A>>tREE*S85c%t;k>)}u(y8=sH{aQ#aJ8-#6ZZeM(w29-cY?dcdW1P4I$SNm22SfVHx7~NozC}^j0IhJhn?WV)HEg#T77d8(o=s5u;D28pu#RL_k z>17f2Iu$7Pg9mT7916@tw%o6-uGGt+i44+0IP;)hF1A$t4^^C69(FWV(z4U@B^hXQr0d~|#IZ?wA~};mv)1dfL}qt) zcW_3K8E_eOGr$H?GIx~7mMAl4tSVHI+p1tU~eBs z3H)n#SwZFtl{xf_P#5%p2B@t4RhcwGTCY%`E4%j<7#WdHC& zpVs_d6@HwNbp-ws6_zrY=|HnYiLip#n*-6j`3|8XYCaxeq7Go%4A4VQ3b zMnOdlC@r-eD~MNcadr-@u6B+LNvOl1P!Dtf)WUNSb097v?rr5g#>Nn5_Nlyr{9WVBU-F)&GR=!95~B)a1vF zw$B#BD&eCuAb47OI^dr5nUr>$H7}2sDbV)!HatUw`NkL!Un&M*=}-4 z6^dZ>mw&A)^9W#Fwt31t2i}GX=WGQt?_XlUIK@&c3B%(_(5-q5MEkjt)pjmrXj9@z zynLH~7i-&J0!La1+C=&i^-SrztFUOelb1{!iDrEsl>h>~Vf*uRM}^}yJZi_=Y$RnYX) zEi|J8-ee=e?z;LMV%j_Wt~9lu3r#hXuacHq@ywm=7Dn?&1w&OpnGDN&+#o3&Zbu+% z|DvDBkBny0U1LY)*6?Asfd@Vuyxg6pZ=hXsP>d&ec&##DTfs(u z%0l)RTVkM38jaV+a$>opG-_8W#deTm{fLCsQ4GP?U@phhPfMn11U@ zOBM9MoD-ZMcag#(>zu~5G)n2f3_T?m$@cbO{kAdU=;SgI=}%(`*$23#tG=#DN8jVV zY_3ofc9R`!cTdkem+CsLt6l~sEp~#>2sv*oYG*cQ@{kLMGQ+-C7qyShtLnR*ZBl0P zdEI}!qq5Y#e09d8^h};_k%2NDHmQs& zmn*8*e4jxQ2@s)Q(J}cN)cw7oJ(f$6snhoS6(-@2FZTh;G!)$I@vU z+#}~8jN0Z>O*1G5;0I$oX(()K$SZ;Id-jlL1l&G`?B1{MKPt_ygtV^q9i~oBpWfT$ zxW})wBR+|*znsiqu2#M~leAr7)C2!w?v!AGS(B%JgCe>7Mq>W(Z4T|~LY5aHw{w{V zB9lJE@>5Q9lCY8W!2aj zK@3mKodAaJ(v^#(?GgKtg^v$IS;#%5Ap3KTfy$$jC9TdypE}cUi`ZP2)rJqW8rKZk zJTM9v{elB$V~pn#l^vCK1IWEFg{K+1oj{k*X5=oJ^2lnudd%S@V^^* z#}JOs5Moc}CCPqr8oJ+!T=MP2lqFjka(t1{>j4Y0+XZ0hNfg2$o_wF7ZNGk{<6X%U z@?d#w_F@R4^=PkR@3&)JR4EnxmETeNX|aMp(ihH0GVG_9>5f9RiwD ztBlwMS_Ih4$JB;0o?<0@Q|K!Ak@992RF=F>#-Sz#QrNn)8Y>psHh^}=zHi z<2UoX>-8NP;D=Vc*=V>wp*(Itg7-cg3c?yK&C^Z*_963@=3fK+;CQ&%PEX!iPjrT| zCR>8-gF+q`69w?qhbFhK5Dph4tLfNK95Gc$l>dWS=ES+Fc#dm6S?FQeRKjn6>AS-V?p3<&eC!GV^YwIW=pY>g zqHn-8g(Kpch@hXh0LC;Wf=(SJi}xfGa6oVUuQbIW_zBwFDY?2z->ZwWAD@Mk!PRhO zIj(-9|BPtLi;|7e|PUVS(ngGHdAxK8oPG8L6Qkd?hLBxO0^8GLA z!1vnb^TkNParoTnoBC%f+(GS10z-o$4)1uPrG7^c?J2YzB7*II@P`5lj(`)5mBplL zyF58I`_sR%3HWg*;F+TsML3$W;VmxwV=KQCIscAsK)Hw!EL+n5o{ zsNBEl>7AvG*AB6#KO;4J@pr3$*J~)iZh4iT7AXRTNbz4ow1Oq=cO^K80>N(y|JQGQ zc?BK}J=}sF>F&_a01DGlj&FaWFzVd9AUec!w% z6{+w!_0Prz*INrug-y)kcj^7957>7RL<(m(*2Dg8Cp0iIs?WI+ z*dCkBAit8YwH(0tvxw~vVD>QAhSpT2uLcV-&E|9BBN%}U#vywQCPqPIT)yXRtTmXJ z@$AP^f8`=n!NjD$DoKl!p&WgNZB`UCC$zkILP&=F~WZ&kdI4Y6nM@5&*8<*~p=%=+vw+fv!ST>7QZ|lStio6 zgUalCSiHxhC0is#ZX@(1ERap=_^{|Z7}?B=6lVKd;enL|UQ%vvxigf_@)IeOVJj(& zd0m@z7ad<52x%1a-Y;07VM z6&1W5%&gz|j@+e4CZUHqf<-wW5lk)tmuut8?_=IV*JqCaQ9mmC$ItJhXFJuO0Wa71 zCs*N~bpLp^BZz1)afumXu^tNeCo0S5JR<|k(Q~lexxBkuE@<}8X2q9XdK1s@wx{=U zcLonMjBDoGZqddw3We`3v^3ox1AN(cHvyk>gviTvYS}#jd1vIE{6t)u-tLGj?IcBm zLp1X%et_fs=1kMJRuS&XIRCcJ0YSn`f$_6%q_EW87-`Ug(eT1kyzu(VRSM7mhD5++ z2>9z``+%eD2BUnF@w;{blp+qGh4R?~M8E~bTwH^$b7MQZa@ofK7a5edZx-LZN&u!R z^oFuODed}lUr4=?;zl}Ah~UMa-4Z*6+N=qovAqDFbQcm9rv3m}%bTM`Sb)snv@^v9 zTHr7MRHt^?3U;b`5~Z|-2WX{D<#StXm7y})j>5DJiKPQ?gteroek!TLcfGo!dT>~J>y@^&6AEs{+Ny>X4ps}I= zi9P)JU6qHAucVfjm$z6R^w+b3F4k9IB=6pTPGu7t#{!VMV30Dd3Z&NZ=5asQkk%VX z44}cDMx?(h*8ay-F@*_p_9qeR#r1%7+OE=lCB2to=vyp%|7}0IJyKqhi_Gvc;6mpg z$of6Dj6J$bKtOPOKbjL~1vIXzKwP}BA2-Z&a%(_n-dv7r8Wtbjo!GP?NB*-DjEE5P zsWn}dVcp>;d-Eg?T=fo*JPj3~c`;+inxKgohOF#qRMmX?^l7TugX8+@)Uej_J>@0S zY$liW*P!^)RSvN69=2A-Gjhm9L4SGhd7Uu1>3@0l;LlvO!=s1;)D3n4_NqBSFqYGK z>RL!hh^P&$G4{Qy4t8a^VcWBZfQWZhJYZYvP}PDB}$P7)ydEDJXP7#w%pWuB2!0e^)RnIN@a>G#u%Vq%eOSbYfYwUDGLLxA*o+U7 zMDEN7t_Pu+cUvmm1}WbvrBHs-1FFJf?oHPVG4e$0BwX3Q-jwafE?B93Kt%)e8UD#G zNl0%LNzqBhH+yu+VE?S&QOj7LOl={fg{z!p$Z=jGl7#!;so9ty27I zWg2numki-bzj7#UG#ebo+|Wb!CnvsGpLT5@zgg z0c5kry3LR{RX@=QQtk0qy_iJEpKBgkO+bmRwT_XKhqS}nHv-;i6cO|Nw2Dc?wKO?d z<8?fJsX0j|`0}2mQf7cY8b9fRA0o&XKc?nLb#{+SaNIe}>d)e{y`TN;xHq@8b4*aL z*&D6}`DJg_dFjE51Zzn2!ytsv(?@Qf1U4s~DUQ|#A})q)SC`_h`jP~>@qaZxFjS`x zdYI+8`hIwOJiA(-|EKUg@$nnBxc8HG8VFn^6CDn=e(JpnZrfAnrIT0TKFFu}?j!dJ zg-qDvsbyaEly4i)oN2rNI>Raaoc8MT51#-Rk_n@gcf}*m9A4J(BLC$A7<|AG!u%fP zZwY5(|Aa;p-w<@=)Q*Y*jF&5d1^KU{kTadpI4BIDC@CpL(5th$rIPFBXv8x*dux{P z0q7_Ess1blJu6CZGrH;FjD$_w(n%Cw?oFTX!KZIgaPvs{_6dCN)ZVbqaNEQXAD6rl zi9ph7HM(n|kQrPzKSMET*}!V?MQhAfL&+SdX&eIVtL{3gZeC{vP$?Ol`Rq>2cBb3V z+n#8LZeg^lr_G!^;b*|f;`QTGBy0A+$>NJHv?Dg!8w;jCF?aIT;6w&l@*9U3a0{sw zd2*Rnm|U*|g?5#hverFwl<6unP0{9;>n1b<7~7nZ4PVzs3gys;QdvpX2YuhLck*-Z z2JVX)()6&qHsO;kXYtzBe8$+~j3yRxi+hpE>lAH~rbnbDix2wJ27R1sJ?nXYYcBXp zU{^4Jw!p4jkw93rXS-^tqJnj5YHE|+O^-H-Tx!s-2dXdEpC#n;tgh?^x%B1=WA=I> zB+nxK0=uze1wb`XCh6;$jX;&_BKVZ?hZ##Jt{qU-BX=hok;)9!T zsp4X(=gh?LY|EpDkc+uaDXho+K87vO$JA~Bg(qY)L3G`lE7tb@4Y9z&Uw*h!a67id z4#54#ce~yAeJ*Ndjs`PbMOc$Rg4QTl1CkMFZIRT^FIZauBHKw3J`uaUAB+dyJ(k|h zMgk24!bvOYPFDlRR@;?s6}`)Yp$l5}usdDXCw|!}-h`_p*^}hG_3Xffl zwSl+|vVN|gv-Rc(v^N59x|91VK4#DzZRcG5!RQg{4<4bkvOxu}*ILidhr826Aq5Mq zKn<&LKig!;x%1f{XP%1($Ed42i_&5s3o`9zk;jZ&?QSlm#$)~?XIG2kB|EvwNj>yx zW!QWtTe(Olen$_me8{PkTIcqq_cEFosUP&uH@E>oh3)XV;n~%x{x>D}j8CuiOQ@GO z_YxRJ8Fs%WMI0Q-7h&R^e~GZniP!67zL!kVpd!V@k|N8CPVy(mrN)QX@TXRJ^vt6T z7CLy14tzvM#v~O6Zhf4s>1aiv(xe;a?tvC>QyrrriFUR(mVrEkuDU{TECE9epOIHCp}a{0bLfpgNB zYjVQ^mw&4Yg$#od5%D(gWtv4fQKY;oWH?MD%Q-wwR5Mo~a=Q0MGO8n6(Cc}@H4<=sJ_Z~Y0#FiDOkg}%nMFr&u`u^yV>4)aJNj*h`Wxt2<(6s@McMRx}t;k4fa zcJuzX12}IL$oa`KCLKQRtXESI%~7_5Qh7jhBXa~Fx-gE3%@?|;?e5O%hS5CePx7(< z;gduHBE!y|$3K)CMDYYpydpo#t$p-g2n791w(3Q*`KGGBE;kh2>%B_&^v+vekeqzO zH$jzwtWQ@|RaN!nqOdse*QI_^{W7bhoWmSLIsWuW+$NFVVag+X05Yl+sm z#(th}W_R#nF!6C9NF|mg4gI=7O&6n|VUQ3AYCLzyi+GDg#;4Xo!y<(y*9PRtUC-hO@KaeW!o{FJgv{TNW~Dr`p6qn4U*_YMM4jIt|IUi@;RA1g_6= z@q+`YI+Q{;qUHQtVY8546J+)$TFw-|b8d7h>_`1Ap!;!5!<5RW>Nu4&hcUj%YJ`d= zuL>{C%max2lyqsPCnO|nF$ytfv75`PS*Qm!$fYooC+Wq_I<#GBn7gbWAZvGmep6*2 zn)3iCkNDs_;VUPLQ2;@jVrnhwQ_!l(uzKm5P&=3!KOdhklK#}L1koF`D*|3Ul#G;g ztff!J2EqayquAD1aneh>ieCr&j-kP=2RrAYZvp z_#jO88Mpe$#B@Ke4);yb^}hEo>CL^~Z>%VtJ^(U|##vAQ9UIBZP)Noz##_fW1O2TM z_PaQmyyiQ=diDVwvUu&sraaEykZ;m9vj9EZG!}B{1h}ZzLkYrNa^35t}dD> z&|GDZ5yHHk2th60PlBMt&0xtu%ft*!bZcc5zj)^DiB&Zx z>dD%BUmXKjisftOZRtmvf)YC{ZViB)`sU9Hd=9$_VC}^vet7*!JH=+21^v;80Ibp= z(kOP&uP}lGLP^E0^w_+~@?|w3PiI`l>*v~8|Gv5!2;4Fy(2H-S454}tTzZea`B8#af6Rx!>b|naviQ0bM?yllPz9uwWgxyN9##TzS5BO zF8c3}rcJD=MpOzZnD{Wf2H)cj`p6AN5a{4|iG+tm;hzEv_Z%AKi_i>C0cmO;`mc$V-L zNmEnv%_BhMc=A?0O~o#N)}{L>XKt}K{Y4b*I%d@~vDZPBAUBPoG52oMZqdw_9jxjx zG+GvX+<^a3m^tS+sA_=Pr3EpM(XC4b75ARZ=^|+Ens4}*?R{sryQ|ujbhqwQxP!u| zW^KTrD&FUb?$blFPc`>IaZ4^e&Quu^WhvXwm5t~18-1g`2LKrRj(Yv8Jy4?;^a9Yi z{Nsk6Y7?bfgB1`2!xEL8-mckTrN`R}sQxFkZVbRVU#e(`QBdX@Z4-U-6}-OuL%6uP zt_dXdEYq*L`27km$~%KXDjUsZ?aL|-ss7kNz!A9;J}~1(o<@Ly*fv-O(@#V(YmFYk z!J39@yWTks|Bg+ZD6wt(lXh!-FTT_Du{A=p#6Y4ywonfw7h=f>*W4FlW3l_#8XieS ztLeTVM_3o$$D`^z)u>107e!O?1^v~ zqX~C#O^qE#pLa-v`hOL5m0?w_O?hujgmIf*5?(XjH+H`kp z;#(fic|ZQ}gKKZrnrEJgduHxY_gP^Qrt_RK9nT<~E>P$PYWVESsPfM z5b=ZO{!k|h>sXFN)XbTQg7#O3Z~kdEl%BK5xU5hBHHA9(c4+hclmc=-fUOk8P60Ov zAfTuFL4f#&e+@L0AVYXNkuQgTeYUPo_~iq56@VPH3FpCWvZZO~iNJ0tQ-|%VLXb`h zph^~z@sNL2;wQJChKx-Nsn`rl6KV3F=PQ-}fNof#QArAPi)FV70JHIk_#BAXj5?oV z=?d}(IgB090GEY6md;xc37;r?47h5QI(2!>I5N*2oDBfgxlWoM&24OME;jlm<`YH4 z9}3__g|ZI7aV0zr@#IR?X;k%@jio96b%mS67C=49_j@B>4uE+^boI#pRR_4>PbC!U zP^zqzz#WPR6lr@IW@`UU=9w1~JY?VCz?v+13ZQS%Kty-L|3F+GY_-gZ1SZ9%e#@{}8{!3UQZMR%bANn5P-VtC6f(4eyGfKxUrh$0g%X&7&N~m>0$9hCwCvB;_$k_fKS_-N#IO{cmHZDO$38TTA@mz9 zx_@t0kCwxZSeeOiq9NdMXl3XYHQe7@P3OyLw0Uv+Jm08O6vpLUfu5-Z*TNwpM#ud??$FmnH?}jud0}y}(29*B$rl0* z0jTbM3Hf;Tx4_o%iDWL84&1L+rh^O6i6~!EuE#$kq}10c5A- z+w`>^fj0xEh(I@?YOLqWle_BSdA7^r_17DS^!E04Efjn>4N~CxQHV!a{!<@C3r6^J zFosINFhM2VpLF>s+PViQf#kO(0`L-0iY~kbPI z-8(W;Qj(iy)}Z54gAtD}tODTJZ;ikON%8#AAn;etrEoo(4z+ry{QzP3LKY$0SV!q|QhlO_!v_u)-PF%;lzt`>c|F$e?69;iS zm~Lnm2QfD{7)>4$5xYz-`5m@J^nRH6M?eq>*GE*L599A~(7utYNktD$q7%sg2JuIII!3o zuVJaVn?j6MgG#Ch8654~fD5*i)IfcLBqn3-A%5{gxnSNv7VZ{_9%SIH${qBlFTCnt zX$*V#6x=IK-B|gpPv)=e=z&7!{IBjLH!Nb*C#aw@>+VVXX5hKr6EP;o%=Pjr8NdAv z#Mb)$!Vu@w8;nGP&5*?HbmvY0x1b($On-HSSRyA-g&EJpTmyDy1GTG9@Jb!g2;bu$!R(PMvoXl`iZ1#7*$egMC~4qq5$?XnPRjD-S4^QKKTA zBl_KRCRnw+-!XGPc6#~TxI5u;5@iH9I;h8ws6pedR<)y-01fU9uQMhhPSt}80OcOoQM^q)ISq_>E8crwd9)0 z8wsqjS+l5z5@9>e=os%9PTFiZaz(m(w%z(VVT3WJ@lLFD*taGZXo`C9<#B`g>+D#_ zqDd$?nYl2cAoV(47ZLbS;Ke4wn12Me|A-fqmH`?PO^qTzbsUn0mXQ4zgByqD2vlHW zmYn{z6to2&8^Y6f_atUb!WV6x!d_={S+iaWki^@!Il~Rxu1FNs-eF%f+uUtsfnhm; zB7*S0mIwk?qXodn1Gl$IsTy4Ri8)UNyVHZl&q+TLltsE!)XTv0F6UNASwP}jT9Q|K z=q}^p5k*c+*g>_z_FiS7-ssy&;!HK-H}VuPSdKk%1Q#-rq33dPc! zKP`a9|Fi&-yd9=8hyk)2kLxbTw1#WcJzG9;aRuyLd&lq1spv{g3!N-tm+D}dR)L41 z^V`@D{7JXfKza2i81OQVOFs36{G zh7Qoc>ax$g-PwWMJ^!6ps4=o+#)!}fc{E@*OG>Sxdw4>$?sBn$9hUBb0qcqKw)&wOL ztC8cLM0t2MNLDaWr)E}jI!RFKS@&5VY_bb~yi^54G2|OS74=-{3l{RyY-096J}Mga z#u=8YYBIAW6wVIMyHHIGJ!F+GtGr(LyRLyG8?^#X{AiWo|v6Wwy zzmzDs>O6_b{%XuIyp{MlBSd4hUn-0BKPDC2u z;mrH(4cePplWMiCm~!GKl!M$;5v^xdxk)2)Yzziv(WJfcg^@BV5tpq;oA@C$%uF+C zp}~k*<|LANn9$V39-$pS~(S zUO5%ra#q$jx<(a|xt5m1c95Bv=DJ8*y43p^pDC97yuvShWPHV8g|t%Z~MtzY)xP6LdOpW8n1lLdA#>J7w7ShSs_ZpFuk zLZ7KzjtUtIBu`#^uEk~21V@JkZYp)Ae)opFiCPW~J&a`s4@?{W#_!=@9Y_-5)ImNK zfz>WML?fW981McHhcVlqHBN{;^Jf7ma>bjws>BI{)^mj)ULK6_=bo#G)?L4TZ=ZjH zgrS1D#+^kPHY@ESTkqfJX%I1%H3$gt&+q1y5T7wu8nrrANOyjEyEcEC8NgmLk!1&v zF{Hf|fqMVi%|!7IdJ0bRV+zx9-Vl(B%RQ^UY1o&?{Xw?0RtM}mV(6^PB;1(6X}Bcs zjEzfcU!7(!M(2l3$(>jT#z+^t3K?YHW@AaiBt*dBMAH|tmzemnabItkKy`I>zemFh z%!+1)f^P4q-unQH2;UcH*69YiBVddi1ite9+Sl|LGJiUy=AL7Wl7;WBN4}7jk)z5t zb-4Tv;;o0_-h9XU{=Z-Vmx^Anb}obbYnI|-QhA}ATz+L?dT{f4%-{` zoDb(U1sK{E)Lzk;7Fq#f|!*UivTvClrYt+xh(6<-QS;c2-?aQkW=ML z6nUv7k2IR84(kh%GA--LC|vD$TVOr1`|kImPBd!VDS`UO=&cXgYxaeQd(vrIPwXIF zrQ@t(7#Jb+&z$O?2712*G-lpG-R2|K(}#d8bH9DCSKsYB^l4`lqL5>Oz65QQ%@|r} z=^dG{@#=y}L?Iv^9a&^Yv? zZ~i+|8mKK*haD$YL2jG7MuK|${K(~m=|SP{uJ=tw@W&r<5N&Tm3*vkAwbl5Yn~S9%wxUvNdK!>Q2^-=vo%6 zR_MtzSzkG6e(lSx8#co9)ccRFG*Pm{IQHn3kNooYs;9!{HkiPbn7_Ze1GQG>6Fjdr zxdJ7V)?&vJPY92LdJBVoq!Z=%N(R3>hWr@g`OwDmV{WlXvtot$!S9g^!ptML%lr42 zASR633^K5*sP{U%39a$@Sfufwm0X)%DeDY&*;?nMqky@F0B_sK$@^`yo0V0g1xFL~ zs)FIyM=Rs*5d5c`h@=xlrq$%}@-~IjjrqLd3e%}uoc?s||4QX~XcH<9I}?H1NnZAc zLu4zyPXu~j(>4#kph$RnHgJ&#dBZQy=Y{i7yjf;{!mBUS?>qEW=v?cBGu+IkP9DYu zk3o}!)YfY0*YD2Sxvg_Nd0rC{q@@8vO&Yft=5vgf3{L>9?dI*MB@V04TL%z>!5our zYjTIzJ=dEc1zoX_r>_^IFm+tu3ICS6%k_rqeGJydy_y*_{>d`%;R)MM0lW7v_1j;5 znRMX)ZF!LNg8CS<@~V5;dm86z2kvxqD{%Dpq%{K~9UteJvTU`P=r=QWj^KCt-Q0Qj zd{d^MqWsWN@$kM+x$9KyS$*?pemOnC-(StgasWLL1cW7twqHg5n~4U;fry&#-@FoC2x+)M zvF6G6%PPs>&yG}!SBvCan$H`-QX(Gt`H-2>CNsp%LpP60QgXs2j^UOM0R^VPfCL08 zqr)w^!$@gRO@ALG=jJhe_x%GZ z-C)9W)60S*l2S%AzoV90V{_YDJI9c(-ifWHX(lfRW0bsLcvsj4i7e41x}*A8AE$n9 z=h*a%1#cmb{+?*Hi(m)4ss-Zl6m0uh!yDHqCqQqAd{5fpsMI-kTv5Ewe%bFpEIh40 ztGAhD+<`sf%J+|&?eE@uo$`HJ?}Lu;`APM%jK8MmzG1%iB;JCwt+iHO&$u3#Qw*uI zs`JH5@a@aE!C5qf+MEOO($RA&WF`!0fOn|*c0j<7LSGDWVEC}_O7sN5dV!edUHc+TITZO!php+3W3+NH~$<?pjh=qG3RWrb2SKO(S$WknWr#`vU=x6JV(CP{$NB^_Q{ZP$}O+Yf4Oa+ zyAHAnH1uOpkfCpfZTQoJQ@}z8O`xa}Nyqq*qz?*+ie<5{qd*x^=S3?CF z@eG%xd}WE&2sIsj0`ZWRi&lvu2^ahZ1#dd4B3?z3`H=4&&ycuynzwdm5%%W`iL{&! zqu^)pT6jW6Sx*IvOtCvgd81HoN#8IOgIFH3^*p@w-Yn&6y8o;gFkdzW>Qw7L;1 z{Gv#?(-m{m^}j|z4Okq8MzeCkzOD!}#iuX=JNv5Y2E?)t&U3Y2p=-|g;Tx~86>%5V zu@t(G;UFt6&iPenw*1!X^hJw4#^4@SOkn?3BqgI{S_^k^mJ#Jg&p2SsmOj}vpUF?^ zb%+KdAo}>VzNYu@yXg1syX!kz33z3$2bvusN@mFVe6&rmj!2RN6Slb$XAAYG+?xzp z7!Pa(gx(DO+Dtk*?=PgF@``gz}NHL}Y3;V{G4`-JvZ zyugqly4{@Ux`Y#I{jMY^9sf4RV9=|gwz~I_`-sIr+?8O}8-n+BDQfcy{7WdNj#|AH zs))V#M@%|pj^dscwF<2uX-SEJWhw?t-AhoTAVcqWLgmR4Q7YLNZzv_trp?R6Bb)^< zjHO%KP&NssUsu6uhr~a+m*hT$NbCR78I7e}efPNVz)zi+M=EU!#+s8NjiLo+|MuUT znf;B4pgwh&4-&TRqqeYlbh6bB7we)o=voLb5V>?m8sW@mL&YwUhfHpJZ43!6*l(ib zI$i}Su8l>0#f{>Y-wEZ=ySC{ib9U?f*xq=EBG1U|c2`v>JjmABRlk)VN5$HHHQH}? zG4S0Wd>VsT{9NgyIoimNg4}`btIy~C0@Yis(v`HPuk2uS?Nt4DoZ^!(i9tN*l;0f& zX6$k6YGv)?@(+W$^14^Fw<{ITIF!x|a7W}UaXRkz&;CT)1GOhy9*!t!C%T4advCD8 z@sf$M@}gmromOYp*fb@|S?pM+!6iT$lW_4Xys0fwkgwWVSFf@t8l+LqFW#G_pUUZwaBeb(Lh{)v z<=a2g{QM^4QBd5?jjPWkJ%aA_N($E3?1JcT;w#xf4IwUdI&P^@q@^-GQx@awU9OiB z^7x)wkS?eCBauQT%;r-bKF+t`>_( zOql%Tj0B(0 z%bP+S{>{xq#yR$8Yz4jEnzsnRo^8h;0fB}yOwZ9DQj_p(HICry< zNbU&gaeO~Cq}9ymHnFn8wXT2Y>rp(czJNEO_WWr`pXFHlb~L#Z+I1nPj`!*s4K!s5 z5rl-9Cq2y;e=4E_ zheOc_xkuQ8?OxqE#+Y1W4?x!8d=H_S`mK#GMe&_zZcsr#4EFrk_UB7bS9Fzo9v)g= z_90x-ULnVYpEkE*;Te%_D#td&Pk7EPB;Ks^MnzApLUV9%5RZP7ZpT1&DQelc8ML)M z{&LsgXcVCgX?aDRU~$uF2IkMC{}yeR;Y!Kd8Jv%s4l zJn3fZII(rbuTRWH;VjfDOt?+pi2UEQ%&u$)^!uL%rx3Omr8dmj~(hvs2$acFhuk2=DCH2GwDs>iHx(-rMn zV(l$BzWIyap)srfxjB;vDCc+C+SJjUsY5Gmit(u=0zp4oI%*N9V#xGkG*%Vt3`nd_ zjtwZ7Y+JX`@z6>HmKVq)`snuE-yAD(i5Hb@Qq;|hja8-y=3g|pToI9W`A^o$ziwLyR)^fi7|E-$5CIFl(Ru2{gnoJT}So+7KlY2_7cv#<_0XmxARbw_}J{<9%$w&;}1FdV%5PAlWXH=VqhvA5Nr!lw^Qj@sx^F5hN z=`SdPQHOzGSDP%IS#k44Z*rf*#W!-iNP(`APYoU~P-;vW!Say#UtW5AxO@!o9d_x$ z%lyr{)+Q+|TIu#8g=z1cexK_q{|F4s@?9R<|7ge4_k7Q9`tm|Nmd+Kd??aToOQY8o zn6fufCdcLOM#Y#3CMFVSh}hK)%mH!M+8gx0OXGD%JyIX(i5Ok+hF%jEk39rtMX z^vgO_p`RyU!ej!mB!v@;J;g=Hk7;-dAkkf@a(N#(rh#qyM6NdvmCa?1j5Y-|b*GP^!p+B2`n85eR0rgEd`uQ%U!6=T4g(4r^xf1Cerj!F{^kdMg? z$`eGT+q64*rBrD60pF~Pw5@rW6kE&lvYPOL^i2$#{(D*CHMmmG2*ucgjs5VZQlE}; zM+vf!TJ|(urOmzsHuDuj)Wk8I(3VVWi9u=SoH=*5dGQkc3pVkw#UJQFu3Poc+Y{Cc z1c*mU2yAM^ohS@#;-mSxG3|D^i01mx>5mG%qn6ke24D2U)4-f%X}o1+nYXXEV~{*W z7T|7uQ_AFPJxR%I&Vww;n6h8jMM_ZdV>z_2iaaqAdwxjq`5(XjA|d6AM$09WL_!sY zIA|R+KJ;Bpg=lG~z(nJ7Wl-l@@f=&+<4w9x7K?$2IN~|thMS&y?j>q&yDir87SvD8;+Tpw z2$|S-lbl>EWP#-hOcWjMkLaPKK1#Mrf@T>t;l?G`bJw;U*|IMVXyR}X-@lQNz`{cG ze)voslgc@X(&@_Wu=!T?ncv>oxwB6z{fuUmHzH&2fFZ=i)B}Ed-Mp_U(`Jw*z5RH3 zff}RM#7crQSj1T2)GuBCg5(LSo02L1%$Mg?f5vhtg*|L7uHV(= ziky))4L67PN^img`Sm>FX+^sm(*ELiwC`gZ`NK{wCtF->qLh*nS_lnpjpeaSP`(c} zW>g|E^?fn9iSmM{GnoM9lS4HuC;&$gQEnjlcG1d`orhGIv~VqJinwgufZ1>czoM#+ zzS`?L(CW*VUq2TN4LdpY_-FuO{v*?PbOMIhuXmg8yw!D=2 zn&t<_$CIPQPVF4Sk7g8q-k-nmQKD{U^bIMG^0is#Lor|bJnq8F&f?$A`~85uXxiwA z6IV`jC31IQOn>L2u}JK_q%(vVR^x-mETXzF`u(#F zsVh-#bVgqHU?)}_Tt=~Kp6KOwX~&3tr8xIul#RnpGK96r)wO)7VtKDti+6jG&yxK_ z{s#hf5Xjl@`86?fQzrv1Hfg4Kp%B$!V{s0~&yaFr-O1+%do1=q*V z8QB-FWiSp3rNU1YlTfg1DflU3U<|oQ)kD8}H~TZKXUL9>`#*}yaLyegbS}>N*TjqV z-F)pSXl5dF&niH+>E81!t8z?N;U12#f$Xb1h|~OSZDmsKV0c-PD4H=Nu_q%pOwPlB zMa+r+SX&;MW8I1J%jAfip8I3+p=yT61@?oPjHP!yW5C6OMCzm&zMHiHzMgM0L$*5U z{g*zhEh6%%{C)&PxZ~~s{W80q83y62lDeNcM70uk3o!975Br-MR-U*Vi5msR$T3-lhzVcHXqeDTx75apV3I#8h zwLp3|i7P^;%;m$KvM#)3S5u#_mwt$%K#V!v>H5@BZ>7yf?5`J;q(C76Y%I0J=e5xn z91nkAUgZFW=JTet-|G|mPI=wae&|>=K)`#2Ks*2YbwU=wc>H9Uke1b@Z?9#hkQi?A z4lZRTbw_)=>M7@g=vp&{AXCUjcn!vqGZBfW159|boJ(C`C!yCOR@zUteCU>BItRk^ z)t%eK@m+`g=34VARyVUtQoVy4#>yDOk%61X#YJ8g+nezP-llk*%qM89PCq<+dD@cXt!g%v9&{D~S7N#OHFY@X_t=4+~z999Gqm_e*T=q$=-cRk-9*_73W)=P8Ma zYgN&UBOJxZ%1CugDMi$ldcQm=Nmu6Hn{zjdC?3-u^T1!VkOQP4t>fo5&V|}L*$d!e z^^c?n9Lr-q!eZ@jRN!~ei@lPP%M7P1g}mHZCD)P`z#$IFYf)=DV~6Nhpp6ZEmvlazP#+uo{zecB6at~DMR_{@k6VG{L_Aa?sX$`bqf+7|&1;a_Ej@>3%DY!%~q|K!~!=$>TpvewXy zElZHl#nTf_@ri_jJw2LtGyH5hx(E|tcaq=7lek?Xy=!!s5aEg}Xd!e%aSX*FQSdE@ z(t1F9p6mo%|4UQAo`3ijX2_Lox+*wKcB{HDRkMY2uh>;8eyyEQ@Bf z9-#=HZ^zux@D5s=nNQnD0GT?s_Xb+#3v1YLC+DQG@wV=ue+>wc^sHy<^|>rf&?~?( zsGRw6`$dP}JB)aq*yLQIRfEU0ly7~(#SwOi8`q4pa6n?r+0K%gWwVj=&UP;DDr9eb zUVG|pdU`roA=q#tEbhXwR1Ly55=R)g%Kr#~+7l|2!oiiOwfn}C=KfKwXJ`nm@fW6h zo_6o}^c2NU(&2eBGR5j&-K&C#W^Y&91j>-6;dp0YDp zOgBI2@DXaT*prRwTS``|;}1zISKTeRnz@sNY~MB8ZJw)+5w;Eej#`feI%jVBah?oO z62o=-w_24S)SKN$)>+=0x%NN32-?C?a*+B7O*bX+M18+ZNE@LU9Ne$r@!;@EdQ*=F zy`+B;{ZAYOc%08fa2T2(qQ?7g%IZcJD6rH~M_3%M(0;#vn-{q)Pt{w`LOAl+gC%@g zJyF#;v5#}Bdh^n3Zf6vd7TUI`5IrDEJp~ljFVeXx_xP4y$)3~SD+qGt`?7zphVdQ8 zZsdu`$d=2mepo_7KI7_5>;=RA-XSB_1DkJ~&&;VkxDc;9GPbWjyz1>d#T19bd&F6q z*<4NcZ6l_vrRN#VSDT+<^huGr?un-95SM``=Hhz9$yx2R>YqGl@=_!pncPwlDV3Ds z24%;y>J-rR%BErPWtt(*O5HyH?@*(lLdhK>DA}e31f^<=5-QY+9lx+#@in-+D~j>< z`>P|n;~Z2HT4qz>4Vz+=S&7Yia^%s9kDTS2gu&b#Zrs2%$^rjNWX$f2B#D~op z^hl!>Oo2@e{q)Q}NuTW;?)=oYxCi#L$<7WeqUrGEO@z02wdI-FN|nEs_p#wdnAd3T z*NVqZqrq~ccXkbCZg0~I#3%Oa&vcpo<42(Uy3na(2U>A6Moza4?}6QvKH`%(I;g?n z;h@=kfsds-n&zJVXUA0cw+anhP1#k7S#^v)aGS#T7vZkVsd7KVO>?`$#pB>wg_y82 znQ=$=~{kvc0nEk#3Ya zI7BJCM>9YiQtH3gqjvps>`W`+ zJ9`K$082}f$FYf?)t(<=<`7LI&ozPCZ=C)(nEFli9qy;%dCwAk4-1Gpk@Bm>v~2nX zH*u`4y!UMGce~KHwz`AuyEgPQ_u2E~AK+H=t%;%z_!^E7B2U06w6*v-gR%wV=eR28 zrD|K?=;)sVj%{;JUz6hkOS$I#LCq_X&0VYaM?JZLMz-7GfIW$S7f(w8k02Il$DV9b zo(nItNS2&87|)-;-h!vJ+{`kcGlm|A>sRa8DMB4;5wHGmWY2yxV+O(xZ5cJwjaU$rVFQ#gcMRBalkIFu%sVgn1*AN8zFt$+gt5WvVX} zNu1CU8>J_xvnJ_fN{)3a9xrY3(sadF>wYUpXB{Rh^FX3oF5Ycd=gf4e2OFbp?DQ+= zuciFN{pY^Wl{#M}hW%t9v~!7_q8wbm85U-G9kO0;b`F8w=3x;qrz2W>OP{h5!L6UM z(R?Gca(@B^*~btscIeqNy>LSbzP_PXEfTyTUqg6^h>)RVb{u6{jB7NRg_*IM z2*t;a+R|?oU}}A-qqpOyQ_EH@czemEyk64Z6Tqv&W#^kTD$I>+dM?e#_Pn-#;c=S#vfuk%8yyTIPW%oOpYoj70%_rXC1DhEgOuR+Wrcu!q5kT3@{3G>UZ2lo#zV+Vj5M`Q4!5^Uhx zos)I-$G%){@n5dW(XGV=6p_yye*8-1sXEVyBQ#yd|8=4zZpGK3+#)=sHHLw%fEiq@ zzjJY0g3z_t3c-$Fx7!z>531DjdO+GmwCycn@;5o#ZHckQVYehB5=hW^)Z_Gs`Z;>x zjsKM-M8_KI&PFeH-MldTKqTt$=X{H1!wo$0*y&1xTev3|W$~Lgh~-kELMr>`sPwZO zRT0f?X``Ut_2F^B8Ze%7N==?(@mSjZh2x;_d8K&LU`#Z>PLx1YP%#yy zvkPMs-}Kd#U&3?Upm87Ra_D`JIo@M!mU9H-f);;CmfFfFDrtQKl$h_oZvIj{OM_Hh z>-Z1uL>&9AI+bc}u8~Nr@lsPB${#eS%ic=tIG34#g3o|69U~QXSK)hwrU~LOf8Z%V z@uutVza?%DuE58>znwmi1%puiAn};(H-QCQPa1rMYz#a%1#Z)kdfBAM*`d48znOpuZWHWZ`o~MT6f@Y$4(pHV{>Dc=Q4+> zPJ>y^o^e$Y)#!1SW29tmD}0qRR?|bGnuZS>MUtDm1o7nFg;BNjT`Rl{m$9=I>Xk!} z>H|~snb5JZsNKQIN5uHPTCW}>Ppmljyb|BVYey@ae~~*hPkP>4{UWnLcRu5>ZbsRT zLs>Tlw&M=4U-_Kjg@fIA-?HQLbLbJ-F2>!%zJ9M$o>r2QlFnUp)VsFOJtsvq*$jx1 zMc%$%75PE2)-k&X>2HDspKuw&*XwTfa(RiBlwQZE9hA40xB#NYyKj@M9wNN3JEyTP z5e6l4DY0bzYsGP?46@aVM+*xJf6pG0rh05Skn={a^erzVz)0+^*gNnwZN_RK9#at$ z49}TP)mSrmAZ3mM3+^36ZNlaUCFP8-eAl*x_A!;ve&IfLVt=$*O`i^`t8~0Ud^mDb zSJ2lpUr(CT4HDq++4%P8Rr3f{cRL0ZVbdLq8!J(RHeBG8w8vd(OLmj$DqJvYcQz;X z7@l^g@oJy$tpQvOs)NttA}Hk`KRwKaJEr4Pdck(jQTj1rFtTo4A~x@B&iL<#m1cK8 z+eEofNu`~Ot1wj93W44=rG3)*Xt1KKRZ<9_lLG{6LTnpR=DhCyH zsyUxEKz-qRhOC0KbpqN<>Y8k3GQ<+`AFr*YAltFH7UPk@$H!K{zTgRqS_RLro~M7U z_V*5SGK0;dBTE5bV*lmkr8ngx4@(?j`^pDKv9NT1Iv1DQssfg$TZ&to`=2q@9NEKT zJ#(EDR9TxSPU2b)^m8tezh^lf?rKvIa}VV>T;#bkhN*Iu$?e-*xBJ7>TH(?;(6v32 z1k?57VnPRv{}cn(1#&t+iEEKYmg)I60SnYJ_}1PPqN%A-inj)WW}F~sxhCtfQILu- zpFuvAdTC1g^qA5O@4aJWV%pI%+z@K>*lBCg^6=0?;!2KgKWT|fmV40(0eFQ%3^ z)QGe-c%=l|y4}?R&aJ76gRQ1CsBT^Z!NIV=$m<}$I3(lE$l~|Eq)H7|qvq>$A0I%p z&EvHHnUv$%g*OATpnGrzPjQ(i7Q-=#xLvPfA&bx*?U&9|c!PIxEB^PPgw@yvw&6z8 zh@atM|Cpi`x^wzekoI9r62-Faw`QkC~QJh&i^ z)g>-)H5e7vB_i-({Mtx3wvU_gm2$VCK*nXEj9na^97lzf_AB?c0#n+*a&k1=ydIQmMPwQOgI)Qg zAh4QU&p68UT2p`>-0?##iymZ3O_lhDQY#+O(t+YZ>80$_37AUOba~PN!r9SR-=u?b z$>0A{`q^Rii6=fTB_kj;4(X=2NvXUDMi46KODydd%|_62s{R|2JKGmH>pYsUU>KH` zH!Yj%~K4a$6 zWj$*?n>l}&CabBoL^duZ@64X*o{l_&ei-WM>1hGjhQQQvF09F+yg>OsS#rJA1WD&0 z6L3WX>;TNb_&QoW|4K7~a_ z&Ibl21MqU``O;DfxA%KtBm-#pRtQ+=PMB#*!qTx0V@6t{FmwjSE3~$@Q%A*BOc^|^ z-|@6&Bprg!mHb0Bw54dudMIEq!aD%!l&`ZznDgcO1=0z$&`0DMX#YXi86O}XfJkH- zIGJcxh9_VNMsLx#+m8?Xh?HYGVY|l>TwAeKe>iw-v-^g$hG0^+$48l{FGP)x*2m30 z;!S+5{v0ExyG4yjmhbw*jik+^q7jEY2+%X0EpK$@aqPw;y&V##Za|NiL^ zi(kKJSxqEBS@IqWs}G=L8M>%)kd2ZW0S&E+js=mU#{Km3Pe+26vCN%Pr{&hSm0x0g zsL(3A_C~X=9t@T*5Hg(09-sTgiBovEEjg`~F;l!17@?(lx)=t{#}C2pah(P1RUlsCCRUXw~Gmpe+qG%F#pF}~uxW*f%_{s{?q zaRKnHQUSsy$>`&j7Zcm5obnlnjI!EAe5N1wJIy<^OnS^`;*tx63cr1Wk5svH2Eg*T zCo_9Eh+Ya3ce{1BZKRcz6;Y0WtuBn`e{aGNzn>sRqdUWes|{5XU^&f z5QY4{m;DmU8X`SF&L}2h5vW#%lV7J~NP}JU`MdB3uzLh0Gft-gYfeRUQJCKp1P0br zCr4p)fXdXYWSw4=cyWK<5}LY%rPqqpg+p!c@@izLjY|WUbY<5T8kBV8Hw=He04jj4 zMa#`j2DupLUu-0c0R3JW$YiBLCkX|8_<(HzJB&xd_P68=$`V?Mk>T|Gnp*r?>F?7! zgXL{S(fZvr2Qc8J_Gv$B(3pQv2~16lj4ttDn*XFn@7Ipm=$X)!QlnyIFqgweslc z>5a=C+a#~j7%m7Ve#5ZD$HyNA#<3giHX$S;2{r+$Lx2+G6S5*|0MScjd7wtaU$yAa zN2RV97{wQPB>uBMBWBCl7kt7gEFuyT6(#ZL3B1_%PlM>6i)Vp-5}#`=Q~?7(-oM)R p@_4l6Z!(yJMfP90K?Ldrg}cmmEW$Y$=MC^9B_=OgCT#Hi{{Z?TS(yL; literal 0 HcmV?d00001 diff --git a/docs/images/ovs-cni-mirror-2A.png b/docs/images/ovs-cni-mirror-2A.png new file mode 100644 index 0000000000000000000000000000000000000000..a79053610f77d7a11a9927c09397730830f4b141 GIT binary patch literal 119867 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 literal 0 HcmV?d00001 diff --git a/docs/images/ovs-cni-mirror-3A.png b/docs/images/ovs-cni-mirror-3A.png new file mode 100644 index 0000000000000000000000000000000000000000..0a9be828d1c34803a05c4943190af404315b5214 GIT binary patch literal 105939 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 Vf4_CJ{sZ_SEv_I|DPkD#e*nTx@!J3Z literal 0 HcmV?d00001 diff --git a/docs/traffic-mirroring.md b/docs/traffic-mirroring.md new file mode 100644 index 000000000..a6ffadf02 --- /dev/null +++ b/docs/traffic-mirroring.md @@ -0,0 +1,579 @@ +# Open vSwitch CNI Plugin - Traffic Mirroring + +## Overview + +This is the documentation of ovs-cni plugin to support the OVS traffic mirroring feature. +The topic has been initially discussed in [issue 219](https://github.com/k8snetworkplumbingwg/ovs-cni/issues/219). + +The main idea is creating and managing multiple mirror ports (SPAN) through ovs-cni in Network Configuration List (Multus) as defined in CNI spec. + +## Supported features + +- Create multiple mirror ports in a specific bridge +- Select source ports +- Select output port (SPAN) + +**RSPAN mirrors are not supported** + +## API and test-cases + +### Premises + +1. The approach relies first on the current `ovs` plugins to create the requested port via pod annotation. Afterwards, the output of the plugin execution is cascaded as input to the plugin that is responsible for managing the mirrors (e.g. `ovs-mirror-producer` and `ovs-mirror-consumer` plugins). This is possible thanks to [Multus chaining capability](https://github.com/containernetworking/cni/blob/spec-v0.4.0/SPEC.md#network-configuration-lists). +2. In all diagrams below we used different colors to represent the logical relation between different entities. In case of OVS they are real DB relations, in case of Pods they represent network connections. Instead, NADs are represented with random colors without a real meaning. +3. In all diagrams below we focused on OVS Mirror `src_port` and `dst_port` to consider the representation with the finest granularity. In this way, we can specify single ports one by one. +For simplicity, we ignore `output_vlan` (used for RSPAN) as mirror output. + + +### Examples + +**Producer NAD** + +```json +{ + "type": "ovs-mirror-producer", + "bridge": BRIDGE_NAME, + "mirrors": [ + { + "name": MIRROR_NAME, + "ingress": INGRESS_ENABLED, + "egress": EGRESS_ENABLED + }, + (...) + ] +} +``` + +`BRIDGE_NAME`: string that represents the unique name of the bridge in ovs database where the mirror should be added + +`MIRROR_NAME`: string that represents the unique name of the mirror in ovs database + +`INGRESS_ENABLED`: if true it enables ovs mirror src_port + +`EGRESS_ENABLED`: if true it enables ovs mirror dst_port + + +**Consumer NAD** + +```json +{ + "type": "ovs-mirror-consumer", + "bridge": BRIDGE_NAME, + "mirrors": [ + { + "name": MIRROR_NAME + } + ] +} +``` + +`BRIDGE_NAME`: string that represents the unique name of the bridge in ovs database where the mirror should be added + +`MIRROR_NAME`: string that represents the unique name of the mirror in ovs database + + +#### Test case 1 + +![ovs-cni-mirror-1A.png](images/ovs-cni-mirror-1A.png) + +```yaml +# Produce to 2 mirrors and consume from 1 +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: ovs-vlan100-prod-mir1-prod-mir2 +spec: + config: |- + { + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br1", + "vlan": 100 + }, + { + "type": "ovs-mirror-producer", + "bridge": "br1", + "mirrors": [ + { + "name": "mirror-1", + "ingress": true, + "egress": true + }, + { + "name": "mirror-2", + "ingress": true, + "egress": true + } + ] + } + ] + } +--- +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: ovs-vlan200-cons-mir1 +spec: + config: |- + { + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br1" + }, + { + "type": "ovs-mirror-consumer", + "bridge": "br1", + "mirrors": [ + { + "name": "mirror-1" + } + ] + } + ] + } +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ovs-prod-cons +spec: + template: + metadata: + annotations: + k8s.v1.cni.cncf.io/networks: |- + [ + { + "name":"ovs-vlan100-prod-mir1-prod-mir2", + "interface":"eth1", + "namespace":"emu-cni" + }, + { + "name":"ovs-vlan200-cons-mir1", + "interface":"eth2", + "namespace":"emu-cni" + } + ] +``` +#### Test case 2 + +![ovs-cni-mirror-2A.png](images/ovs-cni-mirror-2A.png) + +```yaml +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: ovs-vlan100-prod-mir-1-prod-mir-2 +spec: + config: |- + { + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br1", + "vlan": 100 + }, + { + "type": "ovs-mirror-producer", + "bridge": "br1", + "mirrors": [ + { + "name": "mirror-1", + "ingress": true, + "egress": true + }, + { + "name": "mirror-2", + "ingress": true, + "egress": true + } + ] + } + ] + } +--- +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: ovs-vlan100-prod-mir-2 +spec: + config: |- + { + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br1", + "vlan": 100 + }, + { + "type": "ovs-mirror-producer", + "bridge": "br1", + "mirrors": [ + { + "name": "mirror-1", + "ingress": true, + "egress": true + } + ] + } + ] + } +--- +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: ovs-cons-mir-1 +spec: + config: |- + { + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br1" + }, + { + "type": "ovs-mirror-consumer", + "bridge": "br1", + "mirrors": [ + { + "name": "mirror-1" + } + ] + } + ] + } +--- +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: ovs-cons-mir-2 +spec: + config: |- + { + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br1" + }, + { + "type": "ovs-mirror-consumer", + "bridge": "br1", + "mirrors": [ + { + "name": "mirror-2" + } + ] + } + ] + } +--- +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: ovs-vlan200 +spec: + config: |- + { + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br1", + "vlan": 200 + } + ] + } +--- +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: ovs-vlan300 +spec: + config: |- + { + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br1", + "vlan": 300 + } + ] + } +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ovs-producer-1-vlan100-mir1-mir2 +spec: + template: + metadata: + annotations: + k8s.v1.cni.cncf.io/networks: |- + [ + { + "name":"ovs-vlan100-prod-mir-1-prod-mir-2", + "interface":"eth1", + "namespace":"emu-cni" + } + ] +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ovs-producer-2-vlan100-mir1 +spec: + template: + metadata: + annotations: + k8s.v1.cni.cncf.io/networks: |- + [ + { + "name":"ovs-vlan100-prod-mir-1", + "interface":"eth1", + "namespace":"emu-cni" + } + ] +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ovs-consumer1-mir1 +spec: + template: + metadata: + annotations: + k8s.v1.cni.cncf.io/networks: |- + [ + { + "name":"ovs-vlan200", + "interface":"eth1", + "namespace":"emu-cni" + }, + { + "name":"ovs-cons-mir-1", + "interface":"eth2", + "namespace":"emu-cni" + } + ] +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ovs-consumer1-mir2 +spec: + template: + metadata: + annotations: + k8s.v1.cni.cncf.io/networks: |- + [ + { + "name":"ovs-vlan300", + "interface":"eth1", + "namespace":"emu-cni" + }, + { + "name":"ovs-cons-mir-2", + "interface":"eth2", + "namespace":"emu-cni" + } + ] +``` + +#### Test case 3 + +![ovs-cni-mirror-3A.png](images/ovs-cni-mirror-3A.png) + +```yaml +# Produce to 2 mirrors and consume from 1 +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: ovs-vlan100-prod-mir1 +spec: + config: |- + { + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br1", + "vlan": 100 + }, + { + "type": "ovs-mirror-producer", + "bridge": "br1", + "mirrors": [ + { + "name": "mirror-1", + "ingress": true, + "egress": true + } + ] + } + ] + } +--- +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: ovs-vlan200-prod-mir2 +spec: + config: |- + { + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br1", + "vlan": 200 + }, + { + "type": "ovs-mirror-producer", + "bridge": "br1", + "mirrors": [ + { + "name": "mirror-2", + "ingress": true, + "egress": true + } + ] + } + ] + } +--- +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: ovs-vlan300 +spec: + config: |- + { + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br1", + "vlan": 300 + } + ] + } +--- +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: ovs-cons-mir1 +spec: + config: |- + { + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br1" + }, + { + "type": "ovs-mirror-consumer", + "bridge": "br1", + "mirrors": [ + { + "name": "mirror-1" + } + ] + } + ] + } +--- +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: ovs-cons-mir2 +spec: + config: |- + { + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br1" + }, + { + "type": "ovs-mirror-consumer", + "bridge": "br1", + "mirrors": [ + { + "name": "mirror-2" + } + ] + } + ] + } +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ovs-prod-1 +spec: + template: + metadata: + annotations: + k8s.v1.cni.cncf.io/networks: |- + [ + { + "name":"ovs-vlan100-prod-mir1", + "interface":"eth1", + "namespace":"emu-cni" + } + ] +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ovs-prod-2 +spec: + template: + metadata: + annotations: + k8s.v1.cni.cncf.io/networks: |- + [ + { + "name":"ovs-vlan200-prod-mir2", + "interface":"eth1", + "namespace":"emu-cni" + } + ] +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ovs-cons +spec: + template: + metadata: + annotations: + k8s.v1.cni.cncf.io/networks: |- + [ + { + "name":"ovs-vlan300", + "interface":"eth1", + "namespace":"emu-cni" + }, + { + "name":"ovs-cons-mir1", + "interface":"eth2", + "namespace":"emu-cni" + }, + { + "name":"ovs-cons-mir2", + "interface":"eth3", + "namespace":"emu-cni" + } + ] +``` \ No newline at end of file diff --git a/examples/mirror-consumer.yaml b/examples/mirror-consumer.yaml new file mode 100644 index 000000000..356def862 --- /dev/null +++ b/examples/mirror-consumer.yaml @@ -0,0 +1,64 @@ +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: nad-al-cni-cons-1 + namespace: emu-cni + annotations: + k8s.v1.cni.cncf.io/resourceName: nad-al-cni-cons-1 +spec: + config: '{ + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br-emu-cni" + }, + { + "type": "ovs-mirror-consumer", + "bridge": "br-emu-cni", + "mirrors": [ + { + "name": "mirror-1" + }, + { + "name": "mirror-2" + }, + { + "name": "mirror-3" + } + ] + } + ] + }' +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cni-cons-1 + namespace: emu-cni +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: cni-cons-1 + template: + metadata: + annotations: + k8s.v1.cni.cncf.io/networks: | + [ + { + "name":"nad-al-cni-cons-1", + "namespace":"emu-cni", + "interface":"emu-cons" + } + ] + labels: + app: cni-cons-1 + spec: + containers: + - name: samplepod + command: ["/bin/sh"] + args: ["-c", "sleep infinity"] + image: alpine \ No newline at end of file diff --git a/examples/mirror-producer.yaml b/examples/mirror-producer.yaml new file mode 100644 index 000000000..ee013324d --- /dev/null +++ b/examples/mirror-producer.yaml @@ -0,0 +1,66 @@ +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: nad-al-cni-1 + namespace: emu-cni + annotations: + k8s.v1.cni.cncf.io/resourceName: nad-al-cni-1 +spec: + config: '{ + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "ovs", + "bridge": "br-emu-cni", + "vlan": 1 + }, + { + "type": "ovs-mirror-producer", + "bridge": "br-emu-cni", + "mirrors": [ + { + "name": "mirror-1", + "ingress": true, + "egress": true + }, + { + "name": "mirror-2", + "ingress": true, + "egress": false + } + ] + } + ] + }' +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cni-client-1 + namespace: emu-cni +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: cni-client-1 + template: + metadata: + annotations: + k8s.v1.cni.cncf.io/networks: | + [ + { + "name":"nad-al-cni-1", + "namespace":"emu-cni", + "interface":"emu-prod" + } + ] + labels: + app: cni-client-1 + spec: + containers: + - name: samplepod + command: ["/bin/sh"] + args: ["-c", "sleep infinity"] + image: alpine \ No newline at end of file diff --git a/examples/ovs-cni.yml b/examples/ovs-cni.yml index b778cf5fe..c9624ea1e 100644 --- a/examples/ovs-cni.yml +++ b/examples/ovs-cni.yml @@ -45,6 +45,32 @@ spec: volumeMounts: - name: cnibin mountPath: /host/opt/cni/bin + - name: ovs-mirror-producer + image: quay.io/kubevirt/ovs-cni-mirror-producer:latest + command: ['cp', '/ovs-mirror-producer', '/host/opt/cni/bin/ovs-mirror-producer'] + imagePullPolicy: IfNotPresent + securityContext: + privileged: true + resources: + requests: + cpu: "10m" + memory: "15Mi" + volumeMounts: + - name: cnibin + mountPath: /host/opt/cni/bin + - name: ovs-mirror-consumer + image: quay.io/kubevirt/ovs-cni-mirror-consumer:latest + command: ['cp', '/ovs-mirror-consumer', '/host/opt/cni/bin/ovs-mirror-consumer'] + imagePullPolicy: IfNotPresent + securityContext: + privileged: true + resources: + requests: + cpu: "10m" + memory: "15Mi" + volumeMounts: + - name: cnibin + mountPath: /host/opt/cni/bin priorityClassName: system-node-critical containers: - name: ovs-cni-marker diff --git a/go.mod b/go.mod index 52f47171b..a7b9fd9f7 100644 --- a/go.mod +++ b/go.mod @@ -90,4 +90,4 @@ replace ( replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2 -go 1.17 +go 1.18 diff --git a/hack/build-manifests.sh b/hack/build-manifests.sh index 666ecf71f..ef0ef576d 100755 --- a/hack/build-manifests.sh +++ b/hack/build-manifests.sh @@ -30,6 +30,16 @@ export OVS_CNI_MARKER_IMAGE_VERSION=${OVS_CNI_MARKER_IMAGE_VERSION:-latest} export OVS_CNI_MARKER_IMAGE_PULL_POLICY=${OVS_CNI_MARKER_IMAGE_PULL_POLICY:-IfNotPresent} export OVS_CNI_MARKER_HEALTHCHECK_INTERVAL=${OVS_CNI_MARKER_HEALTHCHECK_INTERVAL:-60} +export OVS_CNI_MIRROR_PRODUCER_IMAGE_REPO=${OVS_CNI_MIRROR_PRODUCER_IMAGE_REPO:-quay.io/kubevirt} +export OVS_CNI_MIRROR_PRODUCER_IMAGE_NAME=${OVS_CNI_MIRROR_PRODUCER_IMAGE_NAME:-ovs-cni-mirror-producer} +export OVS_CNI_MIRROR_PRODUCER_IMAGE_VERSION=${OVS_CNI_MIRROR_PRODUCER_IMAGE_VERSION:-latest} +export OVS_CNI_MIRROR_PRODUCER_IMAGE_PULL_POLICY=${OVS_CNI_MIRROR_PRODUCER_IMAGE_PULL_POLICY:-IfNotPresent} + +export OVS_CNI_MIRROR_CONSUMER_IMAGE_REPO=${OVS_CNI_MIRROR_CONSUMER_IMAGE_REPO:-quay.io/kubevirt} +export OVS_CNI_MIRROR_CONSUMER_IMAGE_NAME=${OVS_CNI_MIRROR_CONSUMER_IMAGE_NAME:-ovs-cni-mirror-consumer} +export OVS_CNI_MIRROR_CONSUMER_IMAGE_VERSION=${OVS_CNI_MIRROR_CONSUMER_IMAGE_VERSION:-latest} +export OVS_CNI_MIRROR_CONSUMER_IMAGE_PULL_POLICY=${OVS_CNI_MIRROR_CONSUMER_IMAGE_PULL_POLICY:-IfNotPresent} + for template in manifests/*.in; do name=$(basename ${template%.in}) envsubst < ${template} > examples/${name} diff --git a/hack/docker-builder/Dockerfile b/hack/docker-builder/Dockerfile index 2786c5ea5..45dfbb92f 100644 --- a/hack/docker-builder/Dockerfile +++ b/hack/docker-builder/Dockerfile @@ -6,7 +6,7 @@ RUN dnf -y install make git sudo gcc rsync-daemon rsync openvswitch hostname && ENV GOPATH="/go" RUN \ DESTINATION=/opt && \ - VERSION=1.17.7 && \ + VERSION=1.18.4 && \ TARBALL=go${VERSION}.linux-amd64.tar.gz && \ URL=https://dl.google.com/go && \ mkdir -p ${DESTINATION} && \ diff --git a/hack/install-go.sh b/hack/install-go.sh index b5805b6c8..009abcee5 100755 --- a/hack/install-go.sh +++ b/hack/install-go.sh @@ -1,7 +1,7 @@ #!/bin/bash -xe destination=$1 -version=1.17.7 +version=1.18.4 tarball=go$version.linux-amd64.tar.gz url=https://dl.google.com/go/ diff --git a/manifests/ovs-cni.yml.in b/manifests/ovs-cni.yml.in index 2df6574ec..fb888fdf9 100644 --- a/manifests/ovs-cni.yml.in +++ b/manifests/ovs-cni.yml.in @@ -45,6 +45,32 @@ spec: volumeMounts: - name: cnibin mountPath: /host${CNI_MOUNT_PATH} + - name: ovs-mirror-producer + image: ${OVS_CNI_MIRROR_PRODUCER_IMAGE_REPO}/${OVS_CNI_MIRROR_PRODUCER_IMAGE_NAME}:${OVS_CNI_MIRROR_PRODUCER_IMAGE_VERSION} + command: ['cp', '/ovs-mirror-producer', '/host${CNI_MOUNT_PATH}/ovs-mirror-producer'] + imagePullPolicy: ${OVS_CNI_MIRROR_PRODUCER_IMAGE_PULL_POLICY} + securityContext: + privileged: true + resources: + requests: + cpu: "10m" + memory: "15Mi" + volumeMounts: + - name: cnibin + mountPath: /host${CNI_MOUNT_PATH} + - name: ovs-mirror-consumer + image: ${OVS_CNI_MIRROR_CONSUMER_IMAGE_REPO}/${OVS_CNI_MIRROR_CONSUMER_IMAGE_NAME}:${OVS_CNI_MIRROR_CONSUMER_IMAGE_VERSION} + command: ['cp', '/ovs-mirror-consumer', '/host${CNI_MOUNT_PATH}/ovs-mirror-consumer'] + imagePullPolicy: ${OVS_CNI_MIRROR_CONSUMER_IMAGE_PULL_POLICY} + securityContext: + privileged: true + resources: + requests: + cpu: "10m" + memory: "15Mi" + volumeMounts: + - name: cnibin + mountPath: /host${CNI_MOUNT_PATH} priorityClassName: system-node-critical containers: - name: ovs-cni-marker diff --git a/pkg/config/config.go b/pkg/config/config.go index c892d5827..159447ba4 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -23,6 +23,8 @@ import ( "os" "strings" + current "github.com/containernetworking/cni/pkg/types/100" + "github.com/containernetworking/cni/pkg/version" "github.com/imdario/mergo" "github.com/k8snetworkplumbingwg/ovs-cni/pkg/types" "github.com/k8snetworkplumbingwg/ovs-cni/pkg/utils" @@ -39,7 +41,7 @@ func LoadConf(data []byte) (*types.NetConf, error) { if err != nil { return nil, err } - flatNetConf, err := loadFlatNetConf(netconf.ConfigurationPath) + flatNetConf, err := loadFlatNetConf[types.NetConf](netconf.ConfigurationPath) if err != nil { return nil, err } @@ -47,6 +49,7 @@ func LoadConf(data []byte) (*types.NetConf, error) { if err != nil { return nil, err } + if netconf.LinkStateCheckRetries == 0 { netconf.LinkStateCheckRetries = linkstateCheckRetries } @@ -57,6 +60,38 @@ func LoadConf(data []byte) (*types.NetConf, error) { return netconf, nil } +// LoadMirrorConf parses and validates stdin netconf and returns MirrorNetConf object +func LoadMirrorConf(data []byte) (*types.MirrorNetConf, error) { + netconf, err := loadMirrorNetConf(data) + if err != nil { + return nil, err + } + flatNetConf, err := loadFlatNetConf[types.MirrorNetConf](netconf.ConfigurationPath) + if err != nil { + return nil, err + } + netconf, err = mergeConf(netconf, flatNetConf) + if err != nil { + return nil, err + } + return netconf, nil +} + +// LoadPrevResultConfFromCache retrieve preResult config from cache +func LoadPrevResultConfFromCache(cRef string) (*types.CachedPrevResultNetConf, error) { + netCache := &types.CachedPrevResultNetConf{} + netConfBytes, err := utils.ReadCache(cRef) + if err != nil { + return nil, fmt.Errorf("error reading cached prevResult conf with name %s: %v", cRef, err) + } + + if err = json.Unmarshal(netConfBytes, netCache); err != nil { + return nil, fmt.Errorf("failed to parse prevResult conf: %v", err) + } + + return netCache, nil +} + // LoadConfFromCache retrieve net config from cache func LoadConfFromCache(cRef string) (*types.CachedNetConf, error) { netCache := &types.CachedNetConf{} @@ -82,18 +117,43 @@ func loadNetConf(bytes []byte) (*types.NetConf, error) { if err := json.Unmarshal(bytes, netconf); err != nil { return nil, fmt.Errorf("failed to load netconf: %v", err) } + return netconf, nil +} + +func loadMirrorNetConf(bytes []byte) (*types.MirrorNetConf, error) { + netconf := &types.MirrorNetConf{} + if err := json.Unmarshal(bytes, netconf); err != nil { + return nil, fmt.Errorf("failed to load netconf: %v", err) + } + + // Parse previous result + if netconf.RawPrevResult != nil { + resultBytes, err := json.Marshal(netconf.RawPrevResult) + if err != nil { + return nil, fmt.Errorf("loadNetConf: could not serialize prevResult: %v", err) + } + res, err := version.NewResult(netconf.CNIVersion, resultBytes) + if err != nil { + return nil, fmt.Errorf("loadNetConf: could not parse prevResult: %v", err) + } + netconf.RawPrevResult = nil + netconf.PrevResult, err = current.NewResultFromResult(res) + if err != nil { + return nil, fmt.Errorf("loadNetConf: could not convert result to current version: %v", err) + } + } return netconf, nil } -func loadFlatNetConf(configPath string) (*types.NetConf, error) { +func loadFlatNetConf[T types.NetConfs](configPath string) (*T, error) { confFiles := getOvsConfFiles() if configPath != "" { confFiles = append([]string{configPath}, confFiles...) } // loop through the path and parse the JSON config - flatNetConf := &types.NetConf{} + flatNetConf := new(T) for _, confFile := range confFiles { confExists, err := pathExists(confFile) if err != nil { @@ -119,7 +179,7 @@ func loadFlatNetConf(configPath string) (*types.NetConf, error) { return flatNetConf, nil } -func mergeConf(netconf, flatNetConf *types.NetConf) (*types.NetConf, error) { +func mergeConf[T types.NetConfs](netconf, flatNetConf *T) (*T, error) { if err := mergo.Merge(netconf, flatNetConf); err != nil { return nil, fmt.Errorf("merge with ovs config file: error: %v", err) } diff --git a/pkg/mirror-consumer/consumer.go b/pkg/mirror-consumer/consumer.go new file mode 100644 index 000000000..053f582af --- /dev/null +++ b/pkg/mirror-consumer/consumer.go @@ -0,0 +1,251 @@ +// Copyright 2018-2019 Red Hat, Inc. +// Copyright 2014 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Go version 1.10 or greater is required. Before that, switching namespaces in +// long running processes in go did not work in a reliable way. +//go:build go1.10 +// +build go1.10 + +package plugin + +import ( + "errors" + "fmt" + "log" + "runtime" + + "github.com/k8snetworkplumbingwg/ovs-cni/pkg/config" + "github.com/k8snetworkplumbingwg/ovs-cni/pkg/utils" + + "github.com/containernetworking/cni/pkg/skel" + cnitypes "github.com/containernetworking/cni/pkg/types" + current "github.com/containernetworking/cni/pkg/types/100" + + "github.com/k8snetworkplumbingwg/ovs-cni/pkg/ovsdb" + "github.com/k8snetworkplumbingwg/ovs-cni/pkg/types" +) + +func init() { + // this ensures that main runs only on main thread (thread group leader). + // since namespace ops (unshare, setns) are done for a single thread, we + // must ensure that the goroutine does not jump from OS thread to thread + runtime.LockOSThread() +} + +func logCall(command string, args *skel.CmdArgs) { + log.Printf("CNI %s was called for container ID: %s, network namespace %s, interface name %s, configuration: %s", + command, args.ContainerID, args.Netns, args.IfName, string(args.StdinData[:])) +} + +func getPortUUID(ovsDriver *ovsdb.OvsBridgeDriver, interfaces []*current.Interface) (string, error) { + for _, iface := range interfaces { + uuid, err := ovsDriver.GetPortUUID(iface.Name) + if err == nil { + return uuid.GoUUID, nil + } + } + + return "", errors.New("cannot find port in db") +} + +func attachPortToMirror(ovsDriver *ovsdb.OvsBridgeDriver, portUUIDStr string, mirror *types.Mirror) error { + err := ovsDriver.AttachPortToMirrorConsumer(portUUIDStr, mirror.Name) + if err != nil { + return err + } + + return nil +} + +func detachPortFromMirror(ovsDriver *ovsdb.OvsBridgeDriver, portUUIDStr string, mirror *types.Mirror) error { + err := ovsDriver.DetachPortFromMirrorConsumer(portUUIDStr, mirror.Name) + if err != nil { + return err + } + + return nil +} + +// CmdAdd add handler for attaching container into network +func CmdAdd(args *skel.CmdArgs) error { + logCall("ADD", args) + + netconf, err := config.LoadMirrorConf(args.StdinData) + if err != nil { + return err + } + + ovsDriver, err := ovsdb.NewOvsBridgeDriver(netconf.BrName, netconf.SocketFile) + if err != nil { + return err + } + + // removes all empty mirrors + if err := ovsDriver.CleanEmptyMirrors(); err != nil { + return err + } + + // Cache PrevResult for CmdDel + if err = utils.SaveCache(config.GetCRef(args.ContainerID, args.IfName)+"_cons", + &types.CachedPrevResultNetConf{PrevResult: netconf.PrevResult}); err != nil { + return fmt.Errorf("error saving NetConf %q", err) + } + + portUUID, err := getPortUUID(ovsDriver, netconf.PrevResult.Interfaces) + if err != nil { + return fmt.Errorf("cannot get existing portUuid from db %v", err) + } + + for _, mirror := range netconf.Mirrors { + + err = ovsDriver.CreateMirror(netconf.BrName, mirror.Name) + if err != nil { + return fmt.Errorf("cannot create mirror %s: %v ", mirror.Name, err) + } + + alreadyAttached, err := ovsDriver.IsMirrorConsumerAlreadyAttached(mirror.Name) + if err != nil { + return fmt.Errorf("cannot check if mirror %s has already an output port with error: %v ", mirror.Name, err) + } + if alreadyAttached { + return fmt.Errorf("cannot attach port %s to mirror %s because there is already another port. Error: %v", portUUID, mirror.Name, err) + } + + if err = attachPortToMirror(ovsDriver, portUUID, mirror); err != nil { + return fmt.Errorf("cannot attach port %s to mirror %s: %v", portUUID, mirror.Name, err) + } + } + + result := ¤t.Result{ + Interfaces: netconf.PrevResult.Interfaces, + } + + return cnitypes.PrintResult(result, netconf.CNIVersion) +} + +// CmdDel remove handler for deleting container from network +func CmdDel(args *skel.CmdArgs) error { + logCall("DEL", args) + + cRef := config.GetCRef(args.ContainerID, args.IfName) + cache, err := config.LoadPrevResultConfFromCache(cRef + "_cons") + if err != nil { + // If cmdDel() fails, cached prevResult is cleaned up by + // the followed defer call. However, subsequence calls + // of cmdDel() from kubelet fail in a dead loop due to + // cached prevResult doesn't exist. + // Return nil when LoadPrevResultConfFromCache fails since the rest + // of cmdDel() code relies on prevResult as input argument + // and there is no meaning to continue. + return nil + } + + defer func() { + if err == nil { + utils.CleanCache(cRef + "_cons") + } + }() + + netconf, err := config.LoadMirrorConf(args.StdinData) + if err != nil { + return err + } + // add prevResult, because missing in CNI spec < 0.4.0 + netconf.PrevResult = cache.PrevResult + + ovsDriver, err := ovsdb.NewOvsBridgeDriver(netconf.BrName, netconf.SocketFile) + if err != nil { + return err + } + + portUUID, err := getPortUUID(ovsDriver, netconf.PrevResult.Interfaces) + if err != nil { + return fmt.Errorf("cannot get existing portUuid from db %v", err) + } + + for _, mirror := range netconf.Mirrors { + + mirrorExist, err := ovsDriver.IsMirrorPresent(mirror.Name) + if err != nil { + return err + } + if !mirrorExist { + // skip error because CNI spec states that "Plugins should generally complete a DEL action without error even if some resources are missing" + continue + } + + if err = detachPortFromMirror(ovsDriver, portUUID, mirror); err != nil { + return fmt.Errorf("cannot detach port %s from mirror %s: %v", portUUID, mirror.Name, err) + } + + used, err := ovsDriver.IsMirrorUsed(netconf.BrName, mirror.Name) + if err != nil { + return fmt.Errorf("cannot check if mirror %s is used: %v ", mirror.Name, err) + } + + // if this mirror is not used we can remove it + if !used { + err = ovsDriver.DeleteMirror(netconf.BrName, mirror.Name) + if err != nil { + return fmt.Errorf("cannot delete mirror %s: %v ", mirror.Name, err) + } + } + } + + // removes all empty mirrors + if err := ovsDriver.CleanEmptyMirrors(); err != nil { + return err + } + + result := ¤t.Result{ + Interfaces: netconf.PrevResult.Interfaces, + } + + return cnitypes.PrintResult(result, netconf.CNIVersion) +} + +// CmdCheck check handler to make sure networking is as expected. +func CmdCheck(args *skel.CmdArgs) error { + logCall("CHECK", args) + + netconf, err := config.LoadMirrorConf(args.StdinData) + if err != nil { + return err + } + + ovsDriver, err := ovsdb.NewOvsBridgeDriver(netconf.BrName, netconf.SocketFile) + if err != nil { + return err + } + + portUUID, err := getPortUUID(ovsDriver, netconf.PrevResult.Interfaces) + if err != nil { + return fmt.Errorf("cannot get existing portUuid from db %v", err) + } + + for _, mirror := range netconf.Mirrors { + + mirrorExist, err := ovsDriver.CheckMirrorConsumerWithPorts(mirror.Name, portUUID) + if err != nil { + return err + } + + if !mirrorExist { + return fmt.Errorf("mirror %s not present", mirror.Name) + } + } + + return nil +} diff --git a/pkg/mirror-consumer/consumer_suite_test.go b/pkg/mirror-consumer/consumer_suite_test.go new file mode 100644 index 000000000..21edad232 --- /dev/null +++ b/pkg/mirror-consumer/consumer_suite_test.go @@ -0,0 +1,27 @@ +// Copyright 2018 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestPlugin(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Mirror Consumer Suite") +} diff --git a/pkg/mirror-consumer/consumer_test.go b/pkg/mirror-consumer/consumer_test.go new file mode 100644 index 000000000..188ce799e --- /dev/null +++ b/pkg/mirror-consumer/consumer_test.go @@ -0,0 +1,749 @@ +// Copyright 2018 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "bytes" + "encoding/json" + "fmt" + "os/exec" + + "github.com/containernetworking/cni/pkg/skel" + cnitypes "github.com/containernetworking/cni/pkg/types" + types040 "github.com/containernetworking/cni/pkg/types/040" + current "github.com/containernetworking/cni/pkg/types/100" + cniversion "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/testutils" + "github.com/vishvananda/netlink" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + plugin "github.com/k8snetworkplumbingwg/ovs-cni/pkg/plugin" + + . "github.com/k8snetworkplumbingwg/ovs-cni/pkg/testhelpers" + + "github.com/k8snetworkplumbingwg/ovs-cni/pkg/types" +) + +const bridgeName = "bridge-mir-cons" +const vlanID = 100 +const IFNAME1 = "eth0" +const IFNAME2 = "eth1" +const ovsPortOwner = "ovs-cni.network.kubevirt.io" + +var _ = BeforeSuite(func() { + output, err := exec.Command("ovs-vsctl", "show").CombinedOutput() + Expect(err).NotTo(HaveOccurred(), "Open vSwitch is not available, if you have it installed and running, try to run tests with `sudo -E`: %v", string(output[:])) +}) + +var _ = AfterSuite(func() { + exec.Command("ovs-vsctl", "del-br", "--if-exists", bridgeName).Run() +}) + +var _ = Describe("CNI mirror-consumer 0.3.0", func() { testFunc("0.3.0") }) +var _ = Describe("CNI mirror-consumer 0.3.1", func() { testFunc("0.3.1") }) +var _ = Describe("CNI mirror-consumer 0.4.0", func() { testFunc("0.4.0") }) +var _ = Describe("CNI mirror-consumer 1.0.0", func() { testFunc("1.0.0") }) + +var testFunc = func(version string) { + BeforeEach(func() { + output, err := exec.Command("ovs-vsctl", "add-br", bridgeName).CombinedOutput() + Expect(err).NotTo(HaveOccurred(), "Failed to create testing OVS bridge: %v", string(output[:])) + + bridgeLink, err := netlink.LinkByName(bridgeName) + Expect(err).NotTo(HaveOccurred(), "Interface of testing OVS bridge was not found in the system") + + err = netlink.LinkSetUp(bridgeLink) + Expect(err).NotTo(HaveOccurred(), "Was not able to set bridge UP") + }) + + AfterEach(func() { + output, err := exec.Command("ovs-vsctl", "del-br", bridgeName).CombinedOutput() + Expect(err).NotTo(HaveOccurred(), "Failed to remove testing OVS bridge: %v", string(output[:])) + }) + + createInterfaces := func(ifName string, targetNs ns.NetNS) *current.Result { + confplugin := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs", + "bridge": "%s", + "vlan": %d + }`, version, bridgeName, vlanID) + args := &skel.CmdArgs{ + ContainerID: "dummy-mir-cons", + Netns: targetNs.Path(), + IfName: ifName, + StdinData: []byte(confplugin), + } + + By("Calling ADD command of ovs-cni plugin to create interfaces") + resPlugin, _, err := cmdAddWithArgs(args, func() error { + return plugin.CmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + + By("Checking that result of ADD command of ovs-cni plugin is in expected format") + resultPlugin, err := current.GetResult(resPlugin) + Expect(err).NotTo(HaveOccurred()) + Expect(len(resultPlugin.Interfaces)).To(Equal(2)) + Expect(len(resultPlugin.IPs)).To(Equal(0)) + + return resultPlugin + } + + testCheck := func(conf string, r cnitypes.Result, ifName string, targetNs ns.NetNS) { + if checkSupported, _ := cniversion.GreaterThanOrEqualTo(version, "0.4.0"); !checkSupported { + return + } + + args := &skel.CmdArgs{ + ContainerID: "dummy-mir-cons", + Netns: targetNs.Path(), + IfName: ifName, + StdinData: []byte(conf), + } + + By("Calling CHECK command") + moreThan100, err := cniversion.GreaterThanOrEqualTo(version, "1.0.0") + Expect(err).NotTo(HaveOccurred()) + var confString []byte + if moreThan100 { + netconf := &MirrorNetCurrent{} + err = json.Unmarshal([]byte(conf), &netconf) + Expect(err).NotTo(HaveOccurred()) + result, err := current.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + netconf.PrevResult = *result + + var data bytes.Buffer + err = result.PrintTo(&data) + Expect(err).NotTo(HaveOccurred()) + + var raw map[string]interface{} + err = json.Unmarshal(data.Bytes(), &raw) + Expect(err).NotTo(HaveOccurred()) + netconf.RawPrevResult = raw + + confString, err = json.Marshal(netconf) + Expect(err).NotTo(HaveOccurred()) + } else { + netconf := &MirrorNet040{} + err = json.Unmarshal([]byte(conf), &netconf) + Expect(err).NotTo(HaveOccurred()) + result, err := types040.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + netconf.PrevResult = *result + + var data bytes.Buffer + err = result.PrintTo(&data) + Expect(err).NotTo(HaveOccurred()) + + var raw map[string]interface{} + err = json.Unmarshal(data.Bytes(), &raw) + Expect(err).NotTo(HaveOccurred()) + netconf.RawPrevResult = raw + + confString, err = json.Marshal(netconf) + Expect(err).NotTo(HaveOccurred()) + } + + args.StdinData = confString + + err = cmdCheckWithArgs(args, func() error { + return CmdCheck(args) + }) + Expect(err).NotTo(HaveOccurred()) + } + + testDel := func(conf string, mirrors []types.Mirror, r cnitypes.Result, ifName string, targetNs ns.NetNS) { + By("Checking that mirrors are still in ovsdb") + for _, mirror := range mirrors { + mirrorDb, err := GetMirrorAttribute(mirror.Name, "name") + Expect(err).NotTo(HaveOccurred()) + Expect(mirrorDb).To(Equal(mirror.Name)) + } + + args := &skel.CmdArgs{ + ContainerID: "dummy-mir-cons", + Netns: targetNs.Path(), + IfName: ifName, + StdinData: []byte(conf), + } + + // get portUUID from result + portUUID := GetPortUUIDFromResult(r) + + // if 'output_port' contains only portUUID and both 'select_src_port' and 'select_dst_port' are empty, + // cmdDel will destroy the mirror, otherwise it will remove the portUUID from 'output_port' + var removableMirrors []string + + By("Creating a list with all mirrors that should be removed by cmdDel") + for _, mirror := range mirrors { + // Obtaining 'select_*' ports of 'mirror' + srcPorts, err := GetMirrorSrcPorts(mirror.Name) + Expect(err).NotTo(HaveOccurred()) + dstPorts, err := GetMirrorDstPorts(mirror.Name) + Expect(err).NotTo(HaveOccurred()) + // Obtaining 'output_port' of 'mirror' + outputPorts, err := GetMirrorOutputPorts(mirror.Name) + Expect(err).NotTo(HaveOccurred()) + + if OnlyContainsOrEmpty(outputPorts, portUUID) && len(srcPorts) == 0 && len(dstPorts) == 0 { + // this mirror will be removed by cmdDel + removableMirrors = append(removableMirrors, mirror.Name) + } + } + + By("Calling DEL command") + err := cmdDelWithArgs(args, func() error { + return CmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + + By("Checking mirrors after DEL command") + for _, mirror := range mirrors { + if ContainsElement(removableMirrors, mirror.Name) { + By(fmt.Sprintf("Checking that mirror %s is no longer in ovsdb", mirror.Name)) + exists, err := IsMirrorExists(mirror.Name) + Expect(err).NotTo(HaveOccurred()) + // mirror must be removed by cmdDel + Expect(exists).To(Equal(false)) + } else { + By(fmt.Sprintf("Checking that mirror %s doesn't have portUUID %s in its 'output_port'", mirror.Name, portUUID)) + outputPorts, err := GetMirrorOutputPorts(mirror.Name) + Expect(err).NotTo(HaveOccurred()) + Expect(outputPorts).NotTo(ContainElement(portUUID)) + } + } + } + + testAdd := func(conf string, mirrors []types.Mirror, pluginPrevResult *current.Result, ifName string, hasExternalOwner bool, targetNs ns.NetNS) (string, cnitypes.Result) { + confMirror, r, err := add(version, conf, pluginPrevResult, ifName, targetNs) + + Expect(err).NotTo(HaveOccurred()) + + By("Checking mirror ports") + CheckPortsInMirrors(mirrors, hasExternalOwner, ovsPortOwner, r) + + return confMirror, r + } + + Context("adding host port to a mirror", func() { + Context("as consumer (output_port in ovsdb)", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-cons", + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-consumer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + It("should successfully complete ADD, CHECK and DEL commands", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-consumer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + testCheck(confMirror, result, IFNAME1, targetNs) + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + }) + }) + }) + + Context("adding host port to multiple mirrors", func() { + Context("as consumer (output_port in ovsdb)", func() { + mirrors := []types.Mirror{ + { + Name: "mir-cons1", + }, + { + Name: "mir-cons2", + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-consumer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + It("should successfully complete ADD, CHECK and DEL commands", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-consumer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + testCheck(confMirror, result, IFNAME1, targetNs) + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + }) + }) + }) + + Context("adding multiple ports to a single mirror", func() { + Context("as consumer (output_port in ovsdb)", func() { + mirrors := []types.Mirror{ + { + Name: "mir-cons1", + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-consumer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + It("shouldn't complete ADD, because you cannot override the configuration of an existing mirror", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces/ports using ovs-cni plugin") + prevResult1 := createInterfaces(IFNAME1, targetNs) + prevResult2 := createInterfaces(IFNAME2, targetNs) + + By("run ovs-mirror-consumer ADD command for the first port") + _, _ = testAdd(conf, mirrors, prevResult1, IFNAME1, false, targetNs) + + By("run ovs-mirror-consumer ADD command for the second port expecting an error") + _, _, err := add(version, conf, prevResult2, IFNAME2, targetNs) + Expect(err).To(HaveOccurred()) + + By("verify the error message") + portUUID2 := GetPortUUIDFromResult(prevResult2) + errorMessage := fmt.Sprintf("cannot attach port %s to mirror %s "+ + "because there is already another port. Error:", portUUID2, mirrors[0].Name) + Expect(err.Error()).To(ContainSubstring(errorMessage)) + }) + }) + }) + + Context("adding a mirror with both producer and consumer configuration", func() { + Context("('output_port', 'select_src_port' and 'select_dst_port' defined with valid portUUIDs)", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-cons", + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-consumer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + It("shouldn't be removed after calling cmdDel by this plugin, because it contains 'select_src_port' and 'select_dst_port' configured by a producer", func() { + // This is very important: + // cmdDel of both mirror-consumer and mirror-consumer plugins is able to cleanup a mirror + // without a useful configuration (all traffic outputs and inputs are undefined). + // However, they can remove a mirror only if both + // 'output_port', 'select_src_port' and 'select_dst_port' are empty. + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-consumer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + testCheck(confMirror, result, IFNAME1, targetNs) + + By("create a producer interface and add its port via 'ovs-vsctl' to fill both 'select_src_port' and 'select_dst_port'") + r2 := createInterfaces(IFNAME2, targetNs) + portUUID := GetPortUUIDFromResult(r2) + AddSelectPortToMirror(portUUID, mirrors[0].Name, true, true) + + By("run DEL command of ovs-mirror-consumer") + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + + By("check results: mirror still exists") + exists, err := IsMirrorExists(mirrors[0].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(Equal(true)) + By("check results: 'select_src_port' and 'select_dst_port' must be unchanged") + srcPorts, err := GetMirrorSrcPorts(mirrors[0].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(srcPorts).To(ContainElement(portUUID)) + dstPorts, err := GetMirrorDstPorts(mirrors[0].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(dstPorts).To(ContainElement(portUUID)) + By("check results: 'output_port' must be empty") + outputs, err := GetMirrorOutputPorts(mirrors[0].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(outputs).To(BeEmpty()) + }) + }) + }) + + Context("adding multiple mirrors with both producer and consumer configuration", func() { + Context("('output_port' and either 'select_src_port' or 'select_dst_port' defined with valid portUUIDs)", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-cons1", + }, + { + Name: "mirror-cons2", + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-consumer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + It("shouldn't be removed after calling cmdDel by this plugin, because it contains either 'select_src_port' or 'select_dst_port' configured by a producer", func() { + // This is very important: + // cmdDel of both mirror-consumer and mirror-consumer plugins is able to cleanup a mirror + // without a useful configuration (all traffic outputs and inputs are undefined). + // However, they can remove a mirror only if both + // 'output_port', 'select_src_port' and 'select_dst_port' are empty. + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-consumer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + testCheck(confMirror, result, IFNAME1, targetNs) + + By("create a producer interface and get its portUUID") + r2 := createInterfaces(IFNAME2, targetNs) + portUUID := GetPortUUIDFromResult(r2) + By(fmt.Sprintf("update mirror %s adding portUUID as 'select_src_port'", mirrors[0].Name)) + AddSelectPortToMirror(portUUID, mirrors[0].Name, true, false) + By(fmt.Sprintf("update mirror %s adding portUUID as 'select_dst_port'", mirrors[1].Name)) + AddSelectPortToMirror(portUUID, mirrors[1].Name, false, true) + + By("run DEL command of ovs-mirror-consumer") + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + + By("check results: mirror still exists") + exists, err := IsMirrorExists(mirrors[0].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(Equal(true)) + By("check results: 'select_src_port' and 'select_dst_port' must be unchanged") + srcPorts, err := GetMirrorSrcPorts(mirrors[0].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(srcPorts).To(ContainElement(portUUID)) + dstPorts, err := GetMirrorDstPorts(mirrors[0].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(dstPorts).To(BeEmpty()) + By("check results: 'output_port' must be empty") + outputs, err := GetMirrorOutputPorts(mirrors[0].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(outputs).To(BeEmpty()) + + By("check results: mirror still exists") + exists, err = IsMirrorExists(mirrors[1].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(Equal(true)) + By("check results: 'select_src_port' and 'select_dst_port' must be unchanged") + srcPorts, err = GetMirrorSrcPorts(mirrors[1].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(srcPorts).To(BeEmpty()) + dstPorts, err = GetMirrorDstPorts(mirrors[1].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(dstPorts).To(ContainElement(portUUID)) + By("check results: 'output_port' must be empty") + outputs, err = GetMirrorOutputPorts(mirrors[1].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(outputs).To(BeEmpty()) + }) + }) + }) + + Context("when there are empty mirrors in ovsdb", func() { + Context("that are owned by ovs-cni,", func() { + Context("creating a new mirror", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-cons", + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-consumer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + emptyMirrors := []string{"emptyMirCons1", "emptyMirCons2"} + + It("should remove those that are in the current bridge", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("manually create empty mirrors owned by ovs-cni") + CreateEmptyMirrors(bridgeName, emptyMirrors, ovsPortOwner) + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-consumer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, true, targetNs) + + // 'cmdAdd' mirror function calls automatically cleanEmptyMirrors + // to remove unused mirrors of the bridge + + By("mirrors should not exist anymore") + CheckEmptyMirrorsExistence(emptyMirrors, false) + + testCheck(confMirror, result, IFNAME1, targetNs) + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + }) + }) + + Context("deleting a mirror", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-cons", + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-consumer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + emptyMirrors := []string{"emptyMirCons1", "emptyMirCons2"} + + It("should remove those that are in the current bridge", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-consumer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + testCheck(confMirror, result, IFNAME1, targetNs) + + By("manually create empty mirrors owned by ovs-cni") + CreateEmptyMirrors(bridgeName, emptyMirrors, ovsPortOwner) + + // 'cmdDel' mirror function calls automatically cleanEmptyMirrors + // to remove unused mirrors of the bridge + + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + + By("mirrors should not exist anymore") + CheckEmptyMirrorsExistence(emptyMirrors, false) + }) + }) + }) + + Context("that are NOT owned by ovs-cni,", func() { + Context("creating a new mirror", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-cons", + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-consumer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + emptyMirrors := []string{"emptyMirCons1", "emptyMirCons2"} + + It("should NOT remove those that are in the current bridge", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("manually create an empty mirror WITHOUT specifying an owner") + CreateEmptyMirrors(bridgeName, emptyMirrors, "") + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-consumer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, true, targetNs) + + // 'cmdAdd' mirror function calls automatically cleanEmptyMirrors + // to remove unused mirrors of the bridge + + By("mirrors should still exists") + CheckEmptyMirrorsExistence(emptyMirrors, true) + + testCheck(confMirror, result, IFNAME1, targetNs) + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + }) + }) + + Context("deleting a mirror", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-cons", + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-consumer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + emptyMirrors := []string{"emptyMirCons1", "emptyMirCons2"} + + It("should NOT remove those that are in the current bridge", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-consumer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + testCheck(confMirror, result, IFNAME1, targetNs) + + By("manually create an empty mirror WITHOUT specifying an owner") + CreateEmptyMirrors(bridgeName, emptyMirrors, "") + + // 'cmdDel' mirror function calls automatically cleanEmptyMirrors + // to remove unused mirrors of the bridge + + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + + By("mirrors should still exists") + CheckEmptyMirrorsExistence(emptyMirrors, true) + }) + }) + }) + }) +} + +func newNS() ns.NetNS { + targetNs, err := testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + return targetNs +} + +func closeNS(targetNs ns.NetNS) { + Expect(targetNs.Close()).To(Succeed()) + Expect(testutils.UnmountNS(targetNs)).To(Succeed()) +} + +func cmdAddWithArgs(args *skel.CmdArgs, f func() error) (cnitypes.Result, []byte, error) { + return testutils.CmdAdd(args.Netns, args.ContainerID, args.IfName, args.StdinData, f) +} + +func cmdCheckWithArgs(args *skel.CmdArgs, f func() error) error { + return testutils.CmdCheck(args.Netns, args.ContainerID, args.IfName, args.StdinData, f) +} + +func cmdDelWithArgs(args *skel.CmdArgs, f func() error) error { + return testutils.CmdDel(args.Netns, args.ContainerID, args.IfName, f) +} + +// function to call cmdAdd with the right input +func add(version string, conf string, pluginPrevResult *current.Result, ifName string, targetNs ns.NetNS) (string, cnitypes.Result, error) { + By("Building prevResult to pass it as input to mirror-consumer plugin") + interfacesJSONStr, err := ToJSONString(pluginPrevResult.Interfaces) + Expect(err).NotTo(HaveOccurred()) + + prevResult := fmt.Sprintf(`{ + "cniVersion": "%s", + "interfaces": %s + }`, version, interfacesJSONStr) + + // add prevResult to conf (first we need to remove the last character "}" + // and then concatenate the rest + confMirror := conf[:len(conf)-1] + ", \"prevResult\": " + prevResult + "\n}" + + argsMirror := &skel.CmdArgs{ + ContainerID: "dummy-mir-cons", + Netns: targetNs.Path(), + IfName: ifName, + StdinData: []byte(confMirror), + } + + By("Calling ADD command for mirror-consumer plugin") + r, _, err := cmdAddWithArgs(argsMirror, func() error { + return CmdAdd(argsMirror) + }) + + return confMirror, r, err +} diff --git a/pkg/mirror-producer/producer.go b/pkg/mirror-producer/producer.go new file mode 100644 index 000000000..ea2a765a7 --- /dev/null +++ b/pkg/mirror-producer/producer.go @@ -0,0 +1,242 @@ +// Copyright 2018-2019 Red Hat, Inc. +// Copyright 2014 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Go version 1.10 or greater is required. Before that, switching namespaces in +// long running processes in go did not work in a reliable way. +//go:build go1.10 +// +build go1.10 + +package plugin + +import ( + "errors" + "fmt" + "log" + "runtime" + + "github.com/k8snetworkplumbingwg/ovs-cni/pkg/config" + "github.com/k8snetworkplumbingwg/ovs-cni/pkg/utils" + + "github.com/containernetworking/cni/pkg/skel" + cnitypes "github.com/containernetworking/cni/pkg/types" + current "github.com/containernetworking/cni/pkg/types/100" + + "github.com/k8snetworkplumbingwg/ovs-cni/pkg/ovsdb" + "github.com/k8snetworkplumbingwg/ovs-cni/pkg/types" +) + +func init() { + // this ensures that main runs only on main thread (thread group leader). + // since namespace ops (unshare, setns) are done for a single thread, we + // must ensure that the goroutine does not jump from OS thread to thread + runtime.LockOSThread() +} + +func logCall(command string, args *skel.CmdArgs) { + log.Printf("CNI %s was called for container ID: %s, network namespace %s, interface name %s, configuration: %s", + command, args.ContainerID, args.Netns, args.IfName, string(args.StdinData[:])) +} + +func getPortUUID(ovsDriver *ovsdb.OvsBridgeDriver, interfaces []*current.Interface) (string, error) { + for _, iface := range interfaces { + uuid, err := ovsDriver.GetPortUUID(iface.Name) + if err == nil { + return uuid.GoUUID, nil + } + } + + return "", errors.New("cannot find port in db") +} + +func attachPortToMirror(ovsDriver *ovsdb.OvsBridgeDriver, portUUIDStr string, mirror *types.Mirror) error { + err := ovsDriver.AttachPortToMirrorProducer(portUUIDStr, mirror.Name, mirror.Ingress, mirror.Egress) + if err != nil { + return err + } + + return nil +} + +func detachPortFromMirror(ovsDriver *ovsdb.OvsBridgeDriver, portUUIDStr string, mirror *types.Mirror) error { + err := ovsDriver.DetachPortFromMirrorProducer(portUUIDStr, mirror.Name) + if err != nil { + return err + } + + return nil +} + +// CmdAdd add handler for attaching container into network +func CmdAdd(args *skel.CmdArgs) error { + logCall("ADD", args) + + netconf, err := config.LoadMirrorConf(args.StdinData) + if err != nil { + return err + } + + ovsDriver, err := ovsdb.NewOvsBridgeDriver(netconf.BrName, netconf.SocketFile) + if err != nil { + return err + } + + // removes all empty mirrors + if err := ovsDriver.CleanEmptyMirrors(); err != nil { + return err + } + + // Cache PrevResult for CmdDel + if err = utils.SaveCache(config.GetCRef(args.ContainerID, args.IfName)+"_prod", + &types.CachedPrevResultNetConf{PrevResult: netconf.PrevResult}); err != nil { + return fmt.Errorf("error saving NetConf %q", err) + } + + portUUID, err := getPortUUID(ovsDriver, netconf.PrevResult.Interfaces) + if err != nil { + return fmt.Errorf("cannot get existing portUuid from db %v", err) + } + + for _, mirror := range netconf.Mirrors { + + err = ovsDriver.CreateMirror(netconf.BrName, mirror.Name) + if err != nil { + return fmt.Errorf("cannot create mirror %s: %v ", mirror.Name, err) + } + + if err = attachPortToMirror(ovsDriver, portUUID, mirror); err != nil { + return fmt.Errorf("cannot attach port %s to mirror %s: %v", portUUID, mirror.Name, err) + } + } + + result := ¤t.Result{ + Interfaces: netconf.PrevResult.Interfaces, + } + + return cnitypes.PrintResult(result, netconf.CNIVersion) +} + +// CmdDel remove handler for deleting container from network +func CmdDel(args *skel.CmdArgs) error { + logCall("DEL", args) + + cRef := config.GetCRef(args.ContainerID, args.IfName) + cache, err := config.LoadPrevResultConfFromCache(cRef + "_prod") + if err != nil { + // If cmdDel() fails, cached prevResult is cleaned up by + // the followed defer call. However, subsequence calls + // of cmdDel() from kubelet fail in a dead loop due to + // cached prevResult doesn't exist. + // Return nil when LoadPrevResultConfFromCache fails since the rest + // of cmdDel() code relies on prevResult as input argument + // and there is no meaning to continue. + return nil + } + + defer func() { + if err == nil { + utils.CleanCache(cRef + "_prod") + } + }() + + netconf, err := config.LoadMirrorConf(args.StdinData) + if err != nil { + return err + } + // add prevResult, because missing in CNI spec < 0.4.0 + netconf.PrevResult = cache.PrevResult + + ovsDriver, err := ovsdb.NewOvsBridgeDriver(netconf.BrName, netconf.SocketFile) + if err != nil { + return err + } + + portUUID, err := getPortUUID(ovsDriver, netconf.PrevResult.Interfaces) + if err != nil { + return fmt.Errorf("cannot get existing portUuid from db %v", err) + } + + for _, mirror := range netconf.Mirrors { + + mirrorExist, err := ovsDriver.IsMirrorPresent(mirror.Name) + if err != nil { + return err + } + if !mirrorExist { + // skip error because CNI spec states that "Plugins should generally complete a DEL action without error even if some resources are missing" + continue + } + + if err = detachPortFromMirror(ovsDriver, portUUID, mirror); err != nil { + return fmt.Errorf("cannot detach port %s from mirror %s: %v", portUUID, mirror.Name, err) + } + + used, err := ovsDriver.IsMirrorUsed(netconf.BrName, mirror.Name) + if err != nil { + return fmt.Errorf("cannot check if mirror %s is used: %v ", mirror.Name, err) + } + + // if this mirror is not used we can remove it + if !used { + err = ovsDriver.DeleteMirror(netconf.BrName, mirror.Name) + if err != nil { + return fmt.Errorf("cannot delete mirror %s: %v ", mirror.Name, err) + } + } + } + + // removes all empty mirrors + if err := ovsDriver.CleanEmptyMirrors(); err != nil { + return err + } + + result := ¤t.Result{ + Interfaces: netconf.PrevResult.Interfaces, + } + + return cnitypes.PrintResult(result, netconf.CNIVersion) +} + +// CmdCheck check handler to make sure networking is as expected. +func CmdCheck(args *skel.CmdArgs) error { + logCall("CHECK", args) + + netconf, err := config.LoadMirrorConf(args.StdinData) + if err != nil { + return err + } + + ovsDriver, err := ovsdb.NewOvsBridgeDriver(netconf.BrName, netconf.SocketFile) + if err != nil { + return err + } + + portUUID, err := getPortUUID(ovsDriver, netconf.PrevResult.Interfaces) + if err != nil { + return fmt.Errorf("cannot get existing portUuid from db %v", err) + } + + for _, mirror := range netconf.Mirrors { + mirrorExist, err := ovsDriver.CheckMirrorProducerWithPorts(mirror.Name, mirror.Ingress, mirror.Egress, portUUID) + if err != nil { + return err + } + + if !mirrorExist { + return fmt.Errorf("mirror %s not present", mirror.Name) + } + } + + return nil +} diff --git a/pkg/mirror-producer/producer_suite_test.go b/pkg/mirror-producer/producer_suite_test.go new file mode 100644 index 000000000..9a0e5986e --- /dev/null +++ b/pkg/mirror-producer/producer_suite_test.go @@ -0,0 +1,27 @@ +// Copyright 2018 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestPlugin(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Mirror Producer Suite") +} diff --git a/pkg/mirror-producer/producer_test.go b/pkg/mirror-producer/producer_test.go new file mode 100644 index 000000000..4b522760c --- /dev/null +++ b/pkg/mirror-producer/producer_test.go @@ -0,0 +1,867 @@ +// Copyright 2018 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "bytes" + "encoding/json" + "fmt" + "os/exec" + + "github.com/containernetworking/cni/pkg/skel" + cnitypes "github.com/containernetworking/cni/pkg/types" + types040 "github.com/containernetworking/cni/pkg/types/040" + current "github.com/containernetworking/cni/pkg/types/100" + cniversion "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/testutils" + "github.com/vishvananda/netlink" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + plugin "github.com/k8snetworkplumbingwg/ovs-cni/pkg/plugin" + + . "github.com/k8snetworkplumbingwg/ovs-cni/pkg/testhelpers" + + "github.com/k8snetworkplumbingwg/ovs-cni/pkg/types" +) + +const bridgeName = "bridge-mir-prod" +const vlanID = 100 +const IFNAME1 = "eth0" +const IFNAME2 = "eth1" +const ovsPortOwner = "ovs-cni.network.kubevirt.io" + +var _ = BeforeSuite(func() { + output, err := exec.Command("ovs-vsctl", "show").CombinedOutput() + Expect(err).NotTo(HaveOccurred(), "Open vSwitch is not available, if you have it installed and running, try to run tests with `sudo -E`: %v", string(output[:])) +}) + +var _ = AfterSuite(func() { + exec.Command("ovs-vsctl", "del-br", "--if-exists", bridgeName).Run() +}) + +var _ = Describe("CNI mirror-producer 0.3.0", func() { testFunc("0.3.0") }) +var _ = Describe("CNI mirror-producer 0.3.1", func() { testFunc("0.3.1") }) +var _ = Describe("CNI mirror-producer 0.4.0", func() { testFunc("0.4.0") }) +var _ = Describe("CNI mirror-producer 1.0.0", func() { testFunc("1.0.0") }) + +var testFunc = func(version string) { + BeforeEach(func() { + output, err := exec.Command("ovs-vsctl", "add-br", bridgeName).CombinedOutput() + Expect(err).NotTo(HaveOccurred(), "Failed to create testing OVS bridge: %v", string(output[:])) + + bridgeLink, err := netlink.LinkByName(bridgeName) + Expect(err).NotTo(HaveOccurred(), "Interface of testing OVS bridge was not found in the system") + + err = netlink.LinkSetUp(bridgeLink) + Expect(err).NotTo(HaveOccurred(), "Was not able to set bridge UP") + }) + + AfterEach(func() { + output, err := exec.Command("ovs-vsctl", "del-br", bridgeName).CombinedOutput() + Expect(err).NotTo(HaveOccurred(), "Failed to remove testing OVS bridge: %v", string(output[:])) + }) + + createInterfaces := func(ifName string, targetNs ns.NetNS) *current.Result { + confplugin := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs", + "bridge": "%s", + "vlan": %d + }`, version, bridgeName, vlanID) + args := &skel.CmdArgs{ + ContainerID: "dummy-mir-prod", + Netns: targetNs.Path(), + IfName: ifName, + StdinData: []byte(confplugin), + } + + By("Calling ADD command of ovs-cni plugin to create interfaces") + resPlugin, _, err := cmdAddWithArgs(args, func() error { + return plugin.CmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + + By("Checking that result of ADD command of ovs-cni plugin is in expected format") + resultPlugin, err := current.GetResult(resPlugin) + Expect(err).NotTo(HaveOccurred()) + Expect(len(resultPlugin.Interfaces)).To(Equal(2)) + Expect(len(resultPlugin.IPs)).To(Equal(0)) + + return resultPlugin + } + + testCheck := func(conf string, r cnitypes.Result, ifName string, targetNs ns.NetNS) { + if checkSupported, _ := cniversion.GreaterThanOrEqualTo(version, "0.4.0"); !checkSupported { + return + } + + args := &skel.CmdArgs{ + ContainerID: "dummy-mir-prod", + Netns: targetNs.Path(), + IfName: ifName, + StdinData: []byte(conf), + } + + By("Calling CHECK command") + moreThan100, err := cniversion.GreaterThanOrEqualTo(version, "1.0.0") + Expect(err).NotTo(HaveOccurred()) + var confString []byte + if moreThan100 { + netconf := &MirrorNetCurrent{} + err = json.Unmarshal([]byte(conf), &netconf) + Expect(err).NotTo(HaveOccurred()) + result, err := current.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + netconf.PrevResult = *result + + var data bytes.Buffer + err = result.PrintTo(&data) + Expect(err).NotTo(HaveOccurred()) + + var raw map[string]interface{} + err = json.Unmarshal(data.Bytes(), &raw) + Expect(err).NotTo(HaveOccurred()) + netconf.RawPrevResult = raw + + confString, err = json.Marshal(netconf) + Expect(err).NotTo(HaveOccurred()) + } else { + netconf := &MirrorNet040{} + err = json.Unmarshal([]byte(conf), &netconf) + Expect(err).NotTo(HaveOccurred()) + result, err := types040.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + netconf.PrevResult = *result + + var data bytes.Buffer + err = result.PrintTo(&data) + Expect(err).NotTo(HaveOccurred()) + + var raw map[string]interface{} + err = json.Unmarshal(data.Bytes(), &raw) + Expect(err).NotTo(HaveOccurred()) + netconf.RawPrevResult = raw + + confString, err = json.Marshal(netconf) + Expect(err).NotTo(HaveOccurred()) + } + + args.StdinData = confString + + err = cmdCheckWithArgs(args, func() error { + return CmdCheck(args) + }) + Expect(err).NotTo(HaveOccurred()) + } + + testDel := func(conf string, mirrors []types.Mirror, r cnitypes.Result, ifName string, targetNs ns.NetNS) { + By("Checking that mirrors are still in ovsdb") + for _, mirror := range mirrors { + mirrorDb, err := GetMirrorAttribute(mirror.Name, "name") + Expect(err).NotTo(HaveOccurred()) + Expect(mirrorDb).To(Equal(mirror.Name)) + } + + args := &skel.CmdArgs{ + ContainerID: "dummy-mir-prod", + Netns: targetNs.Path(), + IfName: ifName, + StdinData: []byte(conf), + } + + // get portUUID from result + portUUID := GetPortUUIDFromResult(r) + + // if both 'select_src_port' and 'select_dst_port' contains only 'portUUID', + // cmdDel will destroy the mirror, otherwise it will remove that specific uuid from that mirror. + // However, cmdDel can remove a mirror only if also 'output_port' is empty! + var removableMirrors []string + + By("Creating a list with all mirrors that should be removed by cmdDel") + for _, mirror := range mirrors { + // Obtaining 'select_*' ports of 'mirror' + srcPorts, err := GetMirrorSrcPorts(mirror.Name) + Expect(err).NotTo(HaveOccurred()) + dstPorts, err := GetMirrorDstPorts(mirror.Name) + Expect(err).NotTo(HaveOccurred()) + // Obtaining 'output_port' of 'mirror' + outputPorts, err := GetMirrorOutputPorts(mirror.Name) + Expect(err).NotTo(HaveOccurred()) + + if len(outputPorts) == 0 && OnlyContainsOrEmpty(srcPorts, portUUID) && OnlyContainsOrEmpty(dstPorts, portUUID) { + // this mirror will be removed by cmdDel + removableMirrors = append(removableMirrors, mirror.Name) + } + } + + By("Calling DEL command") + err := cmdDelWithArgs(args, func() error { + return CmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + + By("Checking mirrors after DEL command") + for _, mirror := range mirrors { + if ContainsElement(removableMirrors, mirror.Name) { + By(fmt.Sprintf("Checking that mirror %s is no longer in ovsdb", mirror.Name)) + exists, err := IsMirrorExists(mirror.Name) + Expect(err).NotTo(HaveOccurred()) + // mirror must be removed by cmdDel + Expect(exists).To(Equal(false)) + } else { + if mirror.Ingress { + By(fmt.Sprintf("Checking that mirror %s doesn't have portUUID %s in its 'select_src_port'", mirror.Name, portUUID)) + srcPorts, err := GetMirrorSrcPorts(mirror.Name) + Expect(err).NotTo(HaveOccurred()) + Expect(srcPorts).NotTo(ContainElement(portUUID)) + } + + if mirror.Egress { + By(fmt.Sprintf("Checking that mirror %s doesn't have portUUID %s in its 'select_dst_port'", mirror.Name, portUUID)) + dstPorts, err := GetMirrorDstPorts(mirror.Name) + Expect(err).NotTo(HaveOccurred()) + Expect(dstPorts).NotTo(ContainElement(portUUID)) + } + } + } + } + + testAdd := func(conf string, mirrors []types.Mirror, pluginPrevResult *current.Result, ifName string, hasExternalOwner bool, targetNs ns.NetNS) (string, cnitypes.Result) { + confMirror, r, err := add(version, conf, pluginPrevResult, ifName, targetNs) + + Expect(err).NotTo(HaveOccurred()) + + By("Checking mirror ports") + CheckPortsInMirrors(mirrors, hasExternalOwner, ovsPortOwner, r) + + return confMirror, r + } + + Context("adding host port to a mirror", func() { + Context("as both ingress and egress (select_src_port and select_dst_port in ovsdb)", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-prod", + Ingress: true, + Egress: true, + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-producer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + It("should successfully complete ADD, CHECK and DEL commands", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-producer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + testCheck(confMirror, result, IFNAME1, targetNs) + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + }) + }) + + Context("as ingress, but not egress (only select_src_port in ovsdb)", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-prod", + Ingress: true, + // Egress: false (if omitted, 'Egress' is false by default) + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-producer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + It("should successfully complete ADD, CHECK and DEL commands", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-producer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + testCheck(confMirror, result, IFNAME1, targetNs) + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + }) + }) + + Context("as egress, but not ingress (only select_dst_port in ovsdb)", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-prod", + Ingress: false, // (if omitted, 'Ingress' is false by default) + Egress: true, + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-producer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + It("should successfully complete ADD, CHECK and DEL commands", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-producer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + testCheck(confMirror, result, IFNAME1, targetNs) + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + }) + }) + + Context("without both ingress and egress (select_src_port and select_dst_port in ovsdb)", func() { + mirrorName := "mir-prod1" + mirrors := []types.Mirror{ + { + Name: mirrorName, + Ingress: false, + // Egress omitted, but false by default + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-producer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + It("should FAIL with ADD command", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces/ports using ovs-cni plugin") + prevResult1 := createInterfaces(IFNAME1, targetNs) + portUUID := GetPortUUIDFromResult(prevResult1) + + By("run ovs-mirror-producer ADD command") + // call 'add' instead of 'testAdd' because we want the result of cmdAdd without additional check + _, _, err := add(version, conf, prevResult1, IFNAME1, targetNs) + Expect(err).To(HaveOccurred()) + + By("verify the error message") + errorMessage := fmt.Sprintf("cannot attach port %s to mirror %s: "+ + "a mirror producer must have either a ingress or an egress or both", portUUID, mirrorName) + Expect(err.Error()).To(Equal(errorMessage)) + }) + }) + }) + + Context("adding host port to multiple mirrors", func() { + Context("with different ingress and egress configurations", func() { + mirrors := []types.Mirror{ + { + Name: "mir-prod1", + Ingress: true, + Egress: true, + }, + { + Name: "mir-prod2", + Ingress: false, + Egress: true, + }, + { + Name: "mir-prod3", + Ingress: true, + Egress: false, + }, + { + Name: "mir-prod4", + Ingress: true, + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-producer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + It("should successfully complete ADD, CHECK and DEL commands", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-producer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + testCheck(confMirror, result, IFNAME1, targetNs) + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + }) + }) + }) + + Context("adding multiple ports to a single mirror", func() { + Context("as both ingress and egress (select_src_port and select_dst_port in ovsdb)", func() { + mirrors := []types.Mirror{ + { + Name: "mir-prod1", + Ingress: true, + Egress: true, + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-producer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + It("should successfully complete ADD, CHECK and DEL commands", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces/ports using ovs-cni plugin") + prevResult1 := createInterfaces(IFNAME1, targetNs) + prevResult2 := createInterfaces(IFNAME2, targetNs) + + By("run ovs-mirror-producer passing prevResult") + confMirror1, result1 := testAdd(conf, mirrors, prevResult1, IFNAME1, false, targetNs) + confMirror2, result2 := testAdd(conf, mirrors, prevResult2, IFNAME2, false, targetNs) + testCheck(confMirror1, result1, IFNAME1, targetNs) + testCheck(confMirror2, result2, IFNAME2, targetNs) + + By("run ovs-mirror-producer to delete mirrors") + testDel(confMirror1, mirrors, result1, IFNAME1, targetNs) + testDel(confMirror2, mirrors, result2, IFNAME2, targetNs) + }) + }) + }) + + Context("adding multiple ports to multiple mirrors", func() { + Context("with different ingress and egress configurations", func() { + mirrors := []types.Mirror{ + { + Name: "mir-prod1", + Ingress: true, + Egress: true, + }, + { + Name: "mir-prod2", + Ingress: false, + Egress: true, + }, + { + Name: "mir-prod3", + Ingress: true, + Egress: false, + }, + { + Name: "mir-prod4", + Ingress: true, + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-producer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + It("should successfully complete ADD, CHECK and DEL commands", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces/ports using ovs-cni plugin") + prevResult1 := createInterfaces(IFNAME1, targetNs) + prevResult2 := createInterfaces(IFNAME2, targetNs) + + By("run ovs-mirror-producer passing prevResult") + confMirror1, result1 := testAdd(conf, mirrors, prevResult1, IFNAME1, false, targetNs) + confMirror2, result2 := testAdd(conf, mirrors, prevResult2, IFNAME2, false, targetNs) + testCheck(confMirror1, result1, IFNAME1, targetNs) + testCheck(confMirror2, result2, IFNAME2, targetNs) + + By("run ovs-mirror-producer to delete mirrors") + testDel(confMirror1, mirrors, result1, IFNAME1, targetNs) + testDel(confMirror2, mirrors, result2, IFNAME2, targetNs) + }) + }) + }) + + Context("adding a mirror with both producer and consumer configuration", func() { + Context("('output_port', 'select_src_port' and 'select_dst_port' defined with valid portUUIDs)", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-prod", + Ingress: true, + Egress: true, + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-producer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + It("shouldn't be removed after calling cmdDel by this plugin, because it contains 'output_port' configured by a consumer", func() { + // This is very important: + // cmdDel of both mirror-producer and mirror-consumer plugins is able to cleanup a mirror + // without a useful configuration (all traffic outputs and inputs are undefined). + // However, they can remove a mirror only if both + // 'output_port', 'select_src_port' and 'select_dst_port' are empty. + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-producer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + testCheck(confMirror, result, IFNAME1, targetNs) + + By("create a consumer interface and add its port via 'ovs-vsctl' to fill mirror 'output_port'") + r2 := createInterfaces(IFNAME2, targetNs) + portUUID := GetPortUUIDFromResult(r2) + AddOutputPortToMirror(portUUID, mirrors[0].Name) + + By("run DEL command of ovs-mirror-producer") + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + + By("check results: mirror still exists") + exists, err := IsMirrorExists(mirrors[0].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(Equal(true)) + By("check results: 'select_src_port*' and 'select_dst_port' must be empty") + srcPorts, err := GetMirrorSrcPorts(mirrors[0].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(srcPorts).To(BeEmpty()) + dstPorts, err := GetMirrorDstPorts(mirrors[0].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(dstPorts).To(BeEmpty()) + By("check results: 'output_port' must be unchanged") + outputs, err := GetMirrorOutputPorts(mirrors[0].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(outputs).To(ContainElement(portUUID)) + }) + }) + }) + + Context("when there are empty mirrors in ovsdb", func() { + Context("that are owned by ovs-cni,", func() { + Context("creating a new mirror", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-prod", + Ingress: true, + Egress: true, + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-producer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + emptyMirrors := []string{"emptyMirProd1", "emptyMirProd2"} + + It("should remove those that are in the current bridge", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("manually create empty mirrors owned by ovs-cni") + CreateEmptyMirrors(bridgeName, emptyMirrors, ovsPortOwner) + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-producer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + + // 'cmdAdd' mirror function calls automatically cleanEmptyMirrors + // to remove unused mirrors of the bridge + + By("mirrors should not exist anymore") + CheckEmptyMirrorsExistence(emptyMirrors, false) + + testCheck(confMirror, result, IFNAME1, targetNs) + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + }) + }) + + Context("deleting a mirror", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-prod", + Ingress: true, + Egress: true, + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-producer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + emptyMirrors := []string{"emptyMirProd1", "emptyMirProd2"} + + It("should remove those that are in the current bridge", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-producer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + testCheck(confMirror, result, IFNAME1, targetNs) + + By("manually create empty mirrors owned by ovs-cni") + CreateEmptyMirrors(bridgeName, emptyMirrors, ovsPortOwner) + + // 'cmdDel' mirror function calls automatically cleanEmptyMirrors + // to remove unused mirrors of the bridge + + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + + By("mirrors should not exist anymore") + CheckEmptyMirrorsExistence(emptyMirrors, false) + }) + }) + }) + + Context("that are NOT owned by ovs-cni,", func() { + Context("creating a new mirror", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-prod", + Ingress: true, + Egress: true, + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-producer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + emptyMirrors := []string{"emptyMirProd1", "emptyMirProd2"} + + It("should NOT remove those that are in the current bridge", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("manually create an empty mirror WITHOUT specifying an owner") + CreateEmptyMirrors(bridgeName, emptyMirrors, "") + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-producer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + + // 'cmdAdd' mirror function calls automatically cleanEmptyMirrors + // to remove unused mirrors of the bridge + + By("mirrors should still exists") + CheckEmptyMirrorsExistence(emptyMirrors, true) + + testCheck(confMirror, result, IFNAME1, targetNs) + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + }) + }) + + Context("deleting a mirror", func() { + mirrors := []types.Mirror{ + { + Name: "mirror-prod", + Ingress: true, + Egress: true, + }, + } + mirrorsJSONStr, err := ToJSONString(mirrors) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ovs-mirror-producer", + "bridge": "%s", + "mirrors": %s + }`, version, bridgeName, mirrorsJSONStr) + + emptyMirrors := []string{"emptyMirProd1", "emptyMirProd2"} + + It("should NOT remove those that are in the current bridge", func() { + targetNs := newNS() + defer func() { + closeNS(targetNs) + }() + + By("create interfaces using ovs-cni plugin") + prevResult := createInterfaces(IFNAME1, targetNs) + + By("run ovs-mirror-producer passing prevResult") + confMirror, result := testAdd(conf, mirrors, prevResult, IFNAME1, false, targetNs) + testCheck(confMirror, result, IFNAME1, targetNs) + + By("manually create an empty mirror WITHOUT specifying an owner") + CreateEmptyMirrors(bridgeName, emptyMirrors, "") + + // 'cmdDel' mirror function calls automatically cleanEmptyMirrors + // to remove unused mirrors of the bridge + + testDel(confMirror, mirrors, result, IFNAME1, targetNs) + + By("mirrors should still exists") + CheckEmptyMirrorsExistence(emptyMirrors, true) + }) + }) + }) + }) +} + +func newNS() ns.NetNS { + targetNs, err := testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + return targetNs +} + +func closeNS(targetNs ns.NetNS) { + Expect(targetNs.Close()).To(Succeed()) + Expect(testutils.UnmountNS(targetNs)).To(Succeed()) +} + +func cmdAddWithArgs(args *skel.CmdArgs, f func() error) (cnitypes.Result, []byte, error) { + return testutils.CmdAdd(args.Netns, args.ContainerID, args.IfName, args.StdinData, f) +} + +func cmdCheckWithArgs(args *skel.CmdArgs, f func() error) error { + return testutils.CmdCheck(args.Netns, args.ContainerID, args.IfName, args.StdinData, f) +} + +func cmdDelWithArgs(args *skel.CmdArgs, f func() error) error { + return testutils.CmdDel(args.Netns, args.ContainerID, args.IfName, f) +} + +// function to call cmdAdd with the right input +func add(version string, conf string, pluginPrevResult *current.Result, ifName string, targetNs ns.NetNS) (string, cnitypes.Result, error) { + By("Building prevResult to pass it as input to mirror-producer plugin") + interfacesJSONStr, err := ToJSONString(pluginPrevResult.Interfaces) + Expect(err).NotTo(HaveOccurred()) + + prevResult := fmt.Sprintf(`{ + "cniVersion": "%s", + "interfaces": %s + }`, version, interfacesJSONStr) + + // add prevResult to conf (first we need to remove the last character "}" + // and then concatenate the rest + confMirror := conf[:len(conf)-1] + ", \"prevResult\": " + prevResult + "\n}" + + argsMirror := &skel.CmdArgs{ + ContainerID: "dummy-mir-prod", + Netns: targetNs.Path(), + IfName: ifName, + StdinData: []byte(confMirror), + } + + By("Calling ADD command for mirror-producer plugin") + r, _, err := cmdAddWithArgs(argsMirror, func() error { + return CmdAdd(argsMirror) + }) + + return confMirror, r, err +} diff --git a/pkg/ovsdb/ovsdb.go b/pkg/ovsdb/ovsdb.go index e14cb2057..972ca255e 100644 --- a/pkg/ovsdb/ovsdb.go +++ b/pkg/ovsdb/ovsdb.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "log" + "reflect" "github.com/ovn-org/libovsdb/client" "github.com/ovn-org/libovsdb/model" @@ -56,6 +57,12 @@ type OvsBridgeDriver struct { OvsBridgeName string } +// constants used to identify if a mirror is a comsumer or a producer +const ( + MirrorProducer = iota + MirrorConsumer +) + // connectToOvsDb connect to ovsdb func connectToOvsDb(ovsSocket string) (client.Client, error) { dbmodel, err := model.NewDBModel("Open_vSwitch", @@ -329,7 +336,255 @@ func (ovsd *OvsDriver) GetOFPortVlanState(portName string) (string, *uint, []uin return vlanMode, tag, trunks, nil } -// IsBridgePresent Check if the bridge entry already exists +// CreateMirror Creates a new mirror to a specific bridge +func (ovsd *OvsBridgeDriver) CreateMirror(bridgeName, mirrorName string) error { + mirrorExist, err := ovsd.IsMirrorPresent(mirrorName) + if err != nil { + return err + } + + if !mirrorExist { + // Insert a Mirror and add it into Bridges + // as 2 operations in a transaction. + // The first one returns 'mirrorUUID' to referece the new inserted row + // in the second operation. + mirrorUUID, mirrorOp, err := createMirrorOperation(mirrorName) + if err != nil { + return err + } + attachMirrorOp := attachMirrorOperation(mirrorUUID, bridgeName) + + // Perform OVS transaction + operations := []ovsdb.Operation{*mirrorOp, *attachMirrorOp} + + _, err = ovsd.ovsdbTransact(operations) + return err + } + return nil +} + +// IsMirrorUsed Checks if a mirror of a specific bridge is used (it contains at least a portUUID) +func (ovsd *OvsBridgeDriver) IsMirrorUsed(bridgeName, mirrorName string) (bool, error) { + condition := ovsdb.NewCondition("name", ovsdb.ConditionEqual, mirrorName) + row, err := ovsd.findByCondition("Mirror", condition, nil) + if err != nil { + return false, err + } + + isEmpty, err := isMirrorEmpty(row) + if err != nil { + return false, fmt.Errorf("cannot check if mirror %s of bridge %s is empty error: %v", mirrorName, bridgeName, err) + } + + return !isEmpty, nil +} + +// DeleteMirror Removes a mirror of a specific bridge +func (ovsd *OvsBridgeDriver) DeleteMirror(bridgeName, mirrorName string) error { + condition := ovsdb.NewCondition("name", ovsdb.ConditionEqual, mirrorName) + row, err := ovsd.findByCondition("Mirror", condition, nil) + if err != nil { + return err + } + + externalIDs, err := getExternalIDs(row) + if err != nil { + return fmt.Errorf("get external ids: %v", err) + } + if externalIDs["owner"] != ovsPortOwner { + return fmt.Errorf("mirror not created by ovs-cni") + } + + mirrorUUID := row["_uuid"].(ovsdb.UUID) + + deleteOp := deleteMirrorOperation(mirrorName) + detachFromBridgeOp := detachMirrorFromBridgeOperation(mirrorUUID, bridgeName) + + // Perform OVS transaction + operations := []ovsdb.Operation{*deleteOp, *detachFromBridgeOp} + + _, err = ovsd.ovsdbTransact(operations) + return err +} + +// AttachPortToMirrorProducer Adds a portUUID as 'select_src_port' or 'select_dst_port' to an existing mirror +// based on ingress and egress values +func (ovsd *OvsBridgeDriver) AttachPortToMirrorProducer(portUUIDStr, mirrorName string, ingress, egress bool) error { + portUUID := ovsdb.UUID{GoUUID: portUUIDStr} + + if !ingress && !egress { + return errors.New("a mirror producer must have either a ingress or an egress or both") + } + + attachPortMirrorOp := attachPortToMirrorProducerOperation(portUUID, mirrorName, ingress, egress) + + // Perform OVS transaction + operations := []ovsdb.Operation{*attachPortMirrorOp} + + _, err := ovsd.ovsdbTransact(operations) + return err +} + +// AttachPortToMirrorConsumer Adds portUUID as 'output_port' to an existing mirror +func (ovsd *OvsBridgeDriver) AttachPortToMirrorConsumer(portUUIDStr, mirrorName string) error { + portUUID := ovsdb.UUID{GoUUID: portUUIDStr} + + attachPortMirrorOp := attachPortToMirrorConsumerOperation(portUUID, mirrorName) + + // Perform OVS transaction + operations := []ovsdb.Operation{*attachPortMirrorOp} + + _, err := ovsd.ovsdbTransact(operations) + return err +} + +// DetachPortFromMirrorProducer Removes portUUID as both 'select_src_port' and 'select_dst_port' from an existing mirror +func (ovsd *OvsBridgeDriver) DetachPortFromMirrorProducer(portUUIDStr, mirrorName string) error { + portUUID := ovsdb.UUID{GoUUID: portUUIDStr} + + mutateMirrorOp := detachPortFromMirrorOperation(portUUID, mirrorName, MirrorProducer) + + // Perform OVS transaction + operations := []ovsdb.Operation{*mutateMirrorOp} + + _, err := ovsd.ovsdbTransact(operations) + return err +} + +// DetachPortFromMirrorConsumer Removes portUUID as 'output_port' from an existing mirror +func (ovsd *OvsBridgeDriver) DetachPortFromMirrorConsumer(portUUIDStr, mirrorName string) error { + portUUID := ovsdb.UUID{GoUUID: portUUIDStr} + + mutateMirrorOp := detachPortFromMirrorOperation(portUUID, mirrorName, MirrorConsumer) + + // Perform OVS transaction + operations := []ovsdb.Operation{*mutateMirrorOp} + + _, err := ovsd.ovsdbTransact(operations) + return err +} + +// GetMirrorUUID Retrieves the UUID of a mirror from its name +func (ovsd *OvsBridgeDriver) GetMirrorUUID(mirrorName string) (ovsdb.UUID, error) { + condition := ovsdb.NewCondition("name", ovsdb.ConditionEqual, mirrorName) + row, err := ovsd.findByCondition("Mirror", condition, nil) + if err != nil { + return ovsdb.UUID{}, err + } + + // We make a select transaction using the interface name + // Then get the Mirror UUID from it + mirrorUUID := row["_uuid"].(ovsdb.UUID) + + return mirrorUUID, nil +} + +// GetPortUUID Retrieves the UUID of a port from its name +func (ovsd *OvsBridgeDriver) GetPortUUID(portName string) (ovsdb.UUID, error) { + condition := ovsdb.NewCondition("name", ovsdb.ConditionEqual, portName) + row, err := ovsd.findByCondition("Port", condition, nil) + if err != nil { + return ovsdb.UUID{}, err + } + + // We make a select transaction using the interface name + // Then get the Port UUID from it + portUUID := row["_uuid"].(ovsdb.UUID) + + return portUUID, nil +} + +// IsMirrorConsumerAlreadyAttached Checks if the 'output_port' column of a mirror consumer contains a port UUID +func (ovsd *OvsDriver) IsMirrorConsumerAlreadyAttached(mirrorName string) (bool, error) { + condition := ovsdb.NewCondition("name", ovsdb.ConditionEqual, mirrorName) + row, err := ovsd.findByCondition("Mirror", condition, nil) + if err != nil { + return false, err + } + + outputPorts, err := convertToArray(row["output_port"]) + if err != nil { + return false, fmt.Errorf("cannot convert output_port to an array error: %v", err) + } + + if len(outputPorts) == 0 { + return false, nil + } + return true, nil +} + +// CheckMirrorProducerWithPorts Checks the configuration of a mirror producer based on ingress and egress values +func (ovsd *OvsDriver) CheckMirrorProducerWithPorts(mirrorName string, ingress, egress bool, portUUIDStr string) (bool, error) { + portUUID := ovsdb.UUID{GoUUID: portUUIDStr} + + var conditions []ovsdb.Condition = []ovsdb.Condition{} + conditionName := ovsdb.NewCondition("name", ovsdb.ConditionEqual, mirrorName) + conditions = append(conditions, conditionName) + if ingress { + // select_src_port = Ports on which arriving packets are selected for mirroring + conditionIngress := ovsdb.NewCondition("select_src_port", ovsdb.ConditionIncludes, portUUID) + conditions = append(conditions, conditionIngress) + } + if egress { + // select_dst_port = Ports on which departing packets are selected for mirroring + conditionsEgress := ovsdb.NewCondition("select_dst_port", ovsdb.ConditionIncludes, portUUID) + conditions = append(conditions, conditionsEgress) + } + + // We cannot call findByCondition because we need to pass an array of conditions. + // Also, there is no need to return an error if mirror doesn't exist, because in that case we want to create a new one + return ovsd.isMirrorExistsByConditions(conditions) +} + +// CheckMirrorConsumerWithPorts Checks the configuration of a mirror consumer +func (ovsd *OvsDriver) CheckMirrorConsumerWithPorts(mirrorName string, portUUIDStr string) (bool, error) { + portUUID := ovsdb.UUID{GoUUID: portUUIDStr} + + var conditions []ovsdb.Condition = []ovsdb.Condition{} + conditionName := ovsdb.NewCondition("name", ovsdb.ConditionEqual, mirrorName) + conditions = append(conditions, conditionName) + + // output_port = Output port for selected packets + conditionOutput := ovsdb.NewCondition("output_port", ovsdb.ConditionEqual, portUUID) + conditions = append(conditions, conditionOutput) + + // We cannot call findByCondition because we need to pass an array of conditions. + // Also, there is no need to return an error if mirror doesn't exist, because in that case we want to create a new one + return ovsd.isMirrorExistsByConditions(conditions) +} + +// IsMirrorPresent Checks if the Mirror entry already exists +func (ovsd *OvsDriver) IsMirrorPresent(mirrorName string) (bool, error) { + condition := ovsdb.NewCondition("name", ovsdb.ConditionEqual, mirrorName) + selectOp := []ovsdb.Operation{{ + Op: "select", + Table: "Mirror", + Where: []ovsdb.Condition{condition}, + Columns: []string{"name"}, + }} + + transactionResult, err := ovsd.ovsdbTransact(selectOp) + if err != nil { + return false, err + } + + if len(transactionResult) != 1 { + return false, fmt.Errorf("unknow error") + } + + operationResult := transactionResult[0] + if operationResult.Error != "" { + return false, fmt.Errorf("%s - %s", operationResult.Error, operationResult.Details) + } + + if len(operationResult.Rows) != 1 { + return false, nil + } + + return true, nil +} + +// IsBridgePresent Checks if the bridge entry already exists func (ovsd *OvsDriver) IsBridgePresent(bridgeName string) (bool, error) { condition := ovsdb.NewCondition("name", ovsdb.ConditionEqual, bridgeName) selectOp := []ovsdb.Operation{{ @@ -382,6 +637,23 @@ func (ovsd *OvsDriver) GetOvsPortForContIface(contIface, contNetnsPath string) ( return fmt.Sprintf("%v", port["name"]), true, nil } +// CleanEmptyMirrors removes all empty mirrors +func (ovsd *OvsBridgeDriver) CleanEmptyMirrors() error { + mirrorNames, err := ovsd.findEmptyMirrors() + if err != nil { + return fmt.Errorf("clean mirrors: %v", err) + } + for _, mirrorName := range mirrorNames { + log.Printf("Info: mirror %s is empty: removing it", mirrorName) + if err := ovsd.DeleteMirror(ovsd.OvsBridgeName, mirrorName); err != nil { + // Don't return an error here, just log its occurrence. + // Something else may have removed the mirror already. + log.Printf("cleanEmptyMirrors Error: %v\n", err) + } + } + return nil +} + // FindInterfacesWithError returns the interfaces which are in error state func (ovsd *OvsDriver) FindInterfacesWithError() ([]string, error) { selectOp := ovsdb.Operation{ @@ -479,6 +751,37 @@ func (ovsd *OvsDriver) findByCondition(table string, condition ovsdb.Condition, return operationResult.Rows[0], nil } +// isMirrorExistsByConditions find a mirror by a list conditions. +// It returns true, only if there is a single row as result. +func (ovsd *OvsDriver) isMirrorExistsByConditions(conditions []ovsdb.Condition) (bool, error) { + selectOp := []ovsdb.Operation{{ + Op: "select", + Table: "Mirror", + Where: conditions, + Columns: []string{"name"}, + }} + + transactionResult, err := ovsd.ovsdbTransact(selectOp) + if err != nil { + return false, err + } + + if len(transactionResult) != 1 { + return false, nil + } + + operationResult := transactionResult[0] + if operationResult.Error != "" { + return false, fmt.Errorf("%s - %s", operationResult.Error, operationResult.Details) + } + + if len(operationResult.Rows) != 1 { + return false, nil + } + + return true, nil +} + func createInterfaceOperation(intfName, ovnPortName string) (ovsdb.UUID, *ovsdb.Operation, error) { intfUUIDStr := fmt.Sprintf("Intf%s", intfName) intfUUID := ovsdb.UUID{GoUUID: intfUUIDStr} @@ -601,3 +904,222 @@ func detachPortOperation(portUUID ovsdb.UUID, bridgeName string) *ovsdb.Operatio return &mutateOp } + +func createMirrorOperation(mirrorName string) (ovsdb.UUID, *ovsdb.Operation, error) { + // Create an operation 'named-uuid' with a simple string as defined in RFC7047. + // Spec states that 'uuid-name is only meaningful within the scope of a single transaction'. + // So we use a simple constant string. + mirrorUUIDStr := "newMirror" + mirrorUUID := ovsdb.UUID{GoUUID: mirrorUUIDStr} + + mirror := make(map[string]interface{}) + mirror["name"] = mirrorName + + oMap, err := ovsdb.NewOvsMap(map[string]string{ + "owner": ovsPortOwner, + }) + if err != nil { + return ovsdb.UUID{}, nil, err + } + mirror["external_ids"] = oMap + + // Add an entry in Port table + mirrorOp := ovsdb.Operation{ + Op: "insert", + Table: "Mirror", + Row: mirror, + UUIDName: mirrorUUIDStr, + } + + return mirrorUUID, &mirrorOp, nil +} + +func attachPortToMirrorProducerOperation(portUUID ovsdb.UUID, mirrorName string, ingress, egress bool) *ovsdb.Operation { + // mutate the Ingress and Egress columns of the row in the Mirror table + mutateSet, _ := ovsdb.NewOvsSet(portUUID) + var mutations []ovsdb.Mutation = []ovsdb.Mutation{} + if ingress { + // select_src_port = Ports on which arriving packets are selected for mirroring + mutationIngress := ovsdb.NewMutation("select_src_port", ovsdb.MutateOperationInsert, mutateSet) + mutations = append(mutations, *mutationIngress) + } + if egress { + // select_dst_port = Ports on which departing packets are selected for mirroring + mutationEgress := ovsdb.NewMutation("select_dst_port", ovsdb.MutateOperationInsert, mutateSet) + mutations = append(mutations, *mutationEgress) + } + + condition := ovsdb.NewCondition("name", ovsdb.ConditionEqual, mirrorName) + mutateOp := ovsdb.Operation{ + Op: "mutate", + Table: "Mirror", + Mutations: mutations, + Where: []ovsdb.Condition{condition}, + } + + return &mutateOp +} + +func attachPortToMirrorConsumerOperation(portUUID ovsdb.UUID, mirrorName string) *ovsdb.Operation { + mutateSet, _ := ovsdb.NewOvsSet(portUUID) + // output_port = Output port for selected packets + mutation := ovsdb.NewMutation("output_port", ovsdb.MutateOperationInsert, mutateSet) + + condition := ovsdb.NewCondition("name", ovsdb.ConditionEqual, mirrorName) + mutateOp := ovsdb.Operation{ + Op: "mutate", + Table: "Mirror", + Mutations: []ovsdb.Mutation{*mutation}, + Where: []ovsdb.Condition{condition}, + } + + return &mutateOp +} + +func attachMirrorOperation(mirrorUUID ovsdb.UUID, bridgeName string) *ovsdb.Operation { + // mutate the Mirrors column of the row in the Bridge table + mutateSet, _ := ovsdb.NewOvsSet(mirrorUUID) + mutation := ovsdb.NewMutation("mirrors", ovsdb.MutateOperationInsert, mutateSet) + condition := ovsdb.NewCondition("name", ovsdb.ConditionEqual, bridgeName) + mutateOp := ovsdb.Operation{ + Op: "mutate", + Table: "Bridge", + Mutations: []ovsdb.Mutation{*mutation}, + Where: []ovsdb.Condition{condition}, + } + + return &mutateOp +} + +func detachPortFromMirrorOperation(portUUID ovsdb.UUID, mirrorName string, mirrorType int) *ovsdb.Operation { + // mutate the Ports column of the row in the Bridge table + var mutations []ovsdb.Mutation = []ovsdb.Mutation{} + switch mirrorType { + case MirrorProducer: + mutateSet, _ := ovsdb.NewOvsSet(portUUID) + // select_src_port = Ports on which arriving packets are selected for mirroring + mutationIngress := ovsdb.NewMutation("select_src_port", ovsdb.MutateOperationDelete, mutateSet) + // select_dst_port = Ports on which departing packets are selected for mirroring + mutationEgress := ovsdb.NewMutation("select_dst_port", ovsdb.MutateOperationDelete, mutateSet) + mutations = append(mutations, *mutationIngress, *mutationEgress) + case MirrorConsumer: + // output_port = Output port for selected packets + mutationOutput := ovsdb.NewMutation("output_port", ovsdb.MutateOperationDelete, portUUID) + mutations = append(mutations, *mutationOutput) + default: + log.Printf("skipping detatch mirror operation because mirrorType is unknown for mirror %s", mirrorName) + } + + condition := ovsdb.NewCondition("name", ovsdb.ConditionEqual, mirrorName) + mutateOp := ovsdb.Operation{ + Op: "mutate", + Table: "Mirror", + Mutations: mutations, + Where: []ovsdb.Condition{condition}, + } + + return &mutateOp +} + +func deleteMirrorOperation(mirrorName string) *ovsdb.Operation { + condition := ovsdb.NewCondition("name", ovsdb.ConditionEqual, mirrorName) + mirrorOp := ovsdb.Operation{ + Op: "delete", + Table: "Mirror", + Where: []ovsdb.Condition{condition}, + } + + return &mirrorOp +} + +func detachMirrorFromBridgeOperation(mirrorUUID ovsdb.UUID, bridgeName string) *ovsdb.Operation { + // mutate the Ports column of the row in the Bridge table + mutateSet, _ := ovsdb.NewOvsSet(mirrorUUID) + mutation := ovsdb.NewMutation("mirrors", ovsdb.MutateOperationDelete, mutateSet) + condition := ovsdb.NewCondition("name", ovsdb.ConditionEqual, bridgeName) + mutateOp := ovsdb.Operation{ + Op: "mutate", + Table: "Bridge", + Mutations: []ovsdb.Mutation{*mutation}, + Where: []ovsdb.Condition{condition}, + } + + return &mutateOp +} + +// findEmptyMirrors returns the empty mirrors (no select_src_port, select_dst_port and output ports) +func (ovsd *OvsDriver) findEmptyMirrors() ([]string, error) { + var names []string + + // get all mirrors + selectOp := ovsdb.Operation{ + Op: "select", + Columns: []string{"name", "output_port", "select_src_port", "select_dst_port"}, + Table: "Mirror", + } + transactionResult, err := ovsd.ovsdbTransact([]ovsdb.Operation{selectOp}) + if err != nil { + return nil, err + } + if len(transactionResult) != 1 { + return nil, fmt.Errorf("no transaction result") + } + operationResult := transactionResult[0] + if operationResult.Error != "" { + return nil, fmt.Errorf(operationResult.Error) + } + + // extract mirror names with both output_port, select_src_port and select_dst_port empty + for _, row := range operationResult.Rows { + isEmpty, err := isMirrorEmpty(row) + if err != nil { + return nil, fmt.Errorf("cannot convert select_src_port to an array error: %v", err) + } + if isEmpty { + names = append(names, fmt.Sprintf("%v", row["name"])) + } + } + + if len(names) > 0 { + log.Printf("found %d empty mirrors", len(names)) + } + return names, nil +} + +// isMirrorEmpty Checks if a mirror db row has both output_port, select_src_port and select_dst_port empty +func isMirrorEmpty(dbRow map[string]interface{}) (bool, error) { + // Workaround to check output_port, select_dst_port and select_src_port consistenly, processing all + // of them as array of UUIDs. + // This is useful because ovn-org/libovsdb: + // - when dbRow["column"] is empty in ovsdb, it returns an empty ovsdb.OvsSet + // - when dbRow["column"] contains an UUID reference, it returns a ovsdb.UUID (not ovsdb.OvsSet) + // - when dbRow["column"] contains multiple UUID references, it returns an ovsdb.OvsSet with the elements + selectSrcPorts, err := convertToArray(dbRow["select_src_port"]) + if err != nil { + return false, fmt.Errorf("cannot convert select_src_port to an array error: %v", err) + } + selectDstPorts, err := convertToArray(dbRow["select_dst_port"]) + if err != nil { + return false, fmt.Errorf("cannot convert select_dst_port to an array error: %v", err) + } + outputPorts, err := convertToArray(dbRow["output_port"]) + if err != nil { + return false, fmt.Errorf("cannot convert output_port to an array error: %v", err) + } + isEmpty := len(selectSrcPorts) == 0 && len(selectDstPorts) == 0 && len(outputPorts) == 0 + return isEmpty, nil +} + +// utility function to convert an element (UUID or OvsSet) to an array of UUIDs +func convertToArray(elem interface{}) ([]interface{}, error) { + elemType := reflect.TypeOf(elem) + if elemType.Kind() == reflect.Struct { + if elemType.Name() == "UUID" { + return []interface{}{elem}, nil + } else if elemType.Name() == "OvsSet" { + return elem.(ovsdb.OvsSet).GoSet, nil + } + return nil, errors.New("struct with unknown types") + } + return nil, errors.New("unknown type") +} diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 81ae7e88a..609b809fe 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -269,6 +269,11 @@ func CmdAdd(args *skel.CmdArgs) error { return err } + // removes all ports whose interfaces have an error + if err := cleanPorts(ovsDriver); err != nil { + return err + } + contNetns, err := ns.GetNS(args.Netns) if err != nil { return fmt.Errorf("failed to open netns %q: %v", args.Netns, err) @@ -548,10 +553,6 @@ func CmdDel(args *skel.CmdArgs) error { } else { err = ns.WithNetNSPath(args.Netns, func(ns.NetNS) error { err = ip.DelLinkByName(args.IfName) - if err != nil { - // clean up as many stale ovs resources as possible. - cleanPorts(ovsDriver) - } return err }) // do the following as per cni spec (i.e. Plugins should generally complete a DEL action @@ -560,11 +561,15 @@ func CmdDel(args *skel.CmdArgs) error { if portFound { ip.DelLinkByName(portName) } - cleanPorts(ovsDriver) return nil } } + // removes all ports whose interfaces have an error + if err := cleanPorts(ovsDriver); err != nil { + return err + } + return err } diff --git a/pkg/testhelpers/mirror.go b/pkg/testhelpers/mirror.go new file mode 100644 index 000000000..831c98143 --- /dev/null +++ b/pkg/testhelpers/mirror.go @@ -0,0 +1,308 @@ +package testhelpers + +import ( + "encoding/json" + "fmt" + "os/exec" + "strings" + + cnitypes "github.com/containernetworking/cni/pkg/types" + types040 "github.com/containernetworking/cni/pkg/types/040" + current "github.com/containernetworking/cni/pkg/types/100" + + ginko "github.com/onsi/ginkgo" + "github.com/onsi/gomega" + + "github.com/k8snetworkplumbingwg/ovs-cni/pkg/types" +) + +// MirrorNet040 struct that represent the network configuration for CNI spec 0.4.0 +type MirrorNet040 struct { + CNIVersion string `json:"cniVersion"` + Name string `json:"name"` + Type string `json:"type"` + Bridge string `json:"bridge"` + Mirrors []*types.Mirror `json:"mirrors"` + RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` + PrevResult types040.Result `json:"-"` +} + +// MirrorNetCurrent struct that represent the network configuration for CNI spec 1.0.0 +type MirrorNetCurrent struct { + CNIVersion string `json:"cniVersion"` + Name string `json:"name"` + Type string `json:"type"` + Bridge string `json:"bridge"` + Mirrors []*types.Mirror `json:"mirrors"` + RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` + PrevResult current.Result `json:"-"` +} + +// SelectPort type that represent the kind of select_*_port +type SelectPort string + +const ( + // SelectSrcPort const with value "select_src_port" + // (ports on which arriving packets are selected for mirroring) + SelectSrcPort SelectPort = "select_src_port" + // SelectDstPort const with value "select_dst_port" + // (ports on which departing packets are selected for mirroring) + SelectDstPort SelectPort = "select_dst_port" +) + +// GetPortUUIDFromResult gets portUUID from a cnitypes.Result object +func GetPortUUIDFromResult(r cnitypes.Result) string { + resultMirror, err := current.GetResult(r) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(len(resultMirror.Interfaces)).To(gomega.Equal(2)) + + // Both mirror-producer and mirror-consumer must return the same interfaces of the previous one in the chain (ovs-cni plugin), + // because they don't modify interfaces, but they only update ovsdb. + ginko.By("Checking that result interfaces are equal to those returned by ovs-cni plugin") + hostIface := resultMirror.Interfaces[0] + contIface := resultMirror.Interfaces[1] + gomega.Expect(resultMirror.Interfaces[0]).To(gomega.Equal(hostIface)) + gomega.Expect(resultMirror.Interfaces[1]).To(gomega.Equal(contIface)) + + ginko.By("Getting port uuid for the hostIface") + portUUID, err := GetPortUUIDByName(hostIface.Name) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + return portUUID +} + +// CheckPortsInMirrors extracts ports from results and check if every mirror contains those port UUIDs. +// Since it's not possibile to have mirrors without both ingress and egress, +// it's enough finding the port in either ingress or egress. +// It also verify the mirror owner using 'external_ids' attribute. You can skip this check with hasExternalOwner=true. +func CheckPortsInMirrors(mirrors []types.Mirror, hasExternalOwner bool, ovsPortOwner string, results ...cnitypes.Result) bool { + // build an empty slice of port UUIDs + var portUUIDs = make([]string, 0) + for _, r := range results { + portUUID := GetPortUUIDFromResult(r) + portUUIDs = append(portUUIDs, portUUID) + } + + for _, mirror := range mirrors { + ginko.By(fmt.Sprintf("Checking that mirror %s is in ovsdb", mirror.Name)) + mirrorNameOvsdb, err := GetMirrorAttribute(mirror.Name, "name") + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(mirrorNameOvsdb).To(gomega.Equal(mirror.Name)) + + if !hasExternalOwner { + mirrorExternalIdsOvsdb, err := GetMirrorAttribute(mirror.Name, "external_ids") + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(mirrorExternalIdsOvsdb).To(gomega.ContainSubstring("owner=" + ovsPortOwner)) + } + + if mirror.Ingress { + ginko.By(fmt.Sprintf("Checking that mirror %s has all ports created by ovs-cni plugin in select_src_port column", mirror.Name)) + srcPorts, err := GetMirrorSrcPorts(mirror.Name) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + for _, portUUID := range portUUIDs { + gomega.Expect(srcPorts).To(gomega.ContainElement(portUUID)) + } + } + + if mirror.Egress { + ginko.By(fmt.Sprintf("Checking that mirror %s has all ports created by ovs-cni plugin in select_dst_port column", mirror.Name)) + dstPorts, err := GetMirrorDstPorts(mirror.Name) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + for _, portUUID := range portUUIDs { + gomega.Expect(dstPorts).To(gomega.ContainElement(portUUID)) + } + } + } + return true +} + +// IsMirrorExists checks if a mirror exists by its name +func IsMirrorExists(name string) (bool, error) { + output, err := exec.Command("ovs-vsctl", "find", "Mirror", "name="+name).CombinedOutput() + if err != nil { + return false, fmt.Errorf("failed to check if mirror exists: %v", string(output[:])) + } + return len(output) > 0, nil +} + +// GetPortUUIDByName gets a portUUID by its name +func GetPortUUIDByName(name string) (string, error) { + output, err := exec.Command("ovs-vsctl", "get", "Port", name, "_uuid").CombinedOutput() + if err != nil { + return "", fmt.Errorf("failed to get port uuid by name: %v", string(output[:])) + } + + return strings.TrimSpace(string(output[:])), nil +} + +// GetMirrorAttribute gets a mirror attribute +func GetMirrorAttribute(mirrorName, attributeName string) (string, error) { + output, err := exec.Command("ovs-vsctl", "get", "Mirror", mirrorName, attributeName).CombinedOutput() + if err != nil { + return "", fmt.Errorf("failed to get mirror attribute: %v", string(output[:])) + } + + return strings.TrimSpace(string(output[:])), nil +} + +// GetMirrorPorts gets either 'select_src_port' or 'select_dst_port of a mirror +func GetMirrorPorts(mirrorName string, attributeName SelectPort) ([]string, error) { + output, err := GetMirrorAttribute(mirrorName, string(attributeName)) + if err != nil { + return make([]string, 0), fmt.Errorf("failed to get mirror %s ports: %v", mirrorName, string(output[:])) + } + + // convert into a string, then remove "[" and "]" characters + stringOutput := output[1 : len(output)-1] + + if stringOutput == "" { + // if "stringOutput" is an empty string, + // simply returns a new empty []string (in this way len == 0) + return make([]string, 0), nil + } + + // split the string by ", " to get individual uuids in a []string + outputLines := strings.Split(stringOutput, ", ") + return outputLines, nil +} + +// GetMirrorSrcPorts gets 'select_src_port' of a mirror as a string slice +func GetMirrorSrcPorts(mirrorName string) ([]string, error) { + return GetMirrorPorts(mirrorName, "select_src_port") +} + +// GetMirrorDstPorts gets 'select_dst_port' of a mirror as a string slice +func GetMirrorDstPorts(mirrorName string) ([]string, error) { + return GetMirrorPorts(mirrorName, "select_dst_port") +} + +// GetMirrorOutputPorts gets 'output_port' of a mirror as a string slice +func GetMirrorOutputPorts(mirrorName string) ([]string, error) { + output, err := exec.Command("ovs-vsctl", "get", "Mirror", mirrorName, "output_port").CombinedOutput() + if err != nil { + return make([]string, 0), fmt.Errorf("failed to get mirror %s output_port: %v", mirrorName, string(output[:])) + } + + // convert into a string removing the "\n" character at the end + stringOutput := string(output[0 : len(output)-1]) + + // outport_port field behaviour is quite inconsistent, because: + // - if in empty, it returns an empty slice "[]" with a "\n" character at the end, + // - otherwise, it returns a string with a "\n" character at the end + if stringOutput == "[]" { + // if "stringOutput" is an empty string, + // simply returns a new empty []string (in this way len == 0) + return make([]string, 0), nil + } + return []string{stringOutput}, nil +} + +// AddSelectPortToMirror adds portUUID to a specific mirror via 'ovs-vsctl' as 'select_*' based on ingress and egress values. +// ingress == true => adds portUUID as 'select_src_port' +// egress == true => adds portUUID as 'select_dst_port' +func AddSelectPortToMirror(portUUID, mirrorName string, ingress, egress bool) (bool, error) { + if ingress { + output, err := exec.Command("ovs-vsctl", "add", "Mirror", mirrorName, "select_src_port", portUUID).CombinedOutput() + if err != nil { + return false, fmt.Errorf("failed to set 'select_src_port' for mirror %s with UUID %s: %v", mirrorName, portUUID, string(output[:])) + } + } + + if egress { + output, err := exec.Command("ovs-vsctl", "add", "Mirror", mirrorName, "select_dst_port", portUUID).CombinedOutput() + if err != nil { + return false, fmt.Errorf("failed to set 'select_dst_port' for mirror %s with UUID %s: %v", mirrorName, portUUID, string(output[:])) + } + } + + return true, nil +} + +// AddOutputPortToMirror adds portUUID as 'output_port' to a specific mirror via 'ovs-vsctl' +func AddOutputPortToMirror(portUUID, mirrorName string) (string, error) { + output, err := exec.Command("ovs-vsctl", "set", "Mirror", mirrorName, "output_port="+portUUID).CombinedOutput() + if err != nil { + return "", fmt.Errorf("failed to set output_port for mirror %s with UUID %s: %v", mirrorName, portUUID, string(output[:])) + } + + return strings.TrimSpace(string(output[:])), nil +} + +// CreateEmptyMirrors creates multiple mirrors in a bridge with an optional owner in external_ids. +// If ovsPortOwner is an empty string, it will leave external_ids empty. +func CreateEmptyMirrors(bridgeName string, mirrorNames []string, ovsPortOwner string) { + for _, mirrorName := range mirrorNames { + _, err := createEmptyMirror(bridgeName, mirrorName) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + if ovsPortOwner != "" { + // manually add owner as external_ids + _, err = addOwnerToMirror(ovsPortOwner, mirrorName) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + } + // check existence of the empty mirrors + emptyMirExists, err := IsMirrorExists(mirrorName) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(emptyMirExists).To(gomega.Equal(true)) + } +} + +// CheckEmptyMirrorsExistence checks if all mirrors exist or not, based on 'exists' value. +func CheckEmptyMirrorsExistence(mirrorNames []string, exists bool) { + for _, mirrorName := range mirrorNames { + emptyMirExists, err := IsMirrorExists(mirrorName) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(emptyMirExists).To(gomega.Equal(exists)) + } +} + +// ToJSONString coverts input into a JSON string +func ToJSONString(input interface{}) (string, error) { + b, err := json.Marshal(input) + if err != nil { + return "", err + } + return string(b), nil +} + +// OnlyContainsOrEmpty checks if a list of strings contains only 'el' element or is empty +func OnlyContainsOrEmpty(list []string, el string) bool { + if len(list) > 1 { + // because it has more elements, so 'el' cannot be the only one + return false + } + if len(list) == 0 { + // in empty + return true + } + // otherwise check if the only element in 'list' is equals to 'el' + return ContainsElement(list, el) +} + +// ContainsElement returns true if a list of strings contains a string element +func ContainsElement(list []string, el string) bool { + for _, l := range list { + if l == el { + return true + } + } + return false +} + +// createEmptyMirror creates a new empty mirror with name = 'mirrorName' in bridge 'bridgeName' +func createEmptyMirror(bridgeName, mirrorName string) (string, error) { + output, err := exec.Command("ovs-vsctl", "--", "add", "Bridge", bridgeName, "mirrors", "@m", "--", "--id=@m", "create", "Mirror", "name="+mirrorName).CombinedOutput() + if err != nil { + return "", fmt.Errorf("failed to create empty mirror %s in bridge %s: %v", bridgeName, mirrorName, string(output[:])) + } + + return strings.TrimSpace(string(output[:])), nil +} + +// addOwnerToMirror adds an owner to an existing mirror as external_ids +func addOwnerToMirror(ovsPortOwner, mirrorName string) (string, error) { + output, err := exec.Command("ovs-vsctl", "add", "Mirror", mirrorName, "external_ids", "owner="+ovsPortOwner).CombinedOutput() + if err != nil { + return "", fmt.Errorf("failed to add owner %s to mirror %s: %v", ovsPortOwner, mirrorName, string(output[:])) + } + + return strings.TrimSpace(string(output[:])), nil +} diff --git a/pkg/types/types.go b/pkg/types/types.go index b7d3ec678..29b71aef1 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -16,7 +16,15 @@ package types -import "github.com/containernetworking/cni/pkg/types" +import ( + "github.com/containernetworking/cni/pkg/types" + current "github.com/containernetworking/cni/pkg/types/100" +) + +// NetConfs can be either NetConf or MirrorNetConf +type NetConfs interface { + NetConf | MirrorNetConf +} // NetConf extends types.NetConf for ovs-cni type NetConf struct { @@ -32,6 +40,28 @@ type NetConf struct { LinkStateCheckInterval int `json:"link_state_check_interval"` } +// MirrorNetConf extends types.NetConf for ovs-mirrors +type MirrorNetConf struct { + types.NetConf + + // support chaining for master interface and IP decisions + // occurring prior to running mirror plugin + RawPrevResult *map[string]interface{} `json:"prevResult"` + PrevResult *current.Result `json:"-"` + + BrName string `json:"bridge,omitempty"` + ConfigurationPath string `json:"configuration_path"` + SocketFile string `json:"socket_file"` + Mirrors []*Mirror `json:"mirrors"` +} + +// Mirror configuration +type Mirror struct { + Name string `json:"name"` + Ingress bool `json:"ingress,omitempty"` + Egress bool `json:"egress,omitempty"` +} + // Trunk containing selective vlan IDs type Trunk struct { MinID *uint `json:"minID,omitempty"` @@ -47,3 +77,12 @@ type CachedNetConf struct { Netconf *NetConf OrigIfName string } + +// CachedPrevResultNetConf containing PrevResult. +// this is intended to be used only for storing and retrieving config +// to/from a data store (example file cache). +// This is required with CNI spec < 0.4.0 (like 0.3.0 and 0.3.1), +// because prevResult wasn't available in cmdDel on those versions. +type CachedPrevResultNetConf struct { + PrevResult *current.Result +} diff --git a/tests/cluster/cluster.go b/tests/cluster/cluster.go index 8c69a5dd6..576060537 100644 --- a/tests/cluster/cluster.go +++ b/tests/cluster/cluster.go @@ -88,7 +88,7 @@ func (api *ClusterAPI) RemoveTestNamespace() { } // CreatePrivilegedPodWithIP creates a pod attached with ovs via secondary network -func (api *ClusterAPI) CreatePrivilegedPodWithIP(podName, nadName, bridgeName, cidr string) { +func (api *ClusterAPI) CreatePrivilegedPodWithIP(podName, nadName, bridgeName, cidr, additionalCommands string) { By(fmt.Sprintf("Creating pod %s with priviliged premission and ip %s", podName, cidr)) privileged := true resourceList := make(corev1.ResourceList) @@ -102,7 +102,7 @@ func (api *ClusterAPI) CreatePrivilegedPodWithIP(podName, nadName, bridgeName, c }, Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "test", Image: "quay.io/jitesoft/alpine", - Command: []string{"sh", "-c", fmt.Sprintf("ip address add %s dev net1; sleep 99999", cidr)}, + Command: []string{"sh", "-c", fmt.Sprintf("ip address add %s dev net1; "+additionalCommands+" sleep 99999", cidr)}, Resources: corev1.ResourceRequirements{Limits: resourceList}, SecurityContext: &corev1.SecurityContext{Privileged: &privileged}}}}} @@ -187,6 +187,16 @@ func (api *ClusterAPI) PingFromPod(podName, containerName, targetIP string) erro return nil } +// ReadFileFromPod run the cat command on the pod container to read the content of a file +func (api *ClusterAPI) ReadFileFromPod(podName, containerName, filePath string) (string, error) { + out, _, err := api.execOnPod(podName, containerName, testNamespace, "cat "+filePath) + if err != nil { + return "", errors.Wrapf(err, "Failed to run exec on pod %s", podName) + } + + return out, nil +} + func (api *ClusterAPI) execOnPod(podName, containerName, namespace, command string) (string, string, error) { req := api.Clientset.CoreV1().RESTClient(). Post(). diff --git a/tests/mirror_test.go b/tests/mirror_test.go new file mode 100644 index 000000000..b6a20df32 --- /dev/null +++ b/tests/mirror_test.go @@ -0,0 +1,108 @@ +// Copyright 2018 Red Hat, Inc. +// Copyright 2014 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tests_test + +import ( + "fmt" + "net" + "time" + + "github.com/k8snetworkplumbingwg/ovs-cni/tests/node" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("ovs-mirror 0.3.0", func() { testMirrorFunc("0.3.0") }) +var _ = Describe("ovs-mirror 0.3.1", func() { testMirrorFunc("0.3.1") }) +var _ = Describe("ovs-mirror 0.4.0", func() { testMirrorFunc("0.4.0") }) + +var testMirrorFunc = func(version string) { + Describe("ovs traffic mirroring tests", func() { + Context("when an OVS bridge is configured on a node", func() { + const bridgeName = "br-test" + BeforeEach(func() { + node.AddOvsBridgeOnNode(bridgeName) + }) + AfterEach(func() { + node.RemoveOvsBridgeOnNode(bridgeName) + }) + + Context("and both consumer and producer network attachment definitions are defined with a mirror configuration", func() { + const nadProducerName = "ovs-net-prod" + const nadConsumerName = "ovs-net-cons" + + BeforeEach(func() { + ovsPluginProd := `{ "type": "ovs", "bridge": "` + bridgeName + `", "vlan": 100 }` + mirrorConfProd := `{ "name": "mirror-1", "ingress": true, "egress": true }` + mirrorProducer := `{ "type": "ovs-mirror-producer", "bridge": "` + bridgeName + `", "mirrors": [` + mirrorConfProd + `] }` + plugins := `[` + ovsPluginProd + `, ` + mirrorProducer + `]` + clusterApi.CreateNetworkAttachmentDefinition(nadProducerName, bridgeName, `{ "cniVersion": "`+version+`", "plugins": `+plugins+`}`) + + ovsPluginCons := `{ "type": "ovs", "bridge": "` + bridgeName + `", "vlan": 0 }` + mirrorConfCons := `{ "name": "mirror-1" }` + mirrorConsumer := `{ "type": "ovs-mirror-consumer", "bridge": "` + bridgeName + `", "mirrors": [` + mirrorConfCons + `] }` + pluginsConsumer := `[` + ovsPluginCons + `, ` + mirrorConsumer + `]` + clusterApi.CreateNetworkAttachmentDefinition(nadConsumerName, bridgeName, `{ "cniVersion": "`+version+`", "plugins": `+pluginsConsumer+`}`) + }) + + AfterEach(func() { + clusterApi.RemoveNetworkAttachmentDefinition(nadProducerName) + clusterApi.RemoveNetworkAttachmentDefinition(nadConsumerName) + }) + + Context("and 3 pods (2 producers and 1 consumer) are connected through it", func() { + const ( + podProd1Name = "pod-prod-1" + podProd2Name = "pod-prod-2" + podConsName = "pod-cons" + cidrPodProd1 = "10.0.0.1/24" + cidrPodProd2 = "10.0.0.2/24" + cidrCons = "10.1.0.1/24" + ) + BeforeEach(func() { + consAdditionalCommands := "apk add tcpdump; tcpdump -i net1 > /tcpdump.log;" + clusterApi.CreatePrivilegedPodWithIP(podConsName, nadConsumerName, bridgeName, cidrCons, consAdditionalCommands) + + clusterApi.CreatePrivilegedPodWithIP(podProd1Name, nadProducerName, bridgeName, cidrPodProd1, "") + clusterApi.CreatePrivilegedPodWithIP(podProd2Name, nadProducerName, bridgeName, cidrPodProd2, "") + }) + AfterEach(func() { + clusterApi.DeletePodsInTestNamespace() + }) + + Specify("consumer pod should be able to monitor network traffic between producer pods", func() { + By("Running and parsing tcpdump result") + ipPodProd1, _, err := net.ParseCIDR(cidrPodProd1) + Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("should succeed parsing podProd1's cidr: %s", cidrPodProd1)) + ipPodProd2, _, err := net.ParseCIDR(cidrPodProd2) + Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("should succeed parsing podProd2's cidr: %s", cidrPodProd2)) + + err = clusterApi.PingFromPod(podProd1Name, "test", ipPodProd2.String()) + Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("should be able to ping from pod '%s@%s' to pod '%s@%s'", podProd1Name, ipPodProd1.String(), podProd2Name, ipPodProd2.String())) + + // wait a few seconds for the dump being written + time.Sleep(10 * time.Second) + + tcpDumpResult, err := clusterApi.ReadFileFromPod(podConsName, "test", "/tcpdump.log") + Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("should be able to read 'tcdump' log file from pod '%s'", podConsName)) + Expect(tcpDumpResult).To(ContainSubstring("IP " + ipPodProd1.String() + " > " + ipPodProd2.String() + ": ICMP echo request")) + Expect(tcpDumpResult).To(ContainSubstring("IP " + ipPodProd2.String() + " > " + ipPodProd1.String() + ": ICMP echo reply")) + }) + }) + }) + }) + }) +} diff --git a/tests/ovs_test.go b/tests/ovs_test.go index a0ab53c92..f1889fa09 100644 --- a/tests/ovs_test.go +++ b/tests/ovs_test.go @@ -69,8 +69,8 @@ var testFunc = func(version string) { cidrPod2 = "10.0.0.2/24" ) BeforeEach(func() { - clusterApi.CreatePrivilegedPodWithIP(pod1Name, nadName, bridgeName, cidrPod1) - clusterApi.CreatePrivilegedPodWithIP(pod2Name, nadName, bridgeName, cidrPod2) + clusterApi.CreatePrivilegedPodWithIP(pod1Name, nadName, bridgeName, cidrPod1, "") + clusterApi.CreatePrivilegedPodWithIP(pod2Name, nadName, bridgeName, cidrPod2, "") }) AfterEach(func() { clusterApi.DeletePodsInTestNamespace()