From c613ede9fa79eeabeb23002a6eb9ba9a9b2ac15f Mon Sep 17 00:00:00 2001 From: Ryan Tidwell Date: Fri, 5 Jul 2019 11:35:21 -0500 Subject: [PATCH] Floating IP for routed networks: network:routed API This change is needed for enabling floating IP's on routed networks. To be able to create a subnet that spans all segments of a routed network, a special subnet service type of 'network:routed' is used to denote a network that can span all segments of a routed network. To create floating IP's on a routed network, the subnet must be created with a service_type of 'network:routed'. After the subnet has been created, floating IP's can be allocated and associated as before. See the design spec https://review.opendev.org/#/c/486450/ for reference. One caveat for this approach is that it requires the underlying infrastructure to be aware of and able to route /32 host routes for the floating IP. This implies that in practice, use of the 'network:routed' service type should be done in conjunction with one or both of the following: 1. Third-party SDN backend that handles this service type in its own way 2. neutron-dynamic-routing and the BGP service plugin for announcing the appropriate next-hops for floating IP's. This is compatible with DVR and non-DVR environments. Depends-On: Ibde33bdacba6bd1e9c41cc69d0054bf55e1e6454 Change-Id: I9ae9d193b885364d5a4d90538880d8e9fbc8df74 Co-Author: Thomas Goirand Partial-Bug: #1667329 --- ...-floating-ip-over-l2-segmented-network.rst | 464 ++++++++++++++++++ doc/source/admin/config.rst | 1 + ...-floating-ip-over-l2-segmented-network.png | Bin 0 -> 75200 bytes ...-floating-ip-over-l2-segmented-network.svg | 2 + neutron/db/ipam_backend_mixin.py | 26 +- neutron/objects/subnet.py | 27 + .../tests/unit/db/test_ipam_backend_mixin.py | 4 +- neutron/tests/unit/extensions/test_segment.py | 22 +- ...twork-routed-subnets-cf4874d97ddacd77.yaml | 11 + 9 files changed, 551 insertions(+), 6 deletions(-) create mode 100644 doc/source/admin/config-bgp-floating-ip-over-l2-segmented-network.rst create mode 100644 doc/source/admin/figures/bgp-floating-ip-over-l2-segmented-network.png create mode 100644 doc/source/admin/figures/bgp-floating-ip-over-l2-segmented-network.svg create mode 100644 releasenotes/notes/network-routed-subnets-cf4874d97ddacd77.yaml diff --git a/doc/source/admin/config-bgp-floating-ip-over-l2-segmented-network.rst b/doc/source/admin/config-bgp-floating-ip-over-l2-segmented-network.rst new file mode 100644 index 00000000000..12fe3d534a1 --- /dev/null +++ b/doc/source/admin/config-bgp-floating-ip-over-l2-segmented-network.rst @@ -0,0 +1,464 @@ +.. _config-bgp-floating-ip-over-l2-segmented-network: + +========================================== +BGP floating IPs over l2 segmented network +========================================== + +The general principle is that L2 connectivity will be bound to a single rack. +Everything outside the switches of the rack will be routed using BGP. To +perform the BGP announcement, neutron-dynamic-routing is leveraged. + +To achieve this, on each rack, servers are setup with a different management +network using a vlan ID per rack (light green and orange network below). +Note that a unique vlan ID per rack isn't mandatory, it's also possible to +use the same vlan ID on all racks. The point here is only to isolate L2 +segments (typically, routing between the switch of each racks will be done +over BGP, without L2 connectivity). + + +.. image:: figures/bgp-floating-ip-over-l2-segmented-network.png + + +On the OpenStack side, a provider network must be setup, which is using a +different subnet range and vlan ID for each rack. This includes: + +* an address scope + +* some network segments for that network, which are attached to a named + physical network + +* a subnet pool using that address scope +* one provider network subnet per segment (each subnet+segment pair matches + one rack physical network name) + +A segment is attached to a specific vlan and physical network name. In the +above figure, the provider network is represented by 2 subnets: the dark green +and the red ones. The dark green subnet is on one network segment, and the red +one on another. Both subnet are of the subnet service type +"network:floatingip_agent_gateway", so that they cannot be used by virtual +machines directly. + +On top of all of this, a floating IP subnet without a segment is added, which +spans in all of the racks. This subnet must have the below service types: + +* network:routed + +* network:floatingip + +* network:router_gateway + +Since the network:routed subnet isn't bound to a segment, it can be used on all +racks. As the service types network:floatingip and network:router_gateway are +used for the provider network, the subnet can only be used for floating IPs and +router gateways, meaning that the subnet using segments will be used as +floating IP gateways (ie: the next HOP to reach these floating IP / router +external gateways). + + +Configuring the Neutron API side +-------------------------------- + +On the controller side (ie: API and RPC server), only the Neutron Dynamic +Routing Python library must be installed (for example, in the Debian case, +that would be the neutron-dynamic-routing-common and +python3-neutron-dynamic-routing packages). On top of that, "segments" and +"bgp" must be added to the list of plugins in service_plugins. For example +in neutron.conf: + + .. code-block:: ini + + [DEFAULT] + service_plugins=router,metering,qos,trunk,segments,bgp + + +The BGP agent +------------- + +The neutron-bgp-agent must be installed. Best is to install it twice per rack, +on any machine (it doesn't mater much where). Then each of these BGP agents +will establish a session with one switch, and advertise all of the BGP +configuration. + + +Setting-up BGP peering with the switches +---------------------------------------- + +A peer that represents the network equipment must be created. Then a matching +BGP speaker needs to be created. Then, the BGP speaker must be +associated to a dynamic-routing-agent (in our example, the dynamic-routing +agents run on compute 1 and 4). Finally, the peer is added to the BGP speaker, +so the speaker initiates a BGP session to the network equipment. + + .. code-block:: console + + $ # Create a BGP peer to represent the switch 1, + $ # which runs FRR on 10.1.0.253 with AS 64601 + $ openstack bgp peer create \ + --peer-ip 10.1.0.253 \ + --remote-as 64601 \ + rack1-switch-1 + + $ # Create a BGP speaker on compute-1 + $ BGP_SPEAKER_ID_COMPUTE_1=$(openstack bgp speaker create \ + --local-as 64999 --ip-version 4 mycloud-compute-1.example.com \ + --format value -c id) + + $ # Get the agent ID of the dragent running on compute 1 + $ BGP_AGENT_ID_COMPUTE_1=$(openstack network agent list \ + --host mycloud-compute-1.example.com --agent-type bgp \ + --format value -c ID) + + $ # Add the BGP speaker to the dragent of compute 1 + $ openstack bgp dragent add speaker \ + ${BGP_AGENT_ID_COMPUTE_1} ${BGP_SPEAKER_ID_COMPUTE_1} + + $ # Add the BGP peer to the speaker of compute 1 + $ openstack bgp speaker add peer \ + compute-1.example.com rack1-switch-1 + + $ # Tell the speaker not to advertize tenant networks + $ openstack bgp speaker set \ + --no-advertise-tenant-networks mycloud-compute-1.example.com + + +It is possible to repeat this operation for a 2nd machine on the same rack, +if the deployment is using bonding (and then, LACP between both switches), +as per the figure above. It also can be done on each rack. One way to +deploy is to select two computers in each rack (for example, one compute +node and one network node), and install the neutron-dynamic-routing-agent +on each of them, so they can "talk" to both switches of the rack. All of +this depends on what the configuration is on the switch side. It may be +that you only need to talk to two ToR racks in the whole deployment. The +thing you must know is that you can deploy as many dynamic-routing agent +as needed, and that one agent can talk to a single device. + + +Setting-up physical network names +--------------------------------- + +Before setting-up the provider network, the physical network name must be set +in each host, according to the rack names. On the compute or network nodes, +this is done in /etc/neutron/plugins/ml2/openvswitch_agent.ini using the +bridge_mappings directive: + + .. code-block:: ini + + [ovs] + bridge_mappings = physnet-rack1:br-ex + + +All of the physical networks created this way must be added in the +configuration of the neutron-server as well (ie: this is used by both +neutron-api and neutron-rpc-server). For example, with 3 racks, +here's how /etc/neutron/plugins/ml2/ml2_conf.ini should look like: + + .. code-block:: ini + + [ml2_type_flat] + flat_networks = physnet-rack1,physnet-rack2,physnet-rack3 + + [ml2_type_vlan] + network_vlan_ranges = physnet-rack1,physnet-rack2,physnet-rack3 + + +Once this is done, the provider network can be created, using physnet-rack1 +as "physical network". + + +Setting-up the provider network +------------------------------- + +Everything that is in the provider network's scope will be advertised through +BGP. Here is how to create the network scope: + + .. code-block:: console + + $ # Create the address scope + $ openstack address scope create --share --ip-version 4 provider-addr-scope + + +Then, the network can be ceated using the physical network name set above: + + .. code-block:: console + + $ # Create the provider network that spawns over all racks + $ openstack network create --external --share \ + --provider-physical-network physnet-rack1 \ + --provider-network-type vlan \ + --provider-segment 11 \ + provider-network + + +This automatically creates a network AND a segment. Though by default, this +segment has no name, which isn't convenient. This name can be changed though: + + .. code-block:: console + + $ # Get the network ID: + $ PROVIDER_NETWORK_ID=$(openstack network show provider-network \ + --format value -c id) + + $ # Get the segment ID: + $ FIRST_SEGMENT_ID=$(openstack network segment list \ + --format csv -c ID -c Network | \ + q -H -d, "SELECT ID FROM - WHERE Network='${PROVIDER_NETWORK_ID}'") + + $ # Set the 1st segment name, matching the rack name + $ openstack network segment set --name segment-rack1 ${FIRST_SEGMENT_ID} + + +Setting-up the 2nd segment +-------------------------- + +The 2nd segment, which will be attached to our provider network, is created +this way: + + .. code-block:: console + + $ # Create the 2nd segment, matching the 2nd rack name + $ openstack network segment create \ + --physical-network physnet-rack2 \ + --network-type vlan \ + --segment 13 \ + --network provider-network \ + segment-rack2 + + +Setting-up the provider subnets for the BGP next HOP routing +------------------------------------------------------------ + +These subnets will be in use in different racks, depending on what physical +network is in use in the machines. In order to use the address scope, subnet +pools must be used. Here is how to create the subnet pool with the two ranges +to use later when creating the subnets: + + .. code-block:: console + + $ # Create the provider subnet pool which includes all ranges for all racks + $ openstack subnet pool create \ + --pool-prefix 10.1.0.0/24 \ + --pool-prefix 10.2.0.0/24 \ + --address-scope provider-addr-scope \ + --share \ + provider-subnet-pool + + +Then, this is how to create the two subnets. In this example, we are keeping +the addresses in .1 for the gateway, .2 for the DHCP server, and .253 +.254, +as these addresses will be used by the switches for the BGP announcements: + + .. code-block:: console + + $ # Create the subnet for the physnet-rack-1, using the segment-rack-1, and + $ # the subnet_service_type network:floatingip_agent_gateway + $ openstack subnet create \ + --service-type 'network:floatingip_agent_gateway' \ + --subnet-pool provider-subnet-pool \ + --subnet-range 10.1.0.0/24 \ + --allocation-pool start=10.1.0.3,end=10.1.0.252 \ + --gateway 10.1.0.1 \ + --network provider-network \ + --network-segment segment-rack1 \ + provider-subnet-rack1 + + $ # The same, for the 2nd rack + $ openstack subnet create \ + --service-type 'network:floatingip_agent_gateway' \ + --subnet-pool provider-subnet-pool \ + --subnet-range 10.2.0.0/24 \ + --allocation-pool start=10.2.0.3,end=10.2.0.252 \ + --gateway 10.2.0.1 \ + --network provider-network \ + --network-segment segment-rack2 \ + provider-subnet-rack2 + + +Note the service types. network:floatingip_agent_gateway makes sure that these +subnets will be in use only as gateways (ie: the next BGP hop). The above can +be repeated for each new rack. + + +Adding a subnet for VM floating IPs and router gateways +------------------------------------------------------- + +This is to be repeated each time a new subnet must be created for floating IPs +and router gateways. First, the range is added in the subnet pool, then the +subnet itself is created: + + .. code-block:: console + + $ # Add a new prefix in the subnet pool for the floating IPs: + $ openstack subnet pool set \ + --pool-prefix 203.0.113.0/24 \ + provider-subnet-pool + + $ # Create the floating IP subnet + $ openstack subnet create vm-fip \ + --service-type 'network:routed' \ + --service-type 'network:floatingip' \ + --service-type 'network:router_gateway' \ + --subnet-pool provider-subnet-pool \ + --subnet-range 203.0.113.0/24 \ + --network provider-network + +The service-type network:routed ensures we're using BGP through the provider +network to advertize the IPs. network:floatingip and network:router_gateway +limits the use of these IPs to floating IPs and router gateways. + +Setting-up BGP advertizing +-------------------------- + +The provider network needs to be added to each of the BGP speakers. This means +each time a new rack is setup, the provider network must be added to the 2 BGP +speakers of that rack. + + .. code-block:: console + + $ # Add the provider network to the BGP speakers. + $ openstack bgp speaker add network \ + mycloud-compute-1.example.com provider-network + $ openstack bgp speaker add network \ + mycloud-compute-4.example.com provider-network + + +In this example, we've selected two compute nodes that are also running an +instance of the neutron-dynamic-routing-agent daemon. + + +Per project operation +--------------------- + +This can be done by each customer. A subnet pool isn't mandatory, but it is +nice to have. Typically, the customer network will not be advertized through +BGP (but this can be done if needed). + + .. code-block:: console + + $ # Create the tenant private network + $ openstack network create tenant-network + + $ # Self-service network pool: + $ openstack subnet pool create \ + --pool-prefix 192.168.130.0/23 \ + --share \ + tenant-subnet-pool + + $ # Self-service subnet: + $ openstack subnet create \ + --network tenant-network \ + --subnet-pool tenant-subnet-pool \ + --prefix-length 24 \ + tenant-subnet-1 + + $ # Create the router + $ openstack router create tenant-router + + $ # Add the tenant subnet to the tenant router + $ openstack router add subnet \ + tenant-router tenant-subnet-1 + + $ # Set the router's default gateway. This will use one public IP. + $ openstack router set \ + --external-gateway provider-network tenant-router + + $ # Create a first VM on the tenant subnet + $ openstack server create --image debian-10.5.0-openstack-amd64.qcow2 \ + --flavor cpu2-ram6-disk20 \ + --nic net-id=tenant-network \ + --key-name yubikey-zigo \ + test-server-1 + + $ # Eventually, add a floating IP + $ openstack floating ip create provider-network + +---------------------+--------------------------------------+ + | Field | Value | + +---------------------+--------------------------------------+ + | created_at | 2020-12-15T11:48:36Z | + | description | | + | dns_domain | None | + | dns_name | None | + | fixed_ip_address | None | + | floating_ip_address | 203.0.113.17 | + | floating_network_id | 859f5302-7b22-4c50-92f8-1f71d6f3f3f4 | + | id | 01de252b-4b78-4198-bc28-1328393bf084 | + | name | 203.0.113.17 | + | port_details | None | + | port_id | None | + | project_id | d71a5d98aef04386b57736a4ea4f3644 | + | qos_policy_id | None | + | revision_number | 0 | + | router_id | None | + | status | DOWN | + | subnet_id | None | + | tags | [] | + | updated_at | 2020-12-15T11:48:36Z | + +---------------------+--------------------------------------+ + $ openstack server add floating ip test-server-1 203.0.113.17 + +Cumulus switch configuration +---------------------------- + +Because of the way Neutron works, for each new port associated with an IP +address, a GARP is issued, to inform the switch about the new MAC / IP +association. Unfortunately, this confuses the switches where they may think +they should use local ARP table to route the packet, rather than giving it to +the next HOP to route. The definitive solution would be to patch Neutron to +make it stop sending GARP for any port on a subnet with the network:routed +service type. Such patch would be hard to write, though lucky, there's a fix +that works (at least with Cumulus switches). Here's how. + +In /etc/network/switchd.conf we change this: + + .. code-block:: ini + + # configure a route instead of a neighbor with the same ip/mask + #route.route_preferred_over_neigh = FALSE + route.route_preferred_over_neigh = TRUE + +and then simply restart switchd: + + .. code-block:: console + + systemctl restart switchd + +This reboots the switch ASIC of the switch, so it may be a dangerous thing to +do with no switch redundancy (so be careful when doing it). The completely safe +procedure, if having 2 switches per rack, looks like this: + + .. code-block:: console + + # save clagd priority + OLDPRIO=$(clagctl status | sed -r -n 's/.*Our.*Role: ([0-9]+) 0.*/\1/p') + # make sure that this switch is not the primary clag switch. otherwise the + # secondary switch will also shutdown all interfaces when loosing contact + # with the primary switch. + clagctl priority 16535 + + # tell neighbors to not route through this router + vtysh + vtysh# router bgp 64999 + vtysh# bgp graceful-shutdown + vtysh# exit + systemctl restart switchd + clagctl priority $OLDPRIO + +Verification +------------ + +If everything goes well, the floating IPs are advertized over BGP through the +provider network. Here is an example with 4 VMs deployed on 2 racks. Neutron +is here picking-up IPs on the segmented network as Nexthop. + + .. code-block:: console + + $ # Check the advertized routes: + $ openstack bgp speaker list advertised routes \ + mycloud-compute-4.example.com + +-----------------+-----------+ + | Destination | Nexthop | + +-----------------+-----------+ + | 203.0.113.17/32 | 10.1.0.48 | + | 203.0.113.20/32 | 10.1.0.65 | + | 203.0.113.40/32 | 10.2.0.23 | + | 203.0.113.55/32 | 10.2.0.35 | + +-----------------+-----------+ diff --git a/doc/source/admin/config.rst b/doc/source/admin/config.rst index eee72631794..7e8fccc116b 100644 --- a/doc/source/admin/config.rst +++ b/doc/source/admin/config.rst @@ -37,6 +37,7 @@ Configuration config-subnet-pools config-subnet-onboard config-service-subnets + config-bgp-floating-ip-over-l2-segmented-network config-trunking config-wsgi diff --git a/doc/source/admin/figures/bgp-floating-ip-over-l2-segmented-network.png b/doc/source/admin/figures/bgp-floating-ip-over-l2-segmented-network.png new file mode 100644 index 0000000000000000000000000000000000000000..1a395d6c8fe8f89261e652c49a41a05062d2e563 GIT binary patch literal 75200 zcmeFZWn5KT*EhTo1Vm7}OIkp>B&9p0yQRBRMOr{wS{kHFx=Xqwr5ovPc;?n~&K39b z+#lZ0@Av@gw>Nw3x#k>W{9~>~u!5X83KBjN1Oh>kln_yZKpr$eAaJaR55Ox=_RPq^ zuSa$g8jcVM12|?NhjGgmaDhNbA(A43%5F0|bMDGo=P8_bbd|6V$|NL6Ul}sJGiSe1P zVX|8!r^`F0P&h+0^icIG>1F{;MCM6E9{9(HKDX)Q+q%-rp4pxYb?4W&Ue6igL&4Y} z2OUpkp}!GPz>l}X{~Rv*o0&a$+x>mbAwh;Jy2m)xq)fd~wP4&LrQWDYQ-@))zyIT$ zjVl-7`P`^kWkLd8eBW9!yX(T512Zmi!MIeq0^<8GURvtvQx_Lpu~OMe`93~AKit*n z+uOpT*P?3Xdr;?c zTpTtSRhjzi^Ectm%>sg`cXxL??(27WI5?cl%xJi{xTw&uTOj&GuQ)_+E;h1@OcjOfJNZIe7_zP|A z@27#w|NQyO*p}dNR(7_mj7&`uSOxf@I0?70xSPjpDBr~(E|0|Nt(-FVMm1hxwvKOkj;@D5*GxT&edYZmS8?(S}F zZKYs=+2Z9TytGzSR768V<0WRKr$?5p4v~?Q3ujn#sNI{Z2k%=k;m%fr=_VrOu%2OK zXU9TAYuz~Bu$goHEJHPe0ZE6M=wSRYF)^{XXOpco{j$%ddSh+v^7^{Fqobp%tE+$c z(9Nu@s>`CP4)(){+0Onx2|oVl_Nim_9uJSp&afGqP&yL}3&?@NW(+JWEL7CDrf{#! z-m2u6m6a706%`j3S3yH1cPu+6XK%Kam6Vj!0DBOxML6N}XRy?`goI8D;Il+T-W!7{ zo111Mg|*kr%*^lJy(_r2b*1EOtI=Z{NJ39xO!$L!ol9Q8tFPit$o_chwISi0! zqJxxRWo_4e+qb4f6LY!7Hd_r!YY+C!8y7jib7#+?I?6Ms1hka}%^t3l!TkK^71O-U zA09>b5Q0^Rs7XjjuwN*j-bqbOtv&4LNP5A<#Kg}(Z_HBL%{+i&84?nbp_KpZ*)vB! z8zB(F!uE}i(qX(>t1Kt^?{AKeH-;R!2&tlLO|WC7L|0mVu`n}_jE|4c&GFrHP*JUe zj68mN?`T4c?F)Cyh9gh0ht}YT^?DsCuST1pNR@|!!!((lg9Dp{#P{~Mnr7 zo12r(9hH2)5r&2z6!DgoNIW={6d_Xuk8$DyvKu($o6Rwz;80en$frB75#x0zO z5>}nKg@uKWkB;Es;nz*~&=BFyWsfn?(c4TbHmJ}=axEbcMq?&i(1k&Ozu~_<83VbW zAoI=o=eL_1uLPNZ;nJMxT^Epplipoc9CWe0wRh}fk!DU#PBu{#?_Qyzqc0ObUR_Te zewqRY1M%V#rojwJ;oWPBSf>3@be3v?xR_Xs zl!>ivNvr-8C=-x~o?qk~92{P~YMGTt{qzZyjR*p%(I}b#v#Fw{md4}Cz{<)>PQKEK zqo*yJ(t?Wr-PzT(x~eK)`77wC+mj`(&dw-Lo`71KT(Xm(Ko1s)0%yU)!^6NJ$3z;@ z29%=|tlZsEd~-67Yq(hE?J`a?4FiLinK}^_HT9e$N#(b16O)sioSe3Eb+J9h;KhyA zRYd=`i;D};k=ZaI5MGzb621EYTc1p2+WXsu``eaxpxMq#MnJ!=R$I(4#J6~U+^x2=SbzJ?9mN`;m&>mpa9d12U3PU0eX28_k+?m^%EY;09^b@R zGjf-MK-4WOHLcy<-F0>60e;5o`P8}}o7(zRH@UlKWM-~V3taZ(zs_xIZ$Au%UXl?T z{{4pUU2a|7j4=xzNK4;ySKn@ZriOd#Ypb*X&{)9q?MzqXHZ&x6uPng=9xShIX}MkN zxFMIuUo^Q5=Jjq=n$dc$&KaxGS3qx$34jd1J-`VlDJebA_im5V?s<86KMv7B1X1DA zxyjbBCs98XXS{8r%^UWr$jbSwrC?6sg!PcV*IT_DN!B`l@eU!v0`a{K^;Js5_oUw65E|LO;TNg`f^~nYcYoTacL#v~Zg=5s7m&y1yY1$00O^&L3yV7z z`_fx?_wVoahf?{jBknIEz)0mJB{iCTZhh}Id{bXKnmRdQrWrxrlOpDbD(Y(H*Y7I| znioy4In-*O+ER5kd9*_hO-1uRlLlq!P1H9TsKx}(-BX?o5L7(W=Qpd&-LX-+R)&j&#(Mko7tMkq$DjpJ>QkCFffYyDNysYw_|3O z2G_MEuL=1)DvOKd(iIx+@2&-|Mj{*>9YHI}Rx(#rjZ>x#3kqsMAetcCb2Rb)dQ|?a zZN-1K`z(2M+0b0Wze@u3y@|&o2&3aU$Wqt+ugYI%W@gscO%r6|l9H~MLm5G`ijtyU z^ruN`y(VC%hQ`L}q#iZmGU#uv`}5XjW^UKJ)p*EVAW;&cqN0+M8+>j((iLbTC6PH^<7lTkh$#3S9N8Kc_}~ zzj=9Ts`@aR6fv|@h>5dtP9d)0L%iE?1xz7TJfj}*%{MR=K$a-bRBG-54({YsDM8WO z-@j_d1*ktgc6V1-NawN&XC?~@!S!~Eid`vG2Q^g*1B?Tu3&Q(%;4o-2CCI#Z@q#-A zaOX#G58k~h1(Q@Tj!#THxN_L`#^2xHzE+zdUOHQ8s zctCYu_fV&uoSc}kC9bX-`}*F4_6VY;rUv{3e8jd%JAAlwGZmcBp;kjf<1vl~JvNjX zYyo@%AQKQ6czu0MKtKSPJP06~km3DC%#16hwHY`mC@8qOXNHC%fIi|%{_^EZtrl^F zc(xMkg9i^_VPV$|SQ0Ri9wtFlL%v*WMj~l!q3x~IT=d4qVusKWu)#gkEslq%^KCkL zXhUPRO%x<8SVvHoAmSNxt`_~C0(w@WT^q$lM@~)-_%xvY=?2b5ED2zRL4=lYu(Jc{ z0hR%<7C`g?3_yd5>Z6zu;IggycD360VL(lpIyj74acC8(CJn4Rao-Q!UkU_1B@yFk z??sDOj#2yGYuR7kwtr_#kKI?un)v8_l@%k*A}rbO*eiJO5^zBN?Kc0%_b2lAKxo7P za+#XiWYUKnEJ7A51qO$P?*$b%03)yQ#eIE!p$8P>fRc{0ud2$ z@%R!5tL~M<%Nu}smBfXmtK}g_Qq%Vv21RGpAEce)O3SMHxT7)3C5u2zg)$L zm7;|2 z;w2~N`ShE##*z?NU}>CJsMGH^UGMcoQ&(I6bOCVuA;84`;$R81#HOoX1)w8|0|M4! zalRBta(66nf85g2LOuy2WJc-=x);cW{m_>tUvDJXbc1g?+&OJn0L82atykS~ggZq4)LmrFs0y zhedeueeH884g{i_4dg{)V&cfi2#C2WtUPTSrC3d3UC2 zYHAAj8ankZfV8~pTg$Kq>mQtykN}vAA`K>xD7o@!auO1#!6HNa*E?WhZfh*C$=P_3 z{Dl;?w0_Rbc`tVa1C9|B6N5s)VFidXA0H9*GYCZg;m4030dX~}ngIngF#+mURZHu- zao?Mfg2K2j?ggM$+TjTWsQ66`W5H4BSFcQ_GM^mEXuo;?$!!IXm1-XjqPs4A!j83cIF>G?S)2ZxI@G&Jbr z0KG3$PmYR0CnhE)B6@)y4C-A@1+Spm5n!%R`f$OW*oOdWBQrCJ z%n`FH&2wu#y|f-<3o9!^T->0LkSGa?rKP0@??}nWgh)|=p#WMJaEDe7hxgGT6I|y8 zmUl&t#K2f;s`|MLY!2qWEhjLVz;=T)U_$^WZrq{@xN1DW3;W%Mfkz0n3PEb)5)%VI z0hl{byKa4PaS?bp(LHQnLRndXTTurxymmzhC=I}Q07b#U#OwfYLkQ%@5rDMeLNE}9 zcmg0bVlPx3;0eInEhiw?8g*@%aI=)mJv}|$euuicy0LLqbF(jS zDS*2nA_50&D-cLXNYGzTOe?1OKh27Zi_f)s-KF@SgVBJN2?z*S{fV2K8}ZcehoIcw zyx9P~(ssVVeS6{sN0N?~*7lvt(45WBZ@a(73w$bo!b(fiQ&LiLb>$2b%N&%Fmfqgk z$x_O%)PRA3c@pT?sy_@(l0ki@o7-F9K7tpRZ~=*E)&Jn{KW)VUCdQZrfO2Rj7+21S znH9%RV7x3W2#}KM@439Ndh#JarOT!o2wSi|Ai!!K9@_i0F7jKHtJB9yu>bRzvVs9Tlx(AHy#2vh{-sI^xmaJephjXye z?H=F9&c|o%0jZdlx#9|JH@CJXA|lEj+uD;@m0LT;xbi;7Ll(2P-c{4{J{?!n(`ocz zXJp(5T?#9-6G;CEZ6+*8s)Ur8g@sbVIJEl%MPX*P37ih#=v4v$00-&bsQCJ|z|yIe zw%nt$2L9C8*ckW;Ob?(n_eia)G-auxuWyn%4FOjIh0D>mu;6QDWwl4I2Z)(w5fx$^ zlx-IS5P@p?w}7T!K@Ygn;^HR8#@c-#*rB4(;V(JV0#y>hd$tN_ClDII+5;5_(gh3^ z1kk5?%|6}^4lMix5M4uK<5fU)!5=`qI9XXxRzR2lmr3iJ6~M>oO7qudW;B|>L(*RZ zwj~gkP`9YjbJDOZnb(~KC>VBjc0m1{p?;Du41F~ybfAMl*Q(WePwKKiPee=%unQWo zv1_oCDroo+FDcM)EG-L^@~u*06BF^VuwoCaCD0)ere0nR_0RxR#&rnHC7{baJw0F? zj@7^pGGq7v|5hajn24E~na4jx$VO86Jd@?#fF1$n$;ztmdQiLM$y;_Cef?Jj<6sCD zZ-Be*F9a$IaCHzA=#sOA9UUT-xI(aRhkgNz7R1iZ&hD=Bg6qQ4@g-ngqsb_6f*rv| zfI5I~G*_KR{-nuuP~R@~-8-1Tc11O{jVDN;Q-FR5TAfFn2pL*v=VlOe+hF0Q4xI9G zr(nHrk|4KRoPLaay=SE7a4DD>7Pyk)h(<=8XlP3r63^0xB7&e{2mMYbZXy)Kc_SyM zAAHGL0#E&g%&n}Pa^bjB7?MF-RieiRCbO%;!SS&$0T~MrNPGP!H+zYbQQ*Oa8p`ii(FxnRLEZxC0gs%){Yf1y^+Eh2Y>|d+3zxX`P%s`~VNx$AbEP z=ut5-l@}JEsRPFbEc4!kcWyyJpBbA2S2EbAIDgjRjafErIZP3r}eXv5X zEt*UG2f$mWxA(4}C|kBKjM2kuOxt+^smwt`b{gW ziRV+81xsaKLw&#$smB?kZFzT20#8yzGi?Vkcc)P0bH{M7c{>9!Q1Kxx2(=q4 zj~+b|d`&Mhv+pc7C}vNrfrAYp@f%_J&t0~E+i?50!~d}v_iyk0+u{HJXaBWl`Tt=; z`ra)&)D9yp?vlJldKh+~?sj}mq~-p?%0zt^?0>fSpiV#5!qdXje{Sav_%j47E{JqM z)&C!d|J!^2_Tm5hv&D2=y2mt*>JQA96#ebzmjjNBkwg_VEmQA(`%z-EA1X1&cP@vR zbc$O#JIZ&`<|W@Qn5y_|lQQrYYn)K_e_b*0FE@q>rIU!miX&31)ss+Dd}V;8Y}VvH zkZ8C!Ii#aS`^D?Aw2QJIN^lV}$e_{(pMTyntph-1h%e6RaI3i}T~5lBG^uhQqf$M! z_rFXs>Oo7m398LfN@vAa88;~mSzZo#SnoJ+Xn1L`EFtS$cG1dcQ2Zjg=gIb}xP`J< z%GawPZC7=w7X{8FmY=aj-OaPZPl%{*%jZu~Dl%j~76twJ;*1(#PUXvGC)@_-2GrDAOp7g#okO5K*>tVINkg&~8uh2vmJJGW6h5Mm+fAbHr(4p*?4x9k?Ym}Dd=Yc5!pQCe5WmQb}RZDvux z{?LSS;p&ox;RW5W@}wDX7;7Gi(;{J+n6Ptw(Wiq2+n#QZ`eI1}Yf1NajCazSOz9;?2otZ$l$bV3)9JrQEYhPacP1EG;09`CBGTl<$=I?s} z=xXls)eT`LxvmL5YuT;l73S=ews|cSR5r)*?OSkGXTcMoY2ksy(Wmrsd_KcIp_)S* z7yBIXJ=3JB>N#7CG+0bEmrC!vy7CDbRBf1elONvOYUvs(uQgab516RqCx84P6zDIa zc{!`EdqP?Bpja`teqX|1V(X*!;Vg52mfMNh(cx%2N|$ZX`EJ2nm()uRvTrO900~Gd zRPiwq>>=<(2CB-ckMC^#rojd~*p0kpd{`9=E|D@?S|zqy<@gDtN?fY9z*#RXD-Iin zx*`bJAMJ%9QT_tN8_gfob!m!=@_zK|<>m8g(lNw@HoJ=`c%2|tm!Aanek}6)ogs^& zAfLMl;8^low6!$aWBWFWEu=%&3KM(rS)b9oR5IjOQdgS(!ir&?54P4;osj)wQ~)%J`RSG!l( z*JX0E^6ZGudn zg^yFcW@0UCk3gdVyZ9ggA^7_zbXy<4;+IO-APN_ zh(3+N_aW24h?pjZs!R5nzk*`4v@;86^c88&hve1MpAmCeW?Z^0RZ}zlP{A%;jWt&B zA3(tp|LQy?!e1Zj%)aK3|G951#Dp?7ZyMTEb$yu>V|x0OkC6Pk%#_CoBWb190tpb~ z3SXHj(MPb()dYxyku$gr<9gGZ@U1fTHP|F!@sMmOSdh;QBIeQ?nW{O~p zU73X8g+Or|;lrl7Saz(=PWB#gOzzB_I`!AOZZ$AkNYJ`X%u!8`j*Y_(s)cJs|K1xB zBmF2Pd=dM5b|SuiBq?G>y5=*67qPTou|Re8qg2t_zmsXW7-KM*=;*xn4Q{Bp;qvji zI#A`{{K59U_etMF`uB7%UPNzUA|9X~XL`SxkZ#w4ym z$+_Y4we#CNm9h>)1`aG}OJG%PRa6o7pVfBEQm54qHqq1Dqe9GYc&qTk0#VkwNKutW zHV&s0a~$PEv7kXruSsEN+W1&uFz3D8(LksDQbdV*>9dJNRa5(i+Sr1hfeZTZXr)K| zX`jZFGU(llyW%mcZAZQM{#|*Nc{EgCL)X>-X)iT?sFWg<{vllU27M3a@ znkq-L*?R~_Y?jM8)#D2>hD0~*E>z#zW@dZ40zwS)3uRtw&nfWnxN?Fze;dz29PhYs zRp}VLW`ecGA}j0%Cfnv6uZD_uR^8HeT5@^Y1KHEa6_f`W@46f7b2Ttvv_RgWh_4+X z?V`Lmk_>11}a39w`ngfH8;wY@7*-{3-fT`Vf58eMo$fwaj3>`*!eu0lFq zpN5m|#u{ba7P5|q6#_47A38i2{ElQSefg{>ib&;03|ed_z!16@#iYGo3;Lo)=BIic z*+K%+=}IK09OsCUXqg+ZRk8X5;R}|GLV~X+Ua=a|W#YlS`EO|dH@N=a4*&1(slwtX zLR23j`M(!yQH6g?eM8yVzrA=&l~dHruUJCDI^cU?>r&J=^zmfo+^T%a_jC!RV!X^Q zQXN%U`4cG`yg?uBIC=b@8PCPndZjw1i?&?-Rt}E^!tvr@XH9e@~sl0{*S?n z`YR7WkH%o)Ht?zbdf3#lcy`s=k7=Lh+O#X*{Hw_=*EJGrl)~Wpn1ak_5q~zvCGa?0 z>b_HFck%nlvZaS3+YT|V@3rqQ9Dcdn%f>5hF(nzqriMj-z<9iPnF()ee5^*x?IJ~R zF>&yAs`h!=f;vq{Ww5Ww1j=K7MJ7|+-SIw(=+h-9aO$#Zp5V2sEw**&`eko|#>n)c z{bZ(@GI1!9>Ik13`JB(Df`{&Fk`Il1k8XFj8m?UqjJ`i|*kFU!xI`jul%bH12Wd*r z7mwp>PK_PCALh+9Ki^cn#MT&y5Q@xxbhak>9u~JL<(^?~oK_RQ$R)Qs_H{>k{e-yZ zeS~v-pNeyK!MCOeC-afB4@57?yduFp4*Hbn?IrR*xq zwXN~(xNxq&j+*=Y%vSW|>?%)aa=B7q(URx#$9&M9U-|So%~M!|xxTGET?WX`;6$mr3?*70kIR3y1pZfJIB4{cJr- zLzPlD!Z*+=%+a~7la`sLnW*x~N89HZ#C^RM{MnXHd->|qm1 za7>We9~^iifH@b>{e^csXHIEuI2DI=dJ_&uQY^pYRA*BVFsQ2*{e&TfQLy913^B>T zd5DFdTzo(4BhU>OzYc46LFvH%4k0h(|4_Dve*&uE$B9~g_x7=Ck#YUbJ2`gxa!&x*Jw|2 zavPl0utfCD*Yaz|QGHRIoY?VDOb zS!8+Zk8{nU z9V8d~p=i1+KrVMXGjtazJKXrOQV@p z(raArlzT2E+-yCv9IPxLO+UgdU*+*2MxB>zuTM`!C@#RKN- z*k2V-k{i>hbWZwBgSD|ksi2rVWK;F)&Z{YfjhQ!^I?`5in`i9Me7knr!e41a`-Erk z%WKzSoZbFl-JB(_-A1GMJqtp=ag-yIzdhjf=*qC04i)SIi8Uv-qrxzaDRV+kule^#2vt>ji zQAO-XGfA~#;)%D0goqwEoSvWf7MhbcB2&w*-YTRDjr{@CP|S!-D-Ki`lnkG^>7 ziA$2gq-Va_JkR^eL%)8=A|YtB06AQy*)ijt)7y`vKkmbXmBIyJi{QX|5u^K&_#chV zQ&0qCo9*HAd&}+~eidXBIC$lwEoHTdK4hHSJXaADOmllfLlAZ0>0qQbDH1c^uFuw} zHqON>$D+y3`}x#EG0s-439QlE?60-cj%Rg=Y|J;tz80zvp2}8=FyJWq+Dc|qbiV(R z@gADZ{Qz$02o)UY3z|*9AR-LXYhUPD{dhW+Z#p_E4{F5{fmQM95%7hJ zcwB{#6NLOa4sc(l6mP%qV@Ddes0^(LLYX2OdI7J5S1W`JawM_iT+5l)u!IENDP3+;#20AlDq;RA87}moOdU3jM_7^(XIs^ z#q2nHRicA??-CuCL&HJQNS=Qz;g64Os0`gJ<%;g~xl@hk_E5ll|3v{YF!TTyW_ZZ{ zVU2e2S2~@lQ|~P4&~MZ`-_d^O%)~JN;p&>)Xh+MJ*S;R4D8WmI_-WsCYsMX`%v+Z7 zZOE4#(2S{{&SEI;eGe8B=*DuaEiU;bMZJ~r-fH9=34K@uq_ZU%G@ZwHzdal6IH<_z zulcTDIqHC$RGHi~<1Nm9qEHX-i~T}?)j?eD1Rt6nvu1cVn}$4FP140F4?WAg6WFF(mHFkr2{5u;@%e^XM>P4yzQB#m?# z+d3j*J(Fz-L%)1V;G#02@&qGP(0i>GImQPRjQVNt(%e_E(?vLQLmJO#JZwS!svg?? zs)0f;-2h8+YTL0lITwi`h`MC^HF-<8`(#or>~_QU)U9xzLUxrei6n3uRW)^B?`Ccb zd$d9vWMeME=xa&?GfDumdeX=@sAoeV<)3hg;}4&2<2cVy%87s)*qcF3IeVKK+PPli zmU|vr@2&dNP<~B3{rf1KLjtbanA+#Dt*cGLdZB#s>HXJ#HTd6!qndzMQIt`4DHyyo zAUrJyvw3uj3tf)cz1p#2R}b1*k#y8ik@@-AsqB-NDJ;_rb6cnCQc=Iedxk#7^mXJd zm$q48^1ZRmlf>bx&>)NN%m@bMsQTK_ZNu28*#Ifn;F7*EEz^^%cXu=|kgtlB-%G6+JCnF z0v6Qp%4^jKy87#v>p3C{B(Jj#ihRm{NvXX_8xfXy^rziFqa`f)7|y?fkj4>xW+EAc zUE;me4g35(0Z??y6 z!fXr=$KABg?sz3#&`DYi9{%0f`$PXU_Lz@w;Cx-MKSv_AB@wQqKL`rY^%}C+wO*&6 zZsiUx?3r&ex!nj|Un+7?9GwSdzlt-=jkvhg5x(k|6RCY6jY(MJAV8zT zu&kP7^ZfKn$M4o3%!HWatRXw79+6@(@rxRV0m*nwkLNQU#=9tO(6#0Pcks^eap)3D z#MSj?fNd;`8{D&_jj3bYvp^Ax?v54qQ|ECIamS04w;+?MZ_ieEGC{+(GDcjdktNxN zRNYn5qVcJ!-@*}4SxN3M0dmKlfXPY;fa>Kn@V?4Fmt&8j3h2K$i1N;tK$d3vo3(C~ zkphMPulCM}tNNyb$8!4`WHq}6c9KxE&|hHCm4CFh;#7BMR~+k<_t;pNMH$R@!~b1;RoYPNemj5*g&=nIjSIew>$Lj4hknba;pyLh{x zZd9@^k5o&!l8-n$4O(@eBWsO|aPtWQ3jQ=zs*AxK4batE%Zp{AB2&Azm0mr0kp!&T z5GnQuSxq^;-KI=@O)t(4)6uilE>K5yD3&jOER5YRY13_`zXGLl*rfbp6xdL1ZNUaw zsy~i-&_Uzr_{~z)0f6!{cy3&oX(Z~~koAs>aRf$*PE5ESV^H3;fdlnWFF<>C=jy>|MIBeY!@PK!t@?dWJKI6{c zgN{7Ghu%&mP{TNB9{*z~Qb+>BoyRewlV5H8%O61^&OzSe)mHizR*M4faOq--R}A!y z6bzKE=TRZ}ORa^RPfUz_9KjPK-6dsX3hu?RfT30~yWC?xO5EfJ5ECVM9G(5xwJ5@8 zDwswM4M*@t_4bgqnkT*BGi*4}!{c!WI>k5b@scVV)W-7&QZ#CU2)=tY(!9pSeyb`3 z#8Ptb()k-r$y|v)r1~RO;)-3}uYJ6~p=Ww-$Ucd!H?4}Q3yllPkEFjM#QqIE;Y%w9 zzoDnL!q;nw4BPLj*AXw6EQ<}$weBU=g1w=QpOP5utnTQSIqGbYNz=b-W`5i&U?|#1 zte1@XgfI9H>^01=*+DsslqzWM6_@sT2&XlPw@^5g_a4cMNh6jJ?~#C>?-TCbNo|PJ z2w3}S>C9mfIKhwd8qIlQK&X(WWL@wFlkF9wjF?;cAXt~ZWSHN&UA7@HZ=QJxQUD-N z{CqP{?${|1y8cSy4pU!H8TaI7k{Lwko&i;qL5Ffn&FBhPNCqI&wQ`=hI{Qu@7o)Y* z8M8ouFC%2kt;-}>u_J{6s`Zb|5Iv=BPz}r2*g;|6%6P*ey~>izBLb$hFn9?;p3+tZ ztMKqlk-?oz3fnI+9<7#@+JllhLtCed>IFEFP`)EW-M-;)KF?&Ri*1lWMoWvUW2VUg z&^}pVFQhRVYIq)HrvY|Byk7l%Ahlvr(kJvnk%n#wrf4;NkhZ~m9qkopvaqh84ebNYpNU@B`Z1>>}Rh2l8f z4mQD-Fv{>d9T2R-6!7pVS7Wf|mbA<@h#C1~r5!+zXjIq^6k~NK?KTx zG%S-YQTUR`oKl`-r0l)Wdd-OO@JH_vQ4Dv#qpg{Uune16O;?9Xr)w)c?_rZvUz%`B z-gj63t+1tlQ2C>;6|TTNAdA}_2f0Hk)jtC!+zdanOc!%Jp)dPQXFguo3CQ%-ya#O7 z+IzwOXCqagVYI&zqM@)u$GaOY?Fe5QAgZ5kXiAEgDA067oM)SstYpRl?IY{RrQ^#l zpNcyrqiJr?r73|7y3g|NJf$oUqFaYco{mNxTnKlopXtwzt38U@PJUwV#{VitsCr{$ zWe4ds;_Z+R`fs5DPPH{|BjumKY~8~7m!4X`~i}f zZqQGwVxbMXw$b$?fGT$y9t*O3~#gWChJ@!MBo)cwS2>-N(;lj41&aF%xe4^)9o zeN*%+^63zbWC=`mkg(o-TGuOPTL3DL=~cV;<*GkX1pqVaji?5H0&Z7sp&rMX&R@8) zlN!XLf*xShNBc);pQt~j{m|}3C11`g`u6n>@#zy;VxDo48FsG3A@^N<_->M0QNR=@ zG1}`@!Ys;dWl)Xi&y~ze8jp)tz`yd7PvU{fYeVL|65jYo8leM#S0pUV*53O-$r%PMKWy@0 z8skLw`;97kS__=~CZ{#!O!WF2SDUjjh%zEu?N%-u<2mm@?($CTL{S~34n(x~oQY~4 z0TP~-oo1%o`*iI&&FzO_NK-VFAnFR30(Ni9o3=KjdFuB_oVs5P|Eh%Z>8HVCPZAlC z+6i^5@K9He-_G!hv%`Z-1>p2}yQBZ`BLnw`-8rP|r2mLyxV=lw8rTx5LTW0`Q9H(JZOM)kq8#?{sVjU!C@cRT-QYq5SDRPH90xV^A*i_ z&d91?r~cW_-%oT0`rjf43D?6GN^md%mp?Z3W!S|HJ9nxmqIlOU#rE z58-^z*`LF$XQx9t{jxU(=oC}S_iUowBr*d#?ct; zRsl0JF!}U2P8jXUq|gNHCa*pxQxH>Sd_efi^Y!4?SSv<|O(J|pGJNcOg7;g=;wTC! zAJ7e%=r=tD<-{JmG)eu7`i&J3NaGtUs&f$#zY%4vgs5XPZ|kDS5%@JnRnFn8Y&;WO`%upvDL ze%D7sQ8P%19P)m$6_dse7$Nobn5wkw0~|p%XMBeF5es-~-r}913?Sd3zKW~sggK&3k?ny(LN`Fg4Hh5c>tbTAEY?r=AqA4UYvEnR&r^9AAv?7aAEt=OA}k|Gui94_Hen)kSH59|Z> zn;*plO;9&AL$~U$!Ig~@kKF&h7r;gK4{6VF^Y(4|$509J7p&(Lc|&zz_YN=4uXFtZ zYz4LL>W*UG4a~>=W$m_idO|st=+US=Ceq=uO^gpH*^>1C^0XP{k%2wi=RBijE$k8) z=o9`&!6XrocO@98`+(BFDM|&5?3<|PqS7tRWkRGUM^{G7Ds@!~)Y1wK`E6+}3HuL2 z$t0#^5~NubGhW(_vC$y5xj^8$ky@x~6WmDCRG}r2ufLUmBg8=f92Vj)k2**;N|h zFvkRWOZ{&^vlsg}&=jrx68p=monw^5a2JTl{c?f<tY6u8JX<+}zDd&#BFuoXk^OkmiZ2gn`?A-v zS@F*38SjCr?Rn*G1C)4F$AL_$NK?;tP8eQVxsPJ54p2gWvEWU66kSO^E=84}`6KSQ zPj8(0@|sYV59M!W`r2g-H#`7u1uYaK0I>hq)b2kcD*iFh z(`5cO&_gcXP4zCW{R1u?RDXj@6MPbw7vS7VXkI=4qw4Eu0B%cB1PFK+lj*El2ODhN z5<;&wUd$i9tTfo5%I+UDDJL@&R8SOb!!JZ&e`(~sA>wD_WV)2`isKd(n1MQkUjlSM zZ9qWM8fKT)f?JoI=4=Qw!R5BT`}p2U(Z>|JFe<%UPCB-Dg^FffxkaHR2~>}MrcrBH zQbV^3fi>cK77=}?>9$t;Q3S2mft;>EpCdqsfvD91!T1xy0Zh zsjI$+M^TY*f?mE)rNqeCT2>tQ0@hjm1Dqz0qi^#y9yL&3%wL_n-mpDqbHe5I*}M7M zGgmAo@%s6%Ek(XbznQ9Mrn~)Bs+pTFJ%wfKHWLQWYw58`h(Ewz6c1{@_Vp=vv$VoS zn{B*+A&t!N9_Rw?y3E~8RuAoIKD4!O^on+*@EWa}%pB$~y{mqL;(<@8b8Iv_55QfY zq0zDXOQQrn2i{ocoTN{UZtyT9Vy6R#2+$jlKWb@x65%L1m(|8#Q?NH%P6Q9XVUWQH#<$KBb{+yUU-Wd%;!bg5yNCp zUrJW+JHgz$lUFmn)B&I)S**0g2JlBzz+=EYTSfrnBs#j|SUBHZ{kZa~2N2SfRJlHX zyU#m~4q0z**%h!rno zrf=jeUPPKkbd|!B@L_Ah5H?>SMKH_nN8Ie*fRv~3br2WUz>&- z7Z$Gpm0w7<`T@1b0I_Oz)5XVVZAAbr08ED6%~lCXzvjCR{VE>7dguXQ17+suT}dzd zHkby6IPN#D+}{)tN2Gkn3T^?-tVtQPFw5~GO@3K0tDObm>^b~e7eekrA5T@yI=)j?gaC5g(6z2P> zPlWcQ0)x~gCp;;|5e!lBJ{bzYC(eX+Q+f&Heczecd7d*7bUz#8b>Q_&f^Az+nANP* znhj?;^DyfC{{|qIU;hAzL&6j_<3k^%pBh>V;&pjuo^9cTX#CWOD6dfA49d8DT-RF9GR=BC;l;*Y_>oU4{Y?m z_eXvUl>j?(Hd3hZl}EL0i`!-p*Z8SX2EjbrW3?Yph!H(-li&>=uo<;j$Zj=oU!s)m zfUUrU?O$~+x61`(36)^G$sMoBWxnYjoA{l~Z=2XZT*^KI%VoPj=DdZ=+;B0l)K@j8 z%1dAy>;w>F_yJ2BrN`^U5l)?%m*KkmTIt*>=%u4hk#YpD8GCf6oaU;2RiAG6`J0i2 z`%3qv*u;P*?cszYNNV}53GfHpfP3C;j5c1Qwb z4@B}2sJ~k9uyaQ(V9~>$>gmq!6vSe922ugbwa!+%2TvV@ho@R#=CN* zIQUP=&%K3xH}n?n^>XsdV!aeO1KZ~nUGoq7iyo6qd^T^X?ph)9Oj?wVJf9*y>>NW^6)`1B#8_!P4;C zr>x)hfBEyH6sR2TInTaY=P>^fa>{w~W&|)I%^&R#r!#K)X|Q2g`(Pl2Wy&8|QA(Xy zO|LP4JG}e&f)Z(}X&%xzyYx&SlM>RR8St-vLj;UG5Ws)byCK7+b%;`y0}S99 zsjt4Q&(FpQpoc7UQk_pV1% zkW(yh>+oyRm{jntsl2J_4YEdhn-W{SSIG9Ccgt;VVS(o*W0@?!_8~<*)Gk)nbX&cD zvp=wsR9t?4AI)byMr9q%A^TXz9M;JXTa!D4NKr(z@FTJbA*#5bwu0CjAK05@w0K3Y zk7VQ|L?VP}U1dQ+I`D!I(aI`cacW9E&{3MnG9RPV8r!+Ni$ovguzsD`xN$k$O}>_L z9(B&Q?>KZl?>Sg7Yarj>%fK)=5h5bY5h52WeavY^Bcl&9Tzi>RZ952`uUyKPRDbpv z8zRDU{HSnQspz!vor*FULhE1^fm9ahluqT==jEw@9hB}$O~syoHaLdRB!!yvRS$Xw zZd32)5;U`37E=!;r6(Ak&uhO|hGpcF{}K=w|bu=nO>a(*zuA(tUmQc9?zv zuU=Kwy9p0Op}*7CY)YvwF%!=BqgJG&d5&g+gX6G!&-|?Ulc!aC6bg@YpQr=m`+{=0 z_oH|{_3yL-l#tO@VDyt~F%UvfWve%Lf&ex;TpW;{7WQmc|JYP5LdxpsUe<*tkpgg*5Tl9kf zN$?Qd9fE6c3+}-kf;$8cP9V4k3+@DhyF+ky_u#?Z?JfSZ_rB-c`|3#*MbXqpce8p; z8Dq|#gLcJ9^_=1khN7#e4YC&NlNR;6ow75d?deVI{#*LFrgzD(j)&i^ldpR&Q*pyG zw4UmHo?p>+geEcDqP)rTOuiPr%)vTDpWN5Y;;U)qOqQ1KB}$wP!FpysaZnK*sgDuQaU&Zxi%>3IlcNDy9u}KPG@Q74d~te`lqOhohN~AZfIVYfqF?f z=&S&n4F>)`tNL|M%~8o1v_z!!!aHj1z0ZhXJd$Ou{oPbxQG4|b`@-}gLxKgAAT4)| zbuDpgT@>tZ`a#pUL)vXDlNTHU>ADR^=&jV=8$#8Z^c$u00pr#br1ZU(&bIT)r0wOf zFhbC68hw%0D{?EU4VACNv6;+XmA$kR?z8bSSAY(UYu0F}G180f=;8)NA}w8RBPXWX zx+)L%4?6)K45`CX)Qgn#H1Eo{L{36accMQ2kbU_FYs)X9mE8MU=*IuXSN6WT=Xyb^ zw%i%hUgNd2RNcar_3v#2p(w%6s#&K=!|7=fh!rYP)Qf?8m5V5C2IVmABg8PhC_-hP zZ`e#=7O=!|QDH!$7%6ZjqL^6p{Dv2=!uW-s9 z2mS>q)S3g|VG%0I+u31wQBB+LwJ53_(&&&Vf#kbUgBB`z9o3bSovROf+dYTLsd4Qm zvoU1{D;UlFBySW&usz%Mf80AVgbF#V)<>jacxo|JJm3&YNe3I}c9wr%|1q}OX66OB z-E8*v!J9*9qLm_c#Y)C(T+TbJ>tt!#X*+qAAI%X})Ea($(@`xolUM#SsyDOcdY$Is zly`G?HUlC4F1d0U=)s1Dh}$&S)qRN4Qt}c44fX!Xi!hR4t*>$~JWnTmCWJc{B1rOv zga1cduc6!4WEQfqhNpOLnY1kvlBcV0;>jCWoTjiG3DPlGM#U#y<)zt@&vozEetbt8 zkcu$O?PPJqhij=OQHm;QfwGJprBijhG#9(WO%(Y-NRZTNNO|(3ue|6J_uS@*)WuYZ zl#rTXqlb}ryFTn@j_BrTb~c$d>R*J?i(FY0m(ziV7Xb%P^~7p>kUxj_-`p|DGlo8m z{$U?98Mu7X{`+_5rH`KRambOx#XWoeOGI03Z}Ja+V_Wa}Ie25Y$}9H}^Q81=<{FTr zmANX!N0Phfd;i#}3&q@jl{FAGY;01X!y1^(#xX|?Pr)CkLjE8VYJzI_2kZ66FiM9) zWp-?833Nj`8u+}JVucTAkvJnr6fxSZpREN=i>&uc(41eFUL=b1rbO$X*PAPFnJbjz z2T04AmaFqWtoF~{M!a@g9t&YIJ4QBTkU~zYgN8WL?AS(b_`@kR z+v6A{x^0_IX*R!xeA$nZZB2bELgs#ro5PnB5)csrS0H7GUGt7BUL&e4nbl92M1I=T z{34ICys2Dgj6A;9`PZOH52kc(6q;eX;+p!q@}h6pwbxQGC**R&JP<+0FP|0nKV_g| z5N_n{jo}SdhgV1mC1h03u&ps0F`GFka&eiDooHjZj66NA}{K>8jS=Qa@DV= zmG15(H9!$Fgq&=va53P(h%%Y|_#E_bDvq%W_2T+>J~+=+a|c%OLBe~!`@YRYUDqiv z9L;Hn&)DhvKyhi}tURCzI^yuC_pM`zRx(=MhhO-O5=WVR|NhjiLu!{RX4-jMTPsFY{FTT8}A$0$?}Emr=^_JN|*&?)JD?z{4ryJ`514sPp`c3sto3 z+qh|0@wEGOqU_XoQOT$s2m7bLx}~BaB2>0Q|HUtK7G`(2pTwyNXxnRX$(RUL1C!J2 zsImtkUd8%Vw)SMcN^9!Cv?k4q%1~)E+1Fe>te4veE4%DBlbRG~H5O+1GVlo7MyYcK z(VgWq(FOQ@H-^W&!l$p7SLdhYFd(R}zp=ZF3`&MJB&HkI?B6!!DHK6`!i+9&jd#qv zu-U#ud?zZoSEPdS31Yepjoi5DMNFJCa6LK}U9IMa5Tm`8M>jrRC-dlJRb`VH*rgax zTg@)QSN$j?CbL}hWAsx9kKEGO#L8@m8TvqSr@g+A zQ)imI{xq?(Xt|FRB_r^INJ1$S`QdI{d~>>S(=whTgZ1#fNOOD%<=N^+S_gtpa}D6K zDJ;-le>>mk^*#TQ`1;MER`?h_ky%q(&{#HhH6m7wkZc6-yA_ISKbWIQ%#M4>Pv%?$ z^{b>*;@XFSnOMG$=OZWmN<=ydJ0#NSBC8_d=d z{cVn?E7M#4Fbbpm%x0f?h#mkhDuo3h@BYT8#yc~^PzrYLx&T&D=_`EIQHL%E9RABs zVbS@02BW7Y4hZ9hr0sugn&FIFTO^CX%7L|+#vULcag?%O{6uMrANVymB`D_lw5fxs zlf@~wtWaupiq!xXEm}|_nUJ4@o`%-{VAWXbyUj((h-ESD5ROo6IEu;1EQ7kRHB*wF zz-L`bB8bmTWnJOXT6VF)=pWrAiVzUDv;>~)HdDyuF_e-}>Cw8tA0tt+LdZM6S!+hi zp7vD@j7J~onoo3q8)f-Ehkn&Mjrcb#{J`E4acsKaedHuYeU-0tjHFdzalSfX&}utFq)4L6Uow+4ew`!|Dg`md1nsu*%UF`JDP z^={SKj2~Mx-aoJG$lLw}5hk-b94lCOPmg!9Ln?E?4^(&!CwDzGqr99Hi`g{DE-Gs2 zXuvg87dV&~8L|T|Pu9RysWD4|&Nhxl++C<@WJ4yx*wyMc<_Yt}MWpLX_gm$L5IFPP zGKURlGKj<)^LC!-kZid91TL9r-T3qsFyCb}ILLd}zlj(ctC-kW>t((*MQP$~ z+rWr9#)x+xW}-v?=(06Q)tKjJ{61Xy{bz~u9)Z6{Ca#4Xz{wGKC?Fm<5dOexT?L#eProQ3mA2*Lo zvvZNTJ10+TLkm3?{s}z5|D1se&H9}})N74?L&n4w=y+jh;5W3$!0@f*9ZZ8j~Ic73qq9MByL>B@Mvts|*S_8Qv$ zT?%$J;|`h`H%bKu?Uca{5d~YK9{Fv8{V^PwDK#U)m`HmPDu$}K$3>$DULNMtvD`|& zvSEm&G+0Kl76I>rLe`-_tfJXeolK&I+iF9(-yh_C^#x6vi2u?R?wKVN1X`b}XKmF{ z-(9H9Z%q46k1s3qlq*awh##+j{dF$p;OXEJSGK1pT{>n&sBbk{UyxF7J|@}3v|{B? z_A^;dwMIjLQ6f^Ko;q%$Ai-U1`$`=M|Ps^XNZ_Qy1Qs0M4ics zDG9`X0!jDx&NCg{Gb~7=*9aI@hZXMx4+%s#Y zCE9Ek|H9H9SJK{6b1ocub5mJ=-TP^j>75(;yqW;NDZ%;KmHlSTz7gb$nVsh2{tt** z1~VG_+d6#&76be7ti|Rmi>#J$90gm_XjCGCB>aKrtMpWSoOJi)6CxhunKdl;Io3>{ z+054Vm;Oep1N*Pl`ZD%6xHJ2kQMtemJ;7aTuOrlbmS zTewXVOme{vU_E_4V>+&=!{O_xquZuw&Eo(WQr-qO&*x=YvuN~~*=C>SM}6LLpU-AP z#74p*zu3E)e7ess;U9|lZjqcdmF}JJHcAvxTzZl`=2c=}if`DD$(_{}-mrzapXL@s zgB$xwJ7Rb9%5N`_n?Rtt&I1PXSIK;ZOL?If8v>z8y#Asfwp;a?sQ&%JJUcS&Uq+P8 z@y{Pn{Q`p5z$-^oBB3JXXCK3zxOC^wkEudfB%o+o*~GF;{*6mYjuLdfgITfj8z3X< zcE0I-gN}EZwY=CH^;@Fj)g-v2jv|9)Rn+`a z3c-HO%zq0{czlL@$w@{OHxP;ZLsQr$uECl9c@G36?`!HlTx*vqB0v>?HK|d5d>Ent zPbOx7_2%S_Qcke(>DLNwNl`z?4L9KxnMr)C*0&dFq~K)N3E*Mm+aBKh^-d^KoB;`i zJa5#heSsy`_|I|crxDjeb^~j3MBJ3F(oiw^vY*V&7v^zJvB`22*0%@v0*Y1kLbFCS zlk8WqQ<4m|m&mN%dUBJFd4S_nI4WR0`ZVG*tnSn`vH|RY%AZ!p-}*uK(;I1;QvR3n!X_~V8Iv)TWXe%f|>#pZJ{FrZ(gC7mI6e4y}7 z0IvSBJ1LHk+tS^$g^Zr#%zaCG_bEh6>7^O!fOtM)!GxT2_5wv`%uf~EdG&}E1 zbB%G`&csy+M1xS1uPO3%SN*#e0HhYsVq^Uj67FmmNCe1l*<%b70f6CRTBg-U~MG3%3TH8B` z?cC0rMY>3YGXgpY+FYkdv(nm+mK3jXdZIew{a@(L5i;peKIDWBHky;XUXDKWyG>lT z%Jbd?1(hv!3Vog8hp7h$|16Op;q#RtOvWGx$FgEkyp%`8G-Wpj2m$+=X z-#^46<60F2006YvgZbxU6Vd`H@o$#>xB68gvZP<5((pB+2w7Q4+F8jsbW1BYa`Iiw zm?$XXAL8o+DW+itz25t=nLx^y115%dBPG;*+KfhhKf-^>i$kM5|J2U#oMO z%^+nlfoa^p6mgXg?u*>|X*BC${uj((y!{W%h+*Sxop8Y(#Yv_J>2?;4QD2@fU`ghq z+U3h&oqq4E_PX)g%&k~Ruz0{2$mTbP*5Go0mHsQ6SE>6xgy?1>8qbKJ)&T#{Zk(hC zp$Xssjz@6n0_4{VzDB z0F3LLrrP*|03#uXjPSvl4Pp?fb3NOeL5q;wil zE9XulS)E@PDc8;=_ zNFT@go$}cN04oJJiReC97|6iIllUR_@hx)4XT?b#x^GfsG3dC>Uf(>>!$0)P=mXg9 zO6N`yb!=H+IpYh?Wia}Q78ePkr7FJ!fPwMR%tdwh()38a*(OcV8FJi*#AD4!w@;nB zFbL|eFriV2KRe}8g#gE^Dn!o`{ewHzNgB>p)j;HTpsG!~?yei~w*8_7Iq$Upyt0&* z@5)%Me%LOi82qej@!{`3TxM3*)CP{}avL;LI~A*N{>=`vjnw41c+siC<^{o&T4S}_ z3ors|41;-Jw0WHq5#Q`HTFjjNaVJuNr8icIBL;YQ7O+UbGkc2TK|;k=(08Fa@Sf^F zg;#*5=SrD$gxZI=tAea;9TTaIXeK*3D6<+RsZ_Oxr^6p2NG|w=I$Y34H+PsfL_S+d zQj#~%2}gvNBlerhS8)~IqjjwT1d?yGwl0cOFws6X6W*;{LQill zIgQi#jnk*q=kmIForD{gKQ=2%*5`@r0WZX0xM)AyUI-|MxCET;pmG^ek5c1 zum3H5WySz8cmTbKo}gE#1AtGSK#QXEaH5feWfAV%*n6yq%^gs3q3Sk>O+4vHKno|N8*=AJW{_jxnqIHSqf{;iYqm) zl7rRR8DskIta6%tEu}>geLU^V2H@i6yAPui!=Wgj&&lx>0yualctf$2tDeG-b>se% z>`q5F7VuN?$g>$xpt!O$Hdvqe3B7ymTW_;z8-I~JOul+DO8xf`Y@`NZ zp7i}4v&%no?-VAknS*!0*W$N1A(HG6bEI?HoGfVq`U~)-V+|cM_UAhSH62~k=T+!` zMtXu_bPijCU@E!?zW_ab_{cUGQhh6u0QY%QQ%^&ur;Bo~>`g)+xrOFt_OEZLDr!c+ zx zJD1ch3h&+z-dsrSZPQLswLWbeVOHYs&5~!n4CJdaZk;+y;dwl%U%!K%3jZQtmS0*b z;Ci|Nh6g8uaqo9`#V%A}R3#Z187Jot{hn{gmU?Wr|5f@JD_RO~&cX;4at9k+3#0g% zubyTF`WE40N_u*>2emNJLST@plvMx0^Yr?IS}-$zVUIp{43`!+2Uu+MW+D~H?;AYO zqk>?(JVcNMXjl^uzS`Mx)W5+RFPuaUajP8=$K0xDGx``~r~yhNv79C{IiWM3z@S=r z?>Z9gdSX5kK+MC?y*MJ(R@9njTnuvY4FGyBVBoQ`ni_4K)Sgb=*)WfvpH5{79@syj z`XQ<#rAg8hjTIfo6Rv?k;^V0GX?ecTKQPeL)U;Hi(gX}{)wN~(4}6e`)!!Ex8rB_x z6*A2&vozWk|2Ny2C*~^H)>drOS-g5N^}b%^O^4=ACkepRS zd^o09Uv7}WxcGFS#PaxX7bmru#BNFGeSgv`!I*G0^M3<4I^gZa_$_QKltJppv>0Rx z{5)WrL8&ZTeR;ZoPGKZcdWqMS4a-Vl-V4^Bqqrr>iI@l%L)dovIF8bb$BQ6av^RplHb1As7uk&az!NK58%4w zzLL^$@4ADX=u7q%7%5h&TDB*%LBRzRO2J%TxfITmnbt=qAeeLb$VVZdeUmE(tUx}Xfr;y-dXel3XO7|57LAJr%r7{< z_-X@E%g?5BkCS#+mKanCb3nz)FcHL^Xr&Lp!QS-r0Q5_i*zBd@_NV^M6&w=K6=-(c z89&_|Ao9A_M_J^g!#yAabGN+XrUBY{U)(FV!5nzg`A*TaPm=%kZ#J2DgprBK^IUM< zCdBY*b(pQG2cXGEw=>A*}ARAAWW-KmQKby7vkC3jeZ1T0P5=3V3hEZ(~J&bi!9g|SifCg zCL9Al!+$<1{8KI!C^9&Tt3BF%G%bQ6CL6VyRTo&N_PSp)`?O=p}?$AWFE=bztSm43?k`@6c~BLX+_ zhUytAwkg{?)0n~!^DM#uNe7_U5DB&d>j>}q_ZLZnm_nAexn+9>hP5`G?G68_D!%7i zOpGx4AFxX7ije~iqHVtC-dZJR3jlnol!3n@k2`zz%@^*t*iJ)D1kD}?VaYnH#oSp7 zX$N&7E!B}Tsngbhr65|r05E*oKOo?6w!Gu)s>`aT>0H6Ty5tdk4CjpdZNCdsB3*GR zjpga`>4Ksc9|y8+Mhx%WekFIR4N&*scyi>VBI=5atgGi2p3ep*W`9`WxRI|2C`3?J z3p1)YI~)(SA%{G3U&qOAmXm~UaVk#Qibg;E`aekI@}{AU9U`WtrqcFX?SIiQPflbd zD7f)pxU)eQylt^Hzk>qE&M|&xjU5`XoFSWpZmxj(&pS=NdfphK$YA>63Lqa++WL4q zz}T8Hs!JFj$&y5eg=Y*v)^u#5ere^uhy_kWJ!i7V2v)_JuOzz%;&nMSRai^UCIh?? z&^|g?c0ao4Z+^Z^hrd(&x!8nOx~fL#)_6hAb9sJ1{||xl_flZ3K($1`{j!Kv&*t)% zhjOW+mg`}uT?`_JQ5?K>z_nGU&lora}1DB>JV;xTil)9faRd(*gNKuY>2}(AUN!t6h`mcI)1)iPz zC=(b)&avV#5gOS5GU(FU!1T2~kC){#57R$Y;9_tffKmY)ep6Gl&?ge_SyDlt5q`K= zd?@kFW-*pXT=&PZQ3cf*=F?NF;H26W)jv8MYcu#-0h&bP}1av)LW2*kP|uqf&O z7b9zUm22q0IRYdjZpOrk;^O0PBRpbbK>Ye7%whckyh>Dw3e+-rAauJHx~eHs4dw? z#}7A_da33$UYCbi2Cc%G)2)q9mchOf8G(_DyZSfz$)Em5ohZpDYfHIM9Vi04-jDY~n@Wo^qsNyEwm{&@m5<(HMC}3qLXZ`UwY3-2WY)n`7mi)iul*$Np8~sPeiI)NZ4->Z990Q-^D1-7>*t3hDvNh5O4E z6%BPke11%We__~ynGFrMVC{;3 z{W?8F0bIl~zq<89m}s{3b!bUdpV6{wzSv|Y3Ij zH+TCJ{0prmiLQEt{{zXE@1J}wJZ4{&;s|zoi4B*1^URAX@dA{Kv9ju^e^pde1kx{= zD&385esGjAbJNpX7Y5eAM+cB}a;m<*d!!fl>sRu}!rrMv2MBd$SR9wc_16gV4SZBKG#x z@fz8U5(ufk|MECqm_MGZNEK?WmJ7dsa2`fKA;@knnR?%!5*rEalMNKL_}nI6G^jF| zvT?8g-Z1V%zho$lM5JWUGsSYJJnw=$3WYC2y zk6NYnigK+d@&X0wc^!VyC$l6CBGESkchRtQ7>#iY8l||N>yQf1Y0T9oi1ps*W8w+) zT3h0+GdTa;CI?F$XgA>q<(c}0*thZG2>(}kMCWzEq*I_n*7Ebc@Kop8&WFnon|-RB z1BwrOEY(Us&8pHtM050OSN@J{r^rv^#BCWy{j0K)m6leTChm3&B|Qochh8B@hSZ4C z0mN{{pMmoJ*h=HY%NMT2)ra(CK_EiV5iYbM{NjGMJM`aEcO!{URe7mRSqYZXD=Bz( z@6Bc#oGii2e;{?zba%END)v&hW*IPCk88^71-s6B+rL$-?CU)yK~ieKgbj2_j9ag7 zIJU(={t)&moAC!hO!}}fGqkf%n8Ys+(8p3(U4~r++MgVt_Z}7~cb8@v5-uZdozXx1 zB=12ovG2CK*P6T&+ts6USofRLEoEXO;aF21B}NMdiUW=l)x_GpW0hI91ESNP?*PJq z{f+)5W0*&#iq;dHdk^i_N4cs3!dBBL&2KSRVf-u(Q)NEkaZf3;99}gr*{U6@jp~T3 zr7KSSd+<8Q7g1MC(a1;TOCy7mHJs0n-Y6=<-d89{8x-1rJO!C4H#A9n!3&kig_fGC z$R?<`mCb1Ze$;=AOY^$A*yGYqa_P3GwwWU+>&dcFpL~Oi|*X#eKvv75I zRL+QdfMCqo1G`ONG3Ra(P)b>6TOG|L6s4;zC8|adu8U(m z^|yqSw5+cBnr>tRVYODEv3C4fPiFBd4DWvt5`htiBHzYpA4nqq2q0|lOG7}x?S6H6 z?k}cTha_0zINY-0aYJUaWbbsmwS(SnV6k$e$Ge54yN(TNx~^R}m4tdHK0IDw!6N1x zn`|drTO6@s`Q$COLuz@OdWrF-cQFRouhg7G^0`T4(Nw*u#;lU=-yL#0cJ2vG5DZy2 zJF(8wjL-K!4EWD+xcz7dopE3i&lyqSqTd{!q?G` z6lZ#i8aC2C8S)o`7Fwdeym7~?hHnS+-ZG##Lr>X4e7LEbIOuAq*lH5`@G_RLbx)1) zIGJ4zAgQk*eQOA;THa8l?`v6Yj8Za4=;;skN?YY*WmBWr(QQ22WUpL)zY_8~)nwRb z_i|N9_)SW|()U`QT&^rM{Xwa;d6x{Ol_#lDQeVpCb_LN!t@UOdZYYI%H-*~X2z4xG zXlZn`%%CeoI)UNlkF{NQc->+YT-awJi~Oj+Pf>61-)wll!%(}EVQtrWdIeLnQYM~R z$<{9`>)CDMZwD*TYx*ErxmdNGwN=Bpn+1a#_jhy4xjQ^;>V=xJ^0&Z1d1`~t972z* zQR$dMk58wcmscrIdV-)JSs#MD)3#G{A^|f13kwUX&|4cD*OPoF@p!LEJBsaMwbt*Q z=!Li1h|jZj0PCyp=PEer)i0^Wh}8ZW=sT)lUupKmb6UeL@nXR|Y^o2$0wF zDw%jfQROGmL2Z)YGtaQ5*8dsaq51Tvdn)=@i;dItw5cN|+j zQ~VV}B|lyaTF=P*^$R|Mkt9d}4IN!sNogHH&G-2pN1pF-(t_Qu?Kj@@_07!kfil&9 zuK(PCj)?nD8#?IFZ2L*@+=;$Oz_ziqwKfq!>~&iObV0A%2Lb!xoMS?E>xPv)Y+2agrCB{PYyD~sb5I#(DgSj}Zj z2dmaEH63T+%_KOCB^u^OY8jepqw0@K27g|rip?-nG`xoBEWNb#qDKr`z(y7FD>;;x zKSIT{;(KUSN)23br6~4VpK!P6S*Ea_dZ~pa{sMBI()e-4HD>#u6W&t(!!~0AAxD~Oc`tmX<)_E7 zY#yXuLY9b%XtnLMVYB3CnlE*zg808279UNTa$vSzrpnE@-B)*AEn|3Ydxk!FX>(%x zNddVb!>v^Ru`B+^c7Od$p(|5H{z|tQmk)gW5n!=2^);-!e__GTI=&im`#g1jcy8@6K>OtG}3 z>vPLSuOdqoD8cUT13awCx0qWjGz>uy$Lcr&nS364n9-?p%sl$x*Uc@a?F|GYj3#um zDiu!U8xt6|&PCU)rJ5+M&go`74I>ngjONoNmjbO}Pw)G4vg5Xrqa5Mk{>OvMCtcqA z`49;Y{1>hDJFp8DwTvc6aHw$~G-W)fYhZB4WwFPnkJ9>};&QU~uMvn|S02&^^#r`y zuz}9FyKcC;V6Lyr(Iphe1yNj8S#_3Y&oH1e6>E`qKbe4RGU?koB84DqS znK{nR33nVcQPaDH#j1}s)JTFenwQgSKU<~mCT_dftX{L_B%kAHuDX=Av~K%+t0jH0 z!Uf$}Pw`Us{>Q~&yfM_|@YnBrLelD&y)~r*j}2#cPluX_de(E-zNmk35Vk^TMye-u z!Ui?eeo)dik`@(CY;NUnaSNJ8a&wcI99#P&RMN#DK~NJ>tskgp_#Kp5>Q&lN-apx_ zCV$7^;pcM1LcxPV+R1x><8wKfA@U~ic(*n5(CQc^`?RMn)fALZNww9; z%d9Yr`b@~aKTf8er%rl&4E+B2O_kD12J*!OW_xvw(kY>3&(PH#zH~{)3^cFftq5T> ze68PGHD{d0M6HrK-hbu&IbVbau;B=YY`5)r-pLh01Xb>CINGaVUF$e0KP4yESlzu7 zbxq7_k~tZQ_Ydm|>~HR1$|jYN^2?M+76wtSb1D5J;c3;%Nz-6w*8%vsrMESS))wLf~V zBc7IUB1*qxeBMJa%R1bev@BhA`WrOE_#%1XWwJZVNGWaVfC$42q}LB{Yf3|W^Jk&P zIY6re6~+$!UoRT{zj@;S;h6tFzJ~O_y|eWH>k|L|@V{StO7Y*1@Jn+$P&s=16|XTL zKiAMHUDXN4T~pq8;^xp;aM8#Wy`*5ZXX>oRp*8JiP4GKF1m!4C+|kl~Zl3>rO&9t( zUI`0I`=|gt)XZnFY;BRGsQ6Eftz|DS_rLP(=|8xs3tV!vn&;o-Wp>}X?iB6kR#u$o zU%I5&J_uGL5xIIj41_zXHBahPhx57MxgUnEd)4oZwsnt46{RbAX{9t7Q1H8a4L{p@ z!&JizR(Rqqc-`%lFDEP<(my|Z8ubB(&w0cD!=c0{;chJ-;vl>zdV0CZ7axng6GdoN zp+s#9Uw>(1eg8QNOediXZrBlaRVy5m$*fRwFv0kUPawWDFBslxeax9L;}U;ftutzY zXmq)o-=<@f)cxS8lMvqhiRMT_P72fYejRbeVwC39tl44tHxifSZF>E|;vm0hxD$n) zXuPWdkDH*stL~O(rxu~Lm+$qSdbYByQC75)6Lc!e)E$-guY1?B$H|5`9zp+I%VX7_ zh^8d}0o#4j$NGnzO($1Zb{F2o$4aZ~H;xI$TA_PbTT=|)EsYx6lNzWAOy?z8NW^c2 zZTD?IzIJZnj&xjV?NxT-nIvglWthd|;1Q)-EPW60@%po?nyrS7|wdi6pY+n9EZ*le_~YCEFJ#zQibL&v2@Jn4P>c=~*T!ilG%>Ou8^csL}3 zXQsw3@GyD<07Acw-1Q)j89FiC5F$JrD0Loliim-> z2Slt}O&>0I4b*V>*rMK)nGOxe=&wU)F$1VLn9htZ@UD}4^>+B;TCd6JxQ-a4>+H54 z+R*TAM6DLb8>qov}!`#DdPVu z9#^rGkV7(1gX~ik4YJ_yawu?bm&P)gF75Db;`TlZ&g;Z~YCQAOe~nyAiY$iP{$oMv z`i>AG^F%lX?&WtlO=ZW&KSXd}f1~zOLPcRUzjvSEGXgb&%Ri4^k9?W>m*V)Y__?RO zt~7d#8m&!-5$>feUfuWuB+!sJvA-oM9JJvT^TW0Bqtiu*HSYe(@oAYQP3~oQioXUk z^@ZJIqfLbxqXQo`GI)y*k2sLndKu%)f_bG-As;2tX#`tK);l70 ziKxx-I(+eo0*A5pjnDZKWRt`k&a)R&wts9OEXiUGLLe!BUQPerq_=M6og0HLN_6@7 zDP^5=QHpf+M^z(ToAdTVJDS)^2qF>?&Jg^p8^bAJHYy*eFgvQ}rd+7JJM1~yw9d=? zE*cI}#l(Un=vwc-K(pG`dCj3VE=K6wYX{SRwluG4+!=az-AG85I!nW!`2rH8(`l4$ zuG4b7W?jhrr{G&$`Q;t0#oS;~+l;u5G;1fNVmDW4;LGLt`CYjII4f`3oVX)=f$JVd z0q<<>SrdB;tc3kNxKX%Ka!Oh6>@;k>CD)~a&mm-F8=g~IIqj7u+=0D6%rE*Wr{pUf zw2WBfz6|eKAzN0hkHR&gCISMN%ErjwL89v0=ntHXgk+)8=2MHv6aHmuBC5q(UB}p> z+Jb4`VLNp`n!1Bj@0bC zn%uKEbF$;niDu=p%t)s{S!{-l>O?Z<$29<#JvDgZ@368R5$9 z_)o5iYWzg zpT*N0SDcKiF*{3?^^k$XYmfZWDU>-IDMM0*+eeWD8;3O+?rA*Pv90aCRjzRp3{p2X zDo(*nmWE^McXpXf%^m#0#o(av8PzFlT{{xvPFC!F9P@1U-zhXSGJmUvj3|~46ZDUg z5OPfRSlBJQ$n`IU1BP&wRbtid!e{gJsXZk;mTi}XqPFLiAqMZa@ubu16txXnrFyhm z>UV@j{Nj?g0#p{lOC6XF)wfm@$W&LFq zwSoF^-F*#8(BoqrO=xA%3*s1CorP(g*yA0`%jsl0Uv$UQ1dLw;{5 z2QA3qBU2im+@XOvCC_IaP!>kF#@S=n6gRp)uJy69UVj*Gns^p-!a`aa4(OgVx`>-v znzQH1=2BQ#Gl$wNxJjXClsCGFoso!mNnJlO=YDc$S}E`(NE>#c`?{ePFKE9>`{3t^%P_`eh+U!l8DLXZUMu%mV$u}Nf%PP?jSHn~fja%OZIE(&%UFZ%~l89gD9 zd|mK(M|5evaVLvQD31^E8kdg^;<<3I*5F=1-m!dx+WJa(z}Z-5q*p5=m+`y@F%xyI zGXmyRV}=Zde&jaZTkR#%d%i&6(r{6d{It*iKBIY2sk~-$<49BPv1D@t(^UWxgzi*& zd4UV=@s(k>h15~R<-~}DmQf}S9u6Bs(MDvR?qO4)I@K#{eRuQE9AnvsZoc7JEgg}| zt*ZF=bKCUlt4Z#!uY9ECArNkM5-3N-)TKruPhrXVx$hGE5VLT5`cZYG_{uwOUII;D zdG!y!aRMGB{M4B;w#8?sV~4yg=I_I^c+QApN8EOYa8StK2u)W$g*~qINjvD`-XlLn zR-%+<>n$%_m-)E6p(Jnp<X zQ;W0^i1uJ6bDsT{D`LHPiJl>X;@xYyQTyOe95N+S(BX19FCeHIj*$8`27Gc#BUUts zVBwt3q}xYncW1m%OP40*D<==`s&B~^FfOe|*NytKFOkYEuYVrYc8N7Pa~kBo{w!sI zk0>$X!O(0HwhnptT5H0`WR*S}WKAS)g?GV;?2L0nx9s;J*B2*@5`u<}NC{Dl62EkM zVfMldEvS*WLOtU}C8iIuNj;_ye-@#Sa=E$WgaGV$kF@DN9l5-f9t={)+kmecVhqG| zoUa7wryxEBV2hEGv2?z@BL$}*5|aD_9aKdM8Qp~?&7k=BcE2{%Ewlpf=^c`1Bd^mH zT7T!I=qMAf5jJ?A4Z^^|9FfoI`8XJz6cb3EM{KiZh32DJclFcIJsCaXs=npI1bmtt zRp47o4rdVMzHVcUr;@@#0{8g$H70JKPc9Dn`e!y@%RyCwk%xp{K*GJq{myyk($nH5 zyx8z4^a>NH!!Y2huy%=0TzUDYKkM`H6U8~bbvKd2vWRdH;UkR1*!D@D<*pib*R_A{ zU*LPQt5|_@RY8tBQEJ2KLFZDJ5hQ9R-|IH`2R%{#I}RwHYz5Hc;-FwD!&T>1&E>FW{dum`!Yar6RX^S1RV)PcJisPj2EP<7i%8a$+&F05@}(4N#gzzHv4N{)Q5+g~X8$-c{uG;$f0o*Yjd{bQmaKpU%ON1*euG4V+n>hnyiB2g8PJ-%w41RVgt%> zL}Fs8TTGB0%Zdoc$xLs|JCo`h{3zO1Gbl!kOsgd?*;*!AcO z6%-<7?!}$ch+B4qk2;n+{lrw{thF27%m;m2{HKb2zqihyki7da!a+A! zSf8!#m@^k9pMs@8*qo$ewG#B%Rs5lSds8aaV6Qy@1wT|~7a3D+sNaTHUpjiSFMYVA zY2E1}ymYc=xm??#_3E>}43#p&n^xj)GNwt&9J-!i1QZsh6`$pT6qYPrN4T(U&8@7Q z$N9gJAC$v%6@S!*xtAe;RX@tcuE zOB(C^9qx@-*hN|I4OJ=;cWeCLJwnstT>f(Q!f_D(LxQ`a6xSYDSR?WnOTjt)C_JN} z25~czK*N0>Kz+Y$jrvOXk@>Ln5#C@>kxg^_!w`eq(d&&At35<0JBtNnjOg$?coHXZ zSYcxz_t||!bbKz%h@ZpVFImT3=jB9x!y(mf!F(sP0eD<^-o~4Le=fJVL`0tHQL z_@}v2ZqX@x?B@0v?dQ7 zH1nuv?_8vg+^&jTNyXPYxcY(}FGMQB_lI}0l3|!?ApSZ+pZj4?AhVV5t$oPdNn=7) z7H(1Zlgdl~AO-(*vjA5(Pu~iutyL2lSN}Vr8RiMe?2EU7EA2WerHaES1p!~G)oOhm zj9jeF+y>-I2hBU7Oem3fEmmZ*09ZzAMIa)7NYQD%muFy}MAsd9oGgOXjLlCw>VRY4 zpU{1H?t7qU$G2Eji|Lj3SbJ4NVu8d71{`p3BVUBLGkB(7WMZN(oQ;fwRf~9<#N=26 zPWggcqX4L!YAx`zyflV$s-1QrQT=LiV&48 zuPsf93`Q|YJsdjS6CdxAgRqEAELlIn%&F*zfRKpr2YnK*&qv2GPm39xvqjmOeu&z) z5qzU6-Wp_@-J^qH+PEcjt?c4&zFwLbwY0uoABRnJB902lhP)=d%&xfO=yLehM1i^P zP~^wLX2SIxgFC~=__=w8KhF09%D!==(&2-^DA01;lgh3X3l6Q$*X~22{%6D=0h+Ds z8NwT1x~UjdvHfv6kl&z$D( zk-iZtdARy9O7tND2+Za!&t^eGe)GPSIaz;+wIMY-YZxVy-SqDPzHZ#qF3qps^crxj z*_Q3KH%#We4nHUI8~Lr^FiJrpDqg+D=K(fFRdDQEZ;f}b1j?WyB#f4dijU1yyxh6urmKPWC+`Da{Ugz?B2P(#-Dl6y ztWRS$A9;LyZ^A~(>N-g?$>%4`fTgU@`_UrYHY7*3Hnt_D6a1IaWC4l&v#8y0#!sHo z_+?SeA#dUM1O9Ypk_$dH%iTvkcNg;1bp9CLiRlgu$GU=l6O$(uVjM^_Sti2e9MYH-l4g2{&HTf!}=bVFMD2}#_LU)n}@MCeyK(_h<`sMNU9x%)9e z$|AE~ULm>_Y&O@%GdL(2vTr_Bonmlge7WAg_ASSiRwt(1M(LYVbwz-tFL2A>EJT8= zKiW3Ar4EX=oiemmE}fg)fZc_&W1B_IZ6)^YDFRrQnW5(iraj!h9!bz^t+|^UhVpOx z@ldg~zcAT9#~w2jD}&!Khhxo_oFoJubi%QRv(GLE`DJbXR5bwbJ!>ECx6^Z@DE65@ zD*fozA;t2kFO)QMZnI?r#b4|3Lk7h-45OoVzF&ACMc74X6$UuVtAesLDBg~Qat7&z z^@3B!(+~ORCyulYuiI} z_2zSKUlz@ImY&JD!HHip&ruV%D;(U)XFF6kVSnxBWY$x=*Lul1Uu#7}!;xM!(+2H| zx227^zJ7qu1h&`A1-VFH_ycr*TZz=MOH@ztCCGn{JIjz&z{O4#OW04a+%}U_aAY6SYNuipJSb(%Mxb`SibOeYxd4lb{(l5V_~bgGD6BRG&) zSOGFlNGdt!3X-(l-=+o%*469#MT|0xlG|p2eVy$L-?!E_ERVulL0?9Qp?x<;y2S;q zOLZgEx_bVU^Yn`lbMm;+kLepu9zX~Fow&f7vcJi=zI_Oay9gj{-b9+8#i}bKD4DlT zPo1sjsJAr3&CygMLazD&J;|yH@h^JWPA6~JLUHRHiuy}UE;22HYXqNa-uCkJ9*6LS z18Uw0(fXeVy&29)G4+|y*JH4Jb<$LnbeDv^Muz+586F8y;QlUi9!F;ju^!Lx*WCq) zE9O{NRAf(~bWe9noNDHvq_lm~a9^d7T3(v`49*~iNt=-zTQ<9wFzadEn4{im#=$Ge zscC5&h_%#;!{4X-(_0wE6gOXTY5VfN%bJ~UzvP|{&MTkq=#gJr)cwE{^!b?AX!(fT z`#9sm$WRS?DYy5$3pGU3bShTyBY^whV1V<6R^T>sT;;w4c%x}E8XmY~lvGBN?L5#% zJv`U4h6@Zu?#dqj2re}u$PKH&q!_#&w72>GH2yogzGCJBIN9ylsW5g=!L#pfH6d8q<%)(=0^+D5ea@GN^bi`9Bje|psTkPUA|cC)LSAE}a?1E?Bi*To z>AvlP_$AiOU!zK#%a zdC8GR>0n1Tb)$z>FW(yew6qa7jv(5t>vlSR_i3-b|GP?^zU$7H>FLM1pLeN6giWi2 z)o8d&1nuM)sElCX*@?VRC7p9wvS=ZLkT`T3ZwZ|Ex7mRrgZ>LfB&HQ+OaHIHSWYEL z^lXnc6NZ%vHTZrd*YvEaUNjxSHX%A@nry)!H7NHZSPWEo`ShX6jMWvvw%0 zuU9Wg$2)Govn;@Crd!m+DsTK6nDY}5P5=s~ruv|S9sBfR%+6FDnoMFi77{Hy7~tY8 zyz4Xi*fJXHBK19qz_H&Mmp^5AbLB0+`^PT|xmEX*`RNs@kIGjVP<8p)TcaD``JGSP;~0cuFdw znn`^w7xs~s=B%32KK&O>sJt$8s~dW_(BLf`Jxr-CoJ}{iawZuQnPO|aSnV7(dpRw7Idedhb_5}w%T__Fgkap+{8oqrk zPsO>W<*Mb1Po2&*5UH+hrF}9GC@T)5_RS>+M)ME*h?OZ@VB@($U40+Mc5@cjTx6zB z9-VYH+1r*hOP?ParS%OoXGo_!+GP3Wck3)|GZ&UC1UV1u*vdu1P({ znxUyiApfrek~C&e6}N&ge8Q>2(7nZ2_^Wmi>J-Eg-)b;X-Y{ z*0>rU(%~~lv{DN+CuPhza;;g{?l~&5J!YKq7)xcgE=*T9tT~7_^kHp<%9-=vvWapE zK!3A+O}Uiz=LwcxJ@{c@6tOU+$6`0ye}es%2JMx%XzIuLiuUe>T0=UPWh)}o>>mX>3>IT7>$2&h6ioL;Lggi=)Hs=JLr$Jmek*iOR=TZ>U~0{)$QPf@Za2gJ zf$(%GFO#J$hyxQ<#d0P;CJ60rp9%}>7*y*=HQSi5xy|VPlGQ&Q>dl{??5O*)ez#q( zarso>oi?b>qnxDQ{O8iUz5!>VVCdtV%}g-xW-zqhii>yM z2J(tT!~(Doncn=M3>o&_vZfE20F8oXWJxTlPdg~8^ei^( zeFmUnh}3qJLcWXsHU~bsKs@Plk}*!m9aWX!wJxG;te3Y@C?9REDE8^1le&sVPAXe7 zPxEDi6?`1LS`kg1(Jl1yt!!#y!55qu-K2~Ti5>w;b{II^*8Zdfzu91NMkoPM2*<{GA?_K_waVqS?CG@w=@0`ECs$+6}c)B33uGE;I9;cp{z&`!h zr_U^RwpVM!t44uSzxFE5vcJ8`x3GN2RN6H{cl%vd)}p@Y64`Pc>Z&7EmLoDFNFj9A z4IZRR5mm&fk_|{Xe5Z{M1HlRm1oAE0v39VJRG*>z@-97=uQJd+klzsXzjDK3Z((n+ zUcLY4!~c8Q{{p7}zX0+_X480x(?chpNs}Il-K0uGR*gZEzFmFb@d>17S{$Dk?tF#p zJ@;BCGVDvKz(CBH@D*Xj&J7>skOddRm8qt!(oNq@)*{ni-~asQb)OCs9+SKKq5mzx zMcX=t4{v616x;AtUeoBCvz1-gl}@Mb1#m!k>{%K zVb(+<$cy*w=RRj*bt>d+DC#eCExllh+#kM#`nLonZ2{xP;=B#_{sd&R0v6$syYqjdMX z+jFckou00>TJL$GZwHG(b|ZAdRRb-`qz>$5VU;$(I}N;~gl52>DGZP;E75 z%!}374Hq6>L-IeO@DKcy`hruDi<6PfDLz>=Yj28bD+10b9LLp2`I=igI`L%}&3&1& zT)l|T_Uq83ith94I=CiDyEZ4J*}D8$PRsYs-UG8x)k!IL;{y?*&O^i0jcD{J#i$Oy zcj73<p zdVX+Dl{$IwprMOf5Ftj zeno2~GQHb8;i9^P*(f?;uvgCfh<7ThR(CU(E=;kM8=P}V?@w%}Ct3P|C^%rMh__ z^mH+G&54ri^ov^t&nZdI!`CrV^6O--Fa+Y)TdLQLEmGSjc0F|TK*RDbe~4{R2;x#j z)5=WRN>-`x2A2(FZfh>su^L^=1IN6cHKpkr3i^Ld)m(dU%CP(%8HUUM6q#ppC8>OW zlv!MTVUogTRKUss$^LV^Y7 zRZwJ++yE2KvEa=SD5kjChx>GD>RyB~bqUdW)IrFvZTP&cuQLx?(xON^}?_b4kHmet)_5J8T4osVieHUp4xq&caZ zqcRz&0UIE&?z>z?WSkrv8VTBw>#y}x zW^EB3O?Q1B80375pE9uhGE-smEtmmp8X>pIB43}Zy6XHhhugmaIAcZ&U8fbg3i`;v z+mqUkntmNYCrB`wGTtS*_u)_v4+X)LTdZ>laZbp7=l#==Sh2c+tl^ipF|xT#jOIlb zJ~twz-=|D7zCTt;XWm!~dp*gJna+(+#ZYF-@cR$eE|})L3h2x6ZzC`}H?2WhUj|2@(Lb`PikHW~+@evMK)D#-klr%W%mb@EPP@P>h9)G=MTEd|st zCbN)RUkmtQj&gEtM3F8swkf6xqG1c03TTO?x@7$>cR->W$4ORewE6T~(C9Rj1WiJa zc6kO4juP|4+L$?G>@Sven8aVsIG^pFxA_eVB92rNzTL1Se%*|BGZP8>ouz z?q7E1!30PVuVH?TTq^HcBMHCy0@Fokl%tfxz%%x<0x8-Si3;u5|yISyX(qt0q}PW!6)+kL4raP;hv+r88lwIJnG+ zl56%IrC%Un}KJCkPap>&ZWwWW-!2-9{ z24(-<;{68Fo`VM8XnBchK62ri^FXU_cW?cXEx&N4=i&Ijj(01q7%*_~z>yikNN z{&6-OoD_ysQcA(#{`Vm!!Wwl*(9l-wpPX)2gSn-Y8R%7a&gTK0>@qfcMFZ72BTOIt zIv(aKUVV2>I>rcPYn4TH7ccOo)TW*WRyufWeanUSex08GpBy$us!@X|8mWV?1|yGd zTED3-CImI4QX%K6ndnvf`SY>Y^gNs@=jlH`+xr^Fu0eSrPjS^j(@Fw80ntmbbvp-) zI=w}1eupN^NULq&_^-8bJIfZMw!?K>XJ-oSk;KnD_E&oNr~Rv=fkUtuZOTx=?Tu%L z!%&dSxKmn2!a^D5rL6L8sL8vFvQnLtNQE4U`ST@`w~`O}4(G_UmTpStvdY%((-#;pZmrH(z%SPb1xSc_ad1jVSa6F|9l7u8 zE>yDA@7?l6mDY6Dck`k3G-8AvcBcDilxh#!3lPofD@ z4Qxqi-6i@!bd+}KJ7u6zD6smXujL-&u@-NbNui?a7x`2ubUemNr?mpT?>}$ze2HA^ zuUL60SlgK@^na?YRIom9`GlIPyQ0AJRI%at*!9+#I$3P)kQ@doTCzIs=5Yj7o2NKZ=)M)c7gC_r+#X7!1u6Drk>w zZu$$fOdVR7o?+6k1PdiCwdJL9^u`13^E!#7dq zdSc=At`Dj6S2FVaSXlJAQ_adbn+<^kT060^+`>I%sp3K-P4nxrv90&`6$2GjZJYDc z+sHk_+rWHMkrY;6^|pp)-3=#; zh#52VvBm9~I2>aYCdZ-lckH>&ErFzK|wRr^!jL*q!<*pl241{VNrXt~=TaD~< z`afHn_tc>#%=OcDw>P90|Cg0#VMkIP$g2CW9*AS`Ww!7nRECS2W3gE9^v5h>#cK3# z3wW5F;Yzf^M9Eh@rX{=M?_`_$>yC|c;#pHfD6KzQ6jNhjeZ?=(RUtzhaG%^0xUn_h zaNsG0t{A>7y zMY2i|R4?kIzUA>0^Eu%2^5iDd>7(hn{3DtfYGHPok63TXcJm-TLuG@hWp2q9{2FU# z-Ls{v$~)zlxosM$-QW4^@!qOmYgtIE`kcanmS+eF{>YJ@D(4+xozG= zN66O?6v&45Z^xvxXoaI)y?<^|KW-2<8Q?_%gjm-E)z(NlTz{y{D44pqEU@w7@!95* z27s@1@5s=jL1EPg^b}Vy#w;i+9`khuaR@o)x8@8vNx$3@I zeoP`iA+g6PjX4Dx*vQST0`sRD(*HLGC3filYf#c?|8IlRx5|d7|Au_$3^VtX;^jy} zh@ZQ4Gq>W5e!<64+s6)V4Kvr2K1%20Tg`4kEhGRysK(n;{f_bl(yA<-d%wda;-foL z(gRj&bzFUTG)&o*k??s_SddkvvH4d`v-#e#6c_BmC*A2puc`dq##?aD5i`=1^Anvl+T>0;X@ja#dV2^NshVM6k|z%T zzFnzEd2OiJsqEb4A1hB}wA#@g#=~=Mi+dbPf8BQOiynzY6Ietr-n`Pw**YiGoHIxX zrdqgnqm@Z_k|fZYx^zk0n(N}bujXEivdpEQ3g_$GxVq5uk4>kG9@^V?W`T)4b;fdk z1tygEw1j??&8gZ!fpX&E9q(n5F8`OQG_j8^tE3Vxu+3~ZJ>5pIbp2@{LhN-#)9g0} z!!%dBOwGT$0Pvz0;pg1Potf}(Belm?f;QSOfMZ+!Fo2ZE%$%rMI|01**py)JxF#2ARJ{KLkKL5SqR29Q+il){ljmJSFq3wIal2nZ~ z*OFm+=kF{^MYgrqKqBxESM?!AfEGy?>fPvp3rLkSyC9JsD~QZw34 zTeE@O3fqg}{_TE83t^`I>MKPyQnkC5xtn^dT3r&zR}eF;IeBAVqP!K7fcM1J?2992-z!>T`>$SjvS)RIOcJ0jvxajnbq0OM{fEuxbx$@JMu zOyF+mtDA0mX;3M3-{n53hf{F*>3f+ zfO73jJr+RLZG}s<2p_;0gr&e04HXjS3`U=!{Tk@7-Qo~HK2RATi+Re5=7$8oohKMLS{PV^rtFP70epv7=l zGsVp_&~K>{70;trX@kaPD8@9#I;{oE^<0dXGaB%x!|J>vBt<+dDfF@GtGMVIWKpPD zH#iiX`zrB7$$ZYEv(elS<$^l!ERVBZrc*a^lj4gYJvRl*drj&?tG)^dJ*IVE&lbK< zJOc51avW?86bT)sw_^9E2@*mj_|dYn+ia~IojmGqGc4Klh-`?O(c-^1e-%M)7^R=~ zbGn?wM+cTRZv3uasvv6nLQHmrMsWIvGUL%TCUEf22f{Hco4k`vGF)D4EiR=kSRFo7 z@+tO=Vphrq&@aH7OcF|=Tp&~)RFrd06B=l7=GpxbftUZv)DOyrH?7}qzs`TL0)H|i zrk*$#pHbTKTMHnvMpqG=#^S*q(hH4_-h0vth|Mc1h&Y<nr>NZ#7aC`7RMY_O8OtqkShB&+NeE+MpQyz=M@Mud04L)@b zDxx+Jj4;XHa9;ijMo4>uJtl`{VEvsHhPR)L>N|U?Wt#p5CFZh9tayj|A(P#tk?KYiWlM+ zNO2{&@?xvr-7z46(}c$YyaW**J>kcc!Kuri9%ZeIZEx|7%f`oV?h1OS2a>Nf0>+4eEAEYlo4!y z)DjDL+isvfmNhyDe<%Uc7+U3X8YvMr1sv+M`hPbj`<`4!akUOc{f9_vR6f5E(|T#u zwc@vPNup3<5fZeUV5g9!8naTVKM{T8Pw@wcGs`<;(uT>!%2Xe^lws6TJNfCE5YOiG zV4Sr^S$JBw+1S{5W=WSFDIEmMFyVqoV7X3A33)G6mRTcJ(mWRuGVR;zP3e9&-))t5 zD>mfwQ!dCb4*JS7Kb@NMQ~7MPo-4aPvqevnk`*_kD`Dok-s1vJT5fZ8(8qH2{V>*a z02_{cH>;Ni&2{p079O8NI*N^ne1C1!`?A3@)fMec)j=S=?AG`J$HK-?5NOPWDu>3` z&0=IG{AXo5H0&WcD*Gl@wvZF4JO+V#AM&lFiQkaxD@szH5*hY(z7|816skuo*N$jk z+fE+?yN30s|5ps{Q|jST4kX_2lZA^(Yv{>|zMvOBJAL?Vgn?FS`4ucE>O(KmZGD`1 zEuju#(?cv(0%#Z=eK~@}#mA=)t5CcX?=_;xwpiBG6v_DyQ4+QnpeL!I2Uk6Fki>CR1^NZHI$13LsUxkwG7`Ma#AaAp*58sE)nr>Dj3T*l&TK z$J;P>pXIqHSF^`n`TgM{GgjqL9hV6@jP^H%Nx6PjxoYvbl zRh1=7(C2pfE3S=_Z|TbH_q^cNzE?>KLY_fu#*+ekzg}njcaZ#4QAr8v{y2oaRSjoo zeelUJ-8;;sWp>3re`HUmQ4hyAT25p}i~J(!1Pa_&2%H?@c16sT?v6V%pfWpg;XekC zSsj*u!;OmjeQy?j`P4kr9A0GVp7d;Rq%uc9rd*u)^rOyr@YP5+;<_)UZ3(Gl!w&4g ze1zz+-{+z#!X#li|24KX0;ei(|J!3~MDiCYmWNrflm<}8A`}=J=8=e2W`FhlgBxaQ zml+A?J_tl;1ygbUvIGU&dx%08Uk!e@lCi@rn-odV0sy$nU8(aEj3)Z8&3O zz4PiP_lR%@j!pTG+dm5N+T0YrsS!a>ZksRYLB?;79@OH+Bf~qrwA!T2(oKExTqy<~MV)T@)s6>a9v!6E zlW%e26I3~xs9LyadDvXijb#pcTWzDX^mEtUpgg->0X%(V>&!_v4KBy`DES6e$;{|= zM=eM2;&SFzTo=ClC;TL=$fW+4lc*oJx^KPIi1X6lDgt;%MhgpwubHO z;Um!L8rql@b`le}6M44WHZ!=T*tV%mTB zwnTX-TyK(jq0x90dSkT5TKxLqJIn>~2~Ji*RD#e^knB^bFHEOZf1^yY!J+rJ)HG+wHA30a5?lKt*qs)gjG}I{6nyya>Y$ zX)vH1u$&(>JjpNUD@Sy)wm)d(SG*e@v))2B97It_fvU{ zN$*kJXBk1DHW{7=p$~xf~KdeZZQS`zCWC%g?6zaOY(Crjh+MJBAg5eH2`2%ZsF7HW zGIDCKZGy6kK*VJ9XYyxJOwCn~*fCOC8kA|XT_Yb145Jqwn^{g*q&lOpe^4U+Sm&Sm zlkwkktRp?DDJH1;7hAXl96oG|0RX-(qJ;js-;)S|Mh{mHFC2D@rWeTsp3a)`k)2tE zv$tBvg$P(pa)~2c$4lii_}q91*RIyNmxt`${&U-{%QE`&55Dr`-=4;SrrR*vA!UKy zTQq2Rdqs=SuL$XQKL5F!r-G~tD31J4)N@UfDc7^|Oo*liW0@fjtmykj*|nL2qEW?N z{lGy75_6pqy>fYZzi<780*d;rAY4cOM{({Rtlw@a*HC>{+tLWP--o^|b@~v5y z3tUb10s_?Mnm>K4vla;#xp#9uT*`A^bID~0{HeQUw2gftC$X$EB(ygknJ#j8x2`0# zEWU7mo!r`X=uF_&F4*zYtlWWz$HT8uAf(Ib(zRpuy|42X__%1G`T7o^9ts(q7@)14 zE2q=4#K3YK<8Z(RUX=i@9M41}iWX2Ydy6X&e1KHor561`PCGF0=u8)3; z-}miiW(0F*#QldO5fJCThZY9OkHW7Zz>9~jCaMJ%_e@_8X*T$iP#qvva&R>wUK-dA zPZ`EX+LEieLl+#&Ox&jzie~-86Y`VdVR37j@|BBREq8`qz6mAQ2JxgGBJXh$`Qie| z$ri+chYw!E8LMu5%S;yXanQq$a`+y2erwqHG}&u;O1bb?GYI8KuMs6bS_*ExUY_OD zBWu2E6pdS=G_WK!C19ofGWFZdThP&3a!tje-asan?xPXV6DkTltmJ_N*81%3-E@2l?Z`@x7QdUW=gaN*D9E{XA}VJNJ++&#R4njbBvwU)ZhX zs!LSN(GWSiJuPrp0>GPFXSXzU9RoE&2)t1qnCM^h@4+!lr>Exr88vUCL1?{;3Nyb1 z`zq(szw^|ZY5UmyBjY!Sab*i$_*78@#&GXqOy*dL4yRc*I7gOctQ`a}#9gja5~U2j zoXkm>Sx-2Xu%-x63XE1<*{SWQzqX%EGLrmM3Pb-@22DfzA5n&l@trK*_OV=!4KfZ-6x&x!k zfZw(nZA+me1#CLn-~U03iZL0m1iGoG3HYv`sEx2P+6eF@R^A#MqVkd*mSZTtg+-m( zFY{WS_-Lk{zUnm`%pE+QS23e+)3~i(Q)bp-66|sxR#*tdJOT-%<%0##d5GSlcw9X)_bMZB8)rpFXSE!xMm zyXMuAt%$xFI5Y%t^*Le+1d~L;yf^=d?}6WH{6Hanjbv<7;Jv}o>xBfSG}`>#o8TX9 z#Y@}BTI>SgU}(TNobNA0Ma(%G?X#V`f<*{%JEsyS{4zVG>9+|FKN70wLpoP8=|dIZ z2cj&@l?p|Wwv%!kd1FJhJUG#>)_rF&AG+1o|L|b;HKTp)Xp;3VCVuuTptXfzHZanJ%V15Y>t_Xl zo3hyLfqg-MNK?hJXy$hYlKFxi`v#@Ube&6I`K1bC-)S748Fc-ON=c#R_ap0-8;^^;mN2b6-)*BXx2L2rmf{;0roaiCb3_J2vIHxDwi zD{b*i)vqD7wFJ6XD@H4a>%lYVj=SJyQtPwydB^_^DC9(3^>c=fPBp>$2vkXF8vmbc(wTKXk_^-RQJ{G^w|B%8D?+O}j8o%_Pi1hod+aT2Cq_mp4OjFG@ zc3k_Vkw!a*rZ&B=bZkRI|C5$Ku}uNat*&V#_+1^TWkZ^fzCG}NEG#+^XDgQ8G`Ll> zHp+R!t*y9Klgo_Ivz^(|<0GakWe(z?-f;O9kMaZOj8DJNb-Tsw6dtSQT%_pSzs03w z|DKXaoOc}}01~Yj-}}*8gSvryMCg%*U(W%~XcU;Iqno(SH$we(zId{IvbDj_X*AWa zc0kCO)U1))f0UH2KZb|sjZIlk5MP6L60ylSXTr((506BnyiOlnIu97}qE!IFqtBt6 zf^(BTjfcxn=E~X?QrBN`&n!(M4jEh>!QkT2wvJnR57#y~?3sbCy02QwxmSzBZ+y6v zc>Xwk-Rh>i?siRDFZ)QE%N4g;q8Y(4{qd~p-P;uqxTYzAL4Hpw5rixuuYf3YzF-JM zG~mP{%CgL{3UB_Q4oz6bbA*u5^jSI6=3r0m@o|TH;_E3}=BMsbX-9H)T>Q0;+eg_w zo7B4vZ!BjUGx|K5Eg><2E&-1XU2%tbC?fH~dnhjOZ$IFS1%et$D5KwzWT*ZD5xG0d zw=!QQO|^A3JR|PRwEv^wOCvxA%vYnpE`jw^8dJ7&>15vV#&!LcSMGG==oLnS_)bi9 zyr7UKP1gvH^R}=9Vu^04(925{SyDBG-{oXPns$Pw}ysy=gHZyqBO`Yhlk zoFU>V83!t|N4lLhbT+WRxotak1rxhUbUUeE`ql|<5TROd>u=OWi2?cU_2@V3!k3QQ z<<0p=`TUcmTFuU}7gad>?B?X=wm{2051BN3dLYpEC=4fU;MYpzNbhGjbOf{ z-?=XBv!Rn3rGGfU6w$wp!xTAjKBW8N;B5b0@6M7>tA^RtO|UNCq4|bAZ?Qyi}DDQM56? zrN;7~x-`_qPD1#!F6gh^!b5%&e8j?PC%>;141JX6iS$|#bosLMQfBV~V-^447S6b9 zm98%veWYo>kLJ8MRWB8z?%{W*QIBgHQeE6adxjo|OR8(_C6 z2}F$O{(}_z73uxvl;=kn4p7mZ0U^B;=O{shEHAmW}JOg%EWyxBi}WVbL!XiC2dYpKQ|A@ zv>x10-T(x6U=H*{57H*_fi>6zGxus`_{Fyt-`@7eIyQlF;#;zNd23!<$wSuyPnXg$ zR9#!i%VJfDh|Rp}zsiTS^}=IWYs4|%AwBAUgc`t8f`KI-&W&*+x#Bv8KoFwNf1U^% zUv`kLmY>qoa(u8QQ%#K;?x0vOmM`AgIo-kCj$6{dyz*c&0ZfiZZju;w+X3#sa$u_> z?{1`))6O!fo+pBq6bjG71e(tD?7ehBMSis>dwY|)cDYwA__YsK>d4%`d*;@5!1qHK z#iQd#ALu+hiz1{YA|&+ohU1eDSg+XRGDnVfr75}oRK}E1jwrj4?Fxp}Xx1tfzQDUp zhs|1p#0S0nNX#n86|)nO+BkNf6I{#%LUTqq*BTnDTJ6!G7ExwtEXsS|hzIvQ@7~9> z@LB$?P$DD3tJxkecy-met9hv$Up3NBdYouLG$)fkm5sVQix$p7bbO^v{Aj0 zKVL*#kDT2xTkmg>L?NbS>Fp2?fn*PTp!Hx!UA;m#M!kwOFbW%q()yD9Px)K}+rXS= zprhC4#1*5(fuFYyZ|2_zLJm`Nb`lB5(Zfw5c6trslm=fG2V*R_mcH2pJZT@ot>tMtB@$ zV?pPFs}PT1yCJa^0@wd}FX07Ycnb|g$?n+MyKr%nzKM)V+ahym&$Z%Jh7r<2snUvY8unkx^RKVQv!^tw7{>7yNd1<#!_7o5H_s+6g7mdd(Jtu4RXijdORi#|JqJvumX zH6RF7+jQCnezG$}Skl!p(C1!%ZF`kWzYXUx)4)s~@FP+t|^4oMzjt z-A8_8vA6OA8)V^3FE4#W0RGq0u4Bt}5UAg>x9iP)`R>N(>to|wCt(IGy#GKg1ft0 z;}A3iOK_Lq?(Ptr#=UXZ#@%j_E$6r2-uIpJ$Gz|S9`F@KcTv51*6Ov!9Am7S;SXFL z{>hHy7TaKwJKOXU?;dj}z@^ZMXm6{EmHxFz{b3b_8pj>Hw)B-K;YcO(%o?5v19Ac3 z`|Nxy*lcl66t81YDWlzZc2xz{jIh6GjD6pUa`#k+rkcT{Eq-O?AmXW>aV=l<+nS@L zu`=0ahcG_J51L6Q`*mT^_GiDlEzywB_9)|zruSAXdlyIAyF2P{B{)}?y;s#AT8LtE zRvSe>?T&YY8~e4-4q{iH46}g(Ec#zENcEd?+cqx$J_OH1iuW<*PCct zQ(m}Hj*t1|W~bbh7ZlFtM~?1?FliTGX7Rnbof5WP3j6AIZSBsSYUgsPTk>o}Ey@oe~Qz_H^Yx( z)mP@J_xy?pa#v;vEC{;0EU!)K2 zpRD`Hf2IWcvW3k(_PEI8&&mYv;eYP_+XLRiI*GsA(iyB?G^itbdw%=-(8WMhESIA% zZI3D~jl*Q^SBVaC1o#_ZoYYl$*K@l|-={j3bY0Ghxaj?2lm|49_W11=pi7xjk#;=i zGRd9;Y>x_`{mZB(u|!B3=GJIqje|04$G=N>^g$L$H1zeGqNjrMkKcH7&bo?TL9kTa zvbYnl$2!S7@t1f#V5jPmD=v19iP%_8W^QIr8lhMCyubc3%+4iI4XyRUz7WlTiw+UX zRR|29`j1x;G{LiavUmeORA_qVWevQs$01$x<=lS^ z*Iy#{x7~kx;Qtxme?82iQ)Bin@FAXyil_CR>yB3u)afG!5s51q`L?CY!=_C)RAAwE z!V|&yEw6b}JsJ12DPrZaVoU-Znws09hqH#Fft3rdcUl^WcUNj{gIUZlnwFbXw-27T z1}*uh8I6?>XDv-vC8G$1450DQ$U7P8ok*{ngDEGH&pM88*jskU@?asgAziK2><# zCHk`p6f*cB;0a&fHXZ+14-Z%%Ad4&8UQ8|g@ltM`n)yCYN9o%C*1n~2t?2~AYqsDG z8Q`-PzdO0=9$KeH5pX%Oem3Opcy@{5WjB8Z7y z4m^GN5wT*ub*+k-5$V+r3bXaa*M)OeW6}IZf>mD7UVJZ! zuoz77Us`H}UurL2F?|4~SEaF83HW?4QEV(t|w<$i;2 zf(S}y$$L59{faNCzS|?AZH-0(`VMa{;S~6msl#jR!A(%O){OvA(_=_o?`tD01vFfA z9dF`A?~cIo3z$|~tc(5X@?YfBb8O&V%rlg{;Zj!q#V_-e@}W6)}PI223+BBt%G)bYRVT=KYaYSQ{Zn#i~jhjL(t}mlk8s4nh776mj3AKo3aM? zY-q3UaMPrai_pIx%c;|?Q&KX{^5F)`=P$wcHvg8GxutQ6*~JodB<^zEGjQQ5|Hm8o zjpiGn?GWq=zq`1l8H{R}^2- zi}fnbp75zOZKZf z%;hbwUB^v9mJ0PBL`aU%tygx+5;b;m_<{_e^=GpxR2HQ?CRgxplsCOty)ugyF`S5r zPp^sRYDxBZ>d!iB|Cw*V17mha@vf*qjW)0X1_a#KxGnStd8|%XD(4NN-a8NgM}ee3 zyHVeGwE+W&0UQz<_>pff!vp~XJ}5mM1MGazi&RH~3|ugsOyHj%Px4>W$$#Gcw@F5a zF#Qb^6M8U0tISN#c`)mf)@IgGdlPQ^4Jmzn@)!c|`4O|KtE;PO7HS%D&>lTGUm!x6 zN=OlLt4xg(E-tQm99Gl|(0TL=VDX9{0%S_r7LB;=HcfQXnF1qB5-esA#VWb?|snQ`EG9}?)M@6%BF zoA>VaJ~*&OVe|bH4m&7V*3ZxH_Hs2`#U13dmXqp<{QP-Y@|6%ioQ^7fou!HM z(pN-uVjjOvL>)2U_i5`>jOoGK$M;tl0^|Us?7+r45Ay9FUVzqQ|A24bkig)J*;zFP zAy9+OY8M&_Nor{+3pAb*h-Xan3oB-FF%c1uvk7IDZy=}Tf|BNh_b%l{5NseaiG81} z({-vD8O%xX7{?kKD#rXM-ZA_TZ`BKqP0u2)}~E|Wx7Y5`m-o#T1~W; zTLOglyc_WX6F8fB-zSoE-LstuM0BFoU+|^2K*cYO-{pk^@>MCt7p?C;(oEfrcFY zE@_LfQ1i>z*O$cORPH9h5@<{V6FE_E zCu`c!;o)Je>sfw7124|Su*5==uYao^pw|($=S_@{hY)hfWXaELj|u|a&3n0o@PoW4 z0ujMrGH;E=Ou3KGlR_D9H^X+n=MCV4uy5Z=$F9Wi;Y!C?+X=T~tK8k)Lqu1LH5+gk zRJ{GsS#DFj?ysJq;7rf^KmQYs7cf$O3!osPYt#LhtU#w7BA!tZhQ24PZga2c4G@fT z6;T9tmtP$~j6E+J78x~aG3h}%&X46vfufJM*SjUEV`RXYZs^F-eeai=+%9BN`R8Y5 zT!6Z~Drg`u6sU#EWxe2jx+TZSfourh+T4e9A;Ztl%xn!*v=n%_Wt)U=onIJlxqx3Kn@lqfn1KsP&IMpM7h^z65R*$)MXy2Y= zR7x#V;`U|Md-C9*pch7lDO-45J!5*SV{w@wSQ1tBH0H@Hd~e@2HSQ|(vtg}%p=IQy ze_J|cBT73Yp&1#Wn%NaWq6GARpiCkq{m|RAkdQ)F`IMBnIN}s1;XI}x2?km5Ptp2E zP+k0G^j=g$L&Ic#k86Kit=9TFE(*DzAUP=)mu~dCkD@q~Rx=Say?rK&bJ6j%rj<f`YQMi;C(~Ih!3G8QI3i$)Ll`j|D|4cs1)EjUx6Yvs0R=n2tUQ zD@`WY=uTn$pfu_W{?3YJ2s9!yJKG58va4O8H-{Y*&tDTA^h#Sklgj{Ai1*LKCFs0 z&4Yv|ZnWfJ+SR@JhN&pY|-#D$bRi7TX{@Y6~zRYHKe7wX|OcKQZ+4H_W`6 zg1qJbSii7j*s@ z33#L5+x!{W1Gia@&w=JrE`jou(9s-k^QvD-=v3;Yo+q zl^H@J;UEyzc=K5g$NG86_NYHlYQ8y<^**vbxSN*i)M(DpO&;PSt053_zI@wj&*~_X z5*b}Xw|joEDy*EU;77*qFPwT$lETYunj0Mwrimsn!= z8SD9FnhELn0k3MXj2&>{MsQVFk#YbGd@bkZEnRpwO^Y{t18@QjU;LJX0}0i`wKP$+ z`DPPtK1>k{EG)ctez#X&+`!H6TFEs%J2yS8cKd9%nAfAa=AEOkKs-=V$st)_TUs|P5S`A_-#bL)rm)cfsJ&wX?4 zo9n5TdwYQk`#9`hBt~9m%3fzh)*idz50~LyN4Q=`HJ-aQ&6l0bdQvt7LRN%Atg&#)U?XyZht?oI|!DEYjmhS>7tKLy=xt=KTJOdgvyRktPhH!c{ zR=uBk+negX$-8(p2yX7cyE%?+xqUogzxF+=C_>H)zl8?VwRsA(FOHhwzYc4>ZfYJ5 zF&=<=Vt$yuZA+V8_nR$;{>--(_}nJL9_J5`%WkjR?v~5omfYRz)o_90-Ro}8y>8EQ zV=pHM1wvE=Jo+S2Ojoa*pWr0+fomAJh4=elH;LEnc2262A_v-{a`SQYFB+Rq={LPB zA;23jaWxoax763ycbI!Ad^;|C_bLDhXca%nSP!2B0j4n08;8_8WxOYTz^?A7_u~Un ze;)P|e+umTv6u=1TTCjn$8CEi9IzEeibVMT;&2;q!Y8jo?|1$QSA{k+zKE{ljp28+ zC)(>$9^t z=7INdM8mt&>{xt^!pR{537Ultin6r|k*;B>ir-C?@We7OnR3w#cH)F4Bt&eL#gNi@ zhdqo|nC>MmqTMdF=Q?-K*7jy#lNp;s7l`577!Ad{^{dprcNv@E)lR9&q4)J(4z43; z%vu67Gp?ugyY;YB8&KknSWbtP^RJ) zf8#eyc#T@Z?*6RVPT(XH$`KOJi6J&c(8q2Z)7>|M&jvo3&59C)0yloAnrf?ISj2`G zjH8mB#qmC4RLu!uCl|v6&0n0-Ab@_TYT$Y1e*q0Y9nujWsYeGb$C%55@^{@q#X@ko z#buR>q$l@ee2}gEg{sc98nj3@C&)OoG%D@88o01NpTPUqicxDCdSn+WU+5EWd}m<_ z|8fRUTK0k2DzX6#-&EHi)TQX%Sm$-SBBi)FbulTZJ~30{rAh2$0H*L(Q!U>S${~vR z;s_#QNhFw=ID z*i{02J{h5&wLacA86vv3ZNJDoAX^Q0c9v!SmUxUMiLS}fRrzr*)PH}P7|&dBCS@!Kd`PJoGnKuz1jEm-ma|KrZ;BWeGb7&g@{yJjZhdV{esN& zvhj_BLs5F>vkDKdn2|}ba*@zKzqv|FMr}M;}ZnYJ9p#uI_(N_7E=nHs929H% zddjH7!OT`@9A{PS}OJLxFp?bRMp$$~Jz})bwisDyu zJe)Do5+!cPjnbI(voLiTpT&?LlQO7#SBEfk^jqKhtR#p;$=4Ly zD8%6NKqBPv2v{_!RppT+sX?R+ft$J-w4iuIIZJM;x|FXko{XbD{duKZC0gsy9l-F3 z@R=pHo7U_PMe`3pE5tk0hY0d9^7!0#lV-k_JZY&zzMKUY>d0|M0CdQs81o$C#Y=*ik$JmdJINf;dpcyFV9J})y zqOz2Ts<3T}F24J@BbU?U!3t@HrD0!<(5fqkdxQn@oISnCoznCtXtoB2@cl<$9 zAuMNT#oBeK!&m>g-KRJsS_`cmK_}EnR%(1W1aYDz-*j`LoLMdELp+{Sw{5~38ImK(e z>Xd;KI(l;j^=A}ED{6QMjKKtgiXs)y4dvi+%X8Vs;+j_-j5MVUTtbt%%4LdX?N<;$ zpNijqi9{W?DF6rhArdjE zfmF{P0y=g*&Az+qSiH>YY%|q^77bKd8RD1!PG}^%keH5^B4%5$#o2)mJaU8AhWQP< z?Rg1UX+=Xe^TZyS1|RlYmd#(O+^!x3d+K?@lxK}Gk}MS`>F%sk3dw#oH1Rcl%o}1X(R8QJSfG+G=XEKhJp8(H-Iog=h>A z$wMG>r1R6IFi|CI|EfIe!U?RoB9E_#Pb`hUMnvS&&vq*O|BV{nAeO2FO-CAhnHlbbZ` zM4cMlY0U4J#}C-jjJ1fz+vM>hj2Q%h;_ygb9-M1jAIAs4R>lh~=jp$)B1^N$82M?_ zWku07nd&3T%0$UaI3`CZ?wGA*voM7$m|OudH0*~bnHu8uRjRhvMFgGcGNA<<$$cLZ z(|H7J7jTAAYT|rKM&jJ>iik-_;v*-d$Jib9T1Pm;j=Jh;%p5O90PCr) z4NXS5)j0bZNMc)K8ctNem_$s8_|heq{H)2Rh8oi~{0{0>vC(-;f+In@6hq!l%D|=_ z$XZ@FP>c+ggNx@8g+;~p zV4!Q`w{AloLf zXq+##U0T{4ByzbVLhszL=0;}?V2K1dQFimR5A8qw65eNJ?MIP8Ss$s{XFYH9-Gds-m5u?3v)cd04s2i}lc735f>jLN~GSCh}9~ zV+4MNC*|zl+YLdMFL<;R*uR^~M2S*8&Gmp-Ij9*aqO=D+`T0y&iylTHVlXNqQq1|G zD<<|q5GO&h&-vCw-pjm1Sw*(*^=&uI_u-gVS}%fXwB^-dENj)b5VW5S(ngU~C&U6p z6w3jG)Xg$*blNAmC7YhQ8*SXV`C-y*BV#MaO-vQR(ZxH@f$?3wRn5WAW<}_nTP<|9W zkT;dB?<6fj?1yDSCza_SxD0T?JHyChDpp5L`jsA=U zY{U|a?EAHJ8t7>|@`lmuv(v9yR^FBLQ~msz7Z`8NQ<~OOWmK!G0Nm9|&pz9WuV~-A zBXf_0RhC->Ej#zS5tf16*DUEpc-$E)!*$0u3NZW_`knD#8C-3uE{bOCHE@Ex;~eC# zag`U~!F4KF+fyBiVlh*-B9Rqz)eO{=l5oLhzrDx2-GS=@(XYRL|1t2N9PDUU}~jNUA?$)jphU=(O%jwD($o=h7@>*5I?AGsm40 z1(?sYgt?l1h2L30*7+hx={KqDg>HV$;r}~~Id}2BK84b7OrGCm3UxIDw^e4LeIvZz z*76pIll88dWdO1TA_E31J%%$w_%s+jT>Oa)AS3)E<-Sbkx} z98r}z)k8g(n%1oUExyc!Y(Oygb-= z5uO@ppz*m(dlhimvPLxoYF^s&Yo|-LbGWR&ehT^uC zsLU-#{XXV6auW0P?Q*I{TkMh&x+bj-$p+$Y7(9-tYTt*@KO;RM%||nyNn8TNh`{4gVP(S-5%3Tt`0-C3u7 zqFP@2-K$4prCyTtNOEkf_s&E*KAVDu?WW}7qhj^=x-6SfSQ5avy3Tceir=OBZGcJUS(;s-v}`)Pwv9E+y-G%}l+h^`q9naT zkR;cI26oseN*G{+&xkTt;$|BDQXyr*b(49DMtvth-YqT0XC<$^2~1T-mb$-Rf+S+Y z@88x7vs{DM%|+v`5vDq>ancK@&G!yiYZ&n zb9ztE>|9%Rv$uEbd$Q?9cr(OYU(D-P-B9)4k`%iEB_!6?eD0n+djZs9H-LBG(s9ZW zpd6EPasVFT-}s8)?;4zDUQYqe;ys}gZnrF$3ydyXZ*_UB1KbGSWy*1Wdn|dsySrkZ z+oQe#?6BZyy|5Qg2d+L3mkI0$UNv@8Zx|Y^uP|;<|5wfe#SB)j91d_6+79!*k!>{f z#zRgu7pbY}4Z*7&$!K_tPPfi(qdPEw1UWs3>}cznyswB1R`-Y5h2Ej<){k{0-}SbL z!ST+{yI-!X%+^lNc__6^FM99$ZU;?_gTKYT^p=?Mlqt&X$e_47e=*x)`Z)9R9f!6uK(Ba+VW!m|Y!mi#A#r#pXVjq2D_D2b4i=OrG-Z!fM!n_Hj%NpybDXNsqEY>8dk`qzE#M2DoJH6{{pWP^q$ST_ z%!L%egWpy3XptQrFVyNW6Sg}i*3*Az@U_y+RBI&8iEgg+xib;w*~wx7v}a}7MpQ20 zcMxVfcOp?U{1;0UK{QGF3V99}CrV@QYRleAMew-A3~L*;Gz&v=Rk`6<7M}5NUvvGfW%nwnv^|wE|@yKh?i5XzWTVMLVB}rS-WE4dyzvfhsuZ|ikApB)%U|n#nELO;U zp|XP{!9hQeSm$_Zk-hu&zYu=7Fe#9<*4G%U@c6%obyPG8Dq!U0l%pej7fC*l%E=~C zl=+&zf`WYpO$4Y+d9T{57KUCsQ28-FTa^mU?LJ6M_?y|pJZEA=bqagBOh#dLNS~|S zQe-H+#CCq0K~#05W0idMR5M0e7k-cPx;17!VV;s4D;BPmTI09PRC|+%+@+X0562If zeXiflWZXnN}OQSc1W7%n=l z%?3dVNTclgnw;ujiyog!xnZTU%`CJRF&!r?#jjW~p7i{~3*bHAo6AXLziz(PUS|55 zGSsIUbyd%GRCcHF2eLx)$E!1`m6&w(VpEE--xmG;gzrYVk~1yTysK&TyHB&Q>hv1l zlph83#I1h2S328F&eP`>&+umLqLpIB&Qf>)y3vWPDAM2foM)inNyCYyDVE0pc%FIy z-5ndL%OBroedcwEj=kBzZ`zOoEETnF%s8nn@;G zcHlTm@0No0iEzOYe8%{v_&3Elq&3gyrSo(YD+Ata*fWvm_jzZh&>}4dB)({&A@_X( zdgDeFj3!zaeZ+nC+ehha#cD0`lW~+CJ(ler3rKgXiaM!p2P!qC@A-}>c{p%dk|KAG zt1!2n?^XK6IAEP;LEb9*QI?MF8L1`E&CEy6dGfn42cu^qD6y=S3qN;sj3{C<{hVob zB)fV7-k({ip9P@xtP&Hrk1>UTthvc}mhSs*$}WIc>kowOyi%jF0%rUWaD~S_unoSR zTP4U*?$h-R%_=sJeRcYi23;TVz0I?masOYuvEjAVvcTax5)61 zHld^;w{Nbd($c|_KTc-<;Ug8o+Z?xu{4YbJ&&Z>m&mWKW@;j_Hk8Pw3)>RFbWXXQN z4Pu@Q>&?_QN53p^OA+0_bz=LleS{c`gq58-bk=m->5DkPxIrhcxI#dXyH6qClJ&qP~4*l}CqO;dbM8iWC`eA&UidVrrMKmNw#Q##T^{#e4s5yB;92j9Ph^3l3Q&KiMqQ)b*fS9|DCGz5wcRu{grm^usO(Umwb zv*5$!*QGG^cb-uGjSy4Mb%!8*SzK~e@ik)|!VHlwR@I#e8~nX>DpeT7Zf2YxMg(?{ znNTV#dMi<9)V=NiwhA5|#miE_Te!T{w_;?Y<`qY+?i8)?FR+(D!p;^*$&H;nIY?zP zkNXF0w|df$v`D{1S*HE6EC{wNG(v;aL z2j)r+QQGwY?MjfNh07I&owB|CM8nQ~sjQxBQ~QFV?3CSM0q;OBHi-S_z4vVV6A(V( zxPwY{FB4^Ljx7oqV=gf1p+n>P6Iu{Ez!+9HTGni#_xxV#+M-biP$ah!XRVr~qGR_=xck6>UZ-6?_%o_qkT1ncd-4E|QS*Kl3 z&A4wir^0KpH?iigb~}2dEXIPSnLb%)@{|p9@V9VvB+R6=lh)_o!zB%k*Q>o zeM^3K+mX-*_d~PkW1T%F0?78;f_;kLB>HKWbFVoktZ-Mf1kS zcU6v>VAt9T0kd|B@ATD=lGpkD$(ZirSu5Zg#5Ms8_!`iX{zantMVP`5h0eKoHm9YG zb--D`03}HbC2VI~>$L^HdmU-qoo)-NnYRDkJW0v=AJvL==Xzt~JaV;|u=)M;952rS z$xD0nlZm#C4qV(#X7p;raIMg%F1D`jcAUJIRP*>%`&Toigek8Xt)Yzd_D+>GcZZ!< zmtICwZgq~@@8|H)e)fhPx?Af%TmeBN-j@j&n_Ec)fF;g8Z>$#IEWw1`m)c_7ckhVy z-t%deUN}C2NdSQ|*DrVOQu6}p{7~nKUF(HN9{AfJ%!4-(u>@xjm5;YQYn2+xrc4l! zBX$FA7?Lr*uRl4h+?7GksTe+7r-`_$m|5srI2lt8MPZH(@U9>Z)~&&nD=+bi82 z3=`VS{Eihw=@0PUsaNHO20 zj0#|P>(itFg;7m=fiGlUEF6JQ*?#<8ta!(1Eub_qSpwH+%<=Ix5q$x*bMs_3S(2bQ z`J0sJfVE2S^jJ(8xmZp zz&b=Q#@RDq*;TzhyB0=A5*rn&P?rPDwU7L@#Q2<=ks7qxZ)8MKj((6YhQ~)9f_x%c z^m(W~;A<2k)+!9kN`*&Vg)lv&$7y4B_dDZHY00bFgD6`bP2M*K*Cj_es~9*{UrTK< zU16FRueLF6ZYehku$lJmOZ?nDO#}dq9{~IeWRaL(jCO;!r z+X<$o5*t1VlmL?lHZ)~7u!mUfxs)$KY#sMGsZBQqO)gpQeep4e1nopdxIk}&Fx+R+ zUJAT1LN%8NSr|9X@}^4)0=ttB@_}#~?$K1j;tBWd6bG zT)I+j8yKaRTfIVJO^Ev{j|`ftREgSf`9gTVmmbd|VMyNHX~>pfOPeu{y!#su;`b<9 zk}^-Ret~%0<8P>BA+I3&!L6a|0aE^b5m^8Q2j;jbbzFLet=HW{`yWD zRd3>xTkdYzWMnh8B9z2l?N)cfF0NohzecKj&>y<2R!}IVk;C$Rbc!Z-TyKVePj44v z5-^FMMVlHZBb2%Vv9I&7S4U_^hptteL+f!)PymX42oo+Snz7o5_bME=+oo2Ds*$bZWjRv;H?&{##FS+COm_!Qe5$}7kS*RTpOx}r8V$-Nng(cc9vK4dAN_YylC9alJ|LvL_H?1|B)yDWPvI_331mTaNVv`b^Gw% z7`@dTV`QDTJ)(dhB#)I|O)>M`+oJe__X(Dx?;+Tje0260FV*$^%$!iQVqAWeOxU)* zlp-9~bQyM>pP#&JxpO@G)A>B_kB3YjI)+SBANEyUMJ+ahuEL5%s`D&4gqDNPf+zna zc?r0ot{1cUMs}+DevIYvIl{%k;rq+y=O4=W92Y~I6cPq`x2=*5#=|>{`j9&;htE@6snRn(4pFO*dmM-4BCjHV?>$cKKJL-=O`;`wda_s1Uz=x>Ip|275r zSAW%iK77I6Qtu_XX^AI;{o*)xR@APHe8H6f4wD;;5Y1F_rhJXM+xBxlvtq8GhR*Bg ziS+&09jo?HXep)K03WXvY#o6KuudrxJ+DE&aWz*ZtI3Pb$5vLjNx+#rUY2m* z86-5YoszjTUg6M&WGGzI=unO^Uwcqlmt$|0^>Py51Ca8sshd@JsUkg#-Yu2DTnMU8 zpb*roZW0YLsW?ymsf#{bjMpqRaRr|ggUn4HNlGg*|D+IIeIw2M3oGg#U8rkiffNzm%GF0uC{(kKHIMvX_Y8SV!5- z^Z1LY;d3{j&5?pRxRjd;U>G+P*;Dj;nr9A-h%NOUNg8m1Slbx6$+Vy!WkN-j{wZ%p zS~wJm2{kSxIe3yc7fw;VvUyXV2u8YP>-0mNf??iLQdS{h!j8H2)DL4bEX6h}#4gXD zACGg05-$Fo+(o%aHr+69n4!+H=FZc2tHCDH>{W+&5#{=uDf3HM{3 z@wWw?YA^stefHEVETuUh4)LtLtzl$|M!gi~Kk`U?VkyJA025-x>a>!`+bN3pcL6!K zWcI%;jXC{_w?9ug;tpMKou_CIcyTv!x8Q|6xZE0)o^4IooDTtHuk;Xg{mE1yFMwdF zHNke=)6=tFPIe=X^MF~csOb+sdONY&Hrdu6T+Ayl3kCSpUxW`=OB1EMF09iYPLqxu zKw4AtnqPp}sUtGuP8tBKfdB0T7o)}rBb>SZ(d!1bKiDcv)b7t3%V}_XOd|fc)|^sP zG#olA9uG`OS62<|AiX9HveO#Gxi$y6Mv;_(fh9sI;Zf#hGJemAc+!ibsRw( z3Onc2IlMd*;d&2PY3nX--G%sa0-sLL3Gfhy;Sf!XHNBs9zqAu#x#{r4A8$s!X#fE3 zQDocFp~3hW1hP-vt;yx?QVL{G3j!1y)f)i34Fqlks!e2p@w$ls@p$F*a?Qf&P`aMY z40=+b%k`d5ZJ}D-VRO&K`er%!)r>!@&}m7 z^EXbM7+Q=;_^wWVlHIb^#>MQYvf<}7!085W9XGd40njaDoW9&IM{#3Z(G=RWnN#u_ zgA+kEEN}|9R@dl*3BD8o4mDaN-`?Itl1@~*EPi%7#~Rxn|=)0jq~Oj~Cu^CcQ?d3iN@UZVx)!y(YnS4L%LHKhNCQeFhI7DeEc*z z{1GAAble`Uu5Mj7BLd8+$s&JN@sadAnmD_mK*tY0Pd3MIg)FNjv7g572jFI`uKyp% zN7{8OO&-JF$X9l|oKIKHF>k(Cn9xA$?6N6Oo24*CtD05J=^5qyRulwMd!)um>=249 z;A%}z$QYvsD!M8|`@1osEYypoVY$b);xnJ3-4XydS^6`MoR5$q!|W{=6?8($;HMTW*JiKWfcPef5QL%W7)L%8sgZ9hkuZzi%fMNH#`G zO>l}%@kp^&vm>KttF^#>t4E?cu+f{_sbcmJE;7rkMmv%Z;xqCxv3tDAqNm8qYPARF zvC$$X+rlQ&r+606ZH;N%_2ON{^<#T1ywDynR13=G`STe1W%t2Fx`mcRf&5h}V5V%k7ZYS?op8 ziNHDY!$kO9zgN{-dO#0579*3nIi%^kj1dmquIN|Nj39iPEKsX-u!w02SJm>+>xAbS z3Y>Nt8k`yUv8)nBr7|HS&05Ng`k-SwMmB8BUuqoUg1OimR^yW{I#BeYNFdD~XGx2y ztb*48rR}g6a}?BYfCkU%C`7Rt5e3ncMQ^JJMjC^wc$9#_U4 zuRL*R^R9%c+xy9W)E6)ky*yEm+zT|dh?cOwo0E{L-^guFx7>XtCxR)V$u6Xz+FY&0 zR%?1hDnkHmnsZ5@psU5U`%!SQ-epsys6#>QV&vV-MGX7o`u95dn{CdDO`M1-`nUEZQQpFioK&F1ESljWK^?W zeeR+WRPPi||HifrX?NCAs=dcdZuo3#DW>9-?2E}EA^DvmC!5RThg^lU9z`81BeetB zn4Jp2EP-YJxIG&zwrfJvP5j4pn6=`FL#pRVJlY*g2NdfIiF@QvS~kI`6C;A)ft4t;W{1(=1gRv?$0)pVq;C3@$$_h08Z6>C$>>w>} zA9*WdwWQkMAwrHXu_9R@?}neUWXW=UMN+@ZI+XH_g*pUL%DqJqGUlXI<7(aIEc5Bq zI^sN(+$Lg>{!1B|oA@r$T|Yto|C7wccw?Or%uYX_tNXn?Wd$2+j#R_+#g@KngfE1`g>>z zJ!K{MCwp#ogu?2p8n}Nf{s3BaNlNj*Tln#x5|jU=x%_>d$p5*+mCne)iX#svsrQQq zcM$4}>ePo7S+Ao2|JmOLL5wJYgP!^M)3zOrX3wXZxR1~{Z>HJ}TTX8(?iO5c zd2)(BRP^DRvzLYx3J6!g)vdTPHZBLAYMd*D}H+fRw2?%g`u-r(A0H zI=eO_)X~^?^Q0DgVOpnxg$vwm1<8C*q0*0V`Wz{?&l_dG0XScA3i2QHFMkco9dmQCJ`8Z^_PiN;Ytl zWFN(gnw%2fto|G{`X*a)Tx?lQU$(y5Fr4l4K-7I8D-}jkbld5w($ws)P;%!C~)?8r*`6t*8%%< zTqHfngzT=(q}DqG%?!9(1nEI=V!pobu4kUj?8i9v+e0!V4L^iR>G&K^71OY~E*Ehu zsFh*1BYpo5%9upJ0YPCk%;SEuI_Ws7XYDOGKT+RNCi8e|Jmr+~(4Rma(O{9Bpw{y4Wn1I*XAX%+y^EX=0 zUrgGzPt%YWk)xD*s%1jR?mo}s85qr~#fOH;OUFozy%pDnfYSbWxMdE61}ss|Hcg8= zqoc9}Oiz4VW6;wxWh{AQNWUl8$SBXM#9FGCg^zu{5Ve><^6q33bGh2{Zs(x8$e0mb?LwQ zIzV%f-2v=Z3Mil=HTR_Xa(87+(suSbGj(skrMS)O1Df!VT~wJ|3xDW{1qkZ*|7#To z-p06O{(t(;wRh*XSFXVG1Rk8ayK>T!t<2#9PNn9awR2DC=<(QJ&}6vEEZ-Gw!N9=E z2D&5Q{EAJBT8&P22wOEVT)cDO=Dwn^o7dUWZY?_F1zbV5#!zHds9Cobx-1t;Vq~n6=vZ$>)c=tZeE!;dE7=B#S^DTfod^Sx95Z(GN zV#zA06D&1#YuR*FTSAXa_VxE%eSAH$F{x5 zKJzv7d)};o z7PV}j^9ljx*R$nB6LUNzW=gD|;bVRfn2@qp-`Lvh^&|4BWX{!I)6CmlwR7Y?Rx7K8 z_c3gI6w}tI9COmwzQ<`+7H zZ5w) zvmxlxHK7K^hE_D>xwO{ymzH7Ux49O zuyI36==P^)URAx?`O+#hWmc!y^sPs)^L&V^-D8vV%{AvMhsMny?K5{*to?jon{1SN zZR*osi#P7)1&+?=NeFGfS#ZUmb-t@t*1ij!A2x9;d6&iAY%gyPTnw`@RO{^5Vr@fB z;KH55zTgUKTD<>``xfno__?*sGJp-SwUNJ01vhPw$!jT0t%#Zp+&k2w#)#BRTYOAC z$?LAkch2L>(x#Z~a$aqq$FRn<`m$LlbIk5N%lBgr2-cCAO#lUdO zhQ~9&?R6SY_V+@y4Yq>Od!HJW_T14lo^E_h^9sw3o5A127Jn3rZd`A#axDfpxZYsk zyu2<%Y9(9GizEG9ww@;@JvbkKQ-NiLB%9NZ#MDREr(Ae0VRyOUC}fB`D(h&cs%>we z)*C1POQrV~DBLj-n|W08YR$_fovw^CWPfN0^l%%q+`j64{%jOvjmn?PPerRT9hB!9 z+AeAH)QJ2%bM+;2p8a!nmR!6)!~B&(MqGyJyo4!In#M;asbx8eT{w|2Wws`_@N{P6 zH8^YzJqDjWQw+4;-1)-yf-9GSA#v7|_fsc6_WDYvlLHoQF`IBb5+huQJJ z4t+T&uAF&YW~%nL!YU46L2yd&0n+-J4gWT6m+0MogDdB+;>Kg>L*&nXd`SrWP`!@D zY9ar|=^k_3FFwBxUQqMC7kL5g*8S(=f7rDwY+UH_J;6k&<(~Nug(uanh|O2o=P&w( xW%acex_nOr%0o9{*)9Jt6La(JmuY|h8+{7?Soq>YEAT8E22WQ%mvv4FO#r{G=s5rY literal 0 HcmV?d00001 diff --git a/doc/source/admin/figures/bgp-floating-ip-over-l2-segmented-network.svg b/doc/source/admin/figures/bgp-floating-ip-over-l2-segmented-network.svg new file mode 100644 index 00000000000..e697b739f7c --- /dev/null +++ b/doc/source/admin/figures/bgp-floating-ip-over-l2-segmented-network.svg @@ -0,0 +1,2 @@ + +
RACK 2
RACK 2
RACK 1
RACK 1
Compute 3
Compute 3
Compute 2
Compute 2
Compute 1
Compute 1
VM1
VM1
VM2
VM2
10.1.0.48
VLAN 11
[Not supported by viewer]
Switch 1
Switch 1
10.1.0.1
VLAN 11
[Not supported by viewer]
Switch 2
Switch 2
10.1.0.1
VLAN 11
[Not supported by viewer]
Compute 6
Compute 6
Compute 5
Compute 5
Compute 4
Compute 4
VM4
VM4
VM5
VM5
10.2.0.23
VLAN 13
[Not supported by viewer]
Switch 3
Switch 3
10.2.0.1
VLAN 13
[Not supported by viewer]
Switch 4
Switch 4
10.2.0.1
VLAN 13
[Not supported by viewer]
192.168.1.4
VLAN 12
[Not supported by viewer]
192.168.0.4
VLAN 10
[Not supported by viewer]
192.168.0.1
VLAN10
[Not supported by viewer]
192.168.1.1
VLAN 12
[Not supported by viewer]
ToR Switch 1
ToR Switch 1
INTERNET
INTERNET
BACKBONE SWITCH 1
BACKBONE SWITCH 1
BACKBONE SWITCH 2
BACKBONE SWITCH 2
ToR Switch 2
ToR Switch 2
L2 Provider (service) segmented network: the next HOP for floating IPs RACK 1
L2 Provider (service) segmented network: the next HOP for floating IPs RACK 1
Management network rack 2
Management network rack 2
L3 Floating IP, announced over the L2 Provider segmented network
L3 Floating IP, announced over the L2 Provider segmented network
85.125.24.17
[Not supported by viewer]
85.125.24.16
[Not supported by viewer]
85.125.24.12
[Not supported by viewer]
85.125.24.7
[Not supported by viewer]
Management network rack 1
Management network rack 1
L2 Provider (service) segmented network: the next HOP for floating IPs RACK 2
L2 Provider (service) segmented network: the next HOP for floating IPs RACK 2
Inter-switch full BGP network
Inter-switch full BGP network
Handled by (and configured in) Openstack:
Handled by (and configured in) Openstack:
10.1.0.65
VLAN 11
[Not supported by viewer]
10.2.0.35
VLAN 13
[Not supported by viewer]
192.168.0.1
VLAN 10
[Not supported by viewer]
192.168.1.1
VLAN 12
[Not supported by viewer]
\ No newline at end of file diff --git a/neutron/db/ipam_backend_mixin.py b/neutron/db/ipam_backend_mixin.py index 102f54e053f..5c0960be09c 100644 --- a/neutron/db/ipam_backend_mixin.py +++ b/neutron/db/ipam_backend_mixin.py @@ -350,9 +350,24 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon): subnet_cidr=subnet_cidr) def _validate_segment(self, context, network_id, segment_id, action=None, - old_segment_id=None): - segments = subnet_obj.Subnet.get_values( - context, 'segment_id', network_id=network_id) + old_segment_id=None, requested_service_types=None, + subnet_id=None): + # NOTE(zigo): If we're creating a network:routed subnet (here written + # as: const.DEVICE_OWNER_ROUTED), then the created subnet must be + # removed from the segment list, otherwise its segment ID will be + # returned as None, and SubnetsNotAllAssociatedWithSegments will be + # raised. + if (action == 'create' and requested_service_types and + const.DEVICE_OWNER_ROUTED in requested_service_types): + to_create_subnet_id = subnet_id + else: + to_create_subnet_id = None + + segments = subnet_obj.Subnet.get_subnet_segment_ids( + context, network_id, + ignored_service_type=const.DEVICE_OWNER_ROUTED, + subnet_id=to_create_subnet_id) + associated_segments = set(segments) if None in associated_segments and len(associated_segments) > 1: raise segment_exc.SubnetsNotAllAssociatedWithSegments( @@ -581,7 +596,10 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon): # TODO(slaweq): when check is segment exists will be integrated in # self._validate_segment() method, it should be moved to be done before # subnet object is created - self._validate_segment(context, network['id'], segment_id) + self._validate_segment(context, network['id'], segment_id, + action='create', + requested_service_types=service_types, + subnet_id=subnet.id) # NOTE(changzhi) Store DNS nameservers with order into DB one # by one when create subnet with DNS nameservers diff --git a/neutron/objects/subnet.py b/neutron/objects/subnet.py index 3c492a6ff74..f35cc83a652 100644 --- a/neutron/objects/subnet.py +++ b/neutron/objects/subnet.py @@ -20,6 +20,7 @@ from neutron_lib.utils import net as net_utils from oslo_utils import versionutils from oslo_versionedobjects import fields as obj_fields from sqlalchemy import and_, or_ +from sqlalchemy.sql import exists from neutron.db.models import dns as dns_models from neutron.db.models import segment as segment_model @@ -487,6 +488,32 @@ class Subnet(base.NeutronDbObject): if _target_version < (1, 1): # version 1.1 adds "dns_publish_fixed_ip" primitive.pop('dns_publish_fixed_ip', None) + @classmethod + def get_subnet_segment_ids(cls, context, network_id, + ignored_service_type=None, + subnet_id=None): + query = context.session.query(cls.db_model.segment_id) + query = query.filter(cls.db_model.network_id == network_id) + + # NOTE(zigo): Subnet who hold the type ignored_service_type should be + # removed from the segment list, as they can be part of a segmented + # network but they don't have a segment ID themselves. + if ignored_service_type: + service_type_model = SubnetServiceType.db_model + query = query.filter(~exists().where(and_( + cls.db_model.id == service_type_model.subnet_id, + service_type_model.service_type == ignored_service_type))) + + # (zigo): When a subnet is created, at this point in the code, + # its service_types aren't populated in the subnet_service_types + # object, so the subnet to create isn't filtered by the ~exists + # above. So we just filter out the subnet to create completely + # from the result set. + if subnet_id: + query = query.filter(cls.db_model.id != subnet_id) + + return [segment_id for (segment_id,) in query.all()] + @base.NeutronObjectRegistry.register class NetworkSubnetLock(base.NeutronDbObject): diff --git a/neutron/tests/unit/db/test_ipam_backend_mixin.py b/neutron/tests/unit/db/test_ipam_backend_mixin.py index 1ea1c39f1f0..3c7c0191244 100644 --- a/neutron/tests/unit/db/test_ipam_backend_mixin.py +++ b/neutron/tests/unit/db/test_ipam_backend_mixin.py @@ -27,6 +27,7 @@ from neutron.db import db_base_plugin_v2 from neutron.db import ipam_backend_mixin from neutron.db import portbindings_db from neutron.objects import subnet as subnet_obj +from neutron.services.segments import db as segments_db from neutron.tests import base from neutron.tests.unit.db import test_db_base_plugin_v2 @@ -331,7 +332,8 @@ class TestIpamBackendMixin(base.BaseTestCase): class TestPlugin(db_base_plugin_v2.NeutronDbPluginV2, - portbindings_db.PortBindingMixin): + portbindings_db.PortBindingMixin, + segments_db.SegmentDbMixin): __native_pagination_support = True __native_sorting_support = True diff --git a/neutron/tests/unit/extensions/test_segment.py b/neutron/tests/unit/extensions/test_segment.py index 27dd053ec12..ab14e7c4949 100644 --- a/neutron/tests/unit/extensions/test_segment.py +++ b/neutron/tests/unit/extensions/test_segment.py @@ -130,7 +130,8 @@ class SegmentTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, __native_sorting_support = True supported_extension_aliases = [seg_apidef.ALIAS, portbindings.ALIAS, - ipalloc_apidef.ALIAS] + ipalloc_apidef.ALIAS, + "subnet-service-types"] def get_plugin_description(self): return "Network Segments" @@ -501,6 +502,25 @@ class TestSegmentSubnetAssociation(SegmentTestCase): segment_id=segment['segment']['id']) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) + def test_only_some_subnets_associated_allowed_with_routed_network(self): + with self.network() as network: + net = network['network'] + + segment = self._test_create_segment(network_id=net['id'], + segmentation_id=200) + segment_id = segment['segment']['id'] + + with self.subnet(network=network, segment_id=segment_id) as subnet: + subnet = subnet['subnet'] + + res = self._create_subnet(self.fmt, + net_id=net['id'], + tenant_id=net['tenant_id'], + gateway_ip=constants.ATTR_NOT_SPECIFIED, + cidr='10.0.1.0/24', + service_types=[constants.DEVICE_OWNER_ROUTED]) + self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) + def test_association_to_dynamic_segment_not_allowed(self): cxt = context.get_admin_context() with self.network() as network: diff --git a/releasenotes/notes/network-routed-subnets-cf4874d97ddacd77.yaml b/releasenotes/notes/network-routed-subnets-cf4874d97ddacd77.yaml new file mode 100644 index 00000000000..09c35df7237 --- /dev/null +++ b/releasenotes/notes/network-routed-subnets-cf4874d97ddacd77.yaml @@ -0,0 +1,11 @@ +--- +features: + - | + A new subnet of type ``network:routed`` has been added. If such a subnet is + used, the IPs of that subnet will be advertized with BGP over a provider + network, which itself can use segments. This basically achieves a + BGP-to-the-rack feature, where the L2 connectivity can be confined to a + rack only, and all external routing is done by the switches, using BGP. + In this mode, it is still possible to use VXLAN connectivity between the + compute nodes, and only floating IPs and router gateways are using BGP + routing.