From 1a885ddd1fdf7aa4dbec6a1ebe98152c032184c4 Mon Sep 17 00:00:00 2001 From: Vladimir Kozhukalov Date: Mon, 25 Sep 2023 21:34:52 -0500 Subject: [PATCH] Update deployment documentation Recently we updated our test jobs so that all of them use the `deploy-env` Ansible role which utilizes the Kubeadm to deploy the test Kubernetes cluster. The role works for both multi-node and single-node environments. Although the deployment of Kubernetes itself is out of scope of Openstack-Helm, we recommen using this role to deploy test and development Kubernetes clusters. So at the moment there is no need to provide different sets of tools single-node and multi-node test envs. Now this is a matter of the Ansible inventory file. Also the deployment procedure of OpenStack on top of Kubernetes using Helm is the same for multi-node and single-node clusters because it only relies on the Kubernetes API. We will be improving the `deploy-env` role even futher and we will be cleaning up the deployment scripts and the documentation so to provide a clear experience for the Openstack-Helm users. Change-Id: I70236c4a2b870b52d2b01f65b1ef9b9518646964 --- CONTRIBUTING.rst | 26 -- README.rst | 114 ++++-- doc/source/conf.py | 2 +- doc/source/contributor/contributing.rst | 108 ------ doc/source/gates.rst | 145 -------- doc/source/index.rst | 16 +- doc/source/install/before_deployment.rst | 34 ++ doc/source/install/common-requirements.rst | 70 ---- doc/source/install/deploy_ceph.rst | 52 +++ .../install/deploy_ingress_controller.rst | 52 +++ doc/source/install/deploy_kubernetes.rst | 143 ++++++++ doc/source/install/deploy_openstack.rst | 116 ++++++ .../install/deploy_openstack_backend.rst | 54 +++ .../install/developer/cleaning-deployment.rst | 92 ----- .../install/developer/deploy-ovs-dpdk.rst | 185 ---------- .../install/developer/deploy-with-ceph.rst | 222 ------------ .../install/developer/deploy-with-nfs.rst | 163 --------- .../developer/deploy-with-tungsten-fabric.rst | 147 -------- .../install/developer/exercise-the-cloud.rst | 90 ----- doc/source/install/developer/index.rst | 16 - .../developer/kubernetes-and-common-setup.rst | 135 ------- .../requirements-and-host-config.rst | 100 ------ doc/source/install/ext-dns-fqdn.rst | 244 ------------- doc/source/install/index.rst | 17 +- doc/source/install/kubernetes-gate.rst | 143 -------- doc/source/install/multinode.rst | 331 ----------------- ...deploy-tap-as-a-service-neutron-plugin.rst | 339 ------------------ .../plugins/figures/taas-architecture.png | Bin 98682 -> 0 bytes doc/source/install/plugins/index.rst | 9 - doc/source/install/prepare_kubernetes.rst | 28 ++ doc/source/install/setup_openstack_client.rst | 35 ++ 31 files changed, 611 insertions(+), 2617 deletions(-) delete mode 100644 CONTRIBUTING.rst delete mode 100644 doc/source/contributor/contributing.rst delete mode 100644 doc/source/gates.rst create mode 100644 doc/source/install/before_deployment.rst delete mode 100644 doc/source/install/common-requirements.rst create mode 100644 doc/source/install/deploy_ceph.rst create mode 100644 doc/source/install/deploy_ingress_controller.rst create mode 100644 doc/source/install/deploy_kubernetes.rst create mode 100644 doc/source/install/deploy_openstack.rst create mode 100644 doc/source/install/deploy_openstack_backend.rst delete mode 100644 doc/source/install/developer/cleaning-deployment.rst delete mode 100644 doc/source/install/developer/deploy-ovs-dpdk.rst delete mode 100644 doc/source/install/developer/deploy-with-ceph.rst delete mode 100644 doc/source/install/developer/deploy-with-nfs.rst delete mode 100644 doc/source/install/developer/deploy-with-tungsten-fabric.rst delete mode 100644 doc/source/install/developer/exercise-the-cloud.rst delete mode 100644 doc/source/install/developer/index.rst delete mode 100644 doc/source/install/developer/kubernetes-and-common-setup.rst delete mode 100644 doc/source/install/developer/requirements-and-host-config.rst delete mode 100644 doc/source/install/ext-dns-fqdn.rst delete mode 100644 doc/source/install/kubernetes-gate.rst delete mode 100644 doc/source/install/multinode.rst delete mode 100644 doc/source/install/plugins/deploy-tap-as-a-service-neutron-plugin.rst delete mode 100644 doc/source/install/plugins/figures/taas-architecture.png delete mode 100644 doc/source/install/plugins/index.rst create mode 100644 doc/source/install/prepare_kubernetes.rst create mode 100644 doc/source/install/setup_openstack_client.rst diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index 65ff7812a9..0000000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,26 +0,0 @@ -The source repository for this project can be found at: - - https://opendev.org/openstack/openstack-helm - -Pull requests submitted through GitHub are not monitored. - -To start contributing to OpenStack, follow the steps in the contribution guide -to set up and use Gerrit: - - https://docs.openstack.org/contributors/code-and-documentation/quick-start.html - -Bugs should be filed on StoryBoard: - - https://storyboard.openstack.org/#!/project/openstack/openstack-helm - -For more specific information about contributing to this repository, see the -openstack-helm contributor guide: - - https://docs.openstack.org/openstack-helm/latest/contributor/contributing.html - -Chart tarballs are published and can be found at the respective sub folder under - - https://tarballs.opendev.org/openstack/ - -Versioning and release notes for each chart update are now required in order to -better support the evolving nature of the OpenStack platform. diff --git a/README.rst b/README.rst index 331c3d3404..90ea4377ab 100644 --- a/README.rst +++ b/README.rst @@ -9,55 +9,99 @@ The goal of OpenStack-Helm is to provide a collection of Helm charts that simply, resiliently, and flexibly deploy OpenStack and related services on Kubernetes. +Versions supported +------------------ + +The table below shows the combinations of the Openstack/Platform/Kubernetes versions +that are tested and proved to work. + +.. list-table:: + :widths: 30 30 30 30 + :header-rows: 1 + + * - Openstack version + - Host OS + - Image OS + - Kubernetes version + * - Victoria + - Ubuntu Focal + - Ubuntu Focal + - >=1.24,<=1.26 + * - Wallaby + - Ubuntu Focal + - Ubuntu Focal + - >=1.24,<=1.26 + * - Xena + - Ubuntu Focal + - Ubuntu Focal + - >=1.24,<=1.26 + * - Yoga + - Ubuntu Focal + - Ubuntu Focal + - >=1.24,<=1.26 + * - Zed + - Ubuntu Focal + - Ubuntu Focal + - >=1.24,<=1.26 + * - Zed + - Ubuntu Jammy + - Ubuntu Jammy + - >=1.24,<=1.26 + * - 2023.1 (Antelope) + - Ubuntu Focal + - Ubuntu Focal + - >=1.24,<=1.26 + * - 2023.1 (Antelope) + - Ubuntu Jammy + - Ubuntu Jammy + - >=1.24,<=1.26 + * - 2023.2 (Bobcat) + - Ubuntu Jammy + - Ubuntu Jammy + - >=1.24,<=1.26 + Communication ------------- * Join us on `IRC `_: - #openstack-helm on oftc -* Community `IRC Meetings - `_: - [Every Tuesday @ 1500 UTC], #openstack-helm in IRC (OFTC) -* Meeting Agenda Items: `Agenda - `_ + ``#openstack-helm`` on oftc * Join us on `Slack `_ - - #openstack-helm + (this is preferable way of communication): ``#openstack-helm`` +* Join us on `Openstack-discuss `_ + mailing list (use subject prefix ``[openstack-helm]``) + +The list of Openstack-Helm core team members is available here +`openstack-helm-core `_. Storyboard ---------- -Bugs and enhancements are tracked via OpenStack-Helm's +You found an issue and want to make sure we are aware of it? You can do so on our `Storyboard `_. -Installation and Development ----------------------------- +Bugs should be filed as stories in Storyboard, not GitHub. -Please review our -`documentation `_. -For quick installation, evaluation, and convenience, we have a minikube -based all-in-one solution that runs in a Docker container. The set up -can be found -`here `_. +Please be as much specific as possible while describing an issue. Usually having +more context in the bug description means less efforts for a developer to +reproduce the bug and understand how to fix it. + +Also before filing a bug to the Openstack-Helm `Storyboard `_ +please try to identify if the issue is indeed related to the deployment +process and not to the deployable software. + +Other links +----------- + +Our documentation is available `here `_. This project is under active development. We encourage anyone interested in -OpenStack-Helm to review our -`Installation `_ -documentation. Feel free to ask questions or check out our current -`Storyboard backlog `_. +OpenStack-Helm to review the `code changes `_ -To evaluate a multinode installation, follow the -`Bare Metal `_ -install guide. +Our repositories: -Repository ----------- +* OpenStack charts `openstack-helm `_ +* Infra charts `openstack-helm-infra `_ +* Building images `openstack-helm-images `_ +* Building Openstack images framework `loci `_ -Developers wishing to work on the OpenStack-Helm project should always base -their work on the latest code, available from the OpenStack-Helm git repository. - -`OpenStack-Helm git repository `_ - -Contributing ------------- - -We welcome contributions. Check out `this `_ document if -you would like to get involved. +We welcome contributions in any form: code review, code changes, usage feedback, updating documentation. diff --git a/doc/source/conf.py b/doc/source/conf.py index 9c973ace2c..f337d608a3 100755 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -42,7 +42,7 @@ master_doc = 'index' # General information about the project. project = 'openstack-helm' -copyright = '2016-2022, OpenStack Foundation' +copyright = '2016-2023, OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True diff --git a/doc/source/contributor/contributing.rst b/doc/source/contributor/contributing.rst deleted file mode 100644 index cb08bc95b8..0000000000 --- a/doc/source/contributor/contributing.rst +++ /dev/null @@ -1,108 +0,0 @@ -============================ -So You Want to Contribute... -============================ - -For general information on contributing to OpenStack, please check out the -`contributor guide `_ to get started. -It covers all the basics that are common to all OpenStack projects: the accounts -you need, the basics of interacting with our Gerrit review system, how we -communicate as a community, etc. - -Additional information could be found in -`OpenDev Developer's Guide -`_. - -Below will cover the more project specific information you need to get started -with OpenStack-Helm. - -Communication -~~~~~~~~~~~~~ -.. This would be a good place to put the channel you chat in as a project; when/ - where your meeting is, the tags you prepend to your ML threads, etc. - -* Join us on `IRC `_: - #openstack-helm on oftc -* Join us on `Slack `_ - (this is preferable way of communication): #openstack-helm - -Contacting the Core Team -~~~~~~~~~~~~~~~~~~~~~~~~ -.. This section should list the core team, their irc nicks, emails, timezones - etc. If all this info is maintained elsewhere (i.e. a wiki), you can link to - that instead of enumerating everyone here. - -Project's Core Team could be contacted via IRC or Slack. List of current core reviewers -could be found here `openstack-helm-core `_. - -New Feature Planning -~~~~~~~~~~~~~~~~~~~~ -.. This section is for talking about the process to get a new feature in. Some - projects use blueprints, some want specs, some want both! Some projects - stick to a strict schedule when selecting what new features will be reviewed - for a release. - -New features are planned and implemented trough the process described in -`Project Specifications <../specs/index.html>`_ section of this document. - -Task Tracking -~~~~~~~~~~~~~ -.. This section is about where you track tasks- launchpad? storyboard? is there - more than one launchpad project? what's the name of the project group in - storyboard? - -We track our tasks on our StoryBoard_. - -If you're looking for some smaller, easier work item to pick up and get started -on, search for the 'low-hanging-fruit' tag. - -.. NOTE: If your tag is not 'low-hanging-fruit' please change the text above. - -Other OpenStack-Helm component's tasks could be found on the `group Storyboard`_. - -Reporting a Bug -~~~~~~~~~~~~~~~ -.. Pretty self explanatory section, link directly to where people should report - bugs for your project. - -You found an issue and want to make sure we are aware of it? You can do so on our -Storyboard_. - -If an issue is on one of other OpenStack-Helm components, report it to the -appropriate `group Storyboard`_. - -Bugs should be filed as stories in Storyboard, not GitHub. - -Please be as much specific as possible while describing an issue. Usually having -more context in the bug description means less efforts for a developer to -reproduce the bug and understand how to fix it. - -Also before filing a bug to the Openstack-Helm `_Storyboard` please try to identify -if the issue is indeed related to the deployment process and not to the deployable -software. - -Getting Your Patch Merged -~~~~~~~~~~~~~~~~~~~~~~~~~ -.. This section should have info about what it takes to get something merged. Do - you require one or two +2's before +W? Do some of your repos require unit - test changes with all patches? etc. - -We require two Code-Review +2's from reviewers, before getting your patch merged -with giving Workforce +1. Trivial patches (e.g. typos) could be merged with one -Code-Review +2. - -Changes affecting code base often require CI tests and documentation to be added -in the same patch set. - -Pull requests submitted through GitHub will be ignored. - -Project Team Lead Duties -~~~~~~~~~~~~~~~~~~~~~~~~ -.. this section is where you can put PTL specific duties not already listed in - the common PTL guide (linked below), or if you already have them written - up elsewhere you can link to that doc here. - -All common PTL duties are enumerated in the `PTL guide -`_. - -.. _Storyboard: https://storyboard.openstack.org/#!/project/openstack/openstack-helm -.. _group Storyboard: https://storyboard.openstack.org/#!/project_group/64 diff --git a/doc/source/gates.rst b/doc/source/gates.rst deleted file mode 100644 index 70e5146ea2..0000000000 --- a/doc/source/gates.rst +++ /dev/null @@ -1,145 +0,0 @@ -==================== -OpenStack-Helm Gates -==================== - -To facilitate ease of testing and debugging, information regarding gates and -their functionality can be found here. - -OpenStack-Helm's single node and multinode gates leverage the kubeadm-aio -environment created and maintained for use as a development environment. All -information regarding the kubeadm-aio environment can be found here_. - -.. _here: https://docs.openstack.org/openstack-helm/latest/install/developer/index.html - -Gate Checks ------------ - -OpenStack-Helm currently checks the following scenarios: - -- Testing any documentation changes and impacts. -- Running Make on each chart, which lints and packages the charts. This gate - does not stand up a Kubernetes cluster. -- Provisioning a single node cluster and deploying the OpenStack services. This - check is provided for: Ubuntu-1604, CentOS-7, and Fedora-25. -- Provisioning a multi-node Ubuntu-1604 cluster and deploying the OpenStack - services. This check is provided for both a two node cluster and a three - node cluster. - - -Gate Functions --------------- - -To provide reusable components for gate functionality, functions have been -provided in the gates/funcs directory. These functions include: - -- Functions for common host preparation operations, found in common.sh -- Functions for Helm specific operations, found in helm.sh. These functions - include: installing Helm, serving a Helm repository locally, linting and - building all Helm charts, running Helm tests on a release, installing the - helm template plugin, and running the helm template plugin against a chart. -- Functions for Kubernetes specific operations, found in kube.sh. These - functions include: waiting for pods in a specific namespace to register as - ready, waiting for all nodes to register as ready, install the requirements - for the kubeadm-aio container used in the gates, building the kubeadm-aio - container, launching the kubeadm-aio container, and replacing the - kube-controller-manager with a specific image necessary for ceph functionality. -- Functions for network specific operations, found in network.sh. These - functions include: creating a backup of the host's resolv.conf file before - deploying the kubeadm environments, restoring the original resolv.conf - settings, creating a backup of the host's /etc/hosts file before adding the - hosts interface and address, and restoring the original /etc/hosts file. -- Functions for OpenStack specific operations, found in openstack.sh. These - functions include: waiting for a successful ping, and waiting for a booted - virtual machine's status to return as ACTIVE. - -Any additional functions required for testing new charts or improving the gate -workflow should be placed in the appropriate location. - - -Gate Output ------------ - -To provide meaningful output from the gates, all information pertaining to the -components of the cluster and workflow are output to the logs directory inside -each gate. The contents of the log directory are as follows: - -- The dry-runs directory contains the rendered output of Helm dry-run installs - on each of the OpenStack service charts. This gives visibility into the - manifests created by the templates with the supplied values. When the dry-run - gate fails, the reason should be apparent in the dry-runs output. The logs - found here are helpful in identifying issues resulting from using helm-toolkit - functions incorrectly or other rendering issues with gotpl. -- The K8s directory contains the logs and output of the Kubernetes objects. It - includes: pods, nodes, secrets, services, namespaces, configmaps, deployments, - daemonsets, and statefulsets. Descriptions for the state of all resources - during execution are found here, and this information can prove valuable when - debugging issues raised during a check. When a single node or multi-node - check fails, this is the first place to look. The logs found here are helpful - when the templates render correctly, but the services are not functioning - correctly, whether due to service configuration issues or issues with the - pods themselves. -- The nodes directory contains information about the node the gate tests are - running on in openstack-infra. This includes: the network interfaces, the - contents of iptables, the host's resolv.conf, and the kernel IP routing table. - These logs can be helpful when trying to identify issues with host networking - or other issues at the node level. - - -Adding Services ---------------- - -As charts for additional services are added to OpenStack-Helm, they should be -included in the gates. Adding new services to the gates allows a chart -developer and the review team to identify any potential issues associated with -a new service. All services are currently launched in the gate via -a series of launch scripts of the format ``NNN-service-name.sh`` where ``NNN`` -dictates the order these scripts are launched. The script should contain -an installation command like: - -:: - - helm install --namespace=openstack ${WORK_DIR}/mistral --name=mistral - -Some services in the gate require specific overrides to the default values in -the chart's values.yaml file. If a service requires multiple overrides to -function in the gate, the service should include a separate values.yaml file -placed in the tools/overrides/mvp directory. The .yaml MVP files -provide a configuration file to use for overriding default configuration values -in the chart's values.yaml as an alternative to overriding individual values -during installation. A chart that requires a MVP overrides file -requires the following format: - -:: - - helm install --namespace=openstack ${WORK_DIR}/cinder --name=cinder \ - --values=${WORK_DIR}/tools/overrides/mvp/cinder.yaml - - -Adding Tests ------------- - -As new charts are developed and the services are added to the gate, an -associated Helm test should be introduced to the gates. The appropriate place -for executing these tests is in the respective service's launch script, and -must be placed after the entry for installing the service and any associated -overrides. Any tests that use the Rally testing framework should leverage the -helm_test_deployment function in the aforementioned funcs/helm.sh file. For -example, a Helm test for Mistral might look like: - -:: - - helm_test_deployment mistral 600 - -This results in the gate running the following: - -:: - - helm test --timeout 600 mistral - mkdir -p logs/rally - kubectl logs -n openstack mistral-rally-test > logs/rally/mistral - kubectl delete -n openstack pod mistral-rally-test - -Any tests that do not use the Rally testing framework would need to be handled -in the appropriate manner in launch script. This would ideally result in new -functions that could be reused, or expansion of the gate scripts to include -scenarios beyond basic service launches. diff --git a/doc/source/index.rst b/doc/source/index.rst index 4d41927f22..3d819a169e 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -4,16 +4,14 @@ Welcome to OpenStack-Helm's documentation! Contents: .. toctree:: - :maxdepth: 2 + :maxdepth: 2 - contributor/contributing - devref/index - gates - install/index - readme - specs/index - testing/index - troubleshooting/index + readme + install/index + devref/index + testing/index + troubleshooting/index + specs/index Indices and Tables ================== diff --git a/doc/source/install/before_deployment.rst b/doc/source/install/before_deployment.rst new file mode 100644 index 0000000000..eb8f8df06e --- /dev/null +++ b/doc/source/install/before_deployment.rst @@ -0,0 +1,34 @@ +Before deployment +================= + +Before proceeding with the steps outlined in the following +sections and executing the actions detailed therein, it is +imperative that you clone the essential Git repositories +containing all the required Helm charts, deployment scripts, +and Ansible roles. This preliminary step will ensure that +you have access to the necessary assets for a seamless +deployment process. + +.. code-block:: bash + + mkdir ~/osh + cd ~/osh + git clone https://opendev.org/openstack/openstack-helm.git + git clone https://opendev.org/openstack/openstack-helm-infra.git + + +All further steps assume these two repositories are cloned into the +`~/osh` directory. + +Also before deploying the OpenStack cluster you have to specify the +OpenStack and the operating system version that you would like to use +for deployment. For doing this export the following environment variables + +.. code-block:: bash + + export OPENSTACK_RELEASE=2023.2 + export CONTAINER_DISTRO_NAME=ubuntu + export CONTAINER_DISTRO_VERSION=jammy + +.. note:: + The list of supported versions can be found :doc:`here `. diff --git a/doc/source/install/common-requirements.rst b/doc/source/install/common-requirements.rst deleted file mode 100644 index 0bb175d2ac..0000000000 --- a/doc/source/install/common-requirements.rst +++ /dev/null @@ -1,70 +0,0 @@ -============================== -Common Deployment Requirements -============================== - -Passwordless Sudo -================= - -Throughout this guide the assumption is that the user is: -``ubuntu``. Because this user has to execute root level commands -remotely to other nodes, it is advised to add the following lines -to ``/etc/sudoers`` for each node: - -.. code-block:: shell - - root ALL=(ALL) NOPASSWD: ALL - ubuntu ALL=(ALL) NOPASSWD: ALL - -Latest Version Installs -======================= - -On the host or master node, install the latest versions of Git, CA Certs & Make if necessary - -.. literalinclude:: ../../../tools/deployment/developer/common/000-install-packages.sh - :language: shell - :lines: 1,17- - -Proxy Configuration -=================== - -.. note:: This guide assumes that users wishing to deploy behind a proxy have already - defined the conventional proxy environment variables ``http_proxy``, - ``https_proxy``, and ``no_proxy``. - -In order to deploy OpenStack-Helm behind corporate proxy servers, add the -following entries to ``openstack-helm-infra/tools/gate/devel/local-vars.yaml``. - -.. code-block:: yaml - - proxy: - http: http://username:password@host:port - https: https://username:password@host:port - noproxy: 127.0.0.1,localhost,172.17.0.1,.svc.cluster.local - -.. note:: The ``.svc.cluster.local`` address is required to allow the OpenStack - client to communicate without being routed through proxy servers. The IP - address ``172.17.0.1`` is the advertised IP address for the Kubernetes API - server. Replace the addresses if your configuration does not match the - one defined above. - -Add the address of the Kubernetes API, ``172.17.0.1``, and -``.svc.cluster.local`` to your ``no_proxy`` and ``NO_PROXY`` environment -variables. - -.. code-block:: bash - - export no_proxy=${no_proxy},172.17.0.1,.svc.cluster.local - export NO_PROXY=${NO_PROXY},172.17.0.1,.svc.cluster.local - -By default, this installation will use Google DNS Server IPs (8.8.8.8, 8.8.4.4) -and will update resolv.conf as a result. If those IPs are blocked by the proxy, -this will overwrite the original DNS entries and result in the inability to -connect to anything on the network behind the proxy. These DNS nameserver entries -can be changed by updating the ``external_dns_nameservers`` entry in this file: - -.. code-block:: bash - - openstack-helm-infra/tools/images/kubeadm-aio/assets/opt/playbooks/vars.yaml - -It is recommended to add your own existing DNS nameserver entries to avoid -losing connection. diff --git a/doc/source/install/deploy_ceph.rst b/doc/source/install/deploy_ceph.rst new file mode 100644 index 0000000000..23f2a424bb --- /dev/null +++ b/doc/source/install/deploy_ceph.rst @@ -0,0 +1,52 @@ +Deploy Ceph +=========== + +Ceph is a highly scalable and fault-tolerant distributed storage +system designed to store vast amounts of data across a cluster of +commodity hardware. It offers object storage, block storage, and +file storage capabilities, making it a versatile solution for +various storage needs. Ceph's architecture is based on a distributed +object store, where data is divided into objects, each with its +unique identifier, and distributed across multiple storage nodes. +It uses a CRUSH algorithm to ensure data resilience and efficient +data placement, even as the cluster scales. Ceph is widely used +in cloud computing environments and provides a cost-effective and +flexible storage solution for organizations managing large volumes of data. + +Kubernetes introduced the CSI standard to allow storage providers +like Ceph to implement their drivers as plugins. Kubernetes can +use the CSI driver for Ceph to provision and manage volumes +directly. By means of CSI stateful applications deployed on top +of Kubernetes can use Ceph to store their data. + +At the same time, Ceph provides the RBD API, which applications +can utilize to create and mount block devices distributed across +the Ceph cluster. The OpenStack Cinder service utilizes this Ceph +capability to offer persistent block devices to virtual machines +managed by the OpenStack Nova. + +The recommended way to deploy Ceph on top of Kubernetes is by means +of `Rook`_ operator. Rook provides Helm charts to deploy the operator +itself which extends the Kubernetes API adding CRDs that enable +managing Ceph clusters via Kuberntes custom objects. For details please +refer to the `Rook`_ documentation. + +To deploy the Rook Ceph operator and a Ceph cluster you can use the script +`ceph.sh`_. Then to generate the client secrets to interface with the Ceph +RBD API use this script `ceph_secrets.sh` + +.. code-block:: bash + + cd ~/osh/openstack-helm-infra + ./tools/deployment/openstack-support-rook/020-ceph.sh + ./tools/deployment/openstack-support-rook/025-ceph-ns-activate.sh + +.. note:: + Please keep in mind that these are the deployment scripts that we + use for testing. For example we place Ceph OSD data object on loop devices + which are slow and are not recommended to use in production. + + +.. _Rook: https://rook.io/ +.. _ceph.sh: https://opendev.org/openstack/openstack-helm-infra/src/branch/master/tools/deployment/openstack-support-rook/020-ceph.sh +.. _ceph-ns-activate.sh: https://opendev.org/openstack/openstack-helm-infra/src/branch/master/tools/deployment/openstack-support-rook/025-ceph-ns-activate.sh diff --git a/doc/source/install/deploy_ingress_controller.rst b/doc/source/install/deploy_ingress_controller.rst new file mode 100644 index 0000000000..069382384d --- /dev/null +++ b/doc/source/install/deploy_ingress_controller.rst @@ -0,0 +1,52 @@ +Deploy ingress controller +========================= + +Deploying an ingress controller when deploying OpenStack on Kubernetes +is essential to ensure proper external access and SSL termination +for your OpenStack services. + +In the OpenStack-Helm project, we utilize multiple ingress controllers +to optimize traffic routing. Specifically, we deploy three independent +instances of the Nginx ingress controller for distinct purposes: + +External Traffic Routing +~~~~~~~~~~~~~~~~~~~~~~~~ + +* ``Namespace``: kube-system +* ``Functionality``: This instance monitors ingress objects across all + namespaces, primarily focusing on routing external traffic into the + OpenStack environment. + +Internal Traffic Routing within OpenStack +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* ``Namespace``: openstack +* ``Functionality``: Designed to handle traffic exclusively within the + OpenStack namespace, this instance plays a crucial role in SSL + termination for enhanced security among OpenStack services. + +Traffic Routing to Ceph Rados Gateway Service +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* ``Namespace``: ceph +* ``Functionality``: Dedicated to routing traffic specifically to the + Ceph Rados Gateway service, ensuring efficient communication with + Ceph storage resources. + +By deploying these three distinct ingress controller instances in their +respective namespaces, we optimize traffic management and security within +the OpenStack-Helm environment. + +To deploy these three ingress controller instances use the script `ingress.sh`_ + +.. code-block:: bash + + cd ~/osh/openstack-helm + ./tools/deployment/component/common/ingress.sh + +.. note:: + These script uses Helm chart from the `openstack-helm-infra`_ repository. We assume + this repo is cloned to the `~/osh` directory. See this :doc:`section `. + +.. _ingress.sh: https://opendev.org/openstack/openstack-helm/src/branch/master/tools/deployment/component/common/ingress.sh +.. _openstack-helm-infra: https://opendev.org/openstack/openstack-helm-infra.git diff --git a/doc/source/install/deploy_kubernetes.rst b/doc/source/install/deploy_kubernetes.rst new file mode 100644 index 0000000000..2132d3461c --- /dev/null +++ b/doc/source/install/deploy_kubernetes.rst @@ -0,0 +1,143 @@ +Deploy Kubernetes +================= + +OpenStack-Helm provides charts that can be deployed on any Kubernetes cluster if it meets +the supported version requirements. However, deploying the Kubernetes cluster itself is beyond +the scope of OpenStack-Helm. + +You can use any Kubernetes deployment tool for this purpose. In this guide, we detail how to set up +a Kubernetes cluster using Kubeadm and Ansible. While not production-ready, this cluster is ideal +as a starting point for lab or proof-of-concept environments. + +All OpenStack projects test their code through an infrastructure managed by the CI +tool, Zuul, which executes Ansible playbooks on one or more test nodes. Therefore, we employ Ansible +roles/playbooks to install required packages, deploy Kubernetes, and then execute tests on it. + +To establish a test environment, the Ansible role deploy-env_ is employed. This role establishes +a basic single/multi-node Kubernetes cluster, ensuring the functionality of commonly used +deployment configurations. The role is compatible with Ubuntu Focal and Ubuntu Jammy distributions. + +Install Ansible +--------------- + +.. code-block:: bash + + pip install ansible + +Prepare Ansible roles +--------------------- + +Here is the Ansible `playbook`_ that is used to deploy Kubernetes. The roles used in this playbook +are defined in different repositories. So in addition to OpenStack-Helm repositories +that we assume have already been cloned to the `~/osh` directory you have to clone +yet another one + +.. code-block:: bash + + cd ~/osh + git clone https://opendev.org/zuul/zuul-jobs.git + +Now let's set the environment variable ``ANSIBLE_ROLES_PATH`` which specifies +where Ansible will lookup roles + +.. code-block:: bash + + export ANSIBLE_ROLES_PATH=~/osh/openstack-helm-infra/roles:~/osh/zuul-jobs/roles + +To avoid setting it every time when you start a new terminal instance you can define this +in the Ansible configuration file. Please see the Ansible documentation. + +Prepare Ansible inventory +------------------------- + +We assume you have three nodes, usually VMs. Those nodes must be available via +SSH using the public key authentication and a ssh user (let say `ubuntu`) +must have passwordless sudo on the nodes. + +Create the Ansible inventory file using the following command + +.. code-block:: bash + + cat > ~/osh/inventory.yaml <`. + +.. _rabbitmq.sh: https://opendev.org/openstack/openstack-helm/src/branch/master/tools/deployment/component/common/rabbitmq.sh +.. _mariadb.sh: https://opendev.org/openstack/openstack-helm/src/branch/master/tools/deployment/component/common/mariadb.sh +.. _memcached.sh: https://opendev.org/openstack/openstack-helm/src/branch/master/tools/deployment/component/common/memcached.sh +.. _openstack-helm-infra: https://opendev.org/openstack/openstack-helm-infra.git diff --git a/doc/source/install/developer/cleaning-deployment.rst b/doc/source/install/developer/cleaning-deployment.rst deleted file mode 100644 index 00811b7b22..0000000000 --- a/doc/source/install/developer/cleaning-deployment.rst +++ /dev/null @@ -1,92 +0,0 @@ -======================= -Cleaning the Deployment -======================= - -Removing Helm Charts -==================== - -To delete an installed helm chart, use the following command: - -.. code-block:: shell - - helm delete ${RELEASE_NAME} --purge - -This will delete all Kubernetes resources generated when the chart was -instantiated. However for OpenStack charts, by default, this will not delete -the database and database users that were created when the chart was installed. -All OpenStack projects can be configured such that upon deletion, their database -will also be removed. To delete the database when the chart is deleted the -database drop job must be enabled before installing the chart. There are two -ways to enable the job, set the job_db_drop value to true in the chart's -``values.yaml`` file, or override the value using the helm install command as -follows: - -.. code-block:: shell - - helm install ${RELEASE_NAME} --set manifests.job_db_drop=true - - -Environment tear-down -===================== - -To tear-down, the development environment charts should be removed first from -the 'openstack' namespace and then the 'ceph' namespace using the commands from -the `Removing Helm Charts` section. Additionally charts should be removed from -the 'nfs' and 'libvirt' namespaces if deploying with NFS backing or bare metal -development support. You can run the following commands to loop through and -delete the charts, then stop the kubelet systemd unit and remove all the -containers before removing the directories used on the host by pods. - -.. code-block:: shell - - for NS in openstack ceph nfs libvirt; do - helm ls --namespace $NS --short | xargs -r -L1 -P2 helm delete --purge - done - - sudo systemctl stop kubelet - sudo systemctl disable kubelet - - sudo docker ps -aq | xargs -r -L1 -P16 sudo docker rm -f - - sudo rm -rf /var/lib/openstack-helm/* - - # NOTE(portdirect): These directories are used by nova and libvirt - sudo rm -rf /var/lib/nova/* - sudo rm -rf /var/lib/libvirt/* - sudo rm -rf /etc/libvirt/qemu/* - #NOTE(chinasubbareddy) cleanup LVM volume groups in case of disk backed ceph osd deployments - for VG in `vgs|grep -v VG|grep -i ceph|awk '{print $1}'`; do - echo $VG - vgremove -y $VG - done - # lets delete loopback devices setup for ceph, if the device names are different in your case, - # please update them here as environmental variables as shown below. - : "${CEPH_OSD_DATA_DEVICE:=/dev/loop0}" - : "${CEPH_OSD_DB_WAL_DEVICE:=/dev/loop1}" - if [ ! -z "$CEPH_OSD_DATA_DEVICE" ]; then - ceph_osd_disk_name=`basename "$CEPH_OSD_DATA_DEVICE"` - if losetup -a|grep $ceph_osd_disk_name; then - losetup -d "$CEPH_OSD_DATA_DEVICE" - fi - fi - if [ ! -z "$CEPH_OSD_DB_WAL_DEVICE" ]; then - ceph_db_wal_disk_name=`basename "$CEPH_OSD_DB_WAL_DEVICE"` - if losetup -a|grep $ceph_db_wal_disk_name; then - losetup -d "$CEPH_OSD_DB_WAL_DEVICE" - fi - fi - echo "let's disable the service" - sudo systemctl disable loops-setup - echo "let's remove the service to setup loopback devices" - if [ -f "/etc/systemd/system/loops-setup.service" ]; then - rm /etc/systemd/system/loops-setup.service - fi - - # NOTE(portdirect): Clean up mounts left behind by kubernetes pods - sudo findmnt --raw | awk '/^\/var\/lib\/kubelet\/pods/ { print $1 }' | xargs -r -L1 -P16 sudo umount -f -l - - -These commands will restore the environment back to a clean Kubernetes -deployment, that can either be manually removed or over-written by -restarting the deployment process. It is recommended to restart the host before -doing so to ensure any residual state, eg. Network interfaces are removed. diff --git a/doc/source/install/developer/deploy-ovs-dpdk.rst b/doc/source/install/developer/deploy-ovs-dpdk.rst deleted file mode 100644 index fccb4f6e0e..0000000000 --- a/doc/source/install/developer/deploy-ovs-dpdk.rst +++ /dev/null @@ -1,185 +0,0 @@ -=============== -Deploy OVS-DPDK -=============== - -Requirements -============ - -A correct DPDK configuration depends heavily on the specific hardware resources -and its configuration. Before deploying Openvswitch with DPDK, check the amount -and type of available hugepages on the host OS. - -.. code-block:: shell - - cat /proc/meminfo | grep Huge - AnonHugePages: 0 kB - ShmemHugePages: 0 kB - HugePages_Total: 8 - HugePages_Free: 6 - HugePages_Rsvd: 0 - HugePages_Surp: 0 - Hugepagesize: 1048576 kB - -In this example, 8 hugepages of 1G size have been allocated. 2 of those are -being used and 6 are still available. - -More information on how to allocate and configure hugepages on the host OS can -be found in the `Openvswitch documentation -`_. - -In order to allow OVS inside a pod to make use of hugepages, the corresponding -type and amount of hugepages must be specified in the resource section of the -OVS chart's values.yaml: - -.. code-block:: yaml - - resources: - enabled: true - ovs: - db: - requests: - memory: "128Mi" - cpu: "100m" - limits: - memory: "1024Mi" - cpu: "2000m" - vswitchd: - requests: - memory: "128Mi" - cpu: "100m" - limits: - memory: "1024Mi" - cpu: "2000m" - # set resources to enabled and specify one of the following when using dpdk - hugepages-1Gi: "1Gi" - # hugepages-2Mi: "512Mi" - -Additionally, the default configuration of the neutron chart must be adapted according -to the underlying hardware. The corresponding configuration parameter is labeled with -"CHANGE-ME" in the script "values_overrides/dpdk.yaml". Specifically, the "ovs_dpdk" -configuration section should list all NICs which should be bound to DPDK with -their corresponding PCI-IDs. Moreover, the name of each NIC needs to be unique, -e.g., dpdk0, dpdk1, etc. - -.. code-block:: yaml - - network: - interface: - tunnel: br-phy - conf: - ovs_dpdk: - enabled: true - driver: uio_pci_generic - nics: - - name: dpdk0 - # CHANGE-ME: modify pci_id according to hardware - pci_id: '0000:05:00.0' - bridge: br-phy - migrate_ip: true - bridges: - - name: br-phy - bonds: [] - -In the example above, bonding isn't used and hence an empty list is passed in the "bonds" -section. - -Deployment -========== - -Once the above requirements are met, start deploying Openstack Helm using the deployment -scripts under the dpdk directory in an increasing order - -.. code-block:: shell - - ./tools/deployment/developer/dpdk/ - -One can also specify the name of Openstack release and container OS distribution as -overrides before running the deployment scripts, for instance, - -.. code-block:: shell - - export OPENSTACK_RELEASE=wallaby - export CONTAINER_DISTRO_NAME=ubuntu - export CONTAINER_DISTRO_VERSION=focal - -Troubleshooting -=============== - -OVS startup failure -------------------- - -If OVS fails to start up because of no hugepages are available, check the -configuration of the OVS daemonset. Older versions of helm-toolkit were not -able to render hugepage configuration into the Kubernetes manifest and just -removed the hugepage attributes. If no hugepage configuration is defined for -the OVS daemonset, consider using a newer version of helm-toolkit. - -.. code-block:: shell - - kubectl get daemonset openvswitch-vswitchd -n openstack -o yaml - [...] - resources: - limits: - cpu: "2" - hugepages-1Gi: 1Gi - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi - [...] - -Adding a DPDK port to Openvswitch fails ---------------------------------------- - -When adding a DPDK port (a NIC bound to DPDK) to OVS fails, one source of error -is related to an incorrect configuration with regards to the NUMA topology of -the underlying hardware. Every NIC is connected to one specific NUMA socket. In -order to use a NIC as DPDK port in OVS, the OVS configurations regarding -hugepage(s) and PMD thread(s) need to match the NUMA topology. - -The NUMA socket a given NIC is connected to can be found in the ovs-vswitchd log: - -.. code-block:: - - kubectl logs -n openstack openvswitch-vswitchd-6h928 - [...] - 2019-07-02T13:42:06Z|00016|dpdk|INFO|EAL: PCI device 0000:00:04.0 on NUMA socket 1 - 2019-07-02T13:42:06Z|00018|dpdk|INFO|EAL: probe driver: 1af4:1000 net_virtio - [...] - -In this example, the NIC with PCI-ID 0000:00:04.0 is connected to NUMA socket -1. As a result, this NIC can only be used by OVS if - -1. hugepages have been allocated on NUMA socket 1 by OVS, and -2. PMD threads have been assigned to NUMA socket 1. - -To allocate hugepages to NUMA sockets in OVS, ensure that the -``socket_memory`` attribute in values.yaml specifies a value for the -corresponding NUMA socket. In the following example, OVS will use one 1G -hugepage for NUMA socket 0 and socket 1. - -.. code-block:: - - socket_memory: 1024,1024 - - -To allocate PMD threads to NUMA sockets in OVS, ensure that the ``pmd_cpu_mask`` -attribute in values.yaml includes CPU sockets on the corresponding NUMA socket. -In the example below, the mask of 0xf covers the first 4 CPU cores which are -distributed across NUMA sockets 0 and 1. - -.. code-block:: - - pmd_cpu_mask: 0xf - -The mapping of CPU cores to NUMA sockets can be determined by means of ``lspci``, for instance: - -.. code-block:: shell - - lspci | grep NUMA - NUMA node(s): 2 - NUMA node0 CPU(s): 0,2,4,6,8,10,12,14 - NUMA node1 CPU(s): 1,3,5,7,9,11,13,15 - -More information can be found in the `Openvswitch documentation -`_. diff --git a/doc/source/install/developer/deploy-with-ceph.rst b/doc/source/install/developer/deploy-with-ceph.rst deleted file mode 100644 index 08923187c8..0000000000 --- a/doc/source/install/developer/deploy-with-ceph.rst +++ /dev/null @@ -1,222 +0,0 @@ -==================== -Deployment With Ceph -==================== - -.. note:: - For other deployment options, select appropriate ``Deployment with ...`` - option from `Index <../developer/index.html>`__ page. - -Deploy Ceph -^^^^^^^^^^^ - -We are going to install Ceph OSDs backed by loopback devices as this will -help us not to attach extra disks, in case if you have enough disks -on the node then feel free to skip creating loopback devices by exporting -CREATE_LOOPBACK_DEVICES_FOR_CEPH to false and export the block devices names -as environment variables(CEPH_OSD_DATA_DEVICE and CEPH_OSD_DB_WAL_DEVICE). - -We are also going to separate Ceph metadata and data onto a different devices -to replicate the ideal scenario of fast disks for metadata and slow disks to store data. -You can change this as per your design by referring to the documentation explained in -../openstack-helm-infra/ceph-osd/values.yaml - -This script will create two loopback devices for Ceph as one disk for OSD data -and other disk for block DB and block WAL. If default devices (loop0 and loop1) are busy in -your case, feel free to change them by exporting environment variables(CEPH_OSD_DATA_DEVICE -and CEPH_OSD_DB_WAL_DEVICE). - -.. note:: - if you are rerunning the below script then make sure to skip the loopback device creation - by exporting CREATE_LOOPBACK_DEVICES_FOR_CEPH to false. - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/040-ceph.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/040-ceph.sh - -Activate the OpenStack namespace to be able to use Ceph -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/045-ceph-ns-activate.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/045-ceph-ns-activate.sh - -Deploy MariaDB -^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/050-mariadb.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/050-mariadb.sh - -Deploy RabbitMQ -^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/060-rabbitmq.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/060-rabbitmq.sh - -Deploy Memcached -^^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/070-memcached.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/070-memcached.sh - -Deploy Keystone -^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/080-keystone.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/080-keystone.sh - -Deploy Heat -^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/090-heat.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/090-heat.sh - -Deploy Horizon -^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/100-horizon.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/100-horizon.sh - -Deploy Rados Gateway for object store -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/110-ceph-radosgateway.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/110-ceph-radosgateway.sh - -Deploy Glance -^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/120-glance.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/120-glance.sh - -Deploy Cinder -^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/130-cinder.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/130-cinder.sh - -Deploy OpenvSwitch -^^^^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/140-openvswitch.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/140-openvswitch.sh - -Deploy Libvirt -^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/150-libvirt.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/150-libvirt.sh - -Deploy Compute Kit (Nova and Neutron) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/160-compute-kit.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/160-compute-kit.sh - -Setup the gateway to the public network -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/ceph/170-setup-gateway.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/ceph/170-setup-gateway.sh diff --git a/doc/source/install/developer/deploy-with-nfs.rst b/doc/source/install/developer/deploy-with-nfs.rst deleted file mode 100644 index 0b2f5a5945..0000000000 --- a/doc/source/install/developer/deploy-with-nfs.rst +++ /dev/null @@ -1,163 +0,0 @@ -=================== -Deployment With NFS -=================== - -.. note:: - For other deployment options, select appropriate ``Deployment with ...`` - option from `Index <../developer/index.html>`__ page. - -Deploy NFS Provisioner -^^^^^^^^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/nfs/040-nfs-provisioner.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/nfs/040-nfs-provisioner.sh - -Deploy MariaDB -^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/nfs/050-mariadb.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/nfs/050-mariadb.sh - -Deploy RabbitMQ -^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/nfs/060-rabbitmq.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/nfs/060-rabbitmq.sh - -Deploy Memcached -^^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/nfs/070-memcached.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/nfs/070-memcached.sh - -Deploy Keystone -^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/nfs/080-keystone.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/nfs/080-keystone.sh - -Deploy Heat -^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/nfs/090-heat.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/nfs/090-heat.sh - -Deploy Horizon -^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/nfs/100-horizon.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/nfs/100-horizon.sh - -Deploy Glance -^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/nfs/120-glance.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/nfs/120-glance.sh - -Deploy OpenvSwitch -^^^^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/nfs/140-openvswitch.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/nfs/140-openvswitch.sh - -Deploy Libvirt -^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/nfs/150-libvirt.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/nfs/150-libvirt.sh - -Deploy Compute Kit (Nova and Neutron) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/nfs/160-compute-kit.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/nfs/160-compute-kit.sh - -Setup the gateway to the public network -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/developer/nfs/170-setup-gateway.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/nfs/170-setup-gateway.sh diff --git a/doc/source/install/developer/deploy-with-tungsten-fabric.rst b/doc/source/install/developer/deploy-with-tungsten-fabric.rst deleted file mode 100644 index bca1aa13f9..0000000000 --- a/doc/source/install/developer/deploy-with-tungsten-fabric.rst +++ /dev/null @@ -1,147 +0,0 @@ -=============================== -Deployment with Tungsten Fabric -=============================== - -Intro -^^^^^ - -Tungsten Fabric is the multicloud and multistack network solution which you can -use for your OpenStack as a network plugin. This document decribes how you can deploy -a single node Open Stack based on Tungsten Fabric using openstack helm for development purpose. - -Prepare host -^^^^^^^^^^^^ - -First you have to set up OpenStack and Linux versions and install needed packages - -.. code-block:: shell - - export OPENSTACK_RELEASE=train - export CONTAINER_DISTRO_NAME=ubuntu - export CONTAINER_DISTRO_VERSION=bionic - sudo apt update -y - sudo apt install -y resolvconf - cd ~/openstack-helm - -Install OpenStack packages -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/common/install-packages.sh - -Install k8s Minikube -^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/common/deploy-k8s.sh - -Setup DNS for use cluster DNS -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: shell - - dns_cluster_ip=`kubectl get svc kube-dns -n kube-system --no-headers -o custom-columns=":spec.clusterIP"` - echo "nameserver ${dns_cluster_ip}" | sudo tee -a /etc/resolvconf/resolv.conf.d/head > /dev/null - sudo dpkg-reconfigure --force resolvconf - sudo systemctl restart resolvconf - - -Setup env for apply values_overrides -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: shell - - export FEATURE_GATES=tf - -Setup OpenStack client -^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/common/setup-client.sh - -Setup Ingress -^^^^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/component/common/ingress.sh - -Setup MariaDB -^^^^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/component/common/mariadb.sh - -Setup Memcached -^^^^^^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/component/common/memcached.sh - -Setup RabbitMQ -^^^^^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/component/common/rabbitmq.sh - -Setup NFS -^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/component/nfs-provisioner/nfs-provisioner.sh - -Setup Keystone -^^^^^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/component/keystone/keystone.sh - -Setup Heat -^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/component/heat/heat.sh - -Setup Glance -^^^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/component/glance/glance.sh - -Prepare host and openstack helm for tf -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/component/compute-kit/tungsten-fabric.sh prepare - -Setup libvirt -^^^^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/component/compute-kit/libvirt.sh - -Setup Neutron and Nova -^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/component/compute-kit/compute-kit.sh - -Setup Tungsten Fabric -^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: shell - - ./tools/deployment/component/compute-kit/tungsten-fabric.sh deploy \ No newline at end of file diff --git a/doc/source/install/developer/exercise-the-cloud.rst b/doc/source/install/developer/exercise-the-cloud.rst deleted file mode 100644 index 594de60c37..0000000000 --- a/doc/source/install/developer/exercise-the-cloud.rst +++ /dev/null @@ -1,90 +0,0 @@ -================== -Exercise the Cloud -================== - -Once OpenStack-Helm has been deployed, the cloud can be exercised either with -the OpenStack client, or the same heat templates that are used in the validation -gates. - -.. literalinclude:: ../../../../tools/deployment/developer/common/900-use-it.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/common/900-use-it.sh - -To run further commands from the CLI manually, execute the following to -set up authentication credentials:: - - export OS_CLOUD=openstack_helm - -Note that this command will only enable you to auth successfully using the -``python-openstackclient`` CLI. To use legacy clients like the -``python-novaclient`` from the CLI, reference the auth values in -``/etc/openstack/clouds.yaml`` and run:: - - export OS_USERNAME='admin' - export OS_PASSWORD='password' - export OS_PROJECT_NAME='admin' - export OS_PROJECT_DOMAIN_NAME='default' - export OS_USER_DOMAIN_NAME='default' - export OS_AUTH_URL='http://keystone.openstack.svc.cluster.local/v3' - -The example above uses the default values used by ``openstack-helm-infra``. - --------------------------------- -Subsequent Runs & Post Clean-up --------------------------------- - -Execution of the **900-use-it.sh** script results in the creation of 4 heat stacks and a unique -keypair enabling access to a newly created VM. Subsequent runs of the **900-use-it.sh** script -requires deletion of the stacks, a keypair, and key files, generated during the initial script -execution. - -The following steps serve as a guide to clean-up the client environment by deleting stacks and -respective artifacts created during the **900-use-it.sh** script: - -1. List the stacks created during script execution which will need to be deleted:: - - sudo openstack --os-cloud openstack_helm stack list - # Sample results returned for *Stack Name* include: - # - heat-vm-volume-attach - # - heat-basic-vm-deployment - # - heat-subnet-pool-deployment - # - heat-public-net-deployment - -2. Delete the stacks returned from the *openstack helm stack list* command above:: - - sudo openstack --os-cloud openstack_helm stack delete heat-vm-volume-attach - sudo openstack --os-cloud openstack_helm stack delete heat-basic-vm-deployment - sudo openstack --os-cloud openstack_helm stack delete heat-subnet-pool-deployment - sudo openstack --os-cloud openstack_helm stack delete heat-public-net-deployment - -3. List the keypair(s) generated during the script execution:: - - sudo openstack --os-cloud openstack_helm keypair list - # Sample Results returned for “Name” include: - # - heat-vm-key - -4. Delete the keypair(s) returned from the list command above:: - - sudo openstack --os-cloud openstack_helm keypair delete heat-vm-key - -5. Manually remove the keypair directories created from the script in the ~/.ssh directory:: - - cd ~/.ssh - rm osh_key - rm known_hosts - -6. As a final validation step, re-run the **openstack helm stack list** and - **openstack helm keypair list** commands and confirm the returned results are shown as empty.:: - - sudo openstack --os-cloud openstack_helm stack list - sudo openstack --os-cloud openstack_helm keypair list - -Alternatively, these steps can be performed by running the script directly:: - -./tools/deployment/developer/common/910-clean-it.sh \ No newline at end of file diff --git a/doc/source/install/developer/index.rst b/doc/source/install/developer/index.rst deleted file mode 100644 index 36981ff495..0000000000 --- a/doc/source/install/developer/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -Deployment -========== - -Contents: - -.. toctree:: - :maxdepth: 2 - - requirements-and-host-config - kubernetes-and-common-setup - deploy-with-nfs - deploy-with-tungsten-fabric - deploy-with-ceph - deploy-ovs-dpdk.rst - exercise-the-cloud - cleaning-deployment diff --git a/doc/source/install/developer/kubernetes-and-common-setup.rst b/doc/source/install/developer/kubernetes-and-common-setup.rst deleted file mode 100644 index 0ff12f006d..0000000000 --- a/doc/source/install/developer/kubernetes-and-common-setup.rst +++ /dev/null @@ -1,135 +0,0 @@ -=========================== -Kubernetes and Common Setup -=========================== - -Install Basic Utilities -^^^^^^^^^^^^^^^^^^^^^^^ - -To get started with OSH, we will need ``git``, ``curl`` and ``make``. - -.. code-block:: shell - - sudo apt install git curl make - -Clone the OpenStack-Helm Repos -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Once the host has been configured the repos containing the OpenStack-Helm charts -should be cloned: - -.. code-block:: shell - - #!/bin/bash - set -xe - - git clone https://opendev.org/openstack/openstack-helm-infra.git - git clone https://opendev.org/openstack/openstack-helm.git - -OSH Proxy & DNS Configuration -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. note:: - - If you are not deploying OSH behind a proxy, skip this step and - continue with "Deploy Kubernetes & Helm". - -In order to deploy OSH behind a proxy, add the following entries to -``openstack-helm-infra/tools/gate/devel/local-vars.yaml``: - -.. code-block:: shell - - proxy: - http: http://PROXY_URL:PORT - https: https://PROXY_URL:PORT - noproxy: 127.0.0.1,localhost,172.17.0.1,.svc.cluster.local - -.. note:: - Depending on your specific proxy, https_proxy may be the same as http_proxy. - Refer to your specific proxy documentation. - -By default OSH will use Google DNS Server IPs (8.8.8.8, 8.8.4.4) and will -update resolv.conf as a result. If those IPs are blocked by your proxy, running -the OSH scripts will result in the inability to connect to anything on the -network. These DNS nameserver entries can be changed by updating the -external_dns_nameservers entry in the file -``openstack-helm-infra/tools/images/kubeadm-aio/assets/opt/playbooks/vars.yaml``. - -.. code-block:: shell - - external_dns_nameservers: - - YOUR_PROXY_DNS_IP - - ALT_PROXY_DNS_IP - -These values can be retrieved by running: - -.. code-block:: shell - - systemd-resolve --status - -Deploy Kubernetes & Helm -^^^^^^^^^^^^^^^^^^^^^^^^ - -You may now deploy kubernetes, and helm onto your machine, first move into the -``openstack-helm`` directory and then run the following: - -.. literalinclude:: ../../../../tools/deployment/developer/common/010-deploy-k8s.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/common/010-deploy-k8s.sh - -This command will deploy a single node minikube cluster. This will use the -parameters in ``${OSH_INFRA_PATH}/playbooks/vars.yaml`` to control the -deployment, which can be over-ridden by adding entries to -``${OSH_INFRA_PATH}/tools/gate/devel/local-vars.yaml``. - -Helm Chart Installation -======================= - -Using the Helm packages previously pushed to the local Helm repository, run the -following commands to instruct tiller to create an instance of the given chart. -During installation, the helm client will print useful information about -resources created, the state of the Helm releases, and whether any additional -configuration steps are necessary. - -Install OpenStack-Helm ----------------------- - -.. note:: The following commands all assume that they are run from the - ``openstack-helm`` directory and the repos have been cloned as above. - -Setup Clients on the host and assemble the charts -================================================= - -The OpenStack clients and Kubernetes RBAC rules, along with assembly of the -charts can be performed by running the following commands: - -.. literalinclude:: ../../../../tools/deployment/developer/common/020-setup-client.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/developer/common/020-setup-client.sh - -Deploy the ingress controller -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. literalinclude:: ../../../../tools/deployment/component/common/ingress.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/component/common/ingress.sh - -To continue to deploy OpenStack on Kubernetes via OSH, see -:doc:`Deploy NFS<./deploy-with-nfs>` or :doc:`Deploy Ceph<./deploy-with-ceph>`. diff --git a/doc/source/install/developer/requirements-and-host-config.rst b/doc/source/install/developer/requirements-and-host-config.rst deleted file mode 100644 index 59e1492e06..0000000000 --- a/doc/source/install/developer/requirements-and-host-config.rst +++ /dev/null @@ -1,100 +0,0 @@ -=================================== -Requirements and Host Configuration -=================================== - -Overview -======== - -Below are some instructions and suggestions to help you get started with a -Kubeadm All-in-One environment on Ubuntu 18.04. -Other supported versions of Linux can also be used, with the appropriate changes -to package installation. - -Requirements -============ - -System Requirements -------------------- - -The recommended minimum system requirements for a full deployment are: - -- 16GB of RAM -- 8 Cores -- 48GB HDD - -For a deployment without cinder and horizon the system requirements are: - -- 8GB of RAM -- 4 Cores -- 48GB HDD - -This guide covers the minimum number of requirements to get started. - -All commands below should be run as a normal user, not as root. -Appropriate versions of Docker, Kubernetes, and Helm will be installed -by the playbooks used below, so there's no need to install them ahead of time. - -.. warning:: By default the Calico CNI will use ``192.168.0.0/16`` and - Kubernetes services will use ``10.96.0.0/16`` as the CIDR for services. Check - that these CIDRs are not in use on the development node before proceeding, or - adjust as required. - -Host Configuration ------------------- - -OpenStack-Helm uses the hosts networking namespace for many pods including, -Ceph, Neutron and Nova components. For this, to function, as expected pods need -to be able to resolve DNS requests correctly. Ubuntu Desktop and some other -distributions make use of ``mdns4_minimal`` which does not operate as Kubernetes -expects with its default TLD of ``.local``. To operate at expected either -change the ``hosts`` line in the ``/etc/nsswitch.conf``, or confirm that it -matches: - -.. code-block:: ini - - hosts: files dns - -Host Proxy & DNS Configuration ------------------------------- - -.. note:: - - If you are not deploying OSH behind a proxy, skip this step. - -Set your local environment variables to use the proxy information. This -involves adding or setting the following values in ``/etc/environment``: - -.. code-block:: shell - - export http_proxy="YOUR_PROXY_ADDRESS:PORT" - export https_proxy="YOUR_PROXY_ADDRESS:PORT" - export ftp_proxy="YOUR_PROXY_ADDRESS:PORT" - export no_proxy="localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,172.17.0.1,.svc.cluster.local,$YOUR_ACTUAL_IP" - export HTTP_PROXY="YOUR_PROXY_ADDRESS:PORT" - export HTTPS_PROXY="YOUR_PROXY_ADDRESS:PORT" - export FTP_PROXY="YOUR_PROXY_ADDRESS:PORT" - export NO_PROXY="localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,172.17.0.1,.svc.cluster.local,$YOUR_ACTUAL_IP" - - -.. note:: - Depending on your specific proxy, https_proxy may be the same as http_proxy. - Refer to your specific proxy documentation. - -Your changes to `/etc/environment` will not be applied until you source them: - -.. code-block:: shell - - source /etc/environment - -OSH runs updates for local apt packages, so we will need to set the proxy for -apt as well by adding these lines to `/etc/apt/apt.conf`: - -.. code-block:: shell - - Acquire::http::proxy "YOUR_PROXY_ADDRESS:PORT"; - Acquire::https::proxy "YOUR_PROXY_ADDRESS:PORT"; - Acquire::ftp::proxy "YOUR_PROXY_ADDRESS:PORT"; - -.. note:: - Depending on your specific proxy, https_proxy may be the same as http_proxy. - Refer to your specific proxy documentation. diff --git a/doc/source/install/ext-dns-fqdn.rst b/doc/source/install/ext-dns-fqdn.rst deleted file mode 100644 index 8008c53ac1..0000000000 --- a/doc/source/install/ext-dns-fqdn.rst +++ /dev/null @@ -1,244 +0,0 @@ -============================ -External DNS to FQDN/Ingress -============================ - -Overview -======== - -In order to access your OpenStack deployment on Kubernetes we can use the Ingress Controller -or NodePorts to provide a pathway in. A background on Ingress, OpenStack-Helm fully qualified -domain name (FQDN) overrides, installation, examples, and troubleshooting will be discussed here. - - -Ingress -======= - -OpenStack-Helm utilizes the `Kubernetes Ingress Controller -`__ - -An Ingress is a collection of rules that allow inbound connections to reach the cluster services. - -:: - - internet - | - [ Ingress ] - --|-----|-- - [ Services ] - - -It can be configured to give services externally-reachable URLs, load balance traffic, -terminate SSL, offer name based virtual hosting, and more. - -Essentially the use of Ingress for OpenStack-Helm is an Nginx proxy service. Ingress (Nginx) is -accessible by your cluster public IP - e.g. the IP associated with -``kubectl get pods -o wide --all-namespaces | grep ingress-api`` -Ingress/Nginx will be listening for server name requests of "keystone" or "keystone.openstack" -and will route those requests to the proper internal K8s Services. -These public listeners in Ingress must match the external DNS that you will set up to access -your OpenStack deployment. Note each rule also has a Service that directs Ingress Controllers -allow access to the endpoints from within the cluster. - - -External DNS and FQDN -===================== - -Prepare ahead of time your FQDN and DNS layouts. There are a handful of OpenStack endpoints -you will want to expose for API and Dashboard access. - -Update your lab/environment DNS server with your appropriate host values creating A Records -for the edge node IP's and various FQDN's. Alternatively you can test these settings locally by -editing your ``/etc/hosts``. Below is an example with a dummy domain ``os.foo.org`` and -dummy Ingress IP ``1.2.3.4``. - -:: - - A Records - - 1.2.3.4 horizon.os.foo.org - 1.2.3.4 neutron.os.foo.org - 1.2.3.4 keystone.os.foo.org - 1.2.3.4 nova.os.foo.org - 1.2.3.4 metadata.os.foo.org - 1.2.3.4 glance.os.foo.org - - -The default FQDN's for OpenStack-Helm are - -:: - - horizon.openstack.svc.cluster.local - neutron.openstack.svc.cluster.local - keystone.openstack.svc.cluster.local - nova.openstack.svc.cluster.local - metadata.openstack.svc.cluster.local - glance.openstack.svc.cluster.local - -We want to change the **public** configurations to match our DNS layouts above. In each Chart -``values.yaml`` is a ``endpoints`` configuration that has ``host_fqdn_override``'s for each API -that the Chart either produces or is dependent on. `Read more about how Endpoints are developed -`__. -Note while Glance Registry is listening on a Ingress http endpoint, you will not need to expose -the registry for external services. - - -Installation -============ - -Implementing the FQDN overrides **must** be done at install time. If you run these as helm upgrades, -Ingress will notice the updates though none of the endpoint build-out jobs will run again, -unless they are cleaned up manually or using a tool like Armada. - -Two similar options exist to set the FQDN overrides for External DNS mapping. - -**First**, edit the ``values.yaml`` for Neutron, Glance, Horizon, Keystone, and Nova. - -Using Horizon as an example, find the ``endpoints`` config. - -For ``identity`` and ``dashboard`` at ``host_fdqn_override.public`` replace ``null`` with the -value as ``keystone.os.foo.org`` and ``horizon.os.foo.org`` - -.. code:: bash - - endpoints: - cluster_domain_suffix: cluster.local - identity: - name: keystone - hosts: - default: keystone-api - public: keystone - host_fqdn_override: - default: null - public: keystone.os.foo.org - . - . - dashboard: - name: horizon - hosts: - default: horizon-int - public: horizon - host_fqdn_override: - default: null - public: horizon.os.foo.org - - -After making the configuration changes, run a ``make`` and then install as you would from -AIO or MultiNode instructions. - -**Second** option would be as ``--set`` flags when calling ``helm install`` - -Add to the Install steps these flags - also adding a shell environment variable to save on -repeat code. - -.. code-block:: shell - - export FQDN=os.foo.org - - helm install --name=horizon ./horizon --namespace=openstack \ - --set network.node_port.enabled=true \ - --set endpoints.dashboard.host_fqdn_override.public=horizon.$FQDN \ - --set endpoints.identity.host_fqdn_override.public=keystone.$FQDN - - - -Note if you need to make a DNS change, you will have to do uninstall (``helm delete ``) -and install again. - -Once installed, access the API's or Dashboard at `http://horizon.os.foo.org` - - -Examples -======== - -Code examples below. - -If doing an `AIO install -`__, -all the ``--set`` flags - -.. code-block:: shell - - export FQDN=os.foo.org - - helm install --name=keystone local/keystone --namespace=openstack \ - --set endpoints.identity.host_fqdn_override.public=keystone.$FQDN - - helm install --name=glance local/glance --namespace=openstack \ - --set storage=pvc \ - --set endpoints.image.host_fqdn_override.public=glance.$FQDN \ - --set endpoints.identity.host_fqdn_override.public=keystone.$FQDN - - helm install --name=nova local/nova --namespace=openstack \ - --values=./tools/overrides/mvp/nova.yaml \ - --set conf.nova.libvirt.virt_type=qemu \ - --set conf.nova.libvirt.cpu_mode=none \ - --set endpoints.compute.host_fqdn_override.public=nova.$FQDN \ - --set endpoints.compute_metadata.host_fqdn_override.public=metadata.$FQDN \ - --set endpoints.image.host_fqdn_override.public=glance.$FQDN \ - --set endpoints.network.host_fqdn_override.public=neutron.$FQDN \ - --set endpoints.identity.host_fqdn_override.public=keystone.$FQDN - - helm install --name=neutron local/neutron \ - --namespace=openstack --values=./tools/overrides/mvp/neutron-ovs.yaml \ - --set endpoints.network.host_fqdn_override.public=neutron.$FQDN \ - --set endpoints.compute.host_fqdn_override.public=nova.$FQDN \ - --set endpoints.identity.host_fqdn_override.public=keystone.$FQDN - - helm install --name=horizon local/horizon --namespace=openstack \ - --set=network.node_port.enabled=true \ - --set endpoints.dashboard.host_fqdn_override.public=horizon.$FQDN \ - --set endpoints.identity.host_fqdn_override.public=keystone.$FQDN - - - -Troubleshooting -=============== - -**Review the Ingress configuration.** - -Get the Nginx configuration from the Ingress Pod: - -.. code-block:: shell - - kubectl exec -it ingress-api-2210976527-92cq0 -n openstack -- cat /etc/nginx/nginx.conf - - -Look for *server* configuration with a *server_name* matching your desired FQDN - -:: - - server { - server_name nova.os.foo.org; - listen [::]:80; - set $proxy_upstream_name "-"; - location / { - set $proxy_upstream_name "openstack-nova-api-n-api"; - . - . - } - - - -**Check Chart Status** - -Get the ``helm status`` of your chart. - -.. code-block:: shell - - helm status keystone - - -Verify the *v1beta1/Ingress* resource has a Host with your FQDN value - -:: - - LAST DEPLOYED: Thu Sep 28 20:00:49 2017 - NAMESPACE: openstack - STATUS: DEPLOYED - - RESOURCES: - ==> v1beta1/Ingress - NAME HOSTS ADDRESS PORTS AGE - keystone keystone,keystone.os.foo.org 1.2.3.4 80 35m - - diff --git a/doc/source/install/index.rst b/doc/source/install/index.rst index d23689e5fe..d684e0e0d3 100644 --- a/doc/source/install/index.rst +++ b/doc/source/install/index.rst @@ -4,11 +4,14 @@ Installation Contents: .. toctree:: - :maxdepth: 2 + :maxdepth: 2 + + before_deployment + deploy_kubernetes + prepare_kubernetes + deploy_ceph + setup_openstack_client + deploy_ingress_controller + deploy_openstack_backend + deploy_openstack - common-requirements - developer/index - multinode - kubernetes-gate - ext-dns-fqdn - plugins/index diff --git a/doc/source/install/kubernetes-gate.rst b/doc/source/install/kubernetes-gate.rst deleted file mode 100644 index 3335a2d61e..0000000000 --- a/doc/source/install/kubernetes-gate.rst +++ /dev/null @@ -1,143 +0,0 @@ -===================== -Gate-Based Kubernetes -===================== - -Overview -======== - -You can use any Kubernetes deployment tool to bring up a working Kubernetes -cluster for use with OpenStack-Helm. This guide describes how to simply stand -up a multinode Kubernetes cluster via the OpenStack-Helm gate scripts, -which use KubeADM and Ansible. Although this cluster won't be -production-grade, it will serve as a quick starting point in a lab or -proof-of-concept environment. - -OpenStack-Helm-Infra KubeADM deployment -======================================= - -On the worker nodes: - -.. code-block:: shell - - #!/bin/bash - set -xe - sudo apt-get update - sudo apt-get install --no-install-recommends -y git - - -SSH-Key preparation -------------------- - -Create an ssh-key on the master node, and add the public key to each node that -you intend to join the cluster. - -.. note:: - 1. To generate the key you can use ``ssh-keygen -t rsa`` - 2. To copy the ssh key to each node, this can be accomplished with - the ``ssh-copy-id`` command, for example: *ssh-copy-id - ubuntu@192.168.122.178* - 3. Copy the key: ``sudo cp ~/.ssh/id_rsa /etc/openstack-helm/deploy-key.pem`` - 4. Set correct ownership: ``sudo chown ubuntu - /etc/openstack-helm/deploy-key.pem`` - - Test this by ssh'ing to a node and then executing a command with - 'sudo'. Neither operation should require a password. - -Clone the OpenStack-Helm Repos ------------------------------- - -Once the host has been configured the repos containing the OpenStack-Helm charts -should be cloned onto each node in the cluster: - -.. code-block:: shell - - #!/bin/bash - set -xe - - sudo chown -R ubuntu: /opt - git clone https://opendev.org/openstack/openstack-helm-infra.git /opt/openstack-helm-infra - git clone https://opendev.org/openstack/openstack-helm.git /opt/openstack-helm - - -Create an inventory file ------------------------- - -On the master node create an inventory file for the cluster: - -.. note:: - node_one, node_two and node_three below are all worker nodes, - children of the master node that the commands below are executed on. - -.. code-block:: shell - - #!/bin/bash - set -xe - cat > /opt/openstack-helm-infra/tools/gate/devel/multinode-inventory.yaml < /opt/openstack-helm-infra/tools/gate/devel/multinode-vars.yaml <`_. -In particular, ``kubernetes_cluster_pod_subnet`` can be used to override the -pod subnet set up by Calico (the default container SDN), if you have a -preexisting network that conflicts with the default pod subnet of 192.168.0.0/16. - -.. note:: - This installation, by default will use Google DNS servers, 8.8.8.8 or 8.8.4.4 - and updates resolv.conf. These DNS nameserver entries can be changed by - updating file ``/opt/openstack-helm-infra/tools/images/kubeadm-aio/assets/opt/playbooks/vars.yaml`` - under section ``external_dns_nameservers``. This change must be done on each - node in your cluster. - - -Run the playbooks ------------------ - -On the master node run the playbooks: - -.. code-block:: shell - - #!/bin/bash - set -xe - cd /opt/openstack-helm-infra - make dev-deploy setup-host multinode - make dev-deploy k8s multinode diff --git a/doc/source/install/multinode.rst b/doc/source/install/multinode.rst deleted file mode 100644 index bf39bfd246..0000000000 --- a/doc/source/install/multinode.rst +++ /dev/null @@ -1,331 +0,0 @@ -========= -Multinode -========= - -Overview -======== - -In order to drive towards a production-ready OpenStack solution, our -goal is to provide containerized, yet stable `persistent -volumes `_ -that Kubernetes can use to schedule applications that require state, -such as MariaDB (Galera). Although we assume that the project should -provide a "batteries included" approach towards persistent storage, we -want to allow operators to define their own solution as well. Examples -of this work will be documented in another section, however evidence of -this is found throughout the project. If you find any issues or gaps, -please create a `story `_ -to track what can be done to improve our documentation. - -.. note:: - Please see the supported application versions outlined in the - `source variable file `_. - -Other versions and considerations (such as other CNI SDN providers), -config map data, and value overrides will be included in other -documentation as we explore these options further. - -The installation procedures below, will take an administrator from a new -``kubeadm`` installation to OpenStack-Helm deployment. - -.. note:: Many of the default container images that are referenced across - OpenStack-Helm charts are not intended for production use; for example, - while LOCI and Kolla can be used to produce production-grade images, their - public reference images are not prod-grade. In addition, some of the default - images use ``latest`` or ``master`` tags, which are moving targets and can - lead to unpredictable behavior. For production-like deployments, we - recommend building custom images, or at minimum caching a set of known - images, and incorporating them into OpenStack-Helm via values overrides. - -.. warning:: Until the Ubuntu kernel shipped with 16.04 supports CephFS - subvolume mounts by default the `HWE Kernel - <../troubleshooting/ubuntu-hwe-kernel.html>`__ is required to use CephFS. - -Kubernetes Preparation -====================== - -You can use any Kubernetes deployment tool to bring up a working Kubernetes -cluster for use with OpenStack-Helm. For production deployments, -please choose (and tune appropriately) a highly-resilient Kubernetes -distribution, e.g.: - -- `Airship `_, a declarative open cloud - infrastructure platform -- `KubeADM `_, - the foundation of a number of Kubernetes installation solutions - -For a lab or proof-of-concept environment, the OpenStack-Helm gate scripts -can be used to quickly deploy a multinode Kubernetes cluster using KubeADM -and Ansible. Please refer to the deployment guide -`here <./kubernetes-gate.html>`__. - -Managing and configuring a Kubernetes cluster is beyond the scope -of OpenStack-Helm and this guide. - -Deploy OpenStack-Helm -===================== - -.. note:: - The following commands all assume that they are run from the - ``/opt/openstack-helm`` directory. - - -Setup Clients on the host and assemble the charts -------------------------------------------------- - -The OpenStack clients and Kubernetes RBAC rules, along with assembly of the -charts can be performed by running the following commands: - -.. literalinclude:: ../../../tools/deployment/multinode/010-setup-client.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/010-setup-client.sh - - -Deploy the ingress controller ------------------------------ - -.. code-block:: shell - - export OSH_DEPLOY_MULTINODE=True - -.. literalinclude:: ../../../tools/deployment/component/common/ingress.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - OSH_DEPLOY_MULTINODE=True ./tools/deployment/component/common/ingress.sh - -Create loopback devices for CEPH --------------------------------- - -Create two loopback devices for ceph as one disk for OSD data and other disk for -block DB and block WAL. -If loop0 and loop1 devices are busy in your case , feel free to change them in parameters -by using --ceph-osd-data and --ceph-osd-dbwal options. - -.. code-block:: shell - - ansible all -i /opt/openstack-helm-infra/tools/gate/devel/multinode-inventory.yaml -m shell -s -a "/opt/openstack-helm/tools/deployment/common/setup-ceph-loopback-device.sh --ceph-osd-data /dev/loop0 --ceph-osd-dbwal /dev/loop1" - -Deploy Ceph ------------ - -The script below configures Ceph to use loopback devices created in previous step as backend for ceph osds. -To configure a custom block device-based backend, please refer -to the ``ceph-osd`` `values.yaml `_. - -Additional information on Kubernetes Ceph-based integration can be found in -the documentation for the -`CephFS `_ -and `RBD `_ -storage provisioners, as well as for the alternative -`NFS `_ provisioner. - -.. warning:: The upstream Ceph image repository does not currently pin tags to - specific Ceph point releases. This can lead to unpredictable results - in long-lived deployments. In production scenarios, we strongly recommend - overriding the Ceph images to use either custom built images or controlled, - cached images. - -.. note:: - The `./tools/deployment/multinode/kube-node-subnet.sh` script requires docker - to run. - -.. literalinclude:: ../../../tools/deployment/multinode/030-ceph.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/030-ceph.sh - -Activate the openstack namespace to be able to use Ceph -------------------------------------------------------- - -.. literalinclude:: ../../../tools/deployment/multinode/040-ceph-ns-activate.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/040-ceph-ns-activate.sh - -Deploy MariaDB --------------- - -.. literalinclude:: ../../../tools/deployment/multinode/050-mariadb.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/050-mariadb.sh - -Deploy RabbitMQ ---------------- - -.. literalinclude:: ../../../tools/deployment/multinode/060-rabbitmq.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/060-rabbitmq.sh - -Deploy Memcached ----------------- - -.. literalinclude:: ../../../tools/deployment/multinode/070-memcached.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/070-memcached.sh - -Deploy Keystone ---------------- - -.. literalinclude:: ../../../tools/deployment/multinode/080-keystone.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/080-keystone.sh - -Deploy Rados Gateway for object store -------------------------------------- - -.. literalinclude:: ../../../tools/deployment/multinode/090-ceph-radosgateway.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/090-ceph-radosgateway.sh - -Deploy Glance -------------- - -.. literalinclude:: ../../../tools/deployment/multinode/100-glance.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/100-glance.sh - -Deploy Cinder -------------- - -.. literalinclude:: ../../../tools/deployment/multinode/110-cinder.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/110-cinder.sh - -Deploy OpenvSwitch ------------------- - -.. literalinclude:: ../../../tools/deployment/multinode/120-openvswitch.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/120-openvswitch.sh - -Deploy Libvirt --------------- - -.. literalinclude:: ../../../tools/deployment/multinode/130-libvirt.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/130-libvirt.sh - -Deploy Compute Kit (Nova and Neutron) -------------------------------------- - -.. literalinclude:: ../../../tools/deployment/multinode/140-compute-kit.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/140-compute-kit.sh - -Deploy Heat ------------ - -.. literalinclude:: ../../../tools/deployment/multinode/150-heat.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/150-heat.sh - -Deploy Barbican ---------------- - -.. literalinclude:: ../../../tools/deployment/multinode/160-barbican.sh - :language: shell - :lines: 1,17- - -Alternatively, this step can be performed by running the script directly: - -.. code-block:: shell - - ./tools/deployment/multinode/160-barbican.sh - -Configure OpenStack -------------------- - -Configuring OpenStack for a particular production use-case is beyond the scope -of this guide. Please refer to the -OpenStack `Configuration `_ -documentation for your selected version of OpenStack to determine -what additional values overrides should be -provided to the OpenStack-Helm charts to ensure appropriate networking, -security, etc. is in place. diff --git a/doc/source/install/plugins/deploy-tap-as-a-service-neutron-plugin.rst b/doc/source/install/plugins/deploy-tap-as-a-service-neutron-plugin.rst deleted file mode 100644 index ece14abfe3..0000000000 --- a/doc/source/install/plugins/deploy-tap-as-a-service-neutron-plugin.rst +++ /dev/null @@ -1,339 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - -.. - -========================================================== -Deploy tap-as-a-service (TaaS) Neutron / Dashboard plugin -========================================================== - -This guide explains how to deploy tap-as-a-service (TaaS) Neutron plugin and -TaaS Dashboard plugin in Neutron and Horizon charts respectively. - -TaaS plugin provides a mechanism to mirror certain traffic (for example tagged -with specific VLANs) from a source VM to any traffic analyzer VM. When packet -will be forwarded, the original value of source and target ip/ports information -will not be altered and the system administrator will be able to run, for ex. -tcpdump, on the target VM to trace these packets. - -For more details, refer to TaaS specification: Tap-as-a-service_. - -.. _Tap-as-a-service: https://github.com/openstack/tap-as-a-service/blob/master/specs/mitaka/tap-as-a-service.rst - - -TaaS Architecture -================== - -As any other Neutron plugin, TaaS neutron plugin functionality consists of -following modules: - -.. figure:: figures/taas-architecture.png - :alt: Neutron TaaS Architecture - -**TaaS Plugin**: This is the front-end of TaaS which runs on controller node -(Neutron server). This serves TaaS APIs and stores/retrieves TaaS configuration -state to/from Neutron TaaS DB. - -**TaaS Agent, TaaS OVS Driver and TaaS SR-IOV Driver**: This forms the back-end -of TaaS which runs as a ML2 agent extension on compute nodes. It handles the RPC -calls made by TaaS Plugin and configures the mechanism driver, i.e. OpenVSwitch -or SR-IOV Nic Switch. - -**TaaS Dashboard Plugin**: Horizon Plugin which adds GUI panels for TaaS -resources in the Horizon Dashboard. - - -Prepare LOCI images -====================== - -Before deploying TaaS and/or TaaS Dashboard, it needs to be added in Neutron -and/or Horizon LOCI images. - -This is a two step process, i.e. - -#. Prepare a requirements LOCI image with Neutron TaaS and TaaS Dashboard code - installed. - -#. Prepare Neutron or Horizon LOCI image using this requirements image as - :code:`docker build --build-arg WHEELS` command argument. - -Requirements LOCI image -------------------------- - -* Create a patchset for ``openstack/requirements`` repo - - Add TaaS and TaaS dashboard dependencies in :code:`upper-constraints.txt` - file in :code:`openstack/requirements` repo, i.e. - https://opendev.org/openstack/requirements - - .. path upper-constraints - .. code-block:: none - - git+https://opendev.org/openstack/tap-as-a-service@master#egg=tap-as-a-service - git+https://opendev.org/openstack/tap-as-a-service-dashboard@master#egg=tap-as-a-service-dashboard - - .. end - - For example if gerrit refspec for this commit is "refs/changes/xx/xxxxxx/x", - so export the :code:`REQUIREMENTS_REF_SPEC` variable as follows: - - .. path REQUIREMENTS_REF_SPEC - .. code-block:: bash - - export REQUIREMENTS_REF_SPEC="refs/changes/xx/xxxxxx/x" - - .. end - -* Build the requirements LOCI image using above commit - - Use it as ``docker build --build-arg PROJECT_REF=${REQUIREMENTS_REF_SPEC}`` - command argument to build the requirements LOCI image. - - -Neutron and Horizon LOCI images ---------------------------------- - -* Create a patchset for ``openstack/neutron`` repo - - Add TaaS dependency in ``requirements.txt`` file in ``openstack/neutron`` - repo, i.e. https://opendev.org/openstack/neutron - - .. path patchset-neutron - .. code-block:: none - - tap-as-a-service - - .. end - - For example if gerrit refspec for this commit is "refs/changes/xx/xxxxxx/x"; - so export the :code:`NEUTRON_REF_SPEC` variable as follows: - - .. path patchset-neutron-export - .. code-block:: bash - - export NEUTRON_REF_SPEC="refs/changes/xx/xxxxxx/x" - - .. end - -* Create a patchset for ``openstack/horizon`` repo - - Add TaaS Dashboard dependency in ``requirements.txt`` file in - ``openstack/horizon`` repo, i.e. https://opendev.org/openstack/horizon - - .. path patchset-horizon - .. code-block:: none - - tap-as-a-service-dashboard - - .. end - - For example if gerrit refspec for this commit is "refs/changes/xx/xxxxxx/x"; - so export the :code:`HORIZON_REF_SPEC` variable as follows: - - .. path patchset-horizon-export - .. code-block:: bash - - export HORIZON_REF_SPEC="refs/changes/xx/xxxxxx/x" - - .. end - -* Putting it all together - - Apart from the variables above with gerrit refspec values, additionally - export following environment variables with values as applicable: - - .. path other-env-export - .. code-block:: bash - - export OPENSTACK_VERSION="stable/ocata" - export PRIVATE_REPO="docker.io/username" - - .. end - - Use above gerrit commits to prepare the LOCI images using following script: - - .. path main-script - .. code-block:: bash - - #!/bin/bash - set -ex - - # export following variables with applicable values before invoking the script - #---------- - : ${OPENSTACK_VERSION:="stable/ocata"} - : ${REQUIREMENTS_REF_SPEC:=""} - : ${NEUTRON_REF_SPEC:=""} - : ${HORIZON_REF_SPEC:=""} - : ${PRIVATE_REPO:="docker.io/username"} # Replace with your own dockerhub repo - #---------- - - IMAGE_TAG="${OPENSTACK_VERSION#*/}" - REGEX_GERRIT_REF_SPEC="^refs" - - [[ ${REQUIREMENTS_REF_SPEC} =~ ${REGEX_GERRIT_REF_SPEC} ]] || - (echo "Please set a proper value for REQUIREMENTS_REF_SPEC env variable" && exit) - - [[ ${NEUTRON_REF_SPEC} =~ ${REGEX_GERRIT_REF_SPEC} ]] || - (echo "Please set a proper value for NEUTRON_REF_SPEC env variable" && exit) - - [[ ${HORIZON_REF_SPEC} =~ ${REGEX_GERRIT_REF_SPEC} ]] || - (echo "Please set a proper value for HORIZON_REF_SPEC env variable" && exit) - - # Login to private-repo : provide login password when asked - sudo docker login - - sudo docker run -d \ - --name docker-in-docker \ - --privileged=true \ - --net=host \ - -v /var/lib/docker \ - -v ${HOME}/.docker/config.json:/root/.docker/config.json:ro\ - docker.io/docker:17.07.0-dind \ - dockerd \ - --pidfile=/var/run/docker.pid \ - --host=unix:///var/run/docker.sock \ - --storage-driver=overlay2 - sudo docker exec docker-in-docker apk update - sudo docker exec docker-in-docker apk add git - - # Prepare Requirements image - sudo docker exec docker-in-docker docker build --force-rm --pull --no-cache \ - https://opendev.org/openstack/loci.git \ - --network host \ - --build-arg FROM=gcr.io/google_containers/ubuntu-slim:0.14 \ - --build-arg PROJECT=requirements \ - --build-arg PROJECT_REF=${REQUIREMENTS_REF_SPEC} \ - --tag ${PRIVATE_REPO}/requirements:${IMAGE_TAG} - sudo docker exec docker-in-docker docker push ${PRIVATE_REPO}/requirements:${IMAGE_TAG} - - # Prepare Neutron image - sudo docker exec docker-in-docker docker build --force-rm --pull --no-cache \ - https://opendev.org/openstack/loci.git \ - --build-arg PROJECT=neutron \ - --build-arg PROJECT_REF=${NEUTRON_REF_SPEC} \ - --build-arg FROM=gcr.io/google_containers/ubuntu-slim:0.14 \ - --build-arg PROFILES="fluent neutron linuxbridge openvswitch" \ - --build-arg PIP_PACKAGES="pycrypto" \ - --build-arg WHEELS=${PRIVATE_REPO}/requirements:${IMAGE_TAG} \ - --tag ${PRIVATE_REPO}/neutron:${IMAGE_TAG} - sudo docker exec docker-in-docker docker push ${PRIVATE_REPO}/neutron:${IMAGE_TAG} - - # Prepare Neutron sriov image - sudo docker exec docker-in-docker docker build --force-rm --pull --no-cache \ - https://opendev.org/openstack/loci.git \ - --build-arg PROJECT=neutron \ - --build-arg PROJECT_REF=${NEUTRON_REF_SPEC} \ - --build-arg FROM=docker.io/ubuntu:18.04 \ - --build-arg PROFILES="fluent neutron linuxbridge openvswitch" \ - --build-arg PIP_PACKAGES="pycrypto" \ - --build-arg DIST_PACKAGES="ethtool lshw" \ - --build-arg WHEELS=${PRIVATE_REPO}/requirements:${IMAGE_TAG} \ - --tag ${PRIVATE_REPO}/neutron:${IMAGE_TAG}-sriov-1804 - sudo docker exec docker-in-docker docker push ${PRIVATE_REPO}/neutron:${IMAGE_TAG}-sriov-1804 - - # Prepare Horizon image - sudo docker exec docker-in-docker docker build --force-rm --pull --no-cache \ - https://opendev.org/openstack/loci.git \ - --build-arg PROJECT=horizon \ - --build-arg PROJECT_REF=${HORIZON_REF_SPEC} \ - --build-arg FROM=gcr.io/google_containers/ubuntu-slim:0.14 \ - --build-arg PROFILES="fluent horizon apache" \ - --build-arg PIP_PACKAGES="pycrypto" \ - --build-arg WHEELS=${PRIVATE_REPO}/requirements:${IMAGE_TAG} \ - --tag ${PRIVATE_REPO}/horizon:${IMAGE_TAG} - sudo docker exec docker-in-docker docker push ${PRIVATE_REPO}/horizon:${IMAGE_TAG} - - .. end - - -Deploy TaaS Plugin -================== - -Override images in Neutron chart ---------------------------------- - -Override the :code:`images` section parameters for Neutron chart with the -custom LOCI image's tag, prepared as explained in above sections. - -.. code-block:: yaml - - images: - tags: - neutron_db_sync: ${PRIVATE_REPO}/neutron:ocata - neutron_server: ${PRIVATE_REPO}/neutron:ocata - neutron_dhcp: ${PRIVATE_REPO}/neutron:ocata - neutron_metadata: ${PRIVATE_REPO}/neutron:ocata - neutron_l3: ${PRIVATE_REPO}/neutron:ocata - neutron_openvswitch_agent: ${PRIVATE_REPO}/neutron:ocata - neutron_linuxbridge_agent: ${PRIVATE_REPO}/neutron:ocata - neutron_sriov_agent: ${PRIVATE_REPO}/neutron:ocata-sriov-1804 - neutron_sriov_agent_init: ${PRIVATE_REPO}/neutron:ocata-sriov-1804 - -Configure TaaS in Neutron chart --------------------------------- - -While deploying neutron-server and L2 agents, TaaS should be enabled in -``conf: neutron`` section to add TaaS as a service plugin; in ``conf: plugins`` -section to add TaaS as a L2 agent extension; in ``conf: taas_plugin`` section -to configure the ``service_provider`` endpoint used by Neutron TaaS plugin: - -.. code-block:: yaml - - conf: - neutron: - DEFAULT: - service_plugins: taas - plugins: - ml2_conf: - agent: - extensions: taas - taas: - taas: - enabled: True - taas_plugin: - service_providers: - service_provider: TAAS:TAAS:neutron_taas.services.taas.service_drivers.taas_rpc.TaasRpcDriver:default - - -Deploy TaaS Dashboard Plugin -============================ - -TaaS dashboard plugin can be deployed simply by using custom LOCI images having -TaaS Dashboard code installed (as explained in above sections), i.e. override -the :code:`images` section parameters for Horizon charts: - -.. code-block:: yaml - - images: - tags: - horizon_db_sync: ${PRIVATE_REPO}/horizon:ocata - horizon: ${PRIVATE_REPO}/horizon:ocata - - -Set log level for TaaS -====================== - -Default log level for Neutron TaaS is :code:`INFO`. For changing it, override -following parameter: - -.. code-block:: yaml - - conf: - logging: - logger_neutron_taas: - level: INFO - - -References -========== -#. Neutron TaaS support in Openstack-Helm commits: - - - https://review.openstack.org/#/c/597200/ - - https://review.openstack.org/#/c/607392/ - -#. Add TaaS panel to Horizon Dashboard: - - - https://review.openstack.org/#/c/621606/ diff --git a/doc/source/install/plugins/figures/taas-architecture.png b/doc/source/install/plugins/figures/taas-architecture.png deleted file mode 100644 index a6ad36315cfc0420041454abaf98e773d54439c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98682 zcmeFYRd5_ZvnDELW|qaWc*HD=nbBgj7)H#@%*;#{Gt&qxw%B5g*kWd1{pXzBy>IvR zK5RyGPgF<8RCQKn)|XjdMX4xBqaqO@K|nyD%F0NnK|nx%fq;PQLV){xa)m`{{`mvx zq9!c{Q8zKpXnc1&K>97YG6IuS`}#RKwHo z%n#mJW32U|y|?Ny7^9eot(<_(80@fNn|ruVb1koIc$55g!YahtD_FtXi!T7&OYguR zD1Y3lfhgM2syROp**-Ju8Mh@4aZeiYb#lGjV)wY%;Nv*T;o~rqLntizKavEUsji3m ze)xj=291Z49PjI33m7ChVN|8^Vw5IWLwf*;rY{s*GS)vbgM$ zUlv5fjAfLep*i{<78tMlpidU{c27C76IR;{Opd`T>Uhl4<;*G9xdvWx-c@n9{?Pgp$rG{MmqKBK@y0pRbwi z?+riNkIsJdVv~1yv0wDshGHbPZ`OMY>jpf-Y2fgxNpo@jmxM$03?d+1e!$yF)@7iJ zB{-u*hKG+vO4-45(}%fT{`~KcNEG*eueBWf{a;G?uiH2wC*=J9ZT^o2{(qwl1w=>> z8{uc(>>TgwJN&w#;;OV$a~6z*G2qpXXiH1Wz~B3!%g2(63iuz*hf+jT#8a@uqw%E3F`(owN}s0zBktXdgv+4_hx}Nd%j_(E{vH$W_4njTM3UY|_fL~7 zi~sbb{|E}K>ysFLz-34SZl|;SwLQ`}3XmJOM8XJ7#`9$jRKsk8Y) z#J5MYW63n~sWHX>J&f>C8POSp@pNXr2FqD8eXoPGUzd5|cp$cc7OVMW9C03A?$8&p zJ*D^msIR2Ik_e!BC0?eX1@L{&kAa@U&qN>`5%iTm>Fk)EK+_woX!F6 zhT&Id`J)Td4%WX;<0b&K^Du_)&8;|jQ+sk{o-{m=RWAW7na5`&IXKX=wM zG^~rQR0wfu&F$i4raXikJ7{0G31)s@{fp5oqu-~>rbKyjbE_L3t`fk&$cTv+ktUE{ zWJdHqGK~72;)|5>*z!-kC2u&G09!uAqyTr>1&|`7;2L(saH?P(M&!wL{#9Wu7Td`k3C#k$6dkyI&$DQEPRwYFc& zELc&az`wO>Z+=0@AZKRj3_xgDvs-Pk#wJ0F&{ZBYZOp~PoJYpAL4j?k z+p%*jQjxL$TFbiX2?B;tZ|WyviV#YAumq2 z`jr{t;o?^L#Kgp;`J*jrJ8*@gH+5n0hnW5F{dhhCc;8tqE-h8XRs-QMEST`h`ehMh zv<;##Fc`hVRHzYUL>ROYAUnHAE~k_Wm4AQ8Grlxx%Zw73p#Mb+2Y5%rKPovF%iY}t z6xff628MRx$l~D?u0iW72WV*3l1bbK1Tx&f(NR*f3eMCjuk9F zfHO8RDe$IkH$c+OQVwbyQ8nux-;n!#y$f|rSC?}C?;nH>E;$`&bs{d1#;O-8h#F(W=YJ)>hrPdne3SH3Xb32$L$ zMyZSgE~=1>NX$vTclX5s<~|K(kXJcTq6e?>=zdWadQtvQJT>RW(XFh1hvRW=QGZXy ztT7nsJ`Eht1t82G=Z7?*__v>;T#cvO6>A0M9F76Yn8Zm9d4dPkWEw4r5J_JkiKBra z(~6N;V5k}IkpUs1^E;ZHR!}D(3x28iGD?PE;op3WWCkAiwd7}qrL}Aefsgsc50ybP^+BiZB|*HXL%_`4vVYtKML0Jy7--Xp0u4A!6}D6%$rD?eMEGSkRMqFMXd#D53J*PB!eplNR4jT(1U!b*`|^T(j8D zQ0R)G&rjXjE7wBANU65@`T3&d08{^iP-;#U`dVz6+$;}kY7`tSlx3=_>XVV>Z!ewN z;$20_@vdN+-j%TX)cNd5Gfv-Qxv6x6K5c%t7XpKk<(guWhs2(QBW@IDN$|q2;vuVoQalolX1Z9ZDrZ?$Z#Or` z`}!GYO{C1^SH#hULWvzZL^bKN*h@$M2GScw(f=Wx^to=>j?Hqw7Au}zlvn4BKYvGj zyU2FlFu()3J$e+GH~{?hErVFIzx6Ey7YJ>K8~NNcIQJoo1oS~CWZ>*n-P$8ecy&+5 z(&+JDyVqk29Ch!)>2XZ0dQ8PU+hTcs96~WU(Of-p^?C-(Mb%B_9V653cW2Lgm!hjL zgBs45L7CJrWmaZ5%0}w3@}yWSdru>K4t7-^RQG#gmu(bfK1 zJ7$&H_!vG?T~;QU^tF#l&+zv4cF9NW_wesI0-M658Q&@IP$9I>=wLmit*y-%lBdUw zp<=Y)mM*h0tN+)IR!DK1NX&U;OW7N#0Y5?yizb==A-@STv`ap?EK1hl6B#${o&7?Y zoSITFHrHpiq%Gb<7{luDRfFU zQtWju&asP!UaP8uGS4P~_X&@A^a4-B<1QmtC2k z0E<5A-Hzkp7^ijn_9`;|Is@^La4Gcnp}2JUHk}{r4w0_nl-8;yOv1Y!I2Xr*J$vd{hPUmVG7B`|Wz{dc$Sb zWV_F=x+pN5*X{1Krhb%o>k6){`;6H*zkG)MvOl>`4aNNTPdgcYxKl>3)iD!z#-q^x zChwx>R!QG&Nt5@o=T?IX(uec$Vl&3Fz)!1eVkS>usoQ2B?{l?2Axs%hO={^OQ?07} z%`Tx`?gV8(DMYa*B>}laI0Vy<5mW9@L2zTyIU2E<{Ef{NEMx4?mmbN!3fWEzSRb=v?i}MZCuV9PYQPWV(eBU}0!WSl?4WXY^OWb_ zmL5adv4L$OqBRIcx3*+5VP74>m4KIpf(E-a_9eyvIlsYRSevG#i4;0Us3ANlBIxjI zF|aZ90Go4cP`e#PU3IluX=u#(Oo0fwwmvY-M(7Z#N1?(p-wV#?;Y^izn%bC%s1pK_ z!{<~c`rq?xz}{uRzwXn#T!0xI9*cftr^`{bUotAu{-OoZdAPuW4CFTG3-uW!#V+O7 zEH2x0IcRQLHd2|rePtkYfq)Wj!|DMVs}p9raPpj- z&af<=o!<90Oa@&VmRU8^7l*-vDj| zHlbq=|ARE`Jw-=v?9l}Bdi`%&wK_rgM!u?;?a=UORtC!iu5-qRe{2`Ek=!!6yw)r- zs>lS0{)`$s9g0wG=QHx#9@nU9UGppiTMxCEup%x*OIV!$X|0|z<9F^U17H}MIep^C zp@bR>r!qPm>2@Odq&G!m5waL6>aq80nBXK{hb+U$%d}D8wOL-JS1IxVBh}MiO;}G` zVGJCoy%gInDlrKHDTds_w~Hh&ij$u_!X&@h)5444qToi@UVz&!@iZV+v*YRx#Pa4~ zjsJ^U|7gL_DeitJ2&m;L`;D3mVQeY-^z?PIO?hvL>PSOF{j$))NkW`TZ?(@);G+9d z*lx8!^jUVnKImsTP8%8fzecVC+|HR8x{#AWSAe(du|K!v42iz~^Fb!JefG;VY@E-U$sdz>+4p?(g&8Di1gzn-xNe;F+ zGkn=EHL7#_)hSpfk=eyc3CU*HWkh&=oZ$)MSwB*uvNs8!@`iZX31a4UjriW_OykgV zEyeY8@2dOb?u#obw5v#AH=Gaes;-E`2Cu%;AZ#_4NN<=%2Dk$Kw332^Sk`G{Dx1qT zb&_Zyw@!})r$$eg3of|JavwQa??K5q7t?1!2VS8-*dR$S#GK%z+U?;kv97+>Sjo!GjEy-653UGy6-M$(*IVG)4W*kqhgo)glG@ALBG^}H%1 ziv4wy#T;PAXA$;GaCF%x?krd$bm*EQw=VOZR6INPc`^tI}O zi#?~p=X#9s@zCD?6-vwly35uHgKx~EVN5(FVPn8)sTflBk*UiD_(i~ncP(%XNaelr z(uvBA<}^yH^kV>kNWwPbI|RA7Xi%3mELc1t{V?b#>Ss{`NsJhKIgl^^U@rV}T=I)l zDIi8Yf-JO7a*_=THEsbiPB;E!2ddGvwd$6FiO^2@aKG~ve*G$7v)CGxa zMd$sp9xkBBNCdv#g%ldPi-h30kfq5Z*$iDq;^byHikufNrk7lBR|&6U=a)Ro z+_)@VUFRVY7dF}C>QO;};l0N7t5pwwlhn6W-AVK^$(Zy(eAGB~NJhD~eCGiURQ`o zyL*n_DB}}zX5S2<;_&N427_i?8;f%wV!YF5H_D^^;waEgxhE*w&p>-buy5g?qwl-< zSE1ZBdIdk}TGu>Gcr3$vvNh~D<7j_eJ30s7Pe|+?%MAb1#TQ=|kr<}~>$NjJHvtmB zAjquSlAA^FQd*RR_?e0PvJd$S=`_zgBQXxTS*>>HKs|or@EvdjU5;Lk4c58dXY#p} z6TwVgP5}jphKcoEf*ssR^k*(7PmJOm>+an=gSMl$W49A}GZ!_xX0CY+PI$dgW^JjH z+@L-|X|d$wPE`2lw}v6`H<=f57j8x(fZ1rQYTB1sLgUHY^j#Xb%2(I>oipiN&OqoD z*>4?iFqYtX0z)f1JB<`kr#22V3+D%`c`kO+teepEBE}Qr=Sj2NrFfzFGsx#Lk&hx` zG_`SA^#4@cc0^M!P@(N;4&-|OeLVK3QMfE9@>G$bA11l(6#<-$TN<1b`NwXZrty+7 z+UlIEwc?6UmFNe6-@837M~fw6Qh~qLYAYwZ?8vhU`+lb-n|bp>BCI&>hJJCK<s|2iSlVWkof;)vqmsStO0G2@Eh zPifq zE`U2(@W#l?{a;PS$NhkAFL>0TiVLC*Xu2a_3$C5IZnCCsh{*N%BvVqPY`BlBt2i1N>*_x<^qd`WY;E)n32 z*bE9ajUc=r5qZ1=o*qp6h3)SZm0ER~AXBJ=9GO7i9USR|5<)PDNjISLK-ebq!} zU}SD9bcPgdD< zns}gBXWW@MB1DIpcR4hK?Ncq24Xo02T}51kQc`5q5$igC4*4V!G9=O|+wc&#j^}iZynu6t)Jf9-P4{3*n$w)O-?xjxMJc#Pez7pHm%uc4>_9q+BN(yY zdRRIElsfofV+``B<%uT!2u;k|Z!AJc5DF?_afq;x#E`5vn?T1>C*smC%VNGf<1_oq zFh*fzj!#NTYEui}4~dVtKP7!uM#9j5NLY!{o200g;0(h}lqxXwtB@Q`(TYj=$44Kk z2alBM6VbthaiG@-_c$Jvi`G9G};MYq8-vpHY@zf)Ya9s z7`QYvB(~J$$N$Chesx5~qjba}*ktyGBw?P7o~A=-1x^W+=^b9n$%X5yojDbV366`pUYBAT3!UFrXV(q^B4qAj>=~ zL6&w@@ux)XAM5b%o!->-?Z1{Id4{!^v#Y@FT_LN~!d0I=;SPFe&T1nXudeE?P*&}j`eY6nrW4-1opc0m>*wXyUo|i8%(H@-UWqkLd=oVYz7Almrn-3bonAH6}nE@0OaQ0A-J*z=I5dtdL zP69Il%Mrsr_#Ck02Z%=&Bf1(^$p>HYJPNM{(G>K~Ko?dw!Zk9SSR-fsZ6$B>N~t2h zgSH>3M-nq!g6JK5ilNB`NJi=MZKab!MJ}p@Nm{6Pk{bTKM&aqVQc-jF2}<)I!yDr6~G)?B+Z12K;#)=q%_i2#KAM_y049D`4cvtncaLe!>3l3-jSKs@?9C(K( z(OteiEwf~P%m@9E7BKGbM|JN!6tG9c8Sh-l^LL(MHVL?1bKme23|}4wMIefNaphqTK)E)$}-pZhEvj1SO-nN@AsPo zy&L5NDj^-ZF+o#>e?ETr4jX&J|9t9@AEdjlQrZaOqvLUf)?%QOO5;^OI;uI`9N|ZW zPe8(mtjh5LXSjEd)%Kzzl60Q2vlT3o>#wShGWh;3oRKvJR?=G{0#1`sA}=)k*)+97 zh9c1)Tk)^GVo3Xj6*Td5Z4bp#`IGEb7a;$Ueg4yhfk)(MG^00-h~K1>Mgmv;%z*ou zMR{fVSP$kCiK0;T4;A96$JU~cEQcNp3{%QkL+`m5M0A-f3V#2s-@<39O_IE@l=knH zNu1A{Nf0bP`g^YlN*kSAkqxCe^>mTMWrAK(u1A)(-Kr0>?!G_y>Rg?TH<~D@Db8Sb zzqmyV2St$k6XfOPO-ykICqgTY31;&pH8X#vpB#d0YlV$69)w*6^L{18#e1Qb zp5<>>x?%$BBr$s-ru!eD6!dN4$vN5^yk>Iz{%_FyOMmhs_kI8%95M}pw|LU`NDndU z^vEH`$aUUKro0qp3=D!F1ST6!p@=_xzN4ozo68c8$3X*mQcL)ZpISV{CwI)HsDn;fZ5jjbv^#9Rf{j~~A&-@M#+KWkOB?vSISnlmK6&U*X#ZQtw|u%ipk_6Q@8dp%N3)&=o5AhG>j+%W5Ap@|Y6c3ywlsY_2PMD^e4|85%?#eFr=MXRT6Z7fF41!o7GwA|3Rh8v#K6fm0xzq* zktmyTCKk@gsuj)b@$00(Tnvoi0HKVb=mq=ul5aZhQJN!I3&jUh5;vH)*m?q4{p<(b zb}^&lqw2~+Wej9!oJWu4^D-z?@D&VrUw8GoOFTJ^2mj?ciZ_U$)f4nx7NjKvw|z;E z0x-G(C~u~^XITeGpT7mr2jz@*&H?Eo6`lWenTe9nJMG!to8C>{ga6(-X;Md9hEdPe zVdOxq6l5e2-(X!mV|6&>5=;oGw#e}!OY;F32!=RJaprJ~EBJ!@)ZiUzIE$#P#I)(9 zEk+h$yQT^SkjyXkF-dWeP}#R zUSs4DCmjTIM0l{Rj7D8A&>2!#$0k@LGjPme*kXxRaES>W5Be%>nVw;igm-(wQV%E{ z@;!c{Ted}uE6?!7ZBk$aq;i&Wi|HjS1{|-LddgFlL?B|ZD?7>tlt?FNQHL(@6CZII zlDl7QR1qN`v#1*w_$9~e58Tbh)008&o*_iB0Fm2C3$m?3WN%Qsqzqe;w-b9-juJ`= zKv5focTyj&$bDA|$LGpB_6xW3bJ#=Ro7ZU7y4V*V+cHVQgj#s7T%qWx zzj!r>1$oI4^X0szw8#$Ct;QDe3Y{u61&lx~0YpKkFVQ@TZpB(MYRV!`2O2zePIA1? zf93=tHZ0q|U-d5t5>S~Blf#s2#eDqP(^>zj+(LV0N_PdvhnJr5^H;l>jVy|?%W)`k zxX2lb*)9$`9uE5#jxCx4-G7Y`ZOD_D_8Khl1u3_hYJnrncpGYFgH7p`a_V@ruDG`` zGozA^)O_rv0S_Y(w>X=QYsAjSa};(P9oMC|!-P|KR7kEoU#@jlFo!Zc!hq{t1fwi* zCX0I=pLqi{s=R53n;%ZW7GCNbZnzMpJe~#-ktIU>t> z)v=o{n>>R7(eZ?nB0T`qP#xhw0CB!fl<_@3XM;8UuXI$>9BGUhx$RaR#3}jlw7D75UU7*;oL1Op#(NT?|{f-l_fS25owq!~GL-$FI9Szo}!T{_-BO^O0vPC8vaf6+! z3edtcD-?_$YMbbO1{^lKURf#g2AgSpB_8R>(8DoB*tFnO$LKtCY|67@F|J=>vyCemJl_gN*4 zPt5j}IG8}wJnRbpUSU*o@kPQn>&Ltu!vo95mRX;|Eec|Boi;`&=D5z+>?oRB3raCsih*1fX~@ork)p5t0#FhAUreZXbhi zyMu7G_sw$E{Vz{I8?=lsEUn<+h@Pik=N27F%%y6^M-$y!0Zg&=4p*id`=)}PeWVfb z4`zR=5tcW1Mpu^MuYP)kJ>8!wr7C_EyUr&QR7p*s+vm5WkLF=LmonE!BqL&k?O~2Z z{&xmS``jm7=MpPt0&H}5;2X7f&v(v+83NQQD{ARTl9h!!TcBq%a7{9FjCuh8!LC<; zi5E;je-{GwEA*4Mx&o>oD#BOqSzfw7Xb)i9X;lT5_7bh*K@l`~Rkk(+;{A`&Uc=Zm zg3<}iO4{t!HlYE`k9bC0U`lPTJ zc7WfRMCY|deK0r=-J9vY8fuV%Pr0=V+Q$d$1n5D|VB@MeQY!2$({(%T4ynBr{s|`S zhC99_wdmj=8LD^;!^8-~N|Rf%9!)im(wB{hK412bu8&n?DKBe7$TwX01yWdGKg7?Cmi zj-u7tyN>{L>8m(-xvOW@AHD&y0>bO|AYw-5RXWHx7Ux}$=3II)7?1ZF{PTtAt23{m zhE>v3vHoM#j-TXdjL=UY%>z$8VIO_$r`>9@x51Oay0sfud4i11BhaFkE)M2-eV>M`45%YP@6Hq)a!?d+V%$zO`6%9x?sYnfUc3d8^cI>(-9IcE(hc; z^p7WafYzCKL_Z2&A7zBs=(COmGJFBSPlyAis3COBI~yIFoL*fa57VwI(6zabL;xP=*ptf`vOGOxfNz%6`6Gzd5R3fH=^x*^svwJ{f7QB+uZm!TJ zHF&3R;);~os^I`~c23%E8hQ4-zZRWiDKBqXcwP9B)L3U`mu)mGB3xx3T z!inxr{{l=no{(nT*F7aO5uALQjws8|dXqJ1-Oa{&D3-v)GdQT#!kai2a-NPe+jyFA zofk?nQfw-z7YSu0h4gnaA?x!ff;TGo0vp_u>5Wf8h)&A0%gAwawQZL7G;=5TW}Zz- zHnC!oLU@b33V|(FbL<=2MaS>iB0|g6HZmKTIC3QMQfWq6^)z}wiiIVT9)kzYZf2Tt z$yEt9i(ke5y}ZnAXCGXPm8c&?4r0o(^3aB;5%coAEZsBb;s5SXeO1n4HSb(L^hWKT z%#QToV*?<&$17|cW2JLh5gdA}1H26e)wuSWGUCOFn9c1PBW)rwM4vy+Gtv2X)xsOC z;Sc;fzu&Ek1H8G4lAYSqF0oaPMz_FSFW zzb%Y0fbIc*^)T&!h7?uA+S?s+fs2*CY zHGNvV?R^^W`{LiV8N=G9b3M^$$~!J3N}=$t9_neg9Ao2vJ2fgv_~-9%X1$({?Zc5A z`5=e_x|Ij{H(s_>1W>K#q!kC*)_g+!IjV6?<-vw5*_$PhRy zf7~4wfO;Br5OFZvh$H5P#qHE%d$?O*CE?g@YD8N_f4)vKfE4_Vt9_xH)pvgqopgEp zA>GbIw3-X03pVHE=4NQGA>{Z`SYG}^CK3IIK?cT-PlgY$egC=O;%t~y0Q@wfL3b)y z0bNW!B&o5bWXSw1cSobNF4HFY%)C*tLL-X8L8Kal*= za5+Yrk4`N6;{MmYmlE;Ccnmm4jpf}_USz9T{uk^#sYnRdcc7Oll+ott@R9q=^)#vQ zqR8)%0=E&#gsG=-kXI&C=xN;K%XL8UzwOn-lsm*Kx8O{F%uya(At`=O%D&=Twfr7n zEUdDu*Nx=-8mUP+c+1*+V$Bb_4(wu+#{;}rfB?EW3ZmpsDh#7p)o*K$mf411(5aGM;2MBdOag;Zg# z_+xgf4}t7b$yl@%)$FI*j~F1%oupHMoHmW7glp99pw1E2_o1?2&ll?Xc7&B zg=J=LAq@lBj@piwJsg4GC1x{Y8Fp_**H8-*Nk)!d>06RChuz6&Ecy@?hJOdm`6vzXQSNHQ>~e1= z4HSjKu6XS|T`{ow-h-aNo}Fq#NUm`6SFR#`Zf1yElqcrx`Tj4RH0LXv;f<!-(*d6akP!VOwfMF$1Zt5CKunz6G z|5VK|&4{cn5X~y~=ffoNxVmxbQ6HomR#`(k78&-#Vho_*^@cpFS&M#!cR9mf&~sKH zqGwJYH}R?jaAawh%b+NGj5_&ekq(W)B62=RG#cgm{-jU+N-{i53eeYakZzS2Wms`V zTJTqMWuacd!&$v2eNmm2WcAunp;?gNsQebSXAb&>*|BRroJ$d&7q5zp8tA@*ZBSFfhgy9jShWyMVDzN#q~j{_t{te zIp!Dp#=TRGtbr!*8kMucX_~xEUZ#X{oq0<*j1_uftdq?-5Ikx!l?kZg5XV1Z(cz=` zvKCJHW!JyQ#0AQz(=%B&HaaaST#9=TDvvqWL!=dXb}Bu1CaD4`)Gin|mi9{)fCK?y zlHnzxTc4$MRpX_WZ!KK0iFQl~`)tr`lDs)y5@8&L;){oYTGYRvkyP|Ys|e#;eRrm* z;A0dQjihN7Bs30~u#raJpg&aHd{Qa$5ttU-FJU&nM<+`)wWVIxX`Nkx$G{lY9 z(xYoXSw1l%BZJ??A-L~>#APh&M5oWPIq0N?;u)l4oHDO26bcnXV%%qv4=|m60~vCj z<^UC#e@*1TO06ZvE4<_&_Jn)oyhswv@(fM7=06uCq!;U@31tl&F}`Q`ZOxJ{`|N`K z=nV)#B|LawydUa(A5({9`aUDEs4Ga2Nj$L=0AcZu8vH|!*fEq?^o2pn6@u%7+t?e4 zr=>(na=1*isg%FVKh?1_Kbe+gGboCs*ckVUO{la=Z8TU&ruEywh(r2WMVVQxRukoO zftAkCaeef~Nlv4_e5=KP9?_dYLYGzDH2E9R2K_sE8aY`Ww#=VhAfjD!?OO9CuQl?_ zbzb>6V84kVHfQQWoM|}EmH#R7rF{KBoZ0B@??E;zi87IhmC_XS;LB8hRF4oWGRv;y ze@KEqM)FLE#=I`SiO0+){v%8xL#wCa+jFcLi#D19f-f1nuF3!Ud2aKQZ#4>q##G?% zkg5q20EJWTN_NLyn-ql+9>#&jjEL;FjQEQJE5@m<((xp!KPO$?2%Gjp60HEU#9kYL zln<0AjfaeL2+AT-rndXjLHwfTzqb`s;_$2dhCKU%6prFN`>~$CJo$q8Ptn}8I}>GL z-->Loh5L&?_dXlR$rI1J^`@U@as{VwXa}*aAC0cA)fH-30t_Ft)UdpbfT=UYk@d7qN8OJ)V zH_FI1H0{SoTit#A+=a>V%*6{zD{lJ9e+<2S*VV0#WFHIN&t@vHpDQ$*g}1B#XQF?U z=Oump*^(=CBAa=Y=k@$a0m0wWFdP1f$|A@wf=UUt{_?;jJB>;fLt}mb;`rJ}lD#5HPgJoKq?avZuCVcG9^+~wl z(z2~QsZB3!fTUrfHn*ud&O^9&%!Ri3;#K}vx+yW#DcW$RQsz;XMVEIGaOU$(2|Z%Cw)a5 zRT9UvLmy95D`@BxR-P{QM27}55TI=;T&Yg)vsHlIcAr6K3md7Yukjx%LjIxpa{VT? zWRib5&wr9wrfR4usb}={7c)t1EB~PIoj^{cs)W$6iuz9)X+g2tmMs_4#8CcI7AyDV z*C_@F4nP$QeZ^X1kPPtFLLnj=Mx`dTu&}TxnySe6wi$^>G#t@S%XZJ!+uRe%g=c*P%h(BU zDnYQqk&)&;S;WR&Ly!Z`has!~Yq~-QeX2RKVp{uMKxmGhjzw z+>A~1W-5%m6Jq;xsb=XiNsA14j;)lSI2?D8+|{#0P{(%qrOT`lGZxtjP0~Cr@aiB7 zfe+V<1%Hhb4gno!h&jAM7geWp5fZ1|U!9l6to+qbo>wv#6Am4(ox@Oi!d0L6JWp(l>zxeDVCasJs{4S%e= zl%2Us-X+Q@CDs-?Y*#&@e1$7&8Z-4YUI6$$d?Rp}XobEM(oFitz`XUiLGjkZS5*Eo z<+@pgbgPHeIekeaj?eV!?cBmDN{-$2=@6Q~Y`0$05zFh$D-Z*@-G29_nSMZ_0*yd9b*(B*dAXGtFy&{b?MVj|+l?-Ft zlwzntoQA}(dISC6Ok7R}O*M3J%1Z>9Ce2uHJt{gHDQzr*s7dZy zCd4SWo-^g!zF||C-UkX!JSwpB=DJ32Y!5^{wo@d}pptYn%35bV@SB*Z;jm3Nmu>~9 zIajQ~*t0r<<*ufXtY()~aV6-3X*B@%`g(nhzYg$l5L({ggky9dP4K`OabkKy0w(1k z#eC+;7G_t$BW5Rx;OoB=KYAV;r3B~5aEE#C1rc+Es77!1|3!6N5sfAstrCBU(CFai z8_dVa$OQ{Mi&e%Nk{X&}w~d6k3!WS;?2T843P}h`%U(|Gx_H$m!Nukz3HKxk;px*y zGP2;$O-GNxdQ~-e3TX=-idl{N5X^FoFkG_|83b7*v&cJ;#6+;n4!7s?M_cwH-rFU^ z^^w;faKt&5US#}aJSesZl~z;F_4bdV>SQGzxfOgGE(Sa~l_3h7VCtV`01t!3rM#Oh zsnto~k?ND=mu3cfvQTA(89J8;ybFWceoPZ_xnSrOJpKHQSo&$M-x1nM4Lx_mY@+{s zo&a#O;~k=xUdtKw*t=T|uQ4z8SShyPMY?tM^Xsin;C9@qy?IoDJfE!{V#{s%S_S_# zCxhdQIXli*Y6>CILMj+n>AlEtAt5oGr*6j7ftc)2NV+vnX1-_~;Rwf^y z`NtnyfBs0NP@;EH6hwB44kn&NuX%i$ZS6hA8_r&WE3i$v^>>6R`0kB>yl=48+J+z6sgl)j*29>W|2oI@V$e;6ZXfr)pD2rR*VrVFQ-(h_~IBv-!C0gYUZYh z_=74o&X3}g%*Ho88`~ksu{a<0H=jS;-kHj$D7TTmulo%{*oPje9Rsm@#AmV(ocVDq zs(-sLs(&{QIpyan;y;g;3J2YBVDx(ZT1QQ|-dlm#UVkwZ^QucdowX$f=TmHRzl#m@ zg_ZSZITU(M8X9=}54O4+xEvbte}B2JrfL1mi6v$UN`V)_UU_EJyp2$LnzCIHRF(C) z4j!M>?`V~iCQY-_V7K|_qfdaCo=(xKQk3buHoT@{l^&Lb0!opn1Nc5_;(!9}`IqBC zb}I+1_nf}cjB zxdoR=By@(FVe-=R8;2jDC&~h9ILy$2C=OMO3L&rnm(cO1%PRts158)QuT6d7UUvQ)L z#DCr#Wvs6Qr)%MAQ=BZH1g;0C{i^Rp@a}d)#F)fT%^LMD{2Wx7u_AJ=cS#{}CZ3lM zS>B*QH&cx|6enzD?eN=w4%9Cv@ew87o(3NvgnO+7@mQI{xG+XD^6ez_#8I_2xbYsB zMz03rgpSo?Y0lr(tqZ9M74Pkz3>0?hb;Ag8b(!emuc2Dgh`Yo9T<`h79;3QTY^ee1 zI}(S%h=WV5f$O$_nC{){K@?M{Kfg^m|851A(bPLS*(@)-cn%bgGR-Sb`&r3=0mA^D=U(*xY zh3-b=Yj+@)9{bsyW%c%reixUlYsx}Ld3@AbtgkvsUL!9FESayp9Oet<#1Syf07jhm z&@9G>gu;Yp4Epo*nO{ArQRMthZ9^z#vd?9vei$-|7(pl@Q>{lyBS-CF=@EzLw84U{ zQxUcmCio>95N0K^8WLh^LC(J%@qXdk9{P7|t)ZJSGO!0?n~?#%!%vQ@Tt@#7%@(u4yADwkms&?2blQ&asy!Z&iO%f` zqpFgN?j@)zrlr4W3|el(@IL5W>aDWvtiKThLJBYMh^6%S$VS1d3jq+Ro>2^Mql@!% z)_vYD(Wm{4Eiv*Ti_d%YCql~pN-$F zs%(b_EBV`t4C=0MRF(63nQBv4nOh*`n@Av}>+u3kEx!NMOJUrkeEE?oou(h<~!j-H|p4m{%%6zWajk zbAL)8&oK7HdFUJibjk)_9@yOPZi}WCZK#SeRT)P{m233Lz&dve z7RKV6Vt>9FAVs%8N##s4^`)|5dXM%;Wdk*9l_6tN1I&KvHYDQl2#+_-t_!@eF&U}C zJb^A7(v@b7Ru!SE&OmdaGV4=okN*TNyO7>w6|yYC3jA8LWrB?+DVhZixWjgZ<$!n= zNbWuNoF&VWbn>jLWFXU0Q&WR`a_G%tsb|zI_w8xu6Wy4G7Hs5aM{ZXB0Ga+?2;48c zD92;%>#IXV=X?Z*{UCRbqERf#F{A48d4HP7rg~CT7fElwGeOOnKNz0)DQ9)|3i5Z1 z(YdO7cJs9K$H@VyT<*#mQt91qq!OrvH``^wd-uLudGG_)en=&-MsC*$1s8C?G>GLt zifQX`fjDL$y_Unw7_WEIN`Nk{xZ4Kq1gr?6U)Cs4nu@5sjz56})4nUN}kjm)S zR5*ZNh`g@+)jYht9_Lu=Fow@f0%CUoZ1Mam8;4tv*&J{16uAVa_p6H$m=xfwKo-|2 zhHY!_p=0#>?u>lIBgIm zdv8v9hJ05(y&vxT{{uNe#=fzc5-0SP<1Yv5YyJS8d^LSdzmLMOxL%i`uY8`?*V7k* zgnQDYNovJ^F1&d*A)hw8NH?0YB_|;1OT(0n@UJE`8dVPje~ZOxHpj4RPb>D8RN)u1 z%kV$b%W!S6RT;7jb5Bzz9tq{+&li>9b2G~DpEY8UO8^WfD$i$_9FR_tZ>t8D7dxz~ zH{zuDq@K%cz;O!3J$6yF`?mFA>X<^*Nzg|+y78AD2Yx=cTs`j-^&S;zzN$fXLLN6h zH>DIatWoR?a)@~HebhB6;Xd8HxUXJC9{gf^M9mK6;(K-a5Oq6ICnv{n`llW*HcH$% zo0LgZHYiBq$@kd@jEUx9QJ1@tbjcC6V)l ze4XTuAX6S4EHPjdCK_iKeSLk{v}u!?nv?~*#>>H+%CgWdWu0N9IHk<9OX0+EX{JO> z^gf1&q+eL%SbrGLw+8Wia}dAS@5eWH_T$^T{P>n^zq6|!pV-`w+t&DT>nfj!?8`P? zc#P+6S?R~8wj}T2y$pk4{d}JvOBjAjP@WZ0&m?hV92ghIiE(pDJd<%v#+~n=weIC$ z+OhYKy3p9zIAqHvqx|IhJe-=;>2#h{rfMX?MoQ*&>MHX-b-iGsxZpt%Q1Xb{E416%->mxf8KZ=dc`Px zHPHC)?&`*SSM}hl*Nnj|rwRLywBS?QBDix#5eoYQc%-ox&F&KX>ylD*w0Gk(E4nd% zb_K=+SaEr=E#SaCm)645(TXqZkK)?V`NH%8Ut2GpGCS~zi4~aYN^>0|L>ux#y;$^g z8?KsCi79g46b<3oBYqT?72%^(3sE4~_ew$j?re?C4?D#S+~w|37Fe67ume_dLJxLo_z zmED*m45!TI#CUrQkM8r~hKuTOXOR^)MDUx%|qw^Y!5^OON9{*G$49xt8zj5>m%m#lF>U$0x_T zakR~g4Z#RHBOd%>L3wgIT$LzEu+VsMXCuDTlZPiSE>&)1=}G4nbN?4>_hWV8X#7xMlyvQ3L z-HcV!127epE4Q_5=6ADM;?v)+D$(n&zh2F_bUGFO2mZ1NzkGZbK6LYB+tnui*w_{X^C|4H2Z_Kjpg%@5xX=$k{%xQ>eB#jqCXb5zMV>srIqd6EyUnGuW zfw*#a?3O~RFKSW~M|8w22*yoPKA2D#&c{bJ9V5k(`V#}RY)ThqB8rUPc z-y2brRj{R$;HFRFv)>z5`v?0X=nf}PY!kvEq^QgiQ`3_dNTE?IFW``4Rbqt|3InN@ zZJpbUV!I{Tuq46;5{^$h*BbftcJppN(=XqEcgD#tf;h9sUQ6xrj*7t+BV&I(&x zHJFyN?iSWjEYGSIsSte~YX^+8o`)ymsD5@?CSJkhfgXw2|Jpw;l}YE zxFSF>!)x0T5iCC*K%b-u6@nFNZD|26FE)#^CtKC|y!NhstnQ77!dNhWj2oRTK~$7^ zP$}p3b$GE|tYVYdf*F;0sCP&jM55Tx;YWE%0mj&q#(THVkKG{)=9P(+92O$e8Nuut z7q%Vi#Sy9K$Cl(_W|0M9mcx=JY(dy?vQ0I419S z^=P}qBZjHvPS_;<*R}^RrXn8|k_gHSlkf^iLXP^-6pf>_s1TRuMX;(Hm?3G(v`SAN zwpMiZ`LQ=_#e!11s@&7hvsLm?w-4PmH)addljMah9>$WUfE0#toL^Op(ojE|t!~V5 z#qirLM{%rdG`=xjj0K5*!0W@^FSO##myW|N&KQ=q_M=+Ty_Y6xS zhAnM=cnb3{PRJ?0(<^}lLDOOIoY}w#Y>aZxo zKF?-BmCFK`TvyHocmzhzqACXlBrOdH_D-HkinCl{{L(>n;c>#)c8Ia%7b~Sj3@=|K zh8|%^BrZXDP?s1#0Xf#q1{8k~eko8Elv*)S%At!(%($S?icmtz2`Tj0qfVpCh~v2g zmVJBo?p3-mZ{EDr(V+qAC&zp|^2j5&_~MICjMX|^rO|k{j}=yJG2kMA=JaTOhwVrZj2CB=u40?SOpG zW2c(ZjMqRI@g_);L@h;MZ{UK3DdAO&NY3do>B)$LT1~o1u7swd_VGl?ykWN7EzwWgO z2*FB-nVH6^k@0BwtIA4#lQFY$B4cI>S;aVKD*?d)+8j5c?v>vR$Ee5%4!C~B%q-V& z!Mn7}CC%g>29yRV%TFjHpU1H%yp9T^DtqIc9HSWT!)uhmEWa5i;~fZ|rwbQ*fT+;C zR=r>HvyFz^0x!Hu=n0*&WCDd^={9xpBS_`st-Y>g6WhN8P7>8oI?4Q`@wC z`G2B3$`vF^Nn!kC%QzbQ+#2PADi0}A$gOVo7yARmtO<;bx6VpoFm?Jt{=dr>m%4+B-b%6Ce^S{zn`8{x( zYw*1c-BkPBitrdUi@HXku5#Nh<azxqemt1m*(%;juCteRmTn~sUOnTcD0r`s%5br4v5RH)w zyD5%s$2)M(&LA#oaKj;`NmF+pyd{y6^Y&Nz1TM&OTI&}VkSYO!K!u>%*B zI54Z!uG}HmNMJOMP#cs+q|_pN+L0Zc&@S2GKE-GRBFErV^5U zDP|0Nm=~0oabBTaHEO8|HC_sF#K9<^IK$5Ex8I(1W9C%R(b1uFcJAD{S#Kmd+hD$= zJ7H98v6Y=stm%&9h%btMlU>AyGN)bX>Ul*D%q_F3{7XGGh&J_8>1b+yxWUMAM%E)i zVVY29SvEBX5?I5TPJ2VxE#;Ua9)>d!M!7wK3yZCyuU6E$EmFRj5F&#aB6&DlpuMts z^=j2?$+1z{M$QNbTS0O`K#U%c1-r}fhebj_k`r5KoWpS0fTJA&w5Cc_r`?801-9fY zSgC6U10*2teryw#Pxt541xpi@j#1~fs@6j$f%3CX5sq^SD!o#Kgk+6lN{$DjXcxid{437|$w?Ne;P&=K@bg14yx0-Nlu{37Rpz51->!`6N~cv#+M;^$L|3^z z*TD2*?+(+9=0Qs!j!r4#4oSLg?eHsot#u^u&U!0usc{1)BKYubJ1(rR!ecj0z&#h&;4KaLm|f~Xg^&;0M{JrWVPtyN6PkxGMFXh8*BHQ6>yfWCkOW~@|_4V~?|E02L_zGvfahT$8BZ?=E2XWV0 zAAWqqi~}gaN2XWf(W^({L+2H$%A!;3Ns5Y39u?yz&lFYPfZrn1HyTbtgC;L8PZ|4keL9<9 zOD?C$<}|_VJF5d@+w?b=~5)4 zJCroCnHT9|MFMhCem&Pk(u3(j`@k>h#0G>8(butgHhg_nDIU724)g1(usT?PPw$H2 z0~>wV*AqsuC7zr@d<5t##aK|3x{}S_kAcVu0m+4MHYuC1Q!dm;qfm7*YSEHKQN3*< z>;p+iNtP*JWyLItP+r1f_{1p7C#Pn zlg3%5Or=H3V2b?5aX-{tnWJ?}+11@=fCMZ8%jizg4DhIMjs(4SNZDdU}*=N3IMS1AHEd4_7?rz*=MAd8W7y zd6A65)x?|n!}!+j0N%IL3{O!x{&3|Oyk%UWTKj@|i!Okio$_UQPRnTv-7ABltL*W0 znoXEd;=r#it;OxVv48v1bDFkQ4s;;h9?uxl;7EE&%(llnOs;bliPQw*+ z1uC{Cu)HmR+t>KfXe!2sr`O^O(@Wv8npCTw8Yg8`!g7z6EC`Jo5vDcMoGsU%pI(BG z&m4ul@gn&)L9A>IphU77zu9mENyzl+)3JX2`V&S@F2P0uPBF@!nBiJ(a>{GYruL(K z0eom%6cy!__{N+X+&Ly+kqx@|v3E`Bt6Z-oOCssZ-VnO7^@M=w#diGP+zMPawifrb zIdRw80D|F&8j~|j)m4#>QKLrT=+UF9CnOg-lT`wet3rp*iCJuTgQpi8k2Im1)}cI_ zN~uLlHiY@BOALo?9bPQ*?Lge-P&|c)*pw znO9g3Wy(uhvM(ee99u&o^7Xk@c-ypkym2$|`-1^k6H(f(LfDf=hvLnl+w1iOFiwat6Zu#GVfhY=5k= z3vW*BQx%-qVpWD}ru;eBqLDioX--!Xw&-%T*z|8P9E5P1Vp!iE#K(8VP*PTjAI&RQ z3zTst7-Qj7EoWcYs?Bf0g;9Svw;Uy<75LH7|KAqnB^I>zs8!x(eI z9!nZi!)@$`NyHqF;&)=qe)otSADU5z|D2Go@&#S@^jNH+A<8|AJtFd5CKg!ngL!oj z%8So#3!p0yR)f?tecz!h)RobrN8{MBV>#&s$qE5sq`5k_=pVaZ8u zIhpe5V2KgFL3T8<52iB|;B?u; zNnbhM!=8xZHwOaPC&uh2XH;W!o)uoHclhNq(HL@*@s2dhetnOdUN6-`pEP zM}H8uRL^Cm?>!WSy23iHpr8Pajg48iGqSP}Ar~}Rx^yWjDk@N3UapR18+;A=eD-vA zqqn0KO)YI$Y8{Q;i2`(oqS+tpqA}t@MR8lrxTxHLd0pF3SyG6iiYk)7~Y>{8EV1cS&Pv^OMA;MDw%Ptx+|MEyuB;ZwgAJP)tj{(F@uzktzC5>9 z)s{4FRc6*0`ViG-ZwSBH_cyoTO(lNZSnE`)H0Pqc84NI<%a%rD`jo|^RBo}(z)D^+Qdz$h6NpY08 z?Lr=hj|EEoY-(ykx7gBLOq>%q3}#uA^_9^3`ubD>z+Mn`=j6&~n$Kpp5ue}P)rnwl zH{#)t6hKjRPKxZ*l^Pna4^piu#Wb5OFxwsQ6qll~xJ0c)&U~ILV}OML3kUjYa^7xD zKwd(&2uMpy3to8P1zdaWwW@p>F5s%jUH%~6x66tz%&9}3CAso;wqUaW1tJ_9HPUS+ zy7v~^lI|C3OjwpdpYndOqyKmPQT$?37^NN;Vy59?)Q|{l-@aWf+&OpdTy-p)B*f?Q z;rZvESF4`0C3jdf7)TuLra10D)Q=6(QhfA+Quwm0M`9Pl9+y0v8}sa}!YR7S8iBov zxqwSZ$Tle)p5ApBKRDkGtIdH#j%rrdEB;bn<5|_M>UwpDgj||Bmnfk+_7Va zYBkK|piQI2lI~xjP)HdJh7YKQ#+iVXCJ6xDt=JMy5@7UvMgmEE2+P>!SGjCF7AV#DD{xvjsaV+ylq){(Tm# zzOGVDH9Jy^T&twz(Uvy6b$t{MUt5DR`JD?AViTL)FX{T#Rr~R$=~lR1V$=+SMm3gC ztczE#UX9ysyG@mu*&-m+S9Xg{nUk|}`Aosaf_>cqeD;V3-=0&Y#usF>=fmxY z;nz(GOV4OkFFe%5cY&Ta-<)Lp5yrV1Sg{A+;~Y0 z1{lu`8#Z9_EjC(ix7WXTfDo;_O`YN^NM3^WvIJn*@OXlR@+n185* zR7`FSh0In(nhAV1^Yq4z8!>S@SC$S<%uVrz-5IoX8A2^=`og1h!uu7(~Csfxn%0pu4>TH?580`5UTGD$nSBrT7gpXw>{+UpK}@+i~|e zHzJV84TF5iUSjr2kQ9)}QxB9hOeBqb-!w37$*W7bDLT)TKPhf5r??LD4ggeIQs3 zGH=pdkb~qoLsUirCFj$1p1MdyY0JPt6o2hf3VQzm^@w5A7A$I?s)^Ak>(eY=g@|aGLEeuRn_4ofk&2$A!c& z>b2anXOFU@={jlHK7)a*6A-p`&_{jc%9YAMVr#9ja91v1Bvk6v)RxDL-x?aHi}Z>! z*ZGQt0tbe%caMwVaph8ydm}!t2bzGqI*f12o?I5hG@OP@`9WXu9$x^T*%iZ^rjJFf z%aV21t93h%;&TT>%AhD0>%vh~fd89WhB1jSzP7O$OM9gzj+=4ktXh1q(uzL0&S6U6 zu^mnLaZ?yJm@#i$IX*K%EF(GR66^oTwTJM%wuBfQZv5)TDpb?RPd(EB2Y&jS+i}~b zK4rL+<>%w;^U6^z=VGE36#*ZvSl+G-5sO#^W+CwPHRbrmlzbF9;&^1`aop1xQO9qX z(ST3an$=_zE4LrRlhzX4VeY|a_6L!OJMhCBYH^|154-mt$9s1B5s}E0IL&b8d+@ax zl^8E1EZZc6#@}II6p!vYjPIRihQ}oj9R@}XdyWqtJgDpdHl7ToL1TdWNrUOpM;}%0 zsT};y2AiP{_I_+=@!~tj^YEPoqZDz^7MS7*A!WbX(1-`y@#6jZ8`pDdOn;aA?m3w!6io?+r2p{27QA>owa1?jW9*6%dXE_tX#~wX~t#&D2q%Qp2{5s5+Vm=fM)a$5q$HI?|w1$j9kAyV|>fx|xJd5_}b6FmrsX(V0lN!D!Vc85omrHS z7)2y!koZ8JKAya)OxJGr2)U4=Em=zYs50ShZXblyP9yM zyc+9o8H-02mSE$CCj7P|j(jmhR;)jSuX~H|{2gPl^oo2uw51;*sjb9TJMmmvQ!U3j z@%bJ%w%#@tD{mWv@txiH{8ldla;>G$hpQJK$K@A{!G_x#@IFrh0cSD3HZva{3Fz0$ z4&xi~JZzD3OE0g&^7VW1z2icRLJc0g zs0bEE9{zUiSp55vN{qG3Jz_+qr%N`3Zgf>nE4nO&=<2cXk4g0o>| zquk;%=zYP)PxcNE2lAmBHDVDg?~3Ar@_gmGXngBz2wl4p7Arn`#W?&|fdvf{YO(f~ zarnVRCtBM2@Q#B~EW5b@FN(f?z!Sj-Uu;JUKb0u?S9bT}!*j=Ag`E4*cpJX;+!1V% z=og5-e&yNy2#u=4y4%O%ZN*WnYKf>`iu7l5+|(1hj-#t8sq+tAT#S|Ln(*iDm>M$? zXl%uQY>(iFSB}MtLiQ`VLaJPOU!~;xejnca@DUWxX}|_~{!hkP@QJ0zu->2S5$O^e z^PW{lQ8RTMwu-)9>}$h&R`;Su&VA^z@%Tcu9mRE3QVxy7pQh!hv0LeJ%a(AO-udNj zyx?^r5eW^OMl8mYvp%xHptG|xYw;d*0z$B|=72L+-0T720$wzh_!3&QXnFlY0@Bsh zrHBUyzMpQ-hsLRc1jOt0sx+rDlj*w*Ns+jA1wx2hoN$VvuNEwm_sOONqd>2k*H@k` zc5$~<@PW7q@10SLc^;rS08A>&!zeLMR<|S#p-ZNW!UroXI4J`xwO~^YW&@J)fpnJNh7-^AB;kTq43VV?H0zs88hfCfx8gS^= zm@ROE4vyDiPe`UX3^gI*(FhLo#Zgz_N;VeoSQdqO$QKdalIKU{fgO^Edm@0VI3K^5 zUZV83RrK%X2_8fwj~(-gzS(W~?2Iam7Hhvn&dsaIgUu1fR?!EmIVxe8gdE3}!9Am{ z7+03rRBxc6tCW!FP=y6SO6=1Q5AMn-evZAqi|~haJV11Y`l<+-k{P?zp!?n zyfojH(~t9AGo?#5gbfCDd1mbGiwMsQ5372F^&e-kJ%0SSyh(PD6#~LSh6WND6y2}b zYRh@_hgB=0hQ<&9D{jtf&z?lK>~U6#T+Y08E`kLxT}jzmOTsrCDh?ywv3N`wHS}vx z292D`wy2&76@#>6+=@Zc-XB3@UljYi5w(D4P&LCQ@97TkTttlgu$rMJ6pbMiHsO|; z6`0`d!8H%;!~YxzKyHUkzQGY;3ynVB6Glr&j9Ph~KNM5H8@;lnv$#B*?`^l3A=FX1 zTF`gBnFBSfR@KAe?+c>EAHo-2JdCTJJcP?1J&bR)1GRPqmSX5`B@ zD29xBFJ42W-otq3Yzv=FH#ZB7u5d)s*N6zuy*y70C815ghDr8P82U6=U|1v_G?uEX ztA_;vF~C-4u6pi|aI&1t-+j;}m2G7A#50+XBa-=#iY^@yhP#i0LIlijyoO`6T2i{a zM+|=wnXvrn4_oo^xh2@P{Rn3N^DrLs%C!k`CUH|mjhv$pC*D6t!UX166{o~4l(w+nz`QUNg*sYUS*dzJs89So*vJ|I zq0z+N5BjN(8#fMz4_uw6E(5}7PiJLdYoy@ z_!dK146x$!h(Qw%^((`jkmzSul8;e4#&5GJfn~d!@u>~X`1PI+{Oq6?9TFdjtr|GK z=SVlcwDuV8+0%(%?(N1lu~4jLlZwCB;=%8(8;`F|FUGMwjd;h_KA58+{8_I3jfC~9 z-R*d?CyGE{06$;bh%fEx!f$tX;88w+VMtwB;@c|O<4qrNdV{y+%-*l+}&+TMxZ?`g+xjUzAT-ihxl-izP2MbY2m#m{zih`x5>7yEm0I2;$9@4^??HQ~q9 z`TafE>5r>%O-zGH#V+)9cHp}kyYa&vO;{4k$K9h`ijZ&yIFyuP<&9%RR~zu}MGbi2 z<}rACu@w@80ylgozMCQdso%zy?|9Pt=Ro+G_evprL9tDUoE%6YQ+^m~LLH*b!C@4v z2YGC;kre_$FtTi=BByo3tyX4jc?7a#&_n{;)AtTEZ^IThZ4njcwrTOxS$fBtal^>kmv92>Vwx=f=^G( z$F$-?eD>ne_&~i4F>(~qt*Sr}n}b3$zSo$I%*>8B#9r)zTD*Q)L0vme5D6K5P|IMqxH$`86FtZF76~^$pEgdMR zuf^{~=kFC=zNFBs+!w9pI37LFhYKf-!hh7cF}tD+Ke?g`3-V0}#Z4&Vbaib$Y?J2% zL{~Y(6?-Q`e7;CBmZ|upMEy=3qw&vmIQXt4dFj_>Q#hFaxn@5uiX27EoE(NQoIpE+ zQ~%R#GTlBIEM(P+Va3h$64;GHS1k5~(4bnjY?{<^bCO*N}3;w%ac z^70}Ns(btJWLs?a^j+5lNeUrc>G8|zAS{}SYJ#T9O1YP7gjlNM9%&5co4tb zaU4&yhLt5@H%0ODB}ehI=6)<|?!_0j2XSRXF>0l@;^5I;}cWqw`09T&$M@z;X^ zMZO1q_raD>%z~MtTxj3djz72btC_8;sK22Aet$fG=MMDX{oDE&LFG1SvpI30BaSDU zym+S3i;oGBh{}ne=z=@ij}JbX)T2im+woA38+TMY6q%>jqoJ?9m=&`tZ8&(e8xM-k z-@E-do^KO2Az_3QX3VOvW5bSP_~hmm{9tP{?t1C~KDEoQX5gARt{m?g9mQRbw_!=A zA1j)@`1JZ_Z0x5Sro>Tnliru|NcDbE$y)(YTIy;zX2#?iHxfrW@f#rnJAJVv2}xa> zEnyqs9&ZfZo-SOPACyFN47<9UHVGH=W74C{*OBlY;@n zup-f*utkwRk0c`Oie;+>KZ%g2z<+yPgCoLWR`07L3Rb}>x8z-Z{C06%of<^%c;BDEmMG-%iOTz_5 z-iV0tTpICW_3<7=ibta%&#ZbEBzdwTZ0*bUI5Df(hP_=u^qVXgljp$9YB$2ZF!uLH zF|oD;cT~DjDaT7JLLRC;@B||`*dK-?zYup%D8ML}9hLI!XpC%a6@3r`qq?R9|25Wy zuoy9{*o|kB6qJ>?F;PCxzMdcshwS*s>@rNS+AyZThNku&tUx~gbZIr--cW?MPb$HF zA!JM8!X+g(R2Ju9VZIsbxU&xH(`;Z6NRM}5)X%}Uxy3WtIMga zCrz59%DusgyU}1ky=6}^e-8&xUrEH+8&THZj)yzUm{eV^)}Em;JunXpmT+vusLDK4 zNO~O<{j@n?m~FaejUR8#==12$^x?lyWRAw5T;Y(w`ZPnJ}p)4^Gjw1ETYuf+DH$DBNqkRU@h zU8u5~6zMQYzAE=PFka$kmfsbn9*nWXu}|V3w>vS>ZB}zyq{nYCgNJw~?U0D$`*OD-(%0~XM zhl0o0+Dj9ZwGa&g?i=?x`6C6oTc~?8fbJtkKLGD#9O&Er*VVi6xT_Zbn3b=dO@i>< z=Xc^z^=N!+Ql4T9>``ER3C4k~y#$XlOoOx$s%2V!)4(Bk4F{_8KEv3`7MO+y`(t?e zPz(M$xEAH3Mx&&(O!b%!CxeD$K7Bz#l90gwb@as-UsSENW5$e8Iy_v!xUvDNv*kFR zKh%Y#MKkcB3+q+>WWLQN=prOpOZ8w2!GtCtuM8RlM~@y=E^vz$EmCFEa0AO)_AYm~w_(fSCj8wp2{+A}rbq~V zs7E?zNVRl!7TYIp-pd7!QoTuEY zhJ#heoXoQ4h4vthAJ~ESnGd3{t^p;brK)}>0U1vAE#pf&oxPS_U2uL*sN{ zRJ`0$sYy1BEsvdP#r$f}1SB(9_Rw%=D=oX7FTC)=VcRHTl&f?n>*;96v8E>c%3p<| z3A1qf*b=y<9#Jj|Be=L#u2gAi#Mb0&yS1DtsP=R<;eh*pAMSwN*N!*)HlVz!2F2wS z%FT%7#F-ibX9J9Lb91vYYG%)#Jt)06%;R+HrVG~e>C;s!?QpXp47-OUglW^+bOc*k z`|x|yMBFrICZ-o!m4VAOIYu(*PHE(7enILhUA2ZX_+1$9L;Z2wzr7K6xY{wjZ$C;# z)uT-Gm9EuBoj%;aIJ1L*jRv!3%~F#{oaws^2?9b4EF@TA^2G)o*U%V=FqD}2-7qGM z{C_42ek#xeN`xHJbBm>5XPUv_$!xJAtAjjO?aZ$fvx2;aen#>C^B4}G`vytVv&}&?`}`PlU@5M2_oK9CG)htvQ4F_nAL(GceLkORQN8AxYfiKb zpDvicX*Y18))gyOsIet9_;`Fcf@F#fARH^()6s^GwpRQ;REI$AR9riz3{wj&=x6Vx zHj&OcsDTbEXX69@-Q-82>liL-TZdU?P83wusxh1+Va+b;(wa4EFn8`;HNnGR5|Go^ z{6PR;B3<}m{8-#-(K6D7dELnGh7K5dp>g&=gPkOVhTd>BWC-RX<}bRvuvNIMq7tQb z4Yf1)Oa)lhCyQ(Gs9idNl3XEvNUL?*H`1+ z#a?`@>nZ$g@lxEgdM{RYN0o6ycLG*3nJQl`gD3J|rL`}bz^cv&ezCa`|6H{eA8vgN zcUg~Oa%}}FMmM0MvQm9JenT2FnZEIG5*palH}1b)tXsDZYuBz-gU$&fJ!l*m9u!3S z^M;%79hgR(h@_&b8lxH-FlTfnK5yTJ*$1A&51v|v`}VeDn^z1%d0qCJu>&(x9@8>- zLejz6t&aMmc%~(YZ@#b{fjukniPnGP`U*2fkE%tL5Jr+mW7f*yo);+w0b!lU--8KF zKr}QoMg}5M;EOn<@IO`1HR-9Rp29!>`A_8*La>FF1uiR)frasK6dGoPVb~}lB8E*- zZ9T@;RN;$-$8bl_Y8+hsEPl4B5r1p+qdSruP|2PVLK`?|F3GQuOjEa{Ez|gqhkW?k z5igoIF2?OWYw-WfyD+wH6iVtEP+nQ3#(SjqdhlCj`o_aa_&#(qIvud*gwyyw@W2CV zmO6srFy#7XlR@r>}PutmuL3q$&O-*($=YH_3|pL`N~cJG3Tec}R(*@Q?mruVj9& zXl>Ag=N;od58qOZOl z(N!te+I;~$7q7+|TP>#5*WkjM3Y6JRD3Ii1s;WWUveo)}YG5yia&MFUe$iDn6728s zW9fk=oNw;I?5^#Yly58N4I`oK=HhrOJI)syv=9MhmHNPrC4Wia`umF?;)bD6+*-WKSR{ zVe&e|t;%@Lz;Gz+R%T&JBZ!6`Nz%rR8`TV0>4wor0^gVKYPe-_mHnz(AsY1!7&W>c zH`dtkP1`oSt78fFt$P6vE?a^7b~fSP$AdWFiz;KUss3f(GBVwl$R&RdeETZHyUZ= zFF{rP{j~`dZMw;@-L?H~v6Fj0HV?q*?Hj zG!zQ)DE`>LRg2%s&s>h$BB#{2mJ@3N@HqxyF+d}4@#4ieaNvM?CM)=hFTPlD8ZtQW7!Qq0@7+>JP z)M7Vgmx-Y)b*@SxsY%p^TM?sjAE}_J!FQ+4!F{!rY|3NnJHpu7>BoL=7*k?xsB7DS z3AQk*tWlJ^ZLoO?;LgiOff$s`cSZ~9xj;Hztp~)I0V-vdoB;DFdrmpimEn5DJj=BS z#*G`Nrr}+`e!VJR2Hrc;!19dm&a|Swl7#t5(!4zg_WRHhjN?eyjKiiPY%QFGeorZ; z7CA7tJP&nwW-kdo~9z&HS zf_gXL5#4m;7b35qP}NE7bz>f;o{m(~n~rSufM^1up>Yl(mjr}v70Z?_Q!Z84Uw?h- z=-|Wj*1svsx9L}JKuw`Nb@bKo*kg~W*3*kFx=5`dK(NJ?i<*&u)YjI%j3A9Pure`> zHg0L$koeKSAsGpfh)9|Q{XRtc`w*66Tcd7lLK(JNDo|EffQph*R2CPa*lt0oNS+nQ zwN3tjmi9EHH11V zYKmU!sgRAHNE}^(2->|~G>N`Og~VMLZ^eT6ag4IXV0MXq3gK|L+^UhlDTIUiN?kSF ztkg3^Z%1-IGr;#|9j*z8hK9zVAeRJ$20rKMXA36BRb&gNvkpj8Q`3q0+6fkiY*A!O zD~+Ruh6Yu+k8~Inicz6*OCmz@#rG#6Ibq<)HXQIH7VJk@ejkolaU|wKJDli-9p1bu z#EPomvwBeHGNaUGMXk$*8n*=n!VB3;#m7+6drXqgO>PYg_;~`abbKIQshS*kT>X|m zG+4N=wke@Il{YMTr9X<}fhf8|F&y$ml<^jCIeJNhs9cH+0`ile{6vjwxclzARnKE4 zupD7}@;-v;MFMj4=uy=JGJpR3bHXqjm|v)?hQ1m-Bh*zAx^O6nu#l3t=&LUj#omYm zttJoJA|~`Yi{W#ZBVJOCn8}JN$ex8I^JC}~+ zTfH0ukMs3;j9Wuj*>A`#^|h%#j@Cd7Ex{<7{ZV)$F_fC4h&Szq+uw~^2T)-OqBh=# zI)}WCLv+)V51S*YtA<+@dnTDbjh+r8Z*hO5=P|-~=jF?ntAz+hj~=ZFh=zv7nL#f^ zm>Db%*#klXLN~CnW5 z4c(}&yY9M^2*|zn-m3`6JKy=vS0o?P;(b^Y8zm!AW@=krC zyUk+YPAagWJk*Ku?ncy_0*Y+7MHgKX!F-2R>8@4u)#=Goq=R~D=qz=Wc`{vB&sBXT z`0mtKPWDh&SEmVxhK9zOA(sTCxw#oFEiFT`2V@{v9Fl<0n4xh^x2$ua2jsQDbYZ2# ztsx{MG;)j{62s65v4=#Ao0uGrgo22P#3NzEqhYc3qu3w!ASiZHYs`**X90pD`@In{ zDok*?rBbpvVRCqsmEtNWQU-nU`Kid5#ERfxixkXq;e=S1>?N?vQm%fVK%7(~X_QG2 z%DS=2pYHbNnrC2m4Aglt$6af~mpqQ-4QwD~Z~EfKiQaWn|wFezUS8>~*3GF&Vo zR@Fi(#)}v(+&2h^VZ019Vv_H0?iw-#(}NQ{u;-C3RD(f4nEx0a$CZ z{kd;69vJ${vQ2G+L4?_eAo@u{5*Pg}or7DX@7u<=%{AGRZQE_Oxw$smuFdvl+pf*l zhRt?Owq5(q_jkPiz#KF8GskmZIM2@+8}?P!Lg9v{iEEltSK(KVGam^+V&(bT2@OgG0w-9Jxk*&g``9hT*G9 z!4iXiBpiCe-z@H`z_WXw5|x@8bQmCa7;a51hpXj=a=JSO^@xKk8J%Pt8O1Gs3n!vX z#L(XO&dn(LHN3-)=~r`p^rd}16hIa>z@qX5!x#%U?aB^)PuW6os@k3B65CWX#y>aN z2GNdcMa^GB1|%vSm)#1}-Do43O9>G_93nspbVi#3`Cnp`L z%&D!uCE#U7QGQ*+mm!fi`$hb2gJqIzk!msFCPd0fHOpHG$6VnU@u{L;F1C@#Sf>E> z`im%4d(W}>eo@>n)klV@nv3)kb5;o*tB87#1TXATl`RqJl3lR57AzWrJrg@U_d3cc z86cKcMOwuOBjZS`FKa#j=t$M=(w+-Gfp)qBA6aUFMUQkx%=-V#HV zL*+wii{ldjA|My$Cs7-*61=Wydu&vHa=r%HNDSDPSvqiFis5XiUVHcSHhXcQcBr7E zk_aRh5fd90(`QWZYl?`fOWNlc;?TA=X-glb+3hh-$CX5A?TUNr;`Yk|JeK4lVYwrW zyxC*}N-5oAf%RMDw1!YzMRV zU9$QdL(;kkPe@57+0?wGth<^y7zHCY-@%F+UozqepMhPtE zI46~5Dz}vU+?#X>>Cm}h%(0ywGJNNpH6;u&srI@7*?Enq!26GTju}sQdHz$1vbiHD zl$4Yd>hY!dP%!Zcc*Fx%nwr)7^fol8#6q^8lx8hB#4bygBIJzD(R-L~vpB-p(~hv3 zZ4?6tAxCZkcM}Pm5j0Kd%!)K}81p54V|)OELtg^$C8HWRLw2|}J@Fy}N9RB#iFof! zt`7V6EAyoaYQcW79EZPFD5$a#veazz5Yi7P>9af1AlG^2P;{-lz8)X4i^WXR&WUPC@aeQGTk+QVH!iE~v9FDP33KLaw zd~@+7hNifvwi4_c(zv4Jp-m~XEC)*~W%XD&;PB13W%G_W-LOO= zFr>)BX({Xe8|cIyU($DeyFVfDk85mdawv)MKA*^l1EiLLS4k#b^!Ay$amWNX;`7gz z94I|iE6w!>pToYNm?3Rg9PX?2Brd0N9otFOO;(4bzFAur*F0yJu)d)e;lswv^1Y|> zoLfrLi60EvJm+@kk#2x?fz=NjNwYM2@oWCB)C3ww zxtVhCzmcKDFimDI&rHA&976jF7BB;UagegYY_0e%g5q*3mV}9qG9NKL^)t^ zGVX&$X-@cl)#_&4Pq&5XLWb2McL~iqfOtomU`K3r9AepFC)7E2dYje#g5^*Asb)B< zz+XeN-yo!?r^k4A{0C+|gY7E1lvdz9f*SaRcrVgT20s8)0fUj)n|NprSVBEFuUDo!{U+(Q}KDOp> zgAp+3ANp1Ryp+<^0?N8j`6o+WA5pPvS>0X10RUA;3vc+nr<-G_J703TZU$|e2BvMQfc^s2)wMA@reb}yjaB)2u zK3Y?os6z_m?k}kOiQ$Su-(5upBTUiDn>uEVED?-tk$&*inOVYt<8^}}9-{cn35Xf~ z>s<;GH5`|+w%-rAt*NSazW|vjNJhS+cpII~B&$i3`n2pDz&+Y4=N+#}7n!r@s^MV| zQna6(o{rOE6}xA|mj0fe zSl$#oaMpuF0(NLSsluLO#^Mp-18`w5VvA2ttqH(J<;4HuQ2nVv>rGNIUMBUofg?LX zZU#0sRi_VsILvKW@@+K0VYt;n&^YutyCy?T31)g$G_y}Bf+WG457h-uOo4pEV>IPj z3l;KV0P2eT30S0g@Uf1vg$a_3oW23YP~H_sT9?gEXJC3!@VCb&CYmIVIsgAiHIr-~ zcJG7F!?@_=>|6E~8w2I1XOr|vZ+193K_jthL%F@MNeSG&FMqAE?xUij77~|XfieWJ zqCta1T0-P%dd+~^E}~+?>8k*~A1*t0Er1?nk8-h=mKN)a6x%jKw9NlzRhED+^itRz z**f7hjoJF)f7G>Ycsac&wG)0W{=&?Pn0Qa(QMkd_Lmy)wO>~H$3=~iU*rRP(VE8PN zFK6@Qp?iGCrOR%ZbhG}pCIRC{OElSpbD*aIer79r zW}A+Lq;sIcgO-CuBQ!$x`_K+{WvTgx_zo}8$q}xKfCa5iJ-N*XlGoca3B?fdq)))s zmGp4n08cmgvx~r>WJp_2QzbI}M43xiE?fYN{H@5tP^OYl++nGKz(;6Ry`^;tUj?gg zU&UOy&NDAB+_bh!U`7~>x`)S8B;Z%>m;vI+Q$1%obeEIx6R!qse^h8NB4-$^&~=)^ z^8u*yx*&@hwAE=|8fOkGm&(u&=9RGimmKb~=?1<9QNREe*gbfH?kD#cI;WDjT%z<* z(}r}qkmVZPGINLw9;d=voh46RZQ1k#lvde&rMV&b@(Z~)csB!dZG8u<)ul8MC5jha zA!1GU;YFw9_hdR1Kg6wV40clok8^fTa>I(%56l%SOd6q zxr(Ur8?eNSANv$S}McwyRp(CYMLNBJj|aNEya^*Ld$f~AZxVj_BuyUS z=&1Z)rzii}&scs>B1=`6z+kg?+XXfx9$_`Q33$cqQ&1fgwRR#B+SxUSDC_9NcD;*g z+cL<>)A=iWpn*6m=iSe zqNPXzKu>vJBrq6C*o%R1VYz$-MNmKI7%it|jev%cIhem!Wc91>s|ZUp)P6rcpXja7 z*xkYj`G+yVL#S~IF=0Q?qjZ57ve>4p?w+a;L{YU0)*)`0GFrPu7rjg|0;TbJ63L6I z_`c3x8ccnQm%)2)84p?t{v)FDG=jGVuRC)+*I9}3sw%a|wW`(V|NJG{W;2#YQS}?Z zH(3ZZ;uXu5-Bf5!Bv11%z%vSr4U2-zEl`5NA7lo{mq=LjsCftwHw#9oM@iY$)BSz< zHU%xgkQ|%D?SP*qdJH*8JUGtymiy_YmO`0A-4+_uGqOXT21TKTD-YHL1|;1#JuIA0v?Y~9UpwHYa} zseVO#Q$1)LU`+AL(7p^+U0tV``HnMz!Kz2W8#@=N`#A*#0(19)ZlBqS}d8+Z<@0G>H$u&EaePfr)? zWq=H@>yw_$WWnjA?P<#=W%q8%qX&bN6WmYKp;U{tA;aMdwh5b#05rB^I9Mj=j6YLq zLTjayvXWk9yJI5bzWz>ezsiJjJA32qI@V)c>cLaLOjrK2p1CuBMsP~U+*mp2^RM4w_eHHg5sRtP>+q>4zDA(nH#IXgBdfo=h@T4sM9h%pp{GMKmiIo z2Fz7xg~o5>BP`9d3<)oRI##zJwdV$%42*a26B81@fO1m7CTC%NxW{vr&uW4;v+p;! z^WlO*V0^)2{c>nG)p@^h9YSk$cDkmdw^f(_K6r>BU5vZUp!)~Bh|8tO*lKes8n5E8 zOxFr;yv#wEdq@Tj$!0VDya~ZwVkM#BWQ;v0?16fWB5k5K8sTEz!GJUk!ll88-YB1F zDjG2ri(M}TIF#X{xx-J=2F@DousXw2VZ~yVrZ;Ge{_v6HY#U13CS@8C6UaT!V9P3b zZ!R4VVTa2a8{{AbfWmYyZ+Vas^99(*rjNK7V1sU*x1{9(xh%+!Ax;a#Be}I-e4TBx zQ4^zYO>jZ7Mw$h9Oz<%^^&4yA)RxzHjCCitmoG5~F1or|Y?jC9!&c6*-;NI!;P4+$J4GJN(fnePG%>Wu!oGa=A9}U-{oaD+lbRY1=W~AMon%Cpqfp z;w9^WtS|1A5{23BC1puZ6d1|cEXhbVJ#nugS8yWt{GW^kzOxa@`g+ygw_!|G|5%|?v2HZIIm%w*pIkQs` znffAMdp{630*5F7oZM*SoWhOo*9-too>#EMSfHbXeJdaXzJ)Y!vRpq&5A=Oqdj888 zinq0rl#tLhi~0lDB#|lj4Rf%2g2W{(I&Gv1{QB6>u*mKIFG|_(8A-3Co;-aP+E{8Qn|IBT!iD9HwI63l zO*oWMEf?P;6uA(2X*q^fLRjdkN!3>aS?!w=Fi1VTvTN6h0b~e}gtZSpRs65Edkru0 z0zO~rz^4L$fgshsYK+?%C?i-<^kCwm0N}9L9U}}ANhNvyUb?~|6eQ!5HLjCPJ2uBT zZ)_=2#`J1DJ{9Fe~O6e|%7oMmE{QjLfS(7 zr@#1bytPU~vY>HD`KMJ3n%R#p zT*dEWts|Ct3uR3VA!xemF;5Q86srt^-fTHHGFp?d6+v z@NvP@$ zwj15qv_wG)iYVJq{*c&C)7}rV0SR^XE1PsMb@Jzt%u> zSW?sv*!0uYVPQm{w{=mWEH502xu=jCl_y!VV(n_;7_UOa0)aUjY@1fUSSm8MSVBwTH3(ou?~ zmnF{09)nIifSz#7$!M3fbZ|j#$z=gwG4jJ2)G`GHw$vi5cn{=`pQWK>;Cs#yxUiCqsshXT-~$ad`+`rZ&2lHm z)O9VYK?Gl9VeK7=)G1Nqgamy2IXM&~51XM} zU4`x?j>UWYHS-WAIW;l`x8x30U&jqlD<{5@Mos&b?x>J~w|HCd(XhX>Qt)`84b$|w zFVDwdfe!?#4-!u7-%A#`CM-1hntQ#=E@va)!VI#(6sak#Lc08ZNO2eSql1+7L?E)EoUZYUU{31_q9)%M+X`p%4cy#<8o1# z+(Kp!PzKO%22b=R*?Skgz;)KMc4b9Ps5e*U!s4MtRHk}5*zR=1zY7pZRuq7 z-G6ORIR=@rQBNGHqqf5=pY3^TVNl5{i@}$V2FJQ2{k-Ok)x-kk>oo;*J__f_%q|@Ri*=^D0=Gt%Z6C)6?Sm= z=A^$@#(h+x+UJ29{X8ma4Pr^H@vGyKaNe(hN)jS*7JY!R*`Hk0L&y#U+)KAfL`==h zuxNenPdxblCfzZ>|F+~dKQ}xF@zJ|oJ&-0Ogeh=KSvvpG?f0_ZK|e)i^ErBRr&nF& zzSv{17FGC@Ey8n7Q;$Dq`u2k?IVgCre~OYPH>t4}tXdE3|2c#$bMmVnj<36n?$bFN zF{ECZAzx~jaW*ex4v9aH+aYhdWgR9EwnHEAXdnyv*Gd(#KJ5XT6KKkGX0k{)I?C94 zUS|@%yn`^zBfTjN!6n$eQo&yi3Ei$J8h~cMr$ox-d z!c-jHw}zI#(U(HZpH)*a&?Tyea*Dk8wS=~uW`jhdy9ya>67B7cq(?2@!Z0?deEp5t z9b?YfH!JnVkcZQGb$f|_UvoxE^vgpu_K(8B_w_``Y>7l$z5p@1Y_?@W#|p8+qovh{ zKxI8D@^dz5jhYnS%A8LpT9Lya`bhA zHAUsD5pP^C*PeaFn)3&|e14x7(r`8CNVTtHrW6c%X00M2-Bgl1=Zs@bvYr*&m`wKv z2_Rp|T-^6~B7`Cx1in^A-Yf2_n?Hity_vgC$lh3*rv^ktjzj_ElRvaiZW7++r!3GP zDP9i<17-+9d+8(cxTO}@WA6$?pyFL23^T#e1Wfcpt=556pQ~Yc(#AQ|yB-fA*R82E z16{fQRG-Y`o;P1UGEmArZ6WeIQSab48+qPlxBq>5*lu=cD1g}=F;lyMBwu8xzf>=O zxGV)4QX)crnZfDHNh@arI74GEaoBqYsrXtc0X(kUO#4H;->J^MefKGz>@ssD65_9dAQGaT0RDc$d)RDc#Ae1%tRV|Er-M3sGF-cfF3SFhkp9!XyTtlygMb(AKZnKtf zZ}%xCZX>uu9IjA`bnQ)}%hk3(C~vO~WF*jf4seERK=C@xz_+K@n}5IWO%+H2%L)D+ zG{CvHK;LZR`#m>>jNYbqV?AA54oY#oUC%0@OR|to%amV=I(VQ8^P+!Ka=^{oVpz6C zFmmC5Xcf3SxJt$BqzycZANM(Ezpmj!{Pd-XAXfnqu2!V`Dkb5Sy?3~W!4f-VT}OXF z+{^h&_h9lS)qDDtTKtfkyMw-ik3FQX>q1X6^lv}q)$Q_ZKu`L9NFTclGpdz1)R2Hb zW#n&!NEb_oKA*$yVP2DBNy4M6D1hDnk1%FWEj+ut3NN%*j~C&NUa!)PA;DV1OsedM zDdSNDSh2krx1Fb}SnxjRrQ!K>&-(1-gZAkWKmhB9)>a}g4Q<)xn2j0h4`iqKvXG^R z?gr`0^pKp3Od)0)^8<3uXe87vOyjiNk|AoFQ}TK^n2Uq2^7@D#|9UD?f~GlhLB!dg z*z`OFn-LPshdIRJLf_`4A`a**h0xEH^?j&E6z0d2BtcjCjdizS6xbos8D_KWbk6P; z%9l*M41&l-S!1tm)R?KgmStBKc^jY%)Sw8&|varddjK5 zw2Zj-DpCfzUrlmEmcmDN?Tyt>M_3lIrmg48^VS-ZZ}+_~^B*$6YsczyqT8t@TY!<-~i2u%x(H=aXqVihr}t*##C-egL# z&AbhyCz_qvPi^cXTEy%1%Y_s;4al^dl^E+WlOb<#%gQjI0mEp@M+O6Z*Dkb^)RWAU zU`Vv7kct1^X+D9}eOg`R%FE1SnZar~>e~3&0-dsJB#as5d9tNl&Q$jV&YO zng!xHLpKo%7Jk6*byHpOl3O_2a3&d@Ggk)yMe-S>ZM#_qDKsUU%=mLiSl}EkE{BfdT!T3 zCr=iU#{NjpVABd zUr>l+O|&qUzFL@G*HsyPWm+$@%)H~yd(H6fx?i`o&qlMD8HUQrc%1HDygZ$g2o1kq ziSg?frbC)lUCKl{*P(}YnTh;tAJeqU10k@d_~z}f@NZ0GaVZ@ zc;B=dR5JH=)XzX721Az&HL0(|+#s}VWpEh4*|SIOUTr#Xf6B@_S%9SPx0a1K@ zHX#Bzl|sgmAWj@Jz&>WjMe6ouIGTEQiLVtEJ^7Ygs|w#{HtAbX#k-SA1!QlpHQ*Dk zyzj_k6!n1Q+_Q06SgnU&U*>Q*#M;&<7!1TQbg-eDP>gFb;R%Ko$~r(EgT;?qfgH$a zkd|lb9<7Lo$~!g}-+Mh1Gw%8)Le%4XBmRCgM25MZWGb*y$e0G^?5&A@90dJxjSe_l z`RePj%j0{f&3eg;slw-8>Mc)5^0Fh2R6T7F-??vOtc`)%p(FTlKflvK=o;Dfu%w$3 zjfKdHZQkxt$H!@WCOk_`v47}9Gl9}dp7KY z9ESEZJj#qF;=cETP!=?q}73Dq4I>Pd)WKXUM$m3Ve>&^2ep42M@GCkWt z@w6dT+bJ(m+gxD^1XHsvaK^~c7*0RzlBd&IbT@9(LVQ-)!EONEUpQI4#VToSSvCQX zE#e*4U7E`5mh%Lo^$dm9Xux5Hg?i5dZidmWd{GodNKIFEY1Z~8OEZjI`jviRUVs4l z2$LLTk3gqpJ43FeM#&$IRdj2jqJF#5J95c=LPUZwh|P?tyAMLuFsza`mr|Rs%6iHa z!5{h%JM1{W3QIeXw|RdAQ4*<`d6fEkbPQ;!)wT}#1^doimnFJXrgPe)0%@jlll#Ad z26<-OlGvRZW`A~*oOkcv+}kzb^<#3a;H)H{B%b(+#vP5l2(&ZJzmPKC24<9LU(N@IT&BzhefK@c$0HKgFqll2 zG89XHvg&nYAh*LwLNU#>&7H;yBZM%f=kSx~Ezb@kflY4OF(FIx#O)>&(-K!vy&{_p zC*KatQ)=1`d@v9I9sP9W?MECM2gl(BsC45C=U(=EfXn2xG)z#Rf>$}xFq$T5{c~b3 zK>aa!(;3W9i0GAANZc=pim1=Zg>6T+h<~q-G9HYn-lP8KnOUns!6fsCb=3^FD~#}E z%z`oiU$kuy&G$~400V)sUc!_IQ{G4~uw1c>SQz_|VnZs_%tPm9jMmGSN}op|n?Q6BbyHYZ1JF=`LOP{HbMfqV)%!TSi4^egXD zWJ!*>8}mQAU^iAU{7AgL79b7jmn*07sxMy{HvSCyEQ?UjdI)^c*)=gnl!k-Ulxs5C zO$2q4n@1(xwC|=4`uCKALoRv0eN>`T#XeT$IvADo;YUs`&@1J|@h^uyX74%9e|gXLyAJnbBY-fbZ3pwNV(8GF zhaQY6GNyj)_^CxZoy5>S-)rJ0MYho;mm#G3#X+e$Sn;b-uNXV>@i|c)=V7~i=^sho zNkF5I{h!CetsSg|Ti~Ea#+%)lA2Q_;p=7^urvN>7ZlZ|~FG?bAUiGay z3(}^3?vwFTz1XE=)|+N3MP=zRQ$Z%M@W_u!CT*|nWINXU1Y{wh;QTphYn)o$?y?{C z>?QnLqr&kg$Cha$dmflH!m~3#;8BXB@QoNgj5;Z6yBhGwHPS$5FNU=D{C-O0>#TtK zZ#j7Yla!aBR?!(VdbhzodcJ=- zWxNCIE|!fvgHt$S>47>NZEZ_2x_6Dpr|}3Iq$9(JaWuIITD!TcSmSNRATiS-R8he# z|9Z4mf$d^RIBk==7kv1E)C@&+y`*NdkMcheC^$6vEkqHbBUe&(%~P{%`V^RxUm&;L$1!{22(M^)#~ZX z5%pXZjYnzSf;wgm2`$TlNR>piBX1@K;{_kn`ED_8z>))%Fajw7Y58ZLm5BFb3UaE< z0!)pKjzKP8vq)H2e3N67O?vyWHp09t(9rZUOoKp@CgMc5ow43;745DC9!gGo&+mJl zJ?!v#G%v!{CnHl#_KT^J!HcR5GzX=YK!bQB6A+?~@kTfZiQ4x0_$!L{W9W^t8hVMS zW6%Yfud69~0P_iL${U6}!@slpXbOrinsh_EM_&a@co>r{BA_;d{5}yyk2;VOjjiYsCxGwH3lFxko4~geb{=P2nV#-=} zZSYpW^%)7{lugsv9f?xSM*4n?=5`tD@ZmHB6Tj#zZsFQrWjunO94UTw zTAwzK6=C*Ia-yZ`*5nbuH;hSIG&_jY$d3AbAUm;ftm zcI}?Y6c&3pD)UW4#G3~#PykUbCi3^5hKfVm^xm0?K&JB^D z)&5$THx%fF$*XZAW5`YCUb6VnGpq3Tr^U)D2U}kGGStV-s?O2L*?rw0O|zrylE$Kt ztyuetpNa;T686Ghg)FWL_vmeI({(pKLYNPTo;y-RMH|nPlC*`?vJJn8dEIoKb1RB; z<3R=O?YEL)J@vhWNt_}LSzkTNi-87zU;k3rWLfr3ta;=Lxh?T= zx&iS!&eJ=`x2c6++}dt?S;0>Em@_l^yptI0g`-04`HFA6`zz~8;Rd{HAnP$X>AgM^ zZFDyx#i}Y%bOg8EcSN95L$*n4`?6Im-EwmbCKOrH-NC|wL5ZJ&tZ)6=8b zJgaWX2x1L-7;eVP<*fx%P7@DIBTFP`{^)}V-~ZQ|dNE>nWj{AJswrCoxtrTCk z66#`bCi;D4=O}L_Na;KxUwdx+dhWL8^Bg7XCatpwZUyw*9|hU=eW2$yGNa7 zo6NHUKZ@~KBaE-j1E~ziz}K{OXo7{|gg(TPBKGc9n576|d*3^utynCV-_T1S5w#k` z1D!}ruzwtG@Yc_FtzICN2#X+(AyxjswMNiTKw5fs<~uDtyKD<~q}4ISPEc#o%}sPo zrj0_H<-1Hnh?u;yF)zi~Gs`{XOe*T4QQ@ytxBlg&Vtm;HfZXD%C zLzcKnZh>{mI6gX1x*S@Z$gI~LDDtzDE4yyub_eOTUnPw`f+C3&8-*He8>{6ccPd)N zpHhgfMwPNmqXa^pmOWC{}$M3NmW(NpITa1Lxnh5D$_^Fp4w#$SUI}y&WJ$D?^Q(X#$%pvdC!B28d40N z4e&l88pnxit{y1%-am-_RhMJ<|Ft1%buGat@x`GC_8=LqGEdL0#Yyz8hEa$3%p;qy z+S#MG8rqFkiPFs982G-93AUZK`?c-I>pK0LDo4n=dMnWK0D-so*E$s#%$~K#6CFv` z`eBN?q@nYl6gq8ove5q8%o(_d#44J1@O2H1V?i3it*uq?RjLhniWgm#2I% z2y8d-@Pn^)rA8})kT`89FunCH69CMjhntSGmi%-{8y^iAtm};rk$0%|i}r zaPId8_g8ni8A((Ru2qWI$Lc7umgGchbwnE0NSB)~_~&8X;h%Mw)7xxF@rLT5Kr5?l ztik2u@y$c-tP*}m)A(#<=MbIL>t;v&Cl3a z*z0l|f~_7Gx~(oR0`DQm0!eL9i7^)P<4$xP!O(t_`5savXi=BS*w_KKr@jFvncla} zI|iSb5I{z$iNPH4oK80mIMEFm@9=0|YwQmIS z+Yp7YW!Dh7;7Shi&J+;E!1D<;wRkVJh!$~4xRDLXFDXNlt4Kw!3R$=qdneXMJdR& zPX)C!MXll44t1)UM+K$6avFox7Qu(Lb23Ng?%UH>Ilb}8X1o^9{UElLO^N4=hxzM! zN)XHZ1d7s#<7SYI=f_fo=S`)x>!rDj8#) z&BbrNcOJdC)~n1Y&elb;MQkzk#sMI+6mIL4%@B9yaH;3CdY9X}r}`sRAG2|Vm91d+ z${EveQ>)To@8mrkYucA=cTU;92OBx=^I`6F0nhzID^HpIv8w2st7*caD#1Q8cKQ&( z(E1|olKZusC}Rm9AdV8vuxESPd|W!r_YK+#-GAFvZuQd{Ke_GmqL!zSclzxJk5%vw zX5tCWE^+;vxMTlyxxu6w8h}-C&~?Wwy(Q{`R6pgS2t6P3S-50=zmI-Sp(?8cR_V#S zzeQ0!7k}!yoMBdXc_AR%@&C-@Dp5a@PUMf(;16nh4XV)g0D0?sqYHy^5zQc^ih z)47?-O;(LlJv?h0t(s>IO(`il?Q>5`3zZ{=&a|6;Yjm>=yqnAQKBZku{!^i8yAXb$ zUIl`SJ$I)Sl)_!+xE!vLkab-!`6VUp^_@)SV2do4u1xsE1|X%6@nk>>=*rt4^zP>L z5hQy`RBnzWRuF1-L^ca=mgS49r;OnlOz@Ew`jth{IUJwjYe_nL%y-BMFI5}54+Mek zoRIm@9%U1bH4_}>{94cOhmRzAQ9LvDN{B)5-T0JIP5v%>UqIv#e0Gr3s>0kk2 z5<@K9@RAVc+StSip(;zKC-9^NtF2-(jO*sg*^{MrJgil>a6G?W?u_sX-oz~}#Kr|Q zMLTCC>#5gC#np{I^}MFP9UM$q($+Z=X5IkbF|XJa;EtF1)B>_&b^iSE9eyo~zd>b~ zG~`$r&O(WPmiU1NkIgs*P9R0{kJxG}wXerKWDi?j-Fy>z&e6UL4A4%u%iPC(%sOkALY^$vq zKD@R2FJtm31~4aEbzBF5?PaAMUx~{neIo@a z`8=n1V3&Rm{qkS4)JM!PTqPB!ycp0|I}k+ZFKk*OMu*4e*BI#5-eUSxRooC-MCp7s zr6&6dvBBN~rvl$ASNMy<{4O>Cz0gBugHIQ(QqKl;v$m2jSog!2!6u^5Kr?P#7UcQu zK25K*Q!wN~hWRr0Qu6`IEyrr7dHC@Xbt|g<#$w*r5+w>bQt@LxNELHdFw^n? z*4Pu*0*bGHt4or4eTLAS?~iG;HEe)Kz4$zQHQQrGaCy;c{ojY(quD`Cf3(7An~U02 zp!g^672Se4A7sT0W@o-2aXb<^&t8IIPEJcY78MBJP{t0;OVP*&72r}WJ`bB2YEgE7 zF{HkM9t;2;^{t^{QJ)4Q(vWAgnx_OcIc#Q8nW>^hVJUAFKP46QVs^*CdEsvNbRZJL zK{dG99S0y9X6@#8{pHZ@i^~D#r>A@gprf0@`>e?7;s&{-CyswpD`J+IXy3kxIv|c7 zXJH4j#LD+{_OIvmx}7$4n3xP(*yxkn?$h2sk50ZDS&Oe1yHIlw%#|rB$83bcaJ?3 zA8=lAFsbVhIS;B+;kX%jYf@M0_3(J(gu}+qOk1H1$2{d~q#XS^Q zH5#n5vx;DqZ0-txsSnu-W5>tFZ%JQcGs4f?zrmjQ!(nIEzK(zV0P6*SjY*f2XBe@S zV~0y=Bb9axH1h%4p#9*pwetCMl6|ca-W;p?*oi}-#$eYcw&OMI+IZ0t+Y(MR^ z9P{C+FhmP5dOFr)&n4brgppqNT2cu(JjRpDpD;VU9gQqFOL1T(i`BzXhHL2Kj8W@V z13&Fb!9HJ3>8q(p!Qf}6n;jwlTF8UzQlSW8kbjvd?v+iufanrcdeJ5u@iyL(0Icz< zs)c>bbk<`aN|W=u(><;K{M6=Stzd8Z0aiX zX?ZrICSz=4@vMjbNMmZkWy3gvo6Ab$S+H4*vI?FYg=3~AMt$aa?(Cbi=(FP4>FF<@ zo~Jw|xY~yAJAl25FyzHMspX*PcVoyh229@D4O-1t=B>4O;w6cNh%hlA)1vDbXX#z+ zU!>>d8hw*ovr_$;or07RM?N(s-^+LS=MzpfC%0xo!_}EMY*s$$X>Brf=4t|sY!l9V z?I`%V5;et^HcrdCoi$nh-)<7KTT8KiQ(vZ-puLCEGx%x&D0-||Pk9<)7E70-MJ5DZ z`{t}KDJ<{JL)xU0D7}FcTp?d^>Q0ZJv*o|WBIUrI(%7H+zYxmuJDJA9uIq|fo?Q-~ zrsFTiL3>kT$PTf*_6@*4FJfU_&y--?faFF%SJQO=MgO6bh1KrmmpApJ*<)Xe`Y|)f za*O(*iKDwViUx-kwd#3J#n(HN(vOW+_}`Heuc-7ZMg{7@ieBT{`pu?2{NY5*}aNM{)+VP7YoKzOg_^MaDK!p9wJTuWF)5XOP3(&IZi;; z_q{gzE|hO29IMSy0j2#1j{V8fg~<>W1-Vjj8NOp8ZH0UmaIezW9z&O-2NJZlNR%qu z8?l~!@$f=k$WvH%FxPoF3VqW|dF8IpQs7iPQ&4xk4B;S)`YWCR*JM9t+-a?4|E_h6hjzxd2Z>v|V9}``Ody9Jc=uda7DBFk@=5Du|tW)RE zP0x)#)Hx9YgUn8BYQ$Q~ICGuZNvftRf~g66b*_F~2pdPE6w>G6#y>o@ah28Cu<+k7 z#>8OYKDy$};V^U{lmRcMc+PXd1!gS8LI)mOAqIc7~W=v27h+>YHV=%R*i6@9X!KQK7JD-$V9FCIZWA9qCNVUnhxjOQRH|)(XKQ z!XCtWM0%$vMgCWs=jRIaNjc2%uzFw*MnzFdPO-o2lFvD-K(}z1OId8cg#{Mq@jG$U zP|ldgO0sr)D`I)gh+(w9zmaO<_xTjkVUa2t%}&hu6VngR-edRy)@ErUJ$^8v^_8~; zu&mWKL-Cnu+6OhOcEF4+RTh$nM3u}$bTEx}u&EhBPiN|RnUEkS=ATc4^csFCCjEiC>55geo-TW@vH>h7pMkEaIO z;+_YZ__EAIVd?%h4M>L(|CF@M4^hLfq?dCvsKhbU-;T+}jaNM`pbZ7F8vK$EnT2>dUP`daiP-d2aR4Z4hQU(5hXvv$9=Tjp+4ih5q-VNG{Yw z85ciYHN@kkX|=xiyB>4On3(;yd`ubHyZ7QZUsE;uPb+=$T=n>pJwS?d>3k zp81eg0U>#G&Hfe_4xWM3Z9ZWhg$n9K)!x-6;1ec@Knb^Zbu)T>DBX0WKOHH+TLno# z!S4Bu^RK6o8sg{6rg_&wqB5d>dn(ia0s24%zbP3TllyhJg%*O-QkEU+!fnBK;3z7C zOUkP*f4OBcS6DV_pzHcdFJfi6=k+rMi2%c8JcR-cH11T$xVbeirUwc#H{hrca;KmBemuAMmsvn3BAOpugq)*F3#!%k$;8y5&a46p%ZOkwsb8 zfx*i!zl=BEd{b>IapR3QW?lOqw!0rzHT1y;@4|!%U)Htopn=BC1-LCQM?u(YeE#|8 zt2*vPq>_H4?TV|<-q~F)EGpxi zzO@f9ib>1B<$H0ZnX%Oy@4IQT;Gv~O-?43 zcu^_gh^=rGILo42o@uQwdU{)i1z%iLf|qdW5~7L8-4SV{?bx}?*M3*}k4fIk_FakoHDPc0N^EVGk<)5=IbIKspAc_dg|&qYvgshh!`9<(&n;m!JPyfyO{{KbMw z_3~*f7ZU80h(zMpEiA$Aa$=h3qgRw+Pe%;hl9n>R8&$R>y2Y@s@z5Ae<3MXf$}y)g zxy3)wB;|K+j>i~C43!QWicz_QJ$jof`xfAgci^!0SXI(E#xkHA&t8yo^c z1?M)qS6y|Ls&G!!C>t1z7U-Ufx88b7jSR9^ZXi%d0zyK?Z=i5m62Ij$P#7u%L>nvI z(T{U)_w3oD>N!p^rNMThDW<%a1g4{@0o&?Y@XO*Q_`rD!FgsvVhEAcXOSV%qTp6D- z8U!?a*u>FIIq7TSNCRA7;}f!(W9KLxIcGj|@dU=2_+gxCUOB_j;cMvY@{?h!ODDR= zimX5Sr(gDF^1Lt2@>>~Imiw6xDn7bp-F4n@M8s++X?F#2S0qS;c7gIQzYF#LG&qf~ zUdY)K2Rd+V?`F&l9zey!scNK-#t7wDZLl_IXqdAXH5d%4Z{Co6aI~*KjhP1?ctGjQ zCqMZ~wZ5cK;ImUFN&GowlJ*Iwt?=6>d`_*eJSx5J>_m4^tcV_DbwNyApXYH?>d&-)z5@|Sf#qHJf~s2f^8xYf3zy4m}W zst3efeWX;b>F3FdY}ibPe)c}ylFPaA(!bJ4IqwTM>&DXCSC3@DJXCdwfZ^*r%2ziO z3Vc?dWZrgRmyhqP$65Pc#x1oz6pyV%iI8=@28(5xZ4B2Sl6Y`>B+D>$mmU39k}_DZ z5)j8f3h4Ol7j_D}kK&`3Ps8-FLXQUsO-)UGJ-k$CLp}@)4ukqY;bLZfB#TA@doB7K zPruuUwL4p}WcC=`vT$JMT(k1yhmQtwM2GmNM>naK{_k*5^ObJyy}rrNdC^wOq@+KlfR*TW%Vx?po+Z z$&F{Ag^*&Tz8Jl(BZ)t}yBk+`ti_yEJIbmvAK@c`$XE8PLc@jxnxg?6CE&KTtdsg{ z-G>!x*RE9~4(FV6j#`^hDClR^`l`=4MVxgnMcXNKcZfcx9ZpoGA}DU#jpyn^sPk20 zeyKwlsVqN*c+a}5>2jsZn08ZQ@vpB@v|qYYa<6IkTsf9My64`P>vk;NunOHzq`wrM z4jqRzu4APoH(VV?KEFDR{7pBCss}9o3L&HlB#u%n@pxSXJL~Il{()CV5cpNQYMbA2pQ%UqzpKKiXjS#+HUeEXLDZEBQh(==h z*lNtHslvU<%}A|#27mGTL2T_#s0ERnPQ*rv4Wkhw6#1`=VD{p2!SKJWZNmCpJMm|& z%dpTBMWq-mBoNwYA@Y?$4Nkja#{owNSdQ5-!ri0#n|us5w9b)ia6Je|d1$zC4Hu`f zXkxmcgkTwBi@B*g zg}+<5525uh;|Q!S98vS+hn>O&G4=7Q_Gk@Bgm8c`};727z@Qi3E)}k`TIa z`YFkne$Tn02@3tPC%CG*24lug#CyuZ_*nfjc;=<&@zBP4yxoyd(+xP4AYUV9WONA) zUaq0p-Hs8oozt#3Z=5^maFZDRW_cc} z#>|;Q>l_U?5-rY~*BeQ2eTddq9+?N6`cgTr1~rm5ca??kg^ri-`fJPZ{kOK`*@lSP z^nlZ5n4|gLmk}emBL9^RbG_e-O;P-K{a(EM`YX6c>izfmT2Wm)UWo5_rN=CT)-uVC zO0LhMezVPC*=75o{S8GFjDT?36{jDt0^>YNYlUaTh>@d(dct{|Yz*Id=bfx34L?xl z*$cztXag&LJrbgORteUJY#4O!Rjv?vN(hI~E2RvS*G|IR+DhE#tHZgwpT&w5ui-0e z8nGpqf{$BLO2ft8Z#H7=12Z!6g++x^G`PULB@X=SrY`((<1PeNKZ-v~Y{KHABq}FN zMb((G$|zx3JXV?0!g9H5*DggkIFFo$3`b3spY;{%U9Lr8=K**B zVkZ#wkzhVBFO@&Z+w$6pm?ZghueTmIHoS`Vl~3WH*X+mlclLtI*!`lPY7K_bPa_s# zSzwu9+2E|YGW)s1g$??nBIXK>|#7x4-EF3g-zgYxl{Raw(JS!q3HndF)eg1@P! zdfL@cCLcsW)_`y>CFhZHZl@+61_lNv26cso0Ka0&3Wp2I`U8br1HztEZY@khX8QE$ zS=au@P+0?#3*AVtVW82%VRJ4r=VDhHIdnT#oydFn-r10ljQacmc>P6c=c#kzby(c8 z4v*~Z!qUbVqV6I%>`oLpZE%a7q!vg^7!2CT$Pt1D3(00(IEAe}3H)JO1J>`{f!phs z;pW&*%&G9Bx^@C8$5o?T>I@nqEQ7}*5G-pfhtyZp+5}hq}NWsA$*^ z7OXnQwJy|Kk|Rz>W4b8JC+&}EroVs~hHlZ#DxVv(Y(2QBeLdobcHqZ{64=-oN6hJi zOY+d~5RxOW!RY7c6;cBAp;`Ie6iZ`EcM?zUYQ*o?Z^KQ|ZMc5_^EkiUfyv{lP*q)v zsa-6?nssLb-G&;hYd{8c7c2(N-Cy{j!AmZ=L@9JOI)eeg z@rqjBYsb4G&uco~n=@~9;J`|Ri!-?%;e`SRshv0{bVa{IdLuFJai zKZeRKSoy$ukHR5xu7%@pISnqZjiJHC5fZ)S=dnN|h(dq7k2@0e_Vggq*@}aqB;K}- z#UWP(;^XIGR#h=Bs`R1C3$>-D+UZ41}|&tO|kBGSy2;Lz!fk#tQ3@;~66~WY{^ldGlsPRyg9po_`uNg&-e< zUL!`sX()jDN?l`T0XNS%bm)-!rUJ2+0UPLdhvwCo=_wVNg$K=umokx)ByK67KFqdnk(bXcF(( ztFWeI9^6HxxN>48W|#X>9{FcbRo;X(5w_$I0 z7>mREu&{F*#s^#|b=lw(qs3n=lI@|V0dg|LgP>tUgPB{;vW=#JP0?#U3PC=GDyU~9 zAtd`;;7bz1(Oh-{aWkFV86EtsI|-=g6cUk0P{>Gc51PYqbi`8FU>}QjO6S1lFTte~ zDlxw*fKt&!c;q?8iGtaMoJ{mn?SM)COoK*$-0ChZ@g*d^JWpEUwm!pX2aN>I$L3m) zYp=al^>Cg^B~wTO^5TmxstqVExZncZa?34Q*Z#+FARvS`P`EWO$p^m`LgSA{-;5bE z)b)IIrS*MNH+W71h+&b4uzZBYkPL@<5fy_c+1rIfF*}w!Ct{zy3?-H0P*pu1HAP;G z_c>AHv#SM*t<_c%reI^$hDefWo3QSJh(@Cexm$ZwV->aex*AQSds{i9xU z^qcJl%M%F)r(BVQkhBctkq?alCm;-xnXql!Hq~p(p3jr%*&AtKF<@C>Q=r%|k790X zqCtbbE}TY#6xOvJNpwnOv`fzS^(Ij-$J&4tCh=S8^T6e>quA|2k;ehI{I1|9I|aqU zR7%9yXP#GjU2roZxkrP)9FhoDW_CHAPW{CY*{d265|mrES$bL`nXj921cpk-c)DY8 z1jVw6B~oY&#ngR`!H|SyM`I{~A{Q_<;K1~NuzyMaIi;*{ZJ1HSToS`d!b{0b>L}+l zj^w|J?{HN=xQoiSiXR~*~BV%X9h zM{TqjB~9B=8}CGoBaUf~Fp8zlclwLqD=t;WhVCV0y`YJQt`AP8UZ9b~=^O0LXE`M4 z(DTO!W8~wz-~Fy4LLdM5#}yG8O3?a6onn1Pon@y1^Nk&>`KC}(FZGc^8%U2@Dl#Hr z#Uf$EL_cHE2TieW@1E`?ws%LcxigB%()>6NZbo_6A=J9ls7`jF)|FCv z(mo|qh~lQIM*0(0zyS)uuneuq?%g545Hge z2VXr&1*a>qVA25Zil?P7LwnqoF=4yL6-aiH2~$D+z2l(=n}Uh2kV zzZ=zl8seD>CG#s2zAj(7n5jx9TkEJC&Qk*^Ka@PzP8L))9cL~S=MnIr)2E}d#>@2a zGEaMALbQT$?CFW2Rmjc8c476ABzAQtFt^x-YEjy0-V~p!HwOY-&@M{LK}(Lxst$pyFt$vL2DzYCsyb6dPo?UXwjh?asnP6iX%E^WG3;z? z#{TwBcqJ$2Il3{vbqCJ2cf#fN!Yjs#PYer}M}7y2;PGY_L~GKadr7sNELdl-TyYZw zmOC0T6m|}<%xUAM(0K5{2NeOi=bn3p904JyUlfuDrjwlp96e&k!OWR6`$lcF9_Auf zKlR8ZMNQY@$R#@rVo}wBK=-)FdRrxSxyIq3zXm-iJ0_O}P+L)n@fF1=7ajFV0rN_M z6Ei}MXe#|=JaS}|qosZ9FHm<`{1wTN-_+lbRKx6mjf(#A*d9x$j@5lljX2N|girK$ zrlSWF+jinCTL*k@m*}WVh=C7o>Z~syq(gqYMW?f*gZisU2XQ=|<28<$b0Y+H6tJx6 z&H|2{FpR;_+0#Km2nZ{-Wy_YSMYC)KM;i32=A2EE4!1pxE-|X#?Mz~AcM5xhNd%KN z^~Kbb!;Pffg-RhC69Nv^n=w{uiaDiD#h%%yvsZ%^1$#DBBhSFqn>mb}(_w?7)IuDn za;gV`znO>Pz@uA^TRIYG5;C$sn81PF7#cz;DX0n95>ePvNqEvpOc0CctP&f}uW(?T zkQ2FzxKs|DFU=A?T=VKgpz*;eO_yGJsj7sA0|B8?U5H(iqVsbhS3`rqcr8iL0b_T>?P=x zIxAgVB@KnN2mUg6gp5q^IZ!T*>sYaPDm_uf9gJsAJbpoP6eWD&QA8F@M zUr9o^aGxEnd_VMaELe4!{23C? zItX|!LOY}>oJNo6EjvbAqj5BgE<42La72RWZ9D*5xEuat7_&;~h`X z6CD)>xkWA;Y64Q%`F$cTRQX(}4cL`o!&0eBrd~d#n_Zq} zTArhIlXc^}MAiiq@YNM?vONUX#DYEGViiB*iO>l@VyM=H#glM?3go6f!9^uf_sMiw#9S3>}CH0k~Bh*(Kht#jE zd38e2ABFnKqt;PPN_b@03DHyPW;`55A{IeZ)VfOyHG$x8}R`{H#@Om)L=SHQRkM-G=J)_?^k6KUF@669k`OmsrpT_F0p-yw$&KQ<7 zYN+&C9Q9AS>E9}SS)rp&7LT;OT7+n{h2v-p$Iu>*h%U#mwA zDib{@a{>XeE4=AAirseg+vXH~_KV)Q+)97-`VH#{r53GMR^o9Y@ILATcS~fsV2301 zm3qr*lZ@|F!W$|Wa00@{j7A4DixtV4G@OGCzFrP*+R`a(>PcZ&FoB)HG`2^a*b;JL zuCU<6RbE^)E`T|uc0{BiP@@@=t6G@B$7@s#P7LZV--SAgXmY}BDQN_}+7QC}wg^_W zgfYdN#4Jx7bEG1fUS!Ao5(fg(;73wi?4Aj4xPlc98#}J4;MUfx>`n)TBp_xNtdj%l zP8u8R=^_!KK|^DaM$W8Rvrc(5B%d*<@8vZe7LQt3clQ}R@|#mYI3*;>o>(!s(n&dT z)I{8E^1B^wG^V{cl=7oJP>C*g8FqEYQ6xr=-|L3gQv|y-&|@U+4x0mBX_(WJEPe~Y zm+8$xz$uM1m70)qZeWr7h&<)!C<)K0f^~986hHS)W)G*y#^s92D~Y5xkrd-Z3_wX_ zr(C0ZOxzMl?rIcPqXw~Xueim~5e#Fj$AOu}cKA9QQ5k7NjU$F?ghfvsNQ-#&Ye5eVpYCp8Zm5}X~?h+p|LaA zc;JBtR60NT$xo_Y-k}b@0}=&Jmtwil3)HE%w0$_*%e>cmmJi2!xB94cljqb;O-_`q zW_2_X&rH{%o~A?+qO&P+qn_GR3Dm)dHh9sLa--Q(j$U5{_Vy+vemhEwxXrQ)K7Yoj zEiEgDOLWrB*&7lkM^QYYul!w<)m5g&BS%in%qCOmD5sHAt@{%s3+#OC3s!!F#mM4x zDCQqKgQ>Ut9hKifrRy2Jjf>v${q*#7DZNc3MQ?k;NT-sR6mY2Z8Q!KnN^i>%!z5b> zNvWqCqK^)@SH!IdhC|2(byjs6aHNvIMQ8K~gzhY08uU>k-#aGql?i=6-!Df!xaNZ+ zle)uTxUKidhXE%bWa>+oE>%4N?7^e*3>WZ~vMr<%`E4n+zy4zl2|U-FLS>O3RmFZx zDe?%}aH%0UDqu_=Pp?NXC_G3^I6s{R3UzXOw~)lH7VBJY!Q~UedDO-Zk4y^<9{#4mL(x65>bm@i2>5MXNTyOROAwHQKVB%x8O4bqFvwy7#zi>jB(1l33j4P!Xkkx zx5b5Iu+kBOMGO+B7%U_jb}?48Q9^@`M~XI9sKfebHCC*8+#kjT!(cp|{>YwG8e*Jo zMdO%diEXn^(@+5G2-*%ymMrO)Zygl^T2J)45U%6WBO-j~TpPmC5bDdR!m6Ldv2>LB zsdbd`P_(Z0U89atSCc%7u2N4$-#BHtBjr@<8oJU>gwqZrd`0MtB-9jgZkQ8x`;l-6 zi56`LdNR7odlTLwIIDyxO1xS}+Y@%R&gslcZw7o~r~IvQW%XD7NOgkZbR0d0_KNC> z?g_yvinL1tM6ZNkcw-^)mPWu~N14YCpZIk~LnyT;)f$;{TSD0iY4NY_r`}RW-5xp5 zj9gfCR_lmWwBGi&{xWQqBa&H`BN7gFJaP1c>pj@s>w06jVn7K9m4JJSKmGL6%HZJ8 zF5QMQSXh`1oVvrbaxwI}&LnfNaRCHNY7}8+H!yfYW>({I4 zOl+u6r?N8yWavU07M$Y29xygWG;Fxwm4*&?L8Adle*;x!tzq-XGR8Dfc&>lbxY0%r z&-F1y!-qx>&y#XpjGwf+H*Q#`{=gi zNPQ3^K~5>kU2e0BVWRzT`}35Ljm(ry2V7ElP{*8Nh_F*bJc?aI|8ia2oNh5jgkU&H zG~}G&&`?qLWQ`VWpip>SpIbq9orZw`<77L`DOWUTIHJH_R2nlhh&h)$U%CuV4_X(f zuk0YAzH%gl$0<{$s4vPhuj#M9K%LM>3d<9Zj6)ywZ>^(DBhN`rc+BXh5ESaD=wgZ^ zfO1rN%Cz!cQB-B?h#&5BsR)f+qZ^HQ`ql9$H#urpf1e7D)D2soE^3`prxGvEmClKG z>MTcRDD=bf%?=HYS}1}cdTSRq>MwO#-^(NY=F?s4Z$1l;6qTy%u|L2#n6E4|)LGW= zBpn>}px&~bXZo!14_6F00ihC-fbfH9WNfEf2nz)otEG+Pd$7;}~ejd!?#UTw>F3w5l$g!Xx{7u&XnKu?XYB(iGk|-mNN9 z_S_8hSIf$WhQ->oYZU=uugK|CNQQt6eW*rW8YL1=PPx+yUU_8W#E(yLIG%>ZKtnga zxpg_unNIy%3*EG6qemaLI}Mzq82{>Au4S&PfRxg5ZI}EN_aluq7I$^OTqb$Qg*F&6 zdSTHG`KjIMM=5*OjpRca8$wQabnsk^5$!iutj2p^cmjPS=x#;s`9Of_;s_PX3FBj3 zp?A&Eh75ajna)C_=Ttz$i)CQq#EFU^4L7iCQx|o|0m%n-l_OA`=EmpZd!Qfs9S9sN zT&;Kdx0XI#l;64fsom94By;_6CxcvKXnp<^VNv>aIBXtI2U@2xWi%6JrX%36u4R6c zIEbE#8+BF_3++!!?zwi;LTI;qIF|dFg5gphIo*nS%TY>>n(Fmgdenn@Jy2;VGzOf2 zu*cfV*@Mcd8_ZMoqLQF; z)Pqww^!A^`P)KtwzW@F2t1|wvk9};&FG9};mP6(%^PVFNBq6M;^*iCn2&cVKUpX^m zAc@HFpwp@2w(3`IdU$>!UFB~r$2vb1=(5S9mVD=g{%tkTkEgeUwSHjQa{U$pjFV|* zy}otpR;8<)c1wF%k9x3eCmEr?!Aj>qpdbW<1_$SxlJyUE!sZK*qrbs)XBJu|TTLMcDMeuifT$oTj9zVHk9HtaG z(Gkh)n8HyHj{XmZ$bSUfK&eQXNQEbJu@*$35eN|&u^dTdqi^M zrSV>7NgI#sThn2>oj~!)#4$+14FfpB)fLM;+rOS$iI1N>3Eyoj!gqH?)XpKo&Q+Ni zNA-rgH7p9# zY?a*WRU8Xo0%x|1tb zzb7O4!m!Hm{94m`y0C`VpThFM_q>1qe)Tb7w&Se3si#~g#4^Hjy*^9dHL$#2?WG0g#-23QmT>SK2 zTrRTsvE8^tj#oUr2j@O|2&=?$cI4c5A|YJyF#dYkK3w$FUR?adZhU4-NVzl4eD_d_ zl2Lr+`8~Mor3P%v8tQZxy~ozQO}ONbyKyPgwsbG%|Ly?Z<~8xdFDE~_c0b;;KB%TV z<%T=dgoKksWNN^PuP&-aw7437yDg#03Q0&NpYoThLJGkOiF!ptW4KgMZ@&4a+G6vS zS6<1w7@Q8+;BpELd(7CwO@oFF7fA?>P8y&b&VT2fchr1hmJQxB5~0if@nj@IKO0B6 zNIGfE5HwzNSzx(f`^#xQdcB81;lcN$NeGD`4NZ~{>MB1n%8mqf(!Txn+iJZC>v(Nc z4|nKtdZscGz<0`b%g&)yt5&J{o4UzPB81+ zaTosBk=YKHdvJHg#c*aC7@oK>U6kC>l%XOdp?n7)o?e1kOoHWrWR&X)*t@Q`>(rwjyw6~8q2HZ#G(?j{>MZq`Yx+1M z!p#F%PY##5-Qd{ZJEZ-}N_h!uN)n?3c7 zt+@QzeYolQefW!ay3i$WkVQ5v^;K;&h-;tQi<@8AkN3XXh(mIXqY|R{8v>uzpUAR-;cn4cl{)?S_MU4+(e-)l- zhzfB{svSP^m8(JuE2n+?_Nhip&k?2jPzTd(jNtwQ^Hb+34Ny*ZWp64QGZGN?sL{yb zmc1Mi*|>3|>S?w1hUbsYNLfl42cKVdswGi=`u2$!0BY{ zs2GZCM*9m^BEnv8>MP$L-y`20>vvAi*}HddpMN4>*)THngkZZ&9pqX~uFK-c2SJ@> z2Z64iwGLCihnueU7Y4*4;0w^ZZ*ajCD-~vNz85yoLh$HH#qr&JXLjOwB1v>Wd3 zIAleVPGgu5+gH7*eS4blg*GoSYjxoJFlw>9AFTcyI5I;y*+3pZ?v;OXn8;>jB) z;<`i#_rBJKMk&`N-eO!iCXByc(}tbF1mfamlQ6?l&*zJq(RPHQc%=!rd6Ek=YD*D6 z(1Ca3idU$#F-`Qx-|Acv8?)0Wvh`h8o+aITB;9wejpL`cO~uRCl;QDpo#+(8#4Y^u z#Xs1>Z@h41?2ajAc(^@)U?h4(SsDzX{&3DH8zfdnhtwY*_N{Rl<$@Am!n^7XW8Crf;n94 z1bzYi{`>D&_1j1U-wQ=|&~l`T?~bD(9Ko71XO7Zc8m1(f)K{+UVMhY*Go1%RQwXhh zd{-oooOaCZm-(*wKB=eFR}xK<5Zzh8>$+TwMhpl6;mcrU$Oe%OBj-2j2CvXUu#oWU zpBp1?%qewe)_aKiP!<+zjvf#kZHP;nt0#@a&*l}OEe7-?T=@8WAKqyVBPi!Z6{Yy~ zk}C9y;oKFoJ6(bF5k!5(=ZE+;;5?i73&9r(yoyYR)go3XSbg>qLmo*ZGewRd!4 zpD%!OTsDj?3Sgc;il;} z@W?a7U4DH1yfV0j%<$_4I;^24B&bzhJKEB2gu)~sbUoaN6k4cLG)8EYaQ-MOFS;8T z5G)H;!-i`B^k@i$1`gdh6@UgU8%g%6v!1p#f<^{Ubi2v+ipC44T9G(%N*xUt8ZUg8 ze6Q9XRQ>*jJGlD~pNY>i8lc~|bp(^|mm?yan#IlpPPtpRZk;LztpAxegJnw@Ia>HG zC>-VB=oJYgfAf8kOp;vkol<`}UDoNv15lCI;OjoITlJ-fwQr7 zq>@5D=$0{#hq9;OQWsv`+l^k97zvtR z2JUI=#Cu=ZhijhRgO6=a;=$`D;}S1GzQJ&an51*@lyY2T597wi_uy+gLgd6> zio($le%y#t15$F5uWS`U2$o+Oje3svaH_;enz#;RBu}jvDxgurvcVp48g%SorNPR2 zm_`kaSh}-^m2=K%+_1r9S>nAEgTg`QIfeDiwr$%KLF9JJJg4EJjTjzjlyYh_*A3*7 z$KeisFX+i9pX{5mGFrfQ$aQ_J*Yyr}`khl}IaQ0JmLwr-)~r#T2!t*-BjMc-7wCFY z>l~+9z4g{xYO7_ITXworXGuO-*YdY@dZcc*%{$9rz*-x=BoYv^J}MMn5{bZIfJPjN z$2sX%tZt9OF7KX3`;Zn%H;tnDxxI|irNi>0F z8}{QpZydsx<=U6F1vx~4G@n(j$K+2)Uia95AAfhiTI|ar*?!blcRlG2tI7 zUIxYUkkgOe5zA&Ehz-AFyB_l2gbW; z(gNAKXq z&dSjO{SNs)DXiPsORhT(sIOXQc}{)iMkJh`#k^ui0?Q1`6Yn<|7%*>{$DCrt{O6_t z+|`j&BKgjEUB4^Vt$M_Rq*7nk@5j`uhcRF!mYK|ok}p6T9voKYOJJi{NFm~O2d;9r zW6j=r^sonB$^dQeAuEz>3RBN>ha-&@TkG-LbUB{8ZUTOF^#uI=6c18jE%Tck`x;vC zjYAGRaqUF>LazPh>{4Y(^IkqHzvtoPGfRN#TPGrZxsmH(R9re9Ip1_h$cxX9%d6eu zFO8TmifL2IFkX&pTSADj)G%$5w_GsKh3H%~ACUlPyfe!gmK;>rPVwIx0~XD$#xE9E z!rm3c203RiI^Llq1oP~TrVuWQ9}vRDDFK;EBe!e~w6N0S?iqRnfyN1Y%Z5`WHh=zn z+;!JoIQ#6gvn~dO0?P@@5bI*SYZmKcPV=D9$fbeHo>v;f+6d&4jccy4o3D%-IlgG< zHj%o_?>}&~gQSB-B&SkwbdyAbM3Itf#AwnooOJ(GK+?16;W7eFuVP0a7nW0p`40Gg^wdbc zAHFNPv94u1t?N$xJ`9coZNLEo6+w@J@a3?FjYG?Pq4{3=KnTH??{s@HyUdHlog4Af zjdkkDStMvM4^>Hs2_H?S5EH97ma<`dxfkBfFkWg;VBLXE{QZuwdOi}0l7I)ru^5(h z#<8KU2cO^AEmzWN^rN}C1)o^nfY;ll;p>Uvt!+*CWx|E`O)W<;!_LO12v0PEUmxhj z#Hm&I(+S16b#f_gpIm~QCY0mu3Bbzw9_*87QxbMKkJQ{v(b?`np3_8Qm7}~ z#DOD+G)UMhHXP@Wve$^lEqm$=1_#W>Y zEm9ZH6k3s}L$Vq(=+q%No6n5il7uDc=FVuRBzPKd2dLs$cCGGg|j8eEIU!o$+ACjEf zG9P*qHq0vZLZX6O-r;l4gu0)2Y zSnnBZ;ph(u+oN?M^z2xN583ylqun5WcR3JDuK5141{nWk~b>&#OcDLit- zgG7U5kkhNEOTyfqrTEJ;&H2gT#l;zOiNS$JtV6Qs$j^%{qh{E4mXX%Gi*jVnk zi2=(Y4QJN9yjS)M9jCvcM7?C5(GN!_sjC#W#Uvvvr_^Ol zIw%a+;MfSolK9qBTjB7!_$h}B1+4w@QZ&?c;rXv$gX`yyQy%*ZWRF~=!i70 z7cW*XwovOr_?T2grlY&D4lgywv9#!H+_+#muB-MSN`qLEQkc}799WD!j!e2}tP>s~ zOc^?tSzylTO+50lOZ@V?9|YHYueykns9Ss2Ns9+qITjGlu0GS z3WgO`p}#!H+D;x(*E3gT$V> z^UgaD*IjpA*0uj(yZd1^jCb(CyD&jWyTQqWy?!ifG*oB=l4P(U<}N&%bgAK;chpraT8DX!whzk~$qV(B#Fu3UUrLfDw`g_DHv_k|Z;s9q$MXdo(D?xg?#fnG^OK~%v4!ZfhaWTB%{wl~%z*wTQX zMaQGHW&k)kCx#CtD(Vs&_*ABGY?2O`1~YcG+O9JV}-_xHez_B8}ouj zj+J!umrf6N{O)(ZQ|0Qm+ip{NX>fc{kF0vd&I4-)f>mcV8R3x~3Dj-sIE`(NyyN<=%%X*9F>=@8fn>m$@`b*u^odvAJ_?vmc-_%*=2lK|-L16Uu z^iXgDLTCkHgT-kj+%uWVz~NTDd=`vCfCZ6pkpOi`T7s>Oc&96kwW%^RPh5;S)W#04 z(Lay67m-KZgUE9lJXYhzD!It#cBaUuLv*tWLEYywv8~|qa7qTBgF4E#i`V%q%oA;_ zSdAANGZgABh510yb&8J1AOn^$f_Y?cYS4P3^-9}0EVnG5RsuppoJU@x{t~>V4Q#ES zdP<<4f~>Rzr6^894 zr=MZ!x`(3E%FY4)X4&DW2Gh&w%4|z@-9llRqWCgHc`JTn#%rIyK zL>C?wXcnBhx;j-*xIHh2!njjbZi6vUU^;Z;L$(|drR?tPK%}P&(VkAc9xV|vQUyn4 z4a&w%!sLn~%rACfjMt_7aDR(%2nLU{^4Xj+O>=HXOhbXBQU7 z4xzF*0B>mpib_gRlpXP4MZ^lIP!$j3VOiu3Io$eGZ^g+tvSrKE zzV)1PdCM)gWL^6ohJXw_vdx7mkE}m+9jXl+9yI}>Xo5mFExPkZ8#X*AA)&#~-!y7y zurLnlA`Ks_0i)AE@HhQw{a|{mx?=rJFh0gZcgDr^@E#H%od(teG)Aa#si7*ED#}&T4}3QJ5ySjnrA%ZiLoL z>K^Z5nzSBkUDf(ZH!aqBhDW2T1CK!?AQ~*pEJ&>EIW$HRLNdZR#ETX!QiYX%3IRSo zMSGzkz|sHXkl&^ zhC&&zs^AERKCf*?v z&xo#!3I%Pb@S{f51H>`#?Wey{NCLuVV?)J8gCjZv1w%jz3vE2G{?zBX zKBZ`bfufBY8Zi{s@2qpJqVMB%tD80|_?zJ~JYFO8J?zOPXq0GBe`s{@9&On0$a@(k zuUUx$g>F^@h1Y4cXah&PY2D;KR>MWdqw~h#hKlxkVr-=#ADbx=hbv(4KcqDi~&*_KluNC^7dPM)!H-^n~{-zFUz2lK# zSap_eT35B+YIpkOv-3A$)my^oYhf_x1cYF;EVwlIH3{KJ2+!xvo!hqvO&8jM2IHY< zMWcc!5fTy>iG@Rm2D{M_OJY@e4Bm20M8p%oteP53s4T}=p9du_o7&d5EW6zl2?^Jb zXkub;29PXuh_HF*`VH0};RuQS(;7+QP)8^BHZ`Ls)QcslCR`ZZk6K3@PJc1H zMI!zHylS53;l*XFjC93Qs0v8O!>K47_2Ay_EPEUQq=C+!SZlfl0)-?Xg@PdW zHjNN{WS!3=g>|hy=TVxgs83pjP!ii7ulcdVFAQw@w|OKKNOOh9rj6 zYW96p$f}Afwe1;RX*FPode0F2o+ZqUH$adnV$}L9SNuTgk3NdR5Li~co5lSvZ&6=} zBNa0f^8P3Ccj^lwf!_+z-nalw1m31eG?Leb(bn%ny68EbvYCrceKa%(ImZ}$F2T*l z(P-00^1>FWX0c}4zfdRU9oyGJ2ThUdzKg`CWtDo%l>n(6JAKb!Ro4;Qv7*DIj6Y^T zE^mn$DjBfQMtj|A%-5AnyXUVv@Zx@=8$&-G1C|PavDQw{D=BiZoBJJSsv?>6oDN*3 z(|e$UN?)he`kTRp;nVciqzoDBq(I||p!o%qk>q9`YnTFvSJ>7M;}w}Y+;1OC8;dTI zavpYV-~A^4BJQ{R?F%AY-`Jt!#is${i#MIW+$U8Qp{@cc1_tGO-X#hq6I&=UNHUNB zyr|5OG;DNsYCcPhel$#WNuz?Tpj~N6%|6b;C>6|pv=ELgnLt&04l*e*3Tf$>6>F@8 zG2}p2^2RPjyK2wMrMKbJ<0nGfEDG&5z1J4pY(6HcoM*WMTfrKN=s(1JeyOQ{_&lhk zbgAVzY1bK3)OWUR#F$3oVWxMcNGD${jY%DA`q3U6s>%JLrU*)+)`Cxg(NmECUE8|%l|u)4&0?OT zjRQWv5%D1`FKD8SC0!)P+Ya;Vf0>@7VOv@L5_W*=D1ef8;>E3Cmxk*3ie=iJl3j#} z|9a+KZ5{bP-4VrGS#?%o;TAVN`f{|PgapyjbYent%hTCzrVZ`Q0etb-gJoz(|7Tu% z7q@jYMF4JB%8rwA#wXi~at3Uts;nyQ;sWR6pF^vx~aN*J0lTP7OuZlT=XaDL8VC5c^{fS_tRJ!km%3QNK%BtmLQ)Z`83`EYaZ1^X-bZqL>rZi0GF#Cj$a+ zlNl)%a^YZ!zp(37ICG}vmiv-#-R@gP1YVHIHDC&yTdV%C&Ma9ZuYxU4WxZ7Tt_A~8 z-zcM_0R%RxcYWA(dw+B+TlV4OVKaq5#1%+{%2U>ZCMp$Wg&x8ubcnf(3L*Q}fG)R* z=C+Ux3twq46QgRP#5)C*>mR`k`1HKttQLXq5h+#G13<~p%*@yPh_SD>$oi?rzt7KVOw%~YlEe;J4h32{XXhE9O z)nj-w3*vOCV02DHmJm2zg4Kq7|Bq=03SM5sL^{`%yj$^}q~!BiO`;=N6`e$ixn<>^ zmDr0MT_L^pgJ8+=Qu*Kr*&gB53gL9cIZ6jlUuh9Bhp;$U`Q}y~`9SxvS=aS1L2Uw4WLC4g`r8=WqdI`y#_vJPbFG%@6vRJC$jY#wa z(3|Fdj0S?p8Ex7t@pmw*acMz0<={95f|EaC=;+w!QAW_G)cSnwG&z(0yts65H$=pn z*FyS5eL>zB@#~pQ91;XKU$Q~R?bz#WH%~Ps!S-5?!?0jTEW>( zqoqMX=|q~;ka>*-5koLfnDMZ-%;fgAx={sxo zoMOy7X0xSRY4lmfiV<|wq=E_fuH{B5iE+n9W%(P5{;afopPiI+$Jw}uN}bkR3MEcE zc+TS`OAdag&)pH*zK>n)Nk5>cUQOq%rF04xM3d@EK>nda=EnCiUNuyhO?gDgl+2nGPB(V+BZF>IGcTVwp z+$4Ww)cygeJ`H{zlkcS?UsctZ9%z`t;Vi1L7wLSH=4$MNt7XPbli%3XX&L^G!kqe1 zlFr5j8pu8qCImF!pALS)zfE9N`9tRt0`UGau+jg( z#=+!OQdCe-SR&-ibpG-WN%`2aq_QQ6U zyxP>8Ag=<_sK5RBR`92{=KO58d$s{)3l6K{k#v?bop^Na96gLu>4i?1tNoBabY34Q zqFH(f!7DJ9WFMSL<}{oP3tb#Z4^D|@g5p-FR{11^#l`V(y9fb&=_n`)os;n>D7l!O zfdSUmGI8=WTkvXVr2Nuvn+ZaM6@}q?p^P4C5(N913WW0J(}-JTt`XtD9CqQVlNBbi z=_M#;25xTxznq;{Xf7UyeR|5DO!5T7GOJ~(O^Tc4Z4`9Wwm1CFo2qYcgb{n4l=%KQ z{ZfKk_W&)Skf-a|r}jwh)>Kzso__~u2&9KBD?{E7K0g0O!lej3QJ~j+NkG-`Pby{* zyyR^zdYVi}rCcm^oPCCcwf~dA!(8kw|z+B?=>0WLR0I!WyJhbi>prlwa zyP>;Tj;D77@D6$fj+ERC7lRH~e}*S9GSGhpmI`75CRrptGyoCDVi3B65E-z#8& zg?eTh7zwMWZ8x|Ipyo7ugnxe!g%epRzEPk5x-E=&D8HqX2mYLAP4WOyrK(kz~ zo4{<7`N9O4`j2Ps`t4hP;lIdH|QI2_oh2Qll=^CS-+^Pz7yTK$78gN_1nl_%MekdT6W3>sZn zAFq2Qf8o#pKgP#uO-yC?qeUx{0hTBX1A?A-(ZXOmtGT+3NkY~!`vdy zjl^&iFrfS8JNVaoxZ*;D;7jTze?s(mEZ!e0+g4}7$fKa65X@d&3ZZO?fZi)Pq*Om4 zGWiwn+5`u=*IPKQxP2N}V!nQDF~t4QnhI2{LmAzv*t^nO#=gzFnAX0)hilZlfXm)B1S(2AK2E zx=Rc9OTE(=uXmq~tRp^O?Ahj!PRrK5+}0!|Ss4W*?Mb%OA5Q<9Atb-209)nyBzBVe zvpIb5_nQk7b%n8$vqE;`h0bykwb=LlltUGps0tiHauGM(TG*S^?mYDUYjr|E%qp1P zR&EJ(06td8?9k{p=p?E~c{ZI3Ot*ycr zXF*+o6NN&+;jxZqDOLZx_kN|x?9plPgBsg;(`e2Tx>6Vu`E(6Mlp!>JR3YdQ6~5?m z)wI-ox-keKkpCIEq>zC1uTv~jJ=L$iU)Po*{m}PrCV=woKn_L?Ze#!OJqkf6Y(MxrW2@$w}0l83Iu11$^~{iG{~TvisY2 z(2hn(Udi26HtwABw%lh^qrb=s8j&IB(ZY(sJ6LGa_XTN7dK()4pd(|j{9{`-F%}%? zSq9(R_L&Zr(I{HIGown(i*wjEF1~(rp;|APFf)^aL6?Gng_e?VsKyw^8qHmO4r%1C zwA}I!YD`TcAdOsNPZzch5BH9{IUSYphDuJ#@epIP(aR;B^IkT+IJfrfpV-bYAE@@#G(rVb$fPf)*!^yDqc!|HT!o&=mc@{_R8I z{*kep%FaNvG2g)*#;v5D>Pp#GPg{& z#%_PSmksTtH~$h(MkG9uhkO?>iG3>!5e!@Y&I6 z@uw0OVOTq>%KTWk?CWxY6$fyenpDUJmxhs!^a__TAy)*m=jfj5xrN6=H0DD_q|u5O z21G_$+FZnn2bvOT+_(BI>d6T=DlF`+Sj(kP?2)YB#rhQ8-u zanpX;KKl{aF`_<$zEFqsDWIHo;2{S+{?oR1`iFm{kTVt7LH;ukfP@O9IXN}8a!|LV zx776C{ECMMxeHi<*BwQkUJtSV^A_9bnokD+pNa!KJ!bz7p2GUo2zCcReezed)_>>y zKPj4Sy7ys2{V@!4>T>`2)dvdKz?869>Ic6Q|GU91GZ8(T7c61%0du(d?-hU6!NENM1K9+p$K_CQK2AV3?Dqs!doGGWldar&l7QAP(=;-+OZ83 z2%m2s7(qBP(!eJ9{MS^3fQ==O9#sJB-dHRI;lbbEb>#m0p<Uo&SV>h1KPbAb^}Nm@{Vm{G6CE0Ov~}AwlSV);>`{+xnB1_-|S)L@>`f zvg7i7hz+=N^#9(ui>qtV;?RFr@OgF}*8g3_h^b=zwO+|b-bJ6Yq31dL@Bb=vV0|S& zXP&ncy!UGpdCq{>W%$x`$=~En?EIhE=7PXzthe-zTq%Q~@P8Nm1*%~2vKMbT{KxZr zK>Zc@)eG4Fcc{1c=>S;Kf&~PM8dg^v42FmVTaJ-1H@Urkq;9pevk|Zoy=_-L&UDP+#mS-!JoL@}ai~K*4I=8Ay+qEF|)q9VxMi)Q~X{%e3}QECS0-BtKKc!324**hac;$OtKP=kA_`uRXK$Kn+ycJ}uA z*dA<2(rRkhzlw{8)EQnNo~0u55QMXg4?P@_lanWLaTwKWwqU}32|fU)K`e-9AqgZP ziUK)E>H(TIgCD=T zYB9w>a+58OqMq8zK*`{(1E)yY7m({s{WDmSX9Afv!ogUjiLv^8fz?0IaAcc+waP_WNBU1tI z1-{yuP8G`(6bx*>M<$h5ELbyp?Kb)he&1fxn0e7*U?sb&T^FL>55*Toay2UY3R?cH z`cPP2K5|QyS${sRa+1&Oi>`0#>-yhdR}mDbQxS~~yf`A7Jct0uMf|LsM@kE>FzDZ= z04mb=Al7D0Nk!Q(Os@KW?yin{ zf69bT-Z&tMQd9ZgI|>}Zft}!an6;w=wE@&ck2UP6M3qyi&H=M+8u+mQ=v{Veg?`*b z>U60g^p!ox&I}c~zty7T31KE%6T(yJWZZHg z`9b+*!LfNUE%^IXmh)0k*g~?rl|6+<9YgRaq1zB-4#5R=?RpIm6R(yG1nlY^s*yKN zZ1bK%41Qb3-xffohMw22K4?ke9blsVw*=O1_RZ`y8d|>s3!${Td);kMM`5ZLdx;rRO9!FJD5gyHZSf68G+;$8fU zaKksJogoaDod~L;d&vAXFyNSDuq;vu9JhbjqW~LJln={Ysic%;5M&H!8P_s2oU1F% z%v9#HU_6VHezk8P)E!MChPjQS~QnYJPhwYk`)X0b!yRqo$^Hg^(ZB9$Uh!o_g*PIUGY|l zIL1ATI3AT_ypDV)${@1QYn$Ste2>adk{pazzJE5u@Iaa17qbYqnizYjLdy}m-UqSY z;tw-S5fhv^_~yOaeT0DCtODCQ7r0$ZDce!-J$YAFrz9p4O>d00OLFmPsnyrF%db$z z{-UJL^SO%9_k4ainRgy38YcSp1ntL6gN#k@=MqOvMnRNIO(;=MVQE3ZooW>9U#7l{ zX`377#gNEPa-%>gZV`55jk9uZC3=z+-EzIIQd;k+Lul+&)0YfuYaS0Ytm~dSXF3(s z*TF5|Jm~sp4%)jsUzWr{M+)yb4i^x={$Z>&Reg_J{(B|p$ZaJ?zwui%M|)Zq9d?xWY2Sfis3Mva;^RG8go4kF=^ zUi_rPdNJyvNYGQ!dkja~7jG^w)n>hUz_>u^6TEtl!N|pwFN8e!TDQKsKrrI()nMHMSgQ71Q3; z;-uKvgPqNj-rV(Y4<_4R=e=|Y%PL1--BJs0-k~g#_FYw=Z)l?MHw^FJ9QyHW3*{>D zt(~&@u8(5`2@93Ek5>-~ACDkT&5kzs$!I4xml1CF!y6ss z`bp+H8y{x;igBMfkw4hDE64xfE5}yrAZ&G!0+_+SD9SlpmQ`%MDowAk;nfCN#e#Od zB;3d5gAguN<5|d&`|RYy{=_Ei9O*9Tx=xQdv#ADg#KEUrXrE-x2IG`wxd zKvQZWEUZUDr&7Qm$N|&Ym2>YsCw~IE7xv|F%(_YXo-j&3o})iN>d?W2621Gq{Qu(o zk!YaqM{iLK_fmOd2`+l%e!`OPp0_U&8;onuYMTi!ASLqn)Jmz*JoZ)eNBWG53hQO5 zeYX`1C<8fZx@W(P)Q6sgs@&sc{!=~}L*2Xxncm4aaH~aqsxKk3f0MGB=E)_HG%zuV zv^1yG?50M_g_C!(x@k9GKl1hbi`;ft!OF|3;~M?BN-HPU%=8UeD~jWp)%x=>s%BHg z;fEx*a|~WKqUjS7jaQcNC_XWPxe)HwnZOOp!IMK_brO!jg46P%3|^9 z@XdG^lwg+m%0?3Ix-PtLJ&!x=$_^@?1g8E4n37xP?D@QHSdLGxtK3DFkYWwkkhT! zt(f6m9adY;DGX>f=f6yJpQ*g*v4|p(W{Q>k1#sX`PXK(vqCbJr4IlSk9Rcu-#z&47C}eH79q)I6bCK5x+wF7Y)(IOsxSb3@#4m#=cC_uz7KL7 z#5zl-X|KL~zhx6`JLnWDt>`)HqmL~VFiZFAW+hVP4}V#={!wV&>C8&~ege|)bNpngx6b!~49-Mx%(K9}v$e(Cv!f%|dfd%lFv0QfoC z8sMo3ym}^^e*)WF>`r}pY!IN@MA~>JF^W9hjQE!gm&y}}zN;5Fl_N0@o}Sjb_;a&* zvy3VkR#nB<{CKGK=qOJf8V)q_Z^14GS()T9jh69tBqkXZ(yYlnjs^*h< z@1qvBeXA^Ay#tq2a@@A%k%g!*%WmgxB7L*uX8KK3=pyj?p3o;#oJ<7&Sl%`Tl3oD0 zX1a(stQEux8tFSf7Uw;(uU|m{3g)Ebf_7bo_ARcuZ>xG_2u5nz1v8%|3dTfD?l!pn z)9U^#y3KuI6F2Gv((BvY)fLhEXF%5P2`EEre_*cT7y^tzqKQx6rX~A)*m*eeZ<}TO z1k`tTk+K>-gxgpN)A-#^WKq>z2uwV?i>=(Bjp$bsrq%%q)~6tmbDD7R=WwAZHX`)O zXIq9^drR_u1vhr!Wl!gA`0X$bfqLnHesZCyi&feN@frs?^~1ITuiyQ333pIDVlw$I zp2E)CB`1A_=?g|iMujAup!mpXGeXNlU*s~rrS_X8IF9cS;^!sTfZE#=Dj{qo`y_gK;E1imNifeX6S#yhmgZ^IKE!sy|jM*U4^ z-iCK2Y&(dabO~$-IasnvtVFL(!@Wm|RdSWIM~PII`)@&+wqYwZCd=Hz*k)z*#SSlM z!zF1Qi|QjOt_$&^OedX9D}?+OQen|AyU}*kf2xnyREtBb7yu90+!>nao3<>#y9nOU zSCf~=$Z!MxE-zjpT|oz3k?%~w7GifGqXt8)^G2%3ZWqLRN5gm;8-*PCEFCD> zOGT*2k{eEr(?_!7xv_3(*dqXO*V*$C5r8-Tpc?~HT!NoszdFPW>yb&LYIJtul|(E^v<~2_K4B{Smcg8 zDAW3TJQkvET1MS+x$=HcTSAf$82>eEWlq6+-jr3b`Tzktp-YsiP)G1jLUfyKXgqK>PFCb!n?{h^!S_^62~8eC@brL*ry+zBq!Uko_i&v}Tho^miKrxQ#kR{iuf6)do; zHea~tPUVsloS=xM8tt>_94_?`hV_l1UDkh+iKIu}U&yP>Q$mdnZ*boFwX`4YQt;Aa zdBfP_v$6PMlZRNV!;}lHo4V?Eu+oir>NJ%`;7X-_LQ4+#n>CMtcLchv*uE6BvdhHg z@^cmRM=mo((z3cq33ILeQl?;L_)BIsn6%7yl#P>B^COPVUu>t#K&xm@-wwBAD~O*d zsynrE?OS|})ktQp*9-{PyXUX73Q`J0&ra-*uME4V?uB~t0RU)H7WmNGVXLRpRtKv7 zrtPf7vZoXO=jo}_n;vWH%}53NQuW+~oywk0F?!mMiv++!p8I0|Ca+`IM|e?m(;DYY zP>?F7gok4D?xlvzP`fGzI21I`?YtW3KOkr6q%bBh zCWp*BfR4_0CZ>)ofk}4MEAlD5I_>V%qK0%=$@+7=^ns$#<8qvR1d53wh}w9F35I`T z6ct|#u^NyfYO^7CeZ{GqL@HvCc#>!Kn>FtY442?AUJY$x4m8X>>Q2X0K91bs>Ia+Y zSQ1{?F_&@Gtikt0Pl|AGz3kE2sUp^bxLuZU)p3(e^#CJ1*5)eCF!I{jxJO0w@fE@x zi}k0eQ#V=~Mh~xKJUm)HlP2t^qbWTf_?uuzv5#4nwu~irK^s9Yw;fyeU9*jkdKeCR z-?X#h>tHlj2=84q$j?~~5^%28kx<)9e{@mV$iVeuy@PN#IqnQq&t=utn2Ms2KA>@E zLCDINOk+ta(9p+81|0rmtG;^a$TcHMpZfR-B&mnYP<=;qRy>~BzM3y?pt;#TCuj0m z0v=sBe|wNJcVhg5#NL>+ox#RbN8Itga1Np|sTfpmh}P#uK5eQWyd$k#FFa$7NN!7p z{H%NMolj2swF4PGl%sM?l63W{$h0kzTC#!kYC)J<^7{BK;0lBQrE(k~4#$U@ViSI* zek>(S2JcdPQGZg7Nk_Eg;ghnEUQ0#}KKKKW9&^LqNp#KwXZ2$!wX`J$OY;=UrL;AY>p`$@MWu=$#&t zN&V8{ls%JHvy~oiKqRVeoz%*-3_WjD!(a`7LKi=g+)d(v$5KHd=-dX`lJ$45jzz+t zg?pE3rh;h{no z;xGN3DSd2ZxC?;L?406iF+6T3_R)PUUZt63SIxZR_dkzTZe-N{zmTl@FLnmKkKbvK z+0hazg%^t_$Qs1#Qg^;5%TS-+)>tMHUA<0J!ja4KVvLpWj*m-~B_!cIXwM-L;dT6g zhSLx`che(`Oh2N$>L}!!t5G5%KU%-_4yo*=5b4agcHF+r&B#f~*B2MppCEe|u=x{^*k z^s%A99;K&A4|D~X=Y{xj^E>#=L3e&NRFdx+iLx5La8cd&<&^BT2VklSV>orZASsmV zM0zo|fLjc6d2aLhx!NaJ2?T)8`a;J2B8af=djK>iESVU?1kR~n5iTa80zicC0UGjG zay9AGeB!rI{>bK}CJM{dGAw7|g7x&r9CZAfA(GcqDv{(Q@n@O!6iNghLtN$;5vGfS zJT@NAyu#7WWUCaUiUT#dt3wc((~ge@E!HQ!6^cUV;zq$`us7y=4tyuu4lJtt6AIg4 zE7(Y08eE2LzZ1SAnYj76e)lTgoX>Q7SZo}A?V~FA6P9q-N$_f-&uU+#nz3Yzp%dE( zslzzYyf}&us@{WfM;F`ybI3m_YZEm&;46i7|<=gR-Yiu-BhvT)j&J_LVLwEGwC%{?h=)cS6RW)U>16^E1e zRs>LSY$bkYHAKikFAs^wQgD#sX^W4$tJzyY{?fIrY^Xmuog3frGTH|oMJVs`xR*QC zPKvd@YilPGjg^_|&U2k`hf{K^&5|LYQxOFsmCw28&vr1&S2b^St|R80kN@LkmsbIc zUDCRys=wfp^s#CAcww98kaHpJpB~o}wMgcfy~hP9Ut5dMc-64O&*zad80;rG%L&f{ zu39Sd`3!{&N&U4;z9nEG@`xV<3-J8%g@v`#mGDy@?4UMx;HW1*a-@f7D=Si482Y1Q z^q>APrto>bv_^2;sEBPe+{`+_)#|m2^K|ehBacf&j!e;)`CLCd6=cx#VCslDqBtlR zNVkmVbM|cuAT<<}2Ql4pz!h0EcO-k#F5P04a0=r?V8-1OM|a_tyrom8TyT zoiNf+>XQAS0ugoJrLAKNM#+?DKpKv?btSk!zx*t*r^d;mT~|=XK)10rI+XjLAK>gy znBqZ!Qi~{T%{YWj6sd-XJqeW(s_bqZc12lq@nL*+B_dO%*{#~mCic^eK@Z}N&42M= zF^3?!aKc^hknD8%3>$|6MS9X$R%5l`K!^Gk2cndYoutNDCWfwUTd9tHH|1Il;3#Vl zw!*mPlWMvmhc7vt{6ZngrtOM3tPS?bOO66PAk7i z-W2u4{W_Uz^3r>9)AcTamx%ShXR}%`^7SV-9^ZAot-!_q#B(Aa1rl~9jd=zmkKz6g z@Ot2AT+Jmz?zR1QR;9_sA5Ze{Y`9DZvhqAmJ>@H*?rd|7Ppwz)-!IEaMX7}jBBit` zqGOXK;LSmGtW7No zP8-uGE6!=Xykb$5VqsxNpqGzIZ?N_yQ@6IU>9{>fsWlK~ptesn8mo4m;YpFydym_OC_qE94CDlNRR*F1^}KrY@~}cV?KR?Et#??&J8Sv|75aj!j>?^V2mMTOQ#bf zBdyUsRLs$h@$**37RQ-7coRx*{oa~%H(Trdi#BQ=qqBu}v%-#FO5pdydFsm#@4y82 zkBXOdz$cT^DWo}F9H{bUQ`4=de81zbd_dTQj%4=q3=5f9t6QkWA{hp<1`~k6e12E} zyH&cuc-=*^qFyN4_Sa{jav19i7>?hX7t7Zkp0?mtw}V;pGg?>@yZ*29OxNkR^#EbF zy6hpA{{fE{?hgiovukT>kH~4L{qw0dGJm$Xb+?-d(Ilv@oPLs&09Wf~JbP*`xB}3y zMlnwAm$BK|Uw*Nq{PL9kzw;@{=PW*LaRT|p1nLDl)4@(q%2c6q%&RKjlA@v=TWSr| z7k30A*bEkhad>)4wJrp0qM&Zu7C8exu7I=R=2XMYK-2vp+MXmV4CGqh`ScGO8U*%r z@)cE#!K(&>t*<)Kv*z+!2}N|<8zy?`X(lCah4KH(>czr>a6NUYVcg$b|P0@i-wR* ztSd1e+Z%SK0_B=TLB{N9pJ$54yo#T@JEEjP8}3qC24rq6gtN(Y-7IjT*m}mtDeHC;I(Y=m@*~SQ_+q^u4lt=?-o%qnb=!T@*SjeF zdkZAoSZzz?_qGmqEyd-nWI!`aCxF*hT69OW3JIi^-5oY*JoxQb_Kr?X<=6hs2LQe@ zgobC8;ft>UX@MWicwBh#bSh?NXD7#0JNS6EO*P9Q7la(xdR#$_WJMl?d(!IZ^bjT4 zzR;Shxv+Sn6}y-k1k~Bj2Z5J{{v}@Os;bHZfFD0$&?!%F_7;Um; zQre>oTbb|CUOu3^t_v@q>xrCaabvZh3Mr_>Y$ycL^MY}w9A<`|F%X6&(rrtZhrxPntZg!OaRgcNSl+$vEm{AZyPDwj9mY@G6JLf zd!K!l)p3g=PZ_%d#K$O4cqIboCaO~6>FJG-H3H+CW){eBy_G%GA95f0n|F6Vxk_mA z&;!I98ItF7zS(tv*9YRCS3z-vyk~ijI?F!(^z!Nq_bAA4Yv#QDJURTg$zk)S0w5UQ z5PrDl+2)G)ktgn9;~tm5MMU;bNpo1R#8_ScO+O2{PEd9+1t%rf`9S>EcOSfS`<5(e zi(Kdj$E{MOL?+6OUNT(vqUSn|JC&zI9Iz4nJIt=lbUCD%6D%sicRVI_$pPe@@ix)L zhE;UO1=2EpO|5L^CG^4;FjM>V(A{5FS#jdIKaa#dIPgE5D2!k&Uc{VW&JU2qWK4S+`U;;JZzo!3^UVwjOUgmzp7lGqVd>#ZP0o#~?fgYcb z)2?Q#lOD@|$xu9?Ll!iIet3`}%MQ{PLE$$ob7FvKjG{yXMSy~`;^H2ZhPB_@&t{Ra z*9RV2)%T>@(NAYe%AWVkMG`Bt9h1eN)kB1$Es_r|+UicXn{7+)eX?IYcM-_^vwgEN ztQ@4%5rB_(IsGZuh9wLI6T_+nx7H!?r?#LbllCD3o80vefeh8rGo4nn$* z7fvBJ=e?(c8JRB4&9Mw$9~W{AV(mV)KT$+$HI@u7!UAg*Vla%T6 z2>Hdst$SnM`hGCJ#t1tZ1R})JUQEz-yzEKXCf?ENC4|XTt|a|-3WAuu${-{oNtgGm z(iGXOScCjcs<#hhR*@cOQITcP$_O%O_JoF!x0x`}6K%s)g}T*%;2=jlAvulnNc_*LsA?{>&50Kc@ehXhoI&Yel&AoMc+u#*IExp9sF?j`krSD) z!J}8pr_5K&r&gC1{q&-|Tk#9^(tf54c3Fn-Xd3s&Kh8%dUCgm}yO&-@r+rjLfZ}DD z#|j?R7xluF^gZ|&m9++JRNgE1CWj6FI|fQ`hhb_Q^L$;WU>Ull+3}#dMjeL)+4XsU zKacY)7pSCe)jOw?Dhuv$Nw+gqcK^&xDTuMt(Y3?p`}CjVwM{8|+yY zUz>LbGfyn#L!nO4iRz#MOUdUT6+J9C<)hg#~dZDt=wydcgY7-G0 z>R3f{|kQY{3J(yKE|IGb}mdPdwS641d7^wY)jwa4`$pVIea z4B`=$Qb2o)zqId z_*wOl!z6ymK5PyN@O)>r;T(>!D8xteZL4pRquo(Iu?mkcj={d$JxStZ^|_n2G4R~e z74!0YA>u_B*5~>$L;zbk&w{O#GP1^9RqN!@`X{#k$vHyvv`T_(WidYf?T1aGCcYQ) z3+xKzx*pTNg*kS{bJe5k9eQ2vKhzkrl|;4CHNb67MvU`ag=FM(8JTyq{U>oZ z*Tz1S#N|u?#HwS(;?lxF6{Kq@;~VQmc*Nb3HH>8Gb5{!d!mOtR!^iwPszi~3HI2i7 zwk%<&cy^jezv@vDTiGs4Pb7${u{QzI+{iRKK?6c5|KO#(;k9{+Z`L`IIhNY`*2ktU zcrbb-OCNIF>UQ6dci{{$b*Jhv`SWukRD8tG6I)_;(7O|VZRuncyJ((tKR}E39G_x% z?zMZz%pxMG3u0SpebjqYEJMu;t#YzHCv+_m$Z$`n%;uI^tqz&I;t^t+sh!QE@c-yY zN`NYIyj3o^dZry?8YRp-XQh9TFk~lFQi^J7$O>Nswm*T{la#F#%LFx-S9Njy4Q!`$ z;HtN;R3h%Lp_skdd#~AB^IW~Y2!1L^TJxjb2lT&(}G(9$3f?gZGpVcIzS}yrV}HNFJFyq8I~|1oE$UeI!lhCi60PFv|s3 z1T)`F+UFj0vO14zBB{}@?7`C)@HB(7L!z1QHf^=KxA=5aT6t@SYp z=94C#Uvvv6#&2btBmCU-IpniuPNp3!g_A4$L;YM(ig*pRq`g{>Vw7va61%%(;|d;^ zpBHYKhZpsujecq;ku-6Fhn>Idw^rn{d?)K@MKK9U36UDbg+8jJc`%RIWz493P+W|# zPOq6c9YkP(x52Icqml}1c&+QQO!+Gd=sb%nb#u5-YxO6|R-F`GhTx_Azjbx^B;qxw zaET^d!IlbYeeo7@Nn0c~dUKy^Kv>l>1&`^Z&c+)_f*YgF*Zw6J;S-7E+fbqeYKo42 zwKcQI0s04S!(^vH{h!_K*?4AJD#H8qexG1I@yP4ML0FC^w*gDs(nP;|#9 z|K!PE?68C^IGpw_{K!N*`p8)CD=P!TUvB|L@W6o2wJ2{U@hl%huzWyUUl6w`4~ig3 z11~9h^LXa#_=&QDq%zUbX!k%R-8U&KKeoP^=jdRephMw1UC1yWZq1Im;v|Ky=%TYa{e#q&GSqfGfAwyM->!TA3rkSx&2Tx zF8+3UuN^dl;6~CD`AF%G{F;lA@|BT5De~c^`u(aIMNEl0GzZ_u$`(&xiqSw(MuNEG z2QBMal7-p(IY~FL?wi}?Xh@~WXA(Ef%|QB#lQJ5rFYe3JN(jjN(v#Kj5~VebOn#%# zr69?)!_LNoF9xKlxsz+JLuPoJcAHKB0~R~Oef*On=SIh5{tyzwRWwDaZ_1CK%lCQ) z4jCfYm5-yliBb;+R8=rVDF8IW@_3Ptg+Th0*VRI}RlE)4hJH0JY$@hv3~S0~vV+aicu73+?xYWNY?c>^cQ7nJuRdbWZ5aUoM%}Qriy* z6fqy4Z+d}ai#ulLpeykz1IM?9p~sOpd?3`aES=!tpy=yf_n;X<ASEkXf886_6sby#K6UUl&Z;JR-*C87&iek5!EO(`3&4$)Ejdfg? z*igP-Axlm(e)Z6OYPzMY7u0^plZLYAE?jT3Uw9m6WBWVeLm_$i1ah~_4R7ix@#oJzAszZd$+t$zdqUZ7LiWMU zb|l1V#qA-LR6hGj@;(ZO5935csZ1;Hyj^a-?lopIXiHO-m0rC5MVt>!96(InP_ghd zM6;z=_xU7+zL_Gl^36(7(d*{JKBSgqK&2(sc%9HGt|!le`=cVG=Z6{%m=b%ZjaCvl zwm2_?+{&O&df*2IcYDGodpUMDB?Eh)&k|~4-+t7^b6)Rr9jd|>FERre;k0jWvx_!U zPC~Un`gJ^D%kJP-_>$VY0blaSVdW^lwJdqd-1if}kBu{Mk%t*jx~(Q^$806lFDF!y zq&V$YfmYMT>IfV2jKxzT6gf+G*#2gUR6( z=pl|OsB$sZV?Ys8f+L(F&O`;b?F|w{Okgvmx~dyD77F%7nVXg3kL3z+_cb!6!Afo4 z#`cSg3P^aeJC5zw-RsldnM{ge5#%~Z=W|l8L6+t@lsYJKGH?yuiImz`nV``!;3J#n z{;*VM+dwmE@-pNT1n|BqiRsPk_8IE)d{dXVd+NWDc>DW86q~|p8jC^~fAKVuy0hXc zb^HwJ#_6kmQbQv#r3eSs3je4^1dx{KnZ=$Lk|h(B#g6V?w*HNuVyql85h`JYz*56B z)w3dS;4sHqvB8q-r5fQ@qFNm56mK>AT3@7`t<~1jX@St~rUBSkX+SRUe*W^|??Sb* zN0iaIzs5}yyF_}O)*8fuw#jyyUB7ubf@z{uACYJP?yqPaV+;wMS)}-|rDe>adWzB3 zMuW^VPoYn>jn|qV|gp1plfiyl+BEI}=`r2~JnE~a?+ za$mIQ;hAeT1I=ai zD9G598IFWur4uni4)o!_vUf5mALCQB(39)0OWg^Uh$VbjqkeI=Y(J+W2Y4x9cKd2T z*V15yC?FtO(g$;qaZWt&?P=SVW8Hv2EYWIFFzADsTfl!6`4kJT(xrfV%w6*Yrc;U@ z9LqDWgU)A@3Z*br<1!p}(;ZZPePT@sId+0!;)n1slPE)$r5%R6CpTFcfhk^%CDDJw zW0$)RO8BwfQqy9z`GYCCD_6GtF-#2v1^PkwV`oPw{UVMv5gq)2Ou71DObnI%uWu~A ziYu>vPBcD6=(eU!W*7ewZ|FaN)T??V%veo_*wf^TtN8N(j)gV9yA@%3XmHP2Ab<$~|>qpf|eXMC8qpXVE0EmXFRKON|cbRo)HLSM!IzTuhDcS7)^Nuw3!C06Zq0=A* zeI@zm*Ma*e`&k1M@A5-?r(EuZ2wYL!k9whRJx6Wim_wH(M5iVBvipe_XGz{MZuy8g zLr5~R{_^w|R2M8nOFxm@_8o+OV*f)5zYFRq<(FzBbS@gJ*wa=!!lQzA z=i|lKbv&MZb=*(sxL$WeCF-ihQvFR%byqY4;Lv&y+dBE<0>Gu>=vO&$C&`zp+<;Cn zyC;omOde==?tw~!rN_q#i}M1;Q>deQaRG`Z^ygdAVlxsn;0!Ndx_ zu}ANI(x7WD%k|s@Z@AOe87SEp3tYp@-4n1m6uw29@#_(yM_1lTrwRkg`{LTn(SBVt z95c@RB{S}^_tJTb=tB_F=NrvZy~a65sH1UU~A(C@WrKedtbA zBJ76+44E4j_T7;7cRRgt5+`7YQ|Eks?^%=}M$~E%EX1IE3z!?jJ_+_%!GJNbq{o456Woa-2G5z@MJsdmA|dLdXBQ^@P1bC zL%;+uPD>i;B|$R8krFg^u*?@o6s^TrlS=Znr$kc|BGV|Zz!R{bJKZjR%7)ryb&n)I^!9;wh0oLd!AYKs^S ze(vC^UHYSNkYw0cMqTzqm1trkJ@qmfqo;AC?~mcSl)BjJt0p7spX4i|xxz22?S>6& zfAnW)Iwx6?cmJk~jl?;(jdZpP%AFlRDc-fw<06to=)GvzN&mRe~yy^8rfX2hn1UFE?*Hugv$Oo>_7QaS;uIv|{@z*?!e@-s7b z%pz?wJ|`V|#eT!jC0N?99CsoMGOV@hXCvjC)ZFJ5Bbk|z3QLi$=3NRKS+(=rJo^$| zBCq(`_0-#Fvc}VdbD5_S+9uj~&n|J|N}ty2&!gZ@Tr;cO0{B=0do(e^?C=v9}3y%>rm55em1BLh> z9tp4q3G(M~>2eg;OC5hbTOqaB7b2qitJ{M`hobp2cifHKd!xjXP&nNo*!FM_GJO8w z#}-P1r)>&`2+u>C2zwRpIQ)de)+VyTTWx}rv zb$y%;gJGXO0P~(du5r|SfTMdwQwMz4=^s zab=vbVW@_^!-W4K543#$9m;2XJambXjBFQ3S42`I!L5fxiU$kJ_8Y5|4{xCRqO}sL zwdQKK!`pEqH|psMifOW`kKuo0J*kq>0 zd{n3To9x7o$Ufl^ae9ITQyWRV40+dLfo$!$(fAnq3bskq-EX7R?rwlmq(4XY6*xC* z2?g*10(YS4D_hCEkx;1zYTE>fB#)^^3ccNVVgWhxt zQebjz@oefb%fSlL0fa3LdpMdSh7XIpze`JQd8LE65F0qXJeRYTCa#IVMF(wlJ@H6G zZpG+?Quj;pmWfqBfy4Jc{ezJ=GQJ}HuBU}?_y1a?-M`yZ?Mg4lnz7&TWhv_`IO1+8 zy-Ux9s%Hg?X5lHJw`7LuYsiadZ8vpu$dBsz9FJ4KShTBZ&1VK`;^R^UieYquO5y2> z8P~MV!$QtAAF{*szbl%0AiRnP)Dc~ZM&PjX%IobWJFK2gaH+K7t5XJ)gXV6fXlbS_ z#XR_<<&E(tX&#H$P*sgDFa?+pJ)M-(&~pGi!Rga7bW7Uf6*h8^pH0pqBrV9dC6mgt z|A14^L|TYAxwr~V7eau4N_f^rg-H#3B@{_^tv`+i4*HT!@Hfg&`{7}{uRSiCf3jp& zM4CurPvW2UZ-nG|b_xa)+g6Um{GACN6ZnGn;wmBXe0|yb+gP`YcE1gQ! z^l~reuz&3Z0kf1HjVUGfVW-54ir(e~6kuZ#Xoh_Ft@v9D5gi(VS6h6DODQO#l3{Yo zP8T@H8yl`D0ZoOl$hNkF>r>}_n^HQqD*bATas0WJ^b~sjfss?yQc2&_@@D_2;T>DZ zj~hjogf#(>@x(xK_&5kqOWYl^H2l9i(D4ZYPx}CK?q73pJmv;(quT0EXHV=zp1%=a z2V0eAdCKu1z?~~FVo7_t9s;jZ+KlrU`T{r9|Fo&E-1*oEr>B09#Jtw(Vvi}vzff_P zKa6y!`iKiff#fV5Zsk|3D zi8`MfLnHx*0IA}iM%1<2kerZ79%`f1y80%b#B(Ydy_4jXjI0F&L!Z}H7LD4OnY59Z zC7z`sb$?o#fm%n98;nnV^J2V^u3EVG4S$9L^}zdWv=YTJ38*N^!jA}Zk|lqnS2$#^ zxKd{9It;V5lU6mpF556DX0Y6U#|UVLA*R30OCGFyLy0H01kmvu@hnIcDtFUZBcvsf zj4vwR8M$GSJh_wg;k$CL{2tI+O%T(-`yS+_{?rfT^RI6}N>MN8~tc$EoCc>434s7pZkm!p^mFQdSy8I#o9R{M5nYr@J$t8&r=2 zFJ5qv`f^=yJG|3M{HutugP=lGbW& zFEm@s)^X&PaDV({Gm1)+cq={1fGQ$QlMhuNyIGl1l5a^ph#|_V=#Wdzi=_|V*5+ua zp_%$$Mj z0}nDg7bD{)-l$w9a^(rD)9r4Tsf)MJh!rEPBayTL6cnt_orY}3xEIFKMV zV=cpQely95DC}IsPNtwS!H4rVp>GskiRes2+m1h*mwxT+|0}=8o85QfVJV%9xGu_j zb06ht5*!p|IZJJ2%3_1K+;?Zr;g6UtXfrNvwYs!oH#>|a^LU`sk7WKbY)tsQK!Hl8P5%K&Dz zU3t9EnQHvAaZmFGHxjkqwv-8)PsrP(0$RjesuYb2FZ|x0 zn+&v@F1Ady4sjk}i-8b^=%1olB+%-IgALb1DI1@$g}o&=XK0ahs48ryGjptcluA;H zX<$qpABEkk*i1u=kT;^LrkFIl>j)4gIZ~#T0`2^6#1cgjqQcUkJfWZ5yhJ14D|R#f zWf(lPgeOZ{kyD1KKMAM#;p?o-8f^frE$o}fOgAa*o@^3c`K7aCs+p-S2BL}DlK7O5 zsc=_kU9NCp>;@a5}NV$4FEi00RJ68I-3GhK_YmFtT zIxvyL(&wD8eo5{u^^{OlH~k|c6G!x~hk!@4AKy3Kx$l&qR-K6(dq?w5jm3@d5*5W` z=*(@&HQ8ldm4vrqrgUZ+eUNHnDFL4`JHdwz!0!a_ez&!;czV4}XR&K>qkTP|gb@!u zpPF_eghZum`cl&HW-0$94gTER$P*q>&U^m)`AF$j)ij<=uidbZ7=j&ZSKr4uY)grB z6__C$i13>kQjC?p*u;s>ta>J64k+g@kj)VIt2lK=qYYR# zE;boDUEoxobJ_|31^Kv2mHB%>gltu1dq?G}TTlCDjqd!7SfZg%4{X4nwTR>$vZb@i z{#X~`qH-PqUi+NBI%x7_(dAWAHvW*!FN_gdmFq@K?PHviN~nQ!^{?lxKE@r&Z_vl z_ik(sh63FOEI$}BuNvA0>vw+4)w_DEEEd@3xt{Cn3aeIs`}BPKA3y$$N@{2{D=lDHOy78B8 zJPyS!=}X!<{46(b5`9axq!*k3x~(at(_4S z4;p3G?w8EcQcr(#up*58A~Mfn|JOCl>J?{*=@su^qeFT7mu$Q=IQw$7uKZBO8Lej{ zXxzN3>#`wpp@H7gIJHo@?sw&^VvqL$hcYU_?UvDoVoqgO4mm^MUUR7nc-ymz_{mur*_0<2%6CWFKA zm!#n~RhgbWvGS0%d}2C_8`3?F7bKMfdhG)HJ!C(Wgc`PHhOSfP+nq&HbE6O$e_y%g zDO;(b9z~{IzY=5P=uaeyXB=+D4b^7NgX1YvPML{A#HY!~@REm=-Sd%|-g8YujNeE@ z=M7zEpE2{=rr}sEmohw)9~Gyu-jhx#VE@0+QuDokeybhMnV0i0iGR+{sgjU&Sv;H5 z6d?}yPXUByx@mQX^qoBabes2_dct9;&yPtOWH@0vfR$p!JpQ|gDBEO5U#!_)%vsG- z^ZnD?8*8m{hv$s5I#WN^upp^600y{^7t+nR95W^~=RX@Dgmpc;s~njeCvSeFOS^J) zHT{$K2R>U=WMnvV1rp|rE1OTVwnR(gYaov*o1>ExC8LVDm%^E5wE0q>(Nw{Az{g!J z4KCUjO43xh5)Y-9*AsP=rA18T(LX3%=|CVbJTw;qSMh-JsHiN0AbHSvMm*ehG?9^p z)m64u8~4l$y#lBfbBkdEXr$R343MfJ;<=75qNHaZS3UE#z^pJGV% zEthP%^|dg>yA%aqnO#{vn^fyYm4h)Y&^6q|^mW&#OPxdaWJ=gRxA;SDx-iBwO=Yqc1M3%?fA2Q@aHy!L z(?HHL*vMFTA_t2Q29qXZHup#;rA%gQLvX&a1&32}&m3KMO|Lrcn zp$4s+P7JZIuDe=xmG3yYbF~gngvHIFv(=DHC`p}_dEsNU%?c7py|MVN4Aco1fbnGF z{;raM*<>Z;<^VuD3i#IYW~Rx3QsguM0JLC1oria4bYgh%yuGABJAB67*+{{|9LY~K zl;>r?XL&pu@{ z*YNwkhI~0SXHD%QNNO(l;DRxAan3}4JVMLxhnU4Rf?0*%U@h8`8IB6IC00&O&h*So zFnB;HDIJ{+aMf*WWAlP+G)sne02#SO5Han`&BPROkggg02anB z*>VyHc&ot#hQVXALPQNfg;IM-e|7Gb5w<1?= zRdMm-N;y-k?%aD%FRXkuw1f%vCnno=JflZ6T_^$c?Axlx`AAlxPu?MvSXz*>v8igZ z^PDEq!$#Up#S`)|`@OhM6bKRH;^N8)BbX)yt~FE;9mg--3jx`kyOp49V%A&j70U97 z;-Ch1uaA2ELhU8M=zwN-7h%8K&`~t<1>cCF~HKnL%;wt3!YGegH#deYo%Tf1wcDoKWoBnx!De1qgF2pJf4?K zKtO+rPgzb_a9mCp4i4^4o9TM}5zmb&ks;~GQ)8>eRAicMKQTd*eqQMAB{> z!Xs8d?O88=)4J4jo`g3RYFr=%38}h&2XA_ylx`ciweXCNviuM1^adNS#bGiU8X8J? z;K6T?3dJ$=C)NSEJmW#%ly9oBf@s;_cL1)GsG6#3NNp`6y>7Fdp5D+V!&#UG1l+G4rlh1yYmgcE_B|AsJoo2RM zVSgubSoFF!^Wsyn1D6g32`NHZ7gf8~*W9ILaVrn7k@naupb;%mxd%QS3c{*adMX?W zn&;=^-Q69MDek3-eX(r<`%%fC4BXAzzpQQ1&zBAt@C^=w$Q{v}0mD^)9*zn_XuqVU zrXE3hG~^ZXU`m}Ola`cWLNh|ClUN)=P5&8v3TE#>jJeJA)|uqYJ`YYIy;t8DA?T}b z_*c4EYF(XAS%gkC!e_`G4Ii^UOmJjoc(VGVnP+&9vlC6N#FCSVNs)@Isi~sHfo+?NkO-1@fE=DbcX3A=|2U!eAr4Yshr3Rgy zcP!lOBnE>EAQfMMCI{TB;{gwRU?ctpm#XTuUsE`xL@MY3)$=AW=q7WMttX-YBr_s> z*~9b@NswOd?wl5rxfM(pHrCcNeWD1qdZ7Ax1cT5yD%?|gfm`SGh`FjHq^_dE(MOui zs=KnWM}`~q8&EBUz99^msY%q2r;@Qh+&5}zIBU#&?$>RJNvJbCR?V;yqC3Uxk>E70 zcmf`i3Fz9qym{GwY|QT_az|-;F&ogp?LWMCk1NoEGrS-Y(IDs zobbApq9`~W^=A}{plDR$Bw6jFvXa(MLiyr>>|Q20c^Ux~#)K9hL>=+%=4V#W<^IOjQk3rrm1;)ytcNs!IFc31bokOlcep?E2ptU-Zx@s z*+^?A_xQoP4EZ=t%w;q}iaeQ>kuZ)ubm7c0{eCs&qb>|_(fNj8)~@iT2Fm&;Etfor zW;ORG?88LFzS90*Y75LX>i5%CPT9TWtD*k>8BGew46`o2?-OILTg}jInfduer%7*= zOZfsmu9nfcqXJpq;Q@+>l%s7|d(X{{vnIq( zLJ?RVz7j6ZYe&IuXk&&229B!P%0JtV+SamR8vzsf^3>d!RP&RsVemaCoX2n z3%j~-RJ>+JjBfK2oSDUJI*ec{J>he2V9R$PmIGHZV~TMqLrPXP$b3&7H6jkMPi=kW z{aV6M@AGK?&h2rb=XbDUW}4NAPhl{&j9gJSldc6a5$}1dK84$Zx&l^JLYvl?E*d7d`K?V=bF!Tk`xvlJe_( zg-ia3H?b0_4QHUrb2`_jO4R8|(O?UY7tdkhuF{LumK=%@?FTY+Ayeh}ey1b}VZ&!n z{xZhxhO5iC+4FYtW)!>~Y5fle9hj1-7NRzv2|{!pw~lh)^SGFU4PHw(47bSfAmG=& z$#?z+oYcu)3Vn5 znRT`!;3lo6$>srywo}~OT(Q1O^Oq1wzxp<=5W^}8%ULLf+40c2JNwC#H`vN=HzT9x#Wc5PI;SpeOF8;82L0q~xZtMkJ2)q}+WU zZ6dw7G#RERi%!b4puA+=5vesyiO0A_@<%%?A&NU-F*qs2urmBlSot@LoWX3}`8}6H zK;>6ddBU6+&neSD{C;f294hVw!@UGAe2D;=n|I^MX)Az4R4~_9i-(*{L}oJZPcaly z7Lz!q6?+J}GArw&i!@_N@HjJ&#`$NejliC;9a0jVqjsxRWH2qi1hLtMmvDS$+O zRr6Q{G%^Db3UCWbj7>T1DV`G*(6uFieiY%-hfx^6#l%gr2Y-WNttg`^RD?>UB!lKu zkAckSD8-{NmTE<>_<`*hpr`Aok`x2exaYKyWm!T!VQ47hQYX zXU_OA9C{+U=`h9bVqn?d;n=p=AFOPq=~+Jpu~`;a>_{pyR5bs-&?cx@{xt`titpEC-*pTy$Oi&l$4tKd2ls(MSTN24BkjmYwDA8~240AOjsga{WMncplqCPN zCYAYrFn?quwQ*+Pe|Q>Ixt|$=^uIMlp~nBOU&tjIK%#6bv=)n~9<*de5?FA4W2CES z%i1PhU)l-XQ^DDs7fEdub~Q0V#6OG1QTE@l>M|5PG2xLY>~wm7+Per1Fr~H~hGg0& zy`nlD+8;+`YQ5L2-fT308emI?(Yk8-vN$5qFGwbUdj2 zsgCq&^LH4WPLt5%R{_^^4UEO5qQ)DZ_2Ku-{_l5<>xHEt5%IlkgUdDC7oF0yEc|%M zDt}jjX!e_ntxUX5P1%YLou1?94>8Wg=)kn^0B{x(%@Aso;G`6jM`zcPh{ol$T1jTG zFdb-Ve&Gm8^8W^}V!moOOo1B5Yp4pZ7f>#`oy^);j^lndyxht)o3I%tfr9U*0rVv$ z;K@oUO8Q>eWgJSU<9NE=IvH0UZAu8i9uaqpZVu`#-ARiH@r;W60Tu4|wB%Ed`sky- z1QJ}#EY{L{8qMaPsK=kR%WK_9v(CMBM#SpzM35i0y`HK4_Et$6d+u{me-&m;?==c} z+uE-Fx5G68`{=m;B#xui(*~bco$sTKP~Nk7JmJ-#uJ0@^&a-3n#~N45Zc#=RNUv@r z1fAzl$#5~6sr;VF4}{l3eCA_bUxCeTjaBX4jkg)CyP$|sU%GVNld)&?`N7+X z>hYyTo1wl6(xp?fMWf2Dx~~D-#wo$eqAUZsrRtnp)o!Y1KAc5ho{UGeAQ7AE^s*Aq z@Z~3_-LNegUXbIL&$mgI+^$ZHO(xC|EXT?IufBEF4ljT6nz&ULm+{uxnC#!mHWM5= zS3^&fFdjQ~i+7fk-;Nxs6)snFBiPi|T94v1?%YK}2cGVIMy_h3QFT3thkToS-^(l- zabnCPj}$q%wkn#t6=H4%My?(^I!KQHUACxg$G@Ot(+X%Yuo$&OC+T1y_c&q17*u*A`r1f?8G|02H0@6cJGYE!2wf+&Ti1)FeYB0DG{-$4}rxq$XB?mdP zw&d$YW5B}&k83RegU@%hZ&?NEsYb!fz~1`Wf=R4CM}8ls@aC&e)2z>e_=x41^Z_Jv zM=qwSYpz5c%ISRS1C%a#SwUDos%@XVgA7Ef7JniALu|cwgi@?!8e%#uJ$Wv`6c_d@ z^-7MlUqNP69J1!SC4=?Y&GohV zta7U%2lK-niFJP0wcF1tMa0bR>hB%3x$*G40-2u@Io4z-(^Bk2r~NT;NNY_LLz!$! zC_;YM%=X8BdfnA6J@m9APYCC=o(rAI*a-ToBz0JJ+jy(Bs!jfw<9eQ@>CpQ7zz+BO6r21ny?zjZls_BYR-CV^Si5i zXJ_Ih6JAKIrtGT9wqe5DF%+hTEHEL7QgCn)cQy@b1`~3#mi)|VnsfIDSE#D4bUsr+ z?|0=tK^Ec=AAr4)dtJT~Wbp#Ri>Vm408Lm$tHNlmQqKzl?Cz?a#cy4OG9~c0pB=E4 z^f}Q2wC?sRdT~CdqK^2uavr-Zd-i984A^o5wc}-E&gEED9!O`7;9?z(Yw;Cr@RtF6 zbUd5jk>(|PRmd8KUM{7BRbSY+jN|Vnm>zBn8^>{DS{nn>kIiG}tCp>seV(f(S2E7% z(Xa2UVwD{3O24bbN=w;^k@K*8;+~$>{Czl&^I%sIO~^`=`o*tI$t@v0Jmq1hgK%SxwhxeNXrl=jrwdL)iM#Nv4-nQ=~5g z=O3UzwDHNk&&Mrn<-F*9FVJv65z%b6QfCFEYOp%OF%axR3$fwMK)(&+!l>_}3mxt5nuFcAWV z8~?a3CvD%F#JqGAF(7yZ;Xi}KSFCVp>Q668LOoWB%b6T{Cw|c>S2wpjJ%3ZHl8`<9 z_xT;jjxa3}0`+g}o-{EnzI3=zN*z{Ky9B0g{D=yZ0ieL3uo#m{eWpQ&W$`%F<)}5U zh>1TD?xpnJ)u5G(F2;@-qetqCrZk0b!nbq0LZrEGv+DhCd7)+}BidL|qv-CK{3(kn zn&|hT$#>G@*39G(!!!w}Vi_zo3$tp258^(_{O>Z+>qqZDWUSH&3pJRm=f0iUN150w z<9Qc@X5+mo%kO05pX#ND77kk$e@;Z!0CIFtFW_93s8LZ7tutXGWf&U`M5)RQ>d@!m6gvg)Uh~HvsC%i#QU( zELLNfb*K{TMR5=+X_Ad`GHu1J61pQLFTJFXG~@!tMnUY!YUTtdQqVZ<2#K;2h%6VG zs2T8~yIK33+GoZcwOH<+e1tSd{m8t@Q@{v&TjBx&kfrhnQMM*l%Bu2Ew_*j+ZGPp9b&>9=w?OdSWG8T^qpsktw9e0Z0XNsB7qEcFu4%NG$JFLJ+^Gg%NsE@5l; zmeXe4QDzzs$_3-d4e%4X744;9TlFwJ%oq@kZrT2}v_KHRVTQq%UBgW-wD8IW)a}&t z+)DCPQlOW*H}m(E-8 zEbZ*H5-r8gr|kzf)w?>ihnCmaQ_C^Tl=GqE2u1GvT{cMEy~VsUt6x&J4kCZ%!ja2;yHptG@9-0}R~*v^hqX&UBQLyS zw+BLj;p6@5X${BNGk2uP791``|iEMGZlDvb81NVIIeCGb^ZPPSU~09v=@&iT7lMUp(LKg({`REW5~0=EVm8EtDg$$T7K%x zFH<@o+ds*|ce@*R3(8iSYADV7k#|P^jE1Jgc!(ZKD({X-t6#3Z3RnpQClyz6V+^)@ zI3xU;`aArLTe_ zAgAHJv=L{q|L#%TNmC+fX4_|exL;h} z2I*0G&R@3k_ce{^ZC!9H?S2`wf4J9gxETIg`F`d4a;=zyue@0-*aONFYG+68@Zj8{ zQWx}`d(=^$4<5Q~I$cBqR~fSo-6SFZ>laej3dnJJ8w+E1kvA!$kfG??ac4*Exd?~L z(_Xr!bSDvmQ5tH!;)P-0G91jIbs6QasNZC?O8iF3`iCLQNXKpa8ROYy3$(Xq}?G*n+JcUz5QHfFz$;Usmc+h_{UA(9^h%P!l5c*I~cEI-56^35RV5|2cl2B8@!&JI`#UO^z z&>#^~jceC(f8DcC+czdkS+0=sgnBlRNjA{PVGYbZSDj=O&Cs64Y ze$4Dukj{sr8&|#^Bxi13M8k#~THM|wPQ%TTyX+82G*^muJ&%uWtykKDBQ zbui}08K7-`J@dSYCZp%tT-YA$kxLkw9KnNpnp{C!OSBNtF~Cc2WvKtv=J-mFpRYFl zSz>a^_JPsqX7Ao}x&v>KGilSxx{ltf_9MDKux9-NWz(75xNKvFZ zYnO1=AA*m(b*h;k1Y@sfGLg4LZ|J8-^@RgjFC=+wX?=fKojSB907M{pFwtMew(C$P z0>&=Sp6zdcdiJ%s8}xA=Q0T$64?-FDE8cO}EHRjO82XQPz=P+cvp3+y++;W>eEpB0 zfCCd2MIfW59e5X)>$JDR+9?t;vh3XdXIw?*2GrnNMYSGR04U=~EhFFiMh&%cxSD-FaUJT5qMFq&r zG0Kg*)Qk%<$ydeq_`oHGE53XsM8Ae zV+!*&4i{bb+Jmd8yPW}R8_dp7KbKb*m$O$fL0BCl83p*k5O9LYnP2?7cfD^r2xi=b zpeWed<|deV27{&l`QCt1DT?SFTTR3Not|M|@N5tH{~6Kq|1tFx{Nb%7 diff --git a/doc/source/install/plugins/index.rst b/doc/source/install/plugins/index.rst deleted file mode 100644 index d09fa58b98..0000000000 --- a/doc/source/install/plugins/index.rst +++ /dev/null @@ -1,9 +0,0 @@ -Plugins -======== - -Contents: - -.. toctree:: - :maxdepth: 2 - - deploy-tap-as-a-service-neutron-plugin diff --git a/doc/source/install/prepare_kubernetes.rst b/doc/source/install/prepare_kubernetes.rst new file mode 100644 index 0000000000..2b5a920a7c --- /dev/null +++ b/doc/source/install/prepare_kubernetes.rst @@ -0,0 +1,28 @@ +Prepare Kubernetes +================== + +In this section we assume you have a working Kubernetes cluster and +Kubectl and Helm properly configured to interact with the cluster. + +Before deploying OpenStack components using OpenStack-Helm you have to set +labels on Kubernetes worker nodes which are used as node selectors. + +Also necessary namespaces must be created. + +You can use the `prepare-k8s.sh`_ script as an example of how to prepare +the Kubernetes cluster for OpenStack deployment. The script is assumed to be run +from the openstack-helm repository + +.. code-block:: bash + + cd ~/osh/openstack-helm + ./tools/deployment/common/prepare-k8s.sh + + +.. note:: + Pay attention that in the above script we set labels on all Kubernetes nodes including + Kubernetes control plane nodes which are usually not aimed to run workload pods + (OpenStack in our case). So you have to either untaint control plane nodes or modify the + `prepare-k8s.sh`_ script so it sets labels only on the worker nodes. + +.. _prepare-k8s.sh: https://opendev.org/openstack/openstack-helm/src/branch/master/tools/deployment/common/prepare-k8s.sh diff --git a/doc/source/install/setup_openstack_client.rst b/doc/source/install/setup_openstack_client.rst new file mode 100644 index 0000000000..d39d402220 --- /dev/null +++ b/doc/source/install/setup_openstack_client.rst @@ -0,0 +1,35 @@ +Setup OpenStack client +====================== + +The OpenStack client software is a crucial tool for interacting +with OpenStack services. In certain OpenStack-Helm deployment +scripts, the OpenStack client software is utilized to conduct +essential checks during deployment. Therefore, installing the +OpenStack client on the developer's machine is a vital step. + +The script `setup-client.sh`_ can be used to setup the OpenStack +client. + +.. code-block:: bash + + cd ~/osh/openstack-helm + ./tools/deployment/common/setup-client.sh + +At this point you have to keep in mind that the above script configures +OpenStack client so it uses internal Kubernetes FQDNs like +`keystone.openstack.svc.cluster.local`. In order to be able to resolve these +internal names you have to configure the Kubernetes authoritative DNS server +(CoreDNS) to work as a recursive resolver and then add its IP (`10.96.0.10` by default) +to `/etc/resolv.conf`. This is only going to work when you try to access +to OpenStack services from one of Kubernetes nodes because IPs from the +Kubernetes service network are routed only between Kubernetes nodes. + +If you wish to access OpenStack services from outside the Kubernetes cluster, +you need to expose the OpenStack Ingress controller using an IP address accessible +from outside the Kubernetes cluster, typically achieved through solutions like +`MetalLB`_ or similar tools. In this scenario, you should also ensure that you +have set up proper FQDN resolution to map to the external IP address and +create the necessary Ingress objects for the associated FQDN. + +.. _setup-client.sh: https://opendev.org/openstack/openstack-helm/src/branch/master/tools/deployment/common/setup-client.sh +.. _MetalLB: https://metallb.universe.tf