././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2182183 masakari_monitors-22.0.0/0000775000175000017500000000000015257164140014144 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4262824 masakari_monitors-22.0.0/.coveragerc0000664000175000017500000000015515257164053016271 0ustar00zuulzuul[run] branch = True source = masakarimonitors omit = masakarimonitors/tests/* [report] ignore_errors = True ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4272826 masakari_monitors-22.0.0/.mailmap0000664000175000017500000000013115257164053015563 0ustar00zuulzuul# Format is: # # ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4272826 masakari_monitors-22.0.0/.stestr.conf0000664000175000017500000000007615257164053016423 0ustar00zuulzuul[DEFAULT] test_path=./masakarimonitors/tests/unit top_dir=./ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4272826 masakari_monitors-22.0.0/.yamllint0000664000175000017500000000010715257164053015777 0ustar00zuulzuul--- extends: default ignore: | .tox/ rules: line-length: disable ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4272826 masakari_monitors-22.0.0/.zuul.yaml0000664000175000017500000000107215257164053016110 0ustar00zuulzuul--- - project: queue: masakari templates: - check-requirements - openstack-cover-jobs - openstack-python3-jobs - publish-openstack-docs-pti - release-notes-jobs-python3 - periodic-stable-jobs - periodic-jobs-with-oslo-master check: jobs: - openstack-tox-linters - masakari-functional-devstack-multinode - masakari-functional-devstack-ipv6 gate: jobs: - openstack-tox-linters - masakari-functional-devstack-multinode - masakari-functional-devstack-ipv6 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1202283 masakari_monitors-22.0.0/AUTHORS0000664000175000017500000000514515257164140015221 0ustar00zuulzuulAdam Harwell Andreas Jaeger Chuck Short Corey Bryant Dinesh Bhor Dmitriy Rabotyagov Dmitriy Rabotyagov Doug Hellmann Ghanshyam Mann Hamid Lotfi Hervé Beraud Ivan Anfimov Keiji Niwa Kengo Takahara Kengo Takahara Kevin Schwerdtfeger Liam Young Louie KWAN Maksim Malchuk Mark Goddard Maximilian Sesterhenn Nguyen Hai Nguyen Hung Phuong OpenStack Release Bot Pavlo Shchelokovskyy RadosÅ‚aw Piliszek Sampath Priyankara Sean McGinnis Shilpa Devharakar Stephen Finucane Takahiro Izumi Takashi Kajinami Takashi Kajinami Takashi NATSUME Takashi Natsume Thomas Bechtold Thomas Goirand Tushar Patil Vasyl Saienko XinxinShen YeHaiyang <6161910042@vip.jiangnan.edu.cn> Zain avnish dengzhaosen dineshbhor do-gyun ericxiett gugug huang.zhiping jacky06 jayashri bidwe liyingjun nitesh.vanarase openstack poojajadhav say3no shilpa.devharakar songwenping sue sunjia suzhengwei suzhengwei tpatil wangqiangbj wu.shiming yangkun.lc zhaoleilc <15247232416@163.com> zhaoyixin ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4282827 masakari_monitors-22.0.0/CONTRIBUTING.rst0000664000175000017500000000120315257164053016604 0ustar00zuulzuulThe source repository for this project can be found at: https://opendev.org/openstack/masakari-monitors 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 Launchpad: https://bugs.launchpad.net/masakari-monitors For more specific information about contributing to this repository, see the masakari-monitors contributor guide: https://docs.openstack.org/masakari-monitors/latest/contributor/contributing.html ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765152.083232 masakari_monitors-22.0.0/ChangeLog0000664000175000017500000002365515257164140015731 0ustar00zuulzuulCHANGES ======= 22.0.0 ------ * tox: Remove redundant injection of VIRTUAL\_ENV variable * tox: Drop redundant passenv * Delay string interpolation in logs * Fix debug log in reset handler * Bump hacking * Migrate from python-consul to py-consul * Remove unused config options for cache sub groups * Remove Python 3.10 support * Drop explicit dependency on python-subunit * Make masakari-monitor reproducible * Skip notification on unstable health with monitoring\_samples > 1 * Refactor get\_host\_health * Drop unused testrepository * Restrict [api] api\_interface * Fix missing options for oslo libraries * Drop oslo.middleware * Update master for stable/2026.1 * Add missing pycodestyle 21.0.0 ------ * Improve error logging in host and instance monitoring * Drop leftover of removed M304 check * Add note about requirement lower bounds * Migrate setup configuration to pyproject.toml * Drop use of git.openstack.org * Add operator guide documentation * Update parsing of cibadmin node status * Drop redundant own hacking checks * Replace old libvirt service name * Add note about process\_list.yaml format * Get rid of map in checkGuests * Avoid sudo access by unit tests * Fix a typo in doc * Fix incomplete mocking * docs: Update information about Consul Usage * Add project badge * Fix parse of [consul] agent\_\* options with IPv6 address * Remove unneeded execution bit from Python files * Use native oslo.config feature to parse list options * Bump hacking * add pyproject.toml to support pip 23.1 * reno: Update master for unmaintained/2024.1 * Host-Monitor kubernetes\_check host-status missing * Update master for stable/2025.2 20.0.0 ------ * Omit test code from coverage report * Drop unused doc file * Replace license classifier * fix for minor mistakes in latest release notes * Remove Python 3.8 and 3.9 support * tox: Remove basepython * Remove unused MANIFEST.in * Add a kubernetes driver for monitoring host * Update master for stable/2025.1 19.0.0 ------ * If any of the consul agents such as manage, tenant, or warehouse are not set * Fix docs build * reno: Update master for unmaintained/2023.1 * Update master for stable/2024.2 * reno: Update master for unmaintained/zed 18.0.0 ------ * Update master for stable/2024.1 * reno: Update master for unmaintained/xena * reno: Update master for unmaintained/wallaby * reno: Update master for unmaintained/victoria * reno: Update master for unmaintained/yoga 17.0.1 ------ * Honor libvirt.connection\_uri in introspectivemonitor * [introspectivemonitor] Fix syntax for python3 * Update python classifier in setup.cfg * Fix pep8 for new hacking * Honor test-requirements.txt for pep8 checks * Update master for stable/2023.2 16.0.0 ------ * Update master for stable/2023.1 15.0.0 ------ * not retry to send notification for specific http exception * Fix tox4 issues * [CI] Move queue setting to project level * Switch to 2023.1 Python3 unit tests and generic template name * Update master for stable/zed 14.0.0 ------ * Use daemon property instead of setDaemon method * Update python testing as per zed cycle testing runtime * Libvirt auth support * CI: Fix issue with absent stestr * Fix typo * Add Python3 zed unit tests * Update master for stable/yoga 13.0.0 ------ * Use LOG.warning instead of deprecated LOG.warn * connection too much when large scale failure * Updating python testing classifier as per Yoga testing runtime * host monitor by consul * Updating python testing classifier as per Yoga testing runtime * Fix a typo * Fix some typos * Fix home\_page in setup.cfg * Add note for package maintainers in requirements.txt * Add Python3 yoga unit tests * Update master for stable/xena 12.0.0 ------ * Fix hostmonitor to respect quorum * [CI] Gate on the same jobs as Masakari * [CI] Run cover jobs * Deprecate masakari-processmonitor * Use pre-provided libvirt-python * Use some better linting * Generate PDF documentation * [doc] Add config reference guide * move DriverBase to the base dir * [doc] Add masakari monitors usage * Remove conditionals for an ancient openstacksdk * Fix hostmonitor hanging forever after certain exceptions * Fix typos * Fix several code comment errors in process monitor * Replace "split(' ')" with "split()" in masakari-monitors * [community goal] Update contributor documentation * setup.cfg: Replace dashes with underscores * Fix one reno * Add Python3 xena unit tests * Update master for stable/wallaby 11.0.0 ------ * Repeated check to determine host status * [hostmonitor] Add pacemaker\_node\_type option * Drop CAP\_NET\_ADMIN * Drop unused samples * Revert "remove py37" * remove unused configration * remove unicode from code * remove unused script * remove py37 * Drop lower-constraints * Update TOX\_CONSTRAINTS\_FILE * [CI] Add periodic jobs * Add py38 package metadata * Add Python3 wallaby unit tests * Update master for stable/victoria 10.0.0 ------ * Fix tox for py38 and lower-contraints * [goal] Migrate testing to ubuntu focal * Add .stestr/ to .gitignore * Add missing keystoneauth1 to requirements.txt * Fix constraints URL enforcement for lower-constraints * Drop Babel from reqs * [goal] Migrate testing to ubuntu focal * Use keystoneauth1 config option loading for masakari client * Replace assertRaisesRegexp with assertRaisesRegex * Remove elementtree deprecated methods * Remove unnecessary continue statement * drop mock from lower-constraints * repeated parsing * Switch to newer openstackdocstheme and reno versions * Remove translation sections from setup.cfg * Remove six * Fix hacking min version to 3.0.1 * Add Python3 victoria unit tests * Update master for stable/ussuri * reset nova-compute process name 9.0.0 ----- * Use unittest.mock instead of third party mock * Cleanup py27 support * Update and replace http with https for doc links * Check config file for hostname * Update hacking for Python3 * Use hostname to avoid clash with section * [ussuri][goal] Drop python 2.7 support and testing * Update constraints path to preferred static location * Update master for stable/train * Use crm\_mon for pacemaker-remote deployments 8.0.0 ----- * Add Python 3 Train unit tests * add libvirt-python for libvirt package * Remove deprecated shell scripts * Switch to using stestr * Use template for lower-constraints * OpenDev Migration Patch * Dropping the py35 testing * Switch from oslosphinx to openstackdocstheme * Update master for stable/stein 7.0.0 ----- * Run all jobs by default using python3 * Add line for PyCharm IDE in gitignore file * Removed unnecessary parantheses in yield statements 7.0.0.0b1 --------- * Increment versioning with pbr instruction * import zuul job settings from project-config * fix tox python3 overrides * Pass region\_name and interface parameters during connection initialization * Update reno for stable/rocky 6.0.0 ----- * Remove python-openstackclient from global-requirements 6.0.0.0b3 --------- * add lower-constraints job * Fix stable branch releasenotes * Introspective Instance Monitoring through QEMU Guest Agent 6.0.0.0b2 --------- * Remove dependency on python-masakariclient * Update for new openstacksdk changes to masakari-monitors 6.0.0.0b1 --------- * Updated from global requirements * Updated from global requirements * Update for upcoming openstacksdk changes to masakari-monitors * Replaces yaml.load() with yaml.safe\_load() 5.0.0 ----- * Use status\_code instead of http\_status * fix misspelling of 'configuration' * Support specify connection uri for libvirt 4.0.0 ----- * Updated from global requirements * Remove an extra word in process\_list.yaml.sample * Use os-testr and add PY35 support * Add testcases of hostmonitor * Remove log translations * Pass correct parameters to '\_get\_connection' method * Fix typo of hostmonitor * Change the required olso.privsep version * Fix typo of processmonitor * Add testcases of ha and processmonitor * Add testcases of instancemonitor * Change the condition of success or failure of notifying * Fix syntax errors of README.rst * Remove data\_files definition from setup.cfg 3.0.1 ----- * Add README.rst * Add warning messages about deprecation to process/host monitor * Skip notification retry processing when HTTP status is 409 * Fix global name '\_' is not defined * Return user-friendly error message when monkey\_patch enabled * Add hacking check to ensure \_ is imported * Add implement of calling pre and post script * Add implement of sending notification * Add implement of preventing split-brain * Add implement of sending a notification * Add implement of comparing host status * Add implement of monitoring host * Add implement of restarting processes * Add implement of monitoring processes * Add unit test codes of instancemonitor * Add implement of loading hostmonitor driver * Add python hostmonitor only main process * Add common notification sending functions * Add initial start of processes * Loading the process list written in YAML * Add python processmonitor only main process * Add missing packages in requirements.txt * Using ServiceLauncher instead of ProcessLauncher * Change the section name of settings that related keystone * Update to match latest global-requirements * Add stop method to terminate child process * Fix missing translations for log messages * Remove conversion of 'retry\_interval' parameter * Remove unnecessary return and pass statements * Fix unexpected bash error raised by hostmonitor * Add the start scripts of processmonitor and hostmonitor * Refactor: Move domainEventRegisterAny method in a loop * Fix the regular expression for the check state of RA * Change service type and service name * Allow masakari-instancemonitor command anywhere * To allow processmonitor recognize proc.list which exists anywhere * Fix processmonitor's invalid messages * masakari-instancemonitor: fix incorrect module specification 2.0.0 ----- * Remove unnecessary methods * Add hostmonitor * Add processmonitor * Add implementation of notifying * Add implementation of handling events * Add instancemonitor only main process * Add a mechanism to use the oslo libraries * Initial Cookiecutter Commit * Added .gitreview ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4282827 masakari_monitors-22.0.0/HACKING.rst0000664000175000017500000000071415257164053015747 0ustar00zuulzuulmasakari-monitors Style Commandments =============================================== Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/ masakari-monitors Specific Commandments --------------------------------------- - [M301] Ensure that the _() function is explicitly imported to ensure proper translations. - [M302] Validate that log messages are not translated. - [M305] LOG.warn is deprecated. Enforce use of LOG.warning. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.429283 masakari_monitors-22.0.0/LICENSE0000664000175000017500000002363715257164053015167 0ustar00zuulzuul Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2182183 masakari_monitors-22.0.0/PKG-INFO0000644000175000017500000000504215257164140015240 0ustar00zuulzuulMetadata-Version: 2.4 Name: masakari-monitors Version: 22.0.0 Summary: Monitors for Masakari Author-email: OpenStack License: Apache-2.0 Project-URL: Homepage, https://docs.openstack.org/masakari-monitors/latest/ Project-URL: Repository, https://opendev.org/openstack/masakari-monitors Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Python: >=3.11 Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: automaton>=1.9.0 Requires-Dist: keystoneauth1>=3.4.0 Requires-Dist: openstacksdk>=0.13.0 Requires-Dist: oslo.concurrency>=3.26.0 Requires-Dist: oslo.config>=5.2.0 Requires-Dist: lxml>=4.5.0 Requires-Dist: oslo.cache>=1.26.0 Requires-Dist: oslo.i18n>=3.15.3 Requires-Dist: oslo.log>=3.36.0 Requires-Dist: oslo.privsep>=1.23.0 Requires-Dist: oslo.service!=1.28.1,>=1.24.0 Requires-Dist: oslo.utils>=3.33.0 Requires-Dist: pbr!=2.1.0,>=2.0.0 Requires-Dist: py-consul>=1.4.0 Requires-Dist: kubernetes>=29.0.0 Dynamic: license-file Dynamic: requires-dist ===================== Monitors for Masakari ===================== .. image:: https://governance.openstack.org/tc/badges/masakari-monitors.svg .. Change things from this point on Monitors for Masakari provides Virtual Machine High Availability (VMHA) service for OpenStack clouds by automatically detecting the failure events such as VM process down, provisioning process down, and nova-compute host failure. If it detect the events, it sends notifications to the masakari-api. Original version of Masakari: https://github.com/ntt-sic/masakari Tokyo Summit Session: https://www.youtube.com/watch?v=BmjNKceW_9A Monitors for Masakari is distributed under the terms of the Apache License, Version 2.0. The full terms and conditions of this license are detailed in the LICENSE file. * Free software: Apache license * Documentation: https://docs.openstack.org/masakari-monitors * Source: https://opendev.org/openstack/masakari-monitors * Bugs: https://bugs.launchpad.net/masakari-monitors ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.429283 masakari_monitors-22.0.0/README.rst0000664000175000017500000000173615257164053015645 0ustar00zuulzuul===================== Monitors for Masakari ===================== .. image:: https://governance.openstack.org/tc/badges/masakari-monitors.svg .. Change things from this point on Monitors for Masakari provides Virtual Machine High Availability (VMHA) service for OpenStack clouds by automatically detecting the failure events such as VM process down, provisioning process down, and nova-compute host failure. If it detect the events, it sends notifications to the masakari-api. Original version of Masakari: https://github.com/ntt-sic/masakari Tokyo Summit Session: https://www.youtube.com/watch?v=BmjNKceW_9A Monitors for Masakari is distributed under the terms of the Apache License, Version 2.0. The full terms and conditions of this license are detailed in the LICENSE file. * Free software: Apache license * Documentation: https://docs.openstack.org/masakari-monitors * Source: https://opendev.org/openstack/masakari-monitors * Bugs: https://bugs.launchpad.net/masakari-monitors ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.429283 masakari_monitors-22.0.0/bindep.txt0000664000175000017500000000171715257164053016157 0ustar00zuulzuul# This is a cross-platform list tracking distribution packages needed for install and tests; # see https://docs.openstack.org/infra/bindep/ for additional information. # Due to the nature of libvirt-python package, in DevStack we use the one # provided in the distro alongside libvirtd - to ensure the two are compatible, # and also to avoid the pip error when it tries to uninstall the distro version # (installed in such a way for Nova in DevStack). # Do note libvirt-python is used only for instance-oriented monitors, so, e.g., # it is not used by any host monitor. # TODO(yoctozepto): Refactor code to not require libvirt-python for unit tests, # basically following how it is handled in nova-compute and ceilometer-compute. # libvirt-dev and pkg-config are required to compile libvirt-python package. libvirt-dev [platform:dpkg test] pkg-config [platform:dpkg test] libxml2-dev [platform:dpkg test] libxslt-devel [platform:rpm test] libxslt1-dev [platform:dpkg test] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1932209 masakari_monitors-22.0.0/doc/0000775000175000017500000000000015257164140014711 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.430283 masakari_monitors-22.0.0/doc/requirements.txt0000664000175000017500000000013415257164053020176 0ustar00zuulzuulsphinx>=2.0.0,!=2.1.0 # BSD openstackdocstheme>=2.2.1 # Apache-2.0 reno>=3.1.0 # Apache-2.0 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1942208 masakari_monitors-22.0.0/doc/source/0000775000175000017500000000000015257164140016211 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1942208 masakari_monitors-22.0.0/doc/source/_static/0000775000175000017500000000000015257164140017637 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.430283 masakari_monitors-22.0.0/doc/source/_static/.gitkeep0000664000175000017500000000000015257164053021261 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1942208 masakari_monitors-22.0.0/doc/source/_static/images/0000775000175000017500000000000015257164140021104 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.435284 masakari_monitors-22.0.0/doc/source/_static/images/masakari-monitors.jpg0000664000175000017500000071326515257164053025267 0ustar00zuulzuulÿØÿàJFIF``ÿÛC      ÿÛC  ÿÀ8"ÿÄ ÿĵ}!1AQa"q2�‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2�B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šdó¥¬$Œ8Ô³3’M'%Í-ƒ}ú+Å?e_Û^Ïö¶†ßRѾüFÐü%¬Ù>¥áßë6vi¦xšÑd&€As,ð ’"^Ãm$ˆÙT;'µÕ4Öÿ×˧Ì:Ø(¢Š@QEW.ß|6ŸÓáéÔ⯓Eo-‡ÙåæÁg[s7™·ËÿZÊ»wnç;qÍu-R’Ùßðm?¹¦Ÿšh:¸õ_ªM~ ?F˜Q^Eûi~Úý†>¯‹üO§x�[†k£mo¦h±ÝêW[!–æwŽ'’0Ë ´ɆȎ 3aM_Ú#öѲøâ‡š6•àŸ|LÖ¾'½ØÐì|-&–�"[[ ™%yu ÛHU<£‘‰ =…+«9vvùÿOú³£Iéuuæ¯oÏOøt{=àŸ ?࢞ø¿ªü9Óôý3Åvz§ÄmGZÑRÊþÊ8gÐ5 !ßÙß4…’7�ã23.UÊçÞéíý}ß&¶wºí§Ín½WP¢Š(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW‚|tÿ‚¢~Ï¿³‡‹.|=âÿ‹žÒ¼EfJ\iÚ"âêÍÀÈIã„;BNF€Øøçþ ñÿ*ñÂ-[OøðÿS¼Ñ5}cL]_Å:ÍœÆ+›;)^H ³‚Eù£’c®î¤:Fˆývåü„—Á^Ñ/D¹Ò<;¯-÷ö=û@°Ùj?axóÉ9ù„Os ³cife Z9ýnKÂÏJ5ëÔTã'hõr~Z¯éaœý^n�(s5«ì�Ú?؇þDø5ñÓá·ƒ¿ál_Ø|"ñ߉n¦¶¹°¾’Oì«[ssÌ—Ò*EOÉûÆR%IW}~‚ø+Æú7ÄŸ iÚÿ‡u}3_е‹t»°Ô´Û¤º´¾…ÆRH¥Œ”taÈe$þQµ]*/i²|ö,¶¶çR?�öÖ·Ž/´5Ê ŒÆaX¥`ò+<äUx�jþ�à“_´ÿ‡¿jÿØ_ÁÚç‡ô-‡D‰¼=©èL Ž�yiˆäŠÔ��2ì–%É")£Ï9®ó)¡„Ÿ6§=±¾ñdž¿dë?Žðv»ðóÄGPð§Œô}CEÒŸÿªø{yûFÃc{ðb+¯k?õ¸n´¹æÓõ�Es¶+›íùK­a#¸¹+q>û±æ;É&¾Öø%ûü>ø㛟è–Þ)ÔüIqcý–º·Š<_¬x¦öÒÓx‘­íæÔî®Þ'uFt„¢ÈcŒ¸cmõŠºéTOWy&ŸÝYió«Íå(«YY,©JpQ‹Ú-?[{ÿðW/œ[½ÝïøõãO€¿ü}áß�Ú�Æ /ˆ<77ÀíLò|kðC_ø¦úGˆ‚±Õ<Í?Nº†ãM¿tklÝM3YHÙ_Üt�Ø Løñã­2Ûâ¾�ªüjÓ4o€ºf™g®x¯Â÷¶Qjº˜¸¾ýü–7eš-IbtlM›¨ Ï‚†GÝú)E¯ß9=¯)ËO懲ï^ÑÚÖÙ^ûŽƒö\¶ÖÊ ÿtßþMì×6÷mú—ß³ìõc ~ÒŸ³/ÄO‹ßµÝWÅZÇÀ½+N½ñ5ÏÃë­_SÒüUnöG}ôñ[I5�âC½VâàÇ·k¨pT­vÿðK†V?iCGð§ÃË}oÃ÷%Ü—¿õ¯ƒ¯Ãÿ‡û\o ®«}¨Åk×!ÚI. ޝh^XÉ™ý ¢¶ö¿¼s·Y»ŽS—à祭¢Ié{ãJ0ä_ÜÿÉ#«z¨í®­¾Öùö�øSñ_ö–ý»­“Âwð—…~øRH“Qñ�ƒ/5Ý/Ä:†²^;…¶H¯¬rö¶–¡A,Š¥"‘à ÿg‹þ Õ?f/‡š®«ñ/ÂÇÀÝÅž¾ñÖ…áÈ•'ÓáÓ¶i·‘6§m{mä\Û=¼[˜KûÅ’1!‘ §TVV‹‡Gù©s&Þú^I-¬ü�j{Í>©þ³_;)7½Ö–Z¿ìwìëûM~ËZW„4ïëz‡u/j^"ñ÷™}qq}§É$·Ú…ʨEšææG=76ÈÑT*²(¢Ÿ[‰+6ûëóëó{°¢Š(㟴ßí9}ð·^Ò|àÝ&ÓÄüGm-å­µÝÁ‚ÃG³�•þõÔuD�’wÊ®ÕIe‹Çn>|jñ†ãTý¥|s¤Ü?ÌÐx[¾³±Bz„Kû 邌 ™ˆËe›#ÃIÛÄ?¶‡í1©ÜŸ2ëJñV�á›v<”²ƒÃZN¡cý‘>«vØõ•�zõõð¸X:jRWlá­ZJVG•ÿ—ø±ÿGUñÇÿž ÿåð¥þ,ÑÕ|qÿÁ'‚¿ùA^©EtýV—ò™{i÷<¯þ¿Å�ú:¯Ž?ø$ðWÿ((ÿ…/ñcþŽ«ã�þ <ÿÊ õJ(ú­/åm>ç•ÿ—ø±ÿGUñÇÿž ÿåð¥þ,ÑÕ|qÿÁ'‚¿ùA^©EU¥ü¡í§Üò¿øRÿ?èê¾8ÿà“Á_ü £þ¿Å�ú:¯Ž?ø$ðWÿ(+Õ(£ê´¿”=´ûžWÿ _âÇýWÇüx+ÿ”—ø±ÿGUñÇÿž ÿåz¥}V—ò‡¶ŸsÊÿáKüXÿ£ªøãÿ‚Oò‚�øRÿ?èê¾8ÿà“Á_ü ¯T¢�ªÒþPöÓîy_ü)‹ôu_ðIà¯þPQÿ _âÇýWÇüx+ÿ”ê”QõZ_ÊÚ}Ï+ÿ…/ñcþŽ«ã�þ <ÿÊ ?áKüXÿ£ªøãÿ‚Oò‚½RŠ>«KùCÛO¹åð¥þ,ÑÕ|qÿÁ'‚¿ùAGü)‹ôu_ðIà¯þPWªQGÕi({i÷<¯þ¿Å�ú:¯Ž?ø$ðWÿ((ÿ…/ñcþŽ«ã�þ <ÿÊ õJ(ú­/åm>ç•ÿ—ø±ÿGUñÇÿž ÿåð¥þ,ÑÕ|qÿÁ'‚¿ùA^©EU¥ü¡í§Üò¿øRÿ?èê¾8ÿà“Á_ü £þ¿Å�ú:¯Ž?ø$ðWÿ(+Õ(£ê´¿”=´ûžWÿ _âÇýWÇüx+ÿ”sJ×þ;| “ûNÿÂëÒà»ÐüA¥éúN­qïi{eâ|såÍoåÈØe¸;‡¤QIá)5k VšêzÁï‹Z'Ço†:/‹¼9r×Z6»l.-ÚHÌrÆrUâ•ͱ¸dxÛ ŽŒ¤®–¾dÿ‚qÌÖ~.ý£´xÎÝ?Dø¨VÊÐ ¯ èŒøÿzêöæCï)¯¦ëœydãØôbî®QEHŠ( Š+âÄ¿|$ð¼ÚߊõýÃ:-±mCV¾ŠÊÖ,ôÝ$Œª:¦€6诟ÿáì_²Ïý·Àü8zGÿ$Qÿbý–èå¾áÃÒ?ù"€>€¢¾ÿ‡±~Ë?ôrß?ðáéü‘Gü=‹öYÿ£–øÿ‡HÿäŠúŠùÿþÅû,ÿÑË|ÿǤòEðö/ÙgþŽ[àþ=#ÿ’(è +çÿø{ì³ÿG-ðÿ‘ÿÉÃØ¿eŸú9o€øpô�þH  (¯Ÿÿáì_²Ïý·Àü8zGÿ$Qÿbý–èå¾áÃÒ?ù"€>€¢¾ÿ‡±~Ë?ôrß?ðáéü‘Gü=‹öYÿ£–øÿ‡HÿäŠúŠùÿþÅû,ÿÑË|ÿǤòEðö/ÙgþŽ[àþ=#ÿ’(è +áÇÅ/ ücð´Zç„|G¡x«E�ŠÅ¨h÷ñ_ZÈF2X™”‘‘Ð÷­ê(¢Š(¢Š+‡ý¡><é_³ŸÃY¼Cª[Þê2½Ä6n—b¯u›éÜ$�+2©wcÕ™Q;»*#0î+åÚúvÖ¿oÏ�4çu�—…|aâh£=öÞ]Â91ýá«x úJÿR‡<Ô{“9rų#Sµøññfs¨jß%ø]Ç͉à�M½:zu %ö©ksö‰d[xT‚ÀGœ=Vÿ…/ñcþŽ«ã�þ <ÿÊ õJ+ÛXJIZÇ�í§Üò¿øRÿ?èê¾8ÿà“Á_ü £þ¿Å�ú:¯Ž?ø$ðWÿ(+Õ(§õZ_ÊÚ}Ï+ÿ…/ñcþŽ«ã�þ <ÿÊ ?áKüXÿ£ªøãÿ‚Oò‚½RŠ>«KùCÛO¹åð¥þ,ÑÕ|qÿÁ'‚¿ùAGü)‹ôu_ðIà¯þPWªQGÕi({i÷<¯þ¿Å�ú:¯Ž?ø$ðWÿ((ÿ…/ñcþŽ«ã�þ <ÿÊ õJ(ú­/åm>ç•ÿ—ø±ÿGUñÇÿž ÿåð¥þ,ÑÕ|qÿÁ'‚¿ùA^©EU¥ü¡í§Üò¿øRÿ?èê¾8ÿà“Á_ü £þ¿Å�ú:¯Ž?ø$ðWÿ(+Õ(£ê´¿”=´ûžWÿ _âÇýWÇüx+ÿ”—ø±ÿGUñÇÿž ÿåz¥}V—ò‡¶ŸsÊÿáKüXÿ£ªøãÿ‚Oò‚�øRÿ?èê¾8ÿà“Á_ü ¯T¢�ªÒþPöÓîy_ü)‹ôu_ðIà¯þPQÿ _âÇýWÇüx+ÿ”ê”QõZ_ÊÚ}Ï+ÿ…/ñcþŽ«ã�þ <ÿÊ ?áKüXÿ£ªøãÿ‚Oò‚½RŠ>«KùCÛO¹åð¥þ,ÑÕ|qÿÁ'‚¿ùAGü)‹ôu_ðIà¯þPWªQGÕi({i÷<¯þ¿Å�ú:¯Ž?ø$ðWÿ((ÿ…/ñcþŽ«ã�þ <ÿÊ õJ(ú­/åm>ç—Að×ãO‡¤é¿´¿�5[…;–x[Ãw–lG@ée§ÙJPó¸,ÊÇ ¼ç׿f_ÚsRø“âmOÁ7ҬƢŠ+È;‚Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( À¿ø8oörÕ>ÁCo¼Yu«Ú^Y|XÓm¯4ÛE23éi§ÚÁgqæ¯Ê¤™M»Â¤É‰.üØÎÔâ­bÖ÷Æ:f�­öqq¡A,-v³Kq5ëÈ@2l�ýšØ$QZC ÛAA•º¤Ÿ)'ú/ÿ‚ŸÁ6tOø(¿Â+ ‡|oái$¹ðÞ¸ÐÒÔ˰Oo4`©x& `*ÑÄã;6·âƯø'—ÇßÙ÷Å÷.¿ðoâ&«$2G¨xOÃ÷¾%Ó¯T‰"–Ê)U‡ L‘8Îñ_¡pÍ\¢½Ã0kÚBöæv�¼µ·Ëç©òù¼1Ôê9aW»-쵿çó‰ñcÄ'Æ~±´™æm/NšÂÎmçfý¦5·Ha˜AÜI5ðGì9ÿCø¹ûZøŽÒódzðŸáÚJ�q¬ÀÖšî§rðÚY¶$�™FÓ5ÀO/x+¤2�Üèm|;û=ü!H,4ÇÒü+àÝ(Goa¤i³]5¥¥¼XX ¶�Y6¢€±ÆŒÇMrñ¦eƒÄT§KïʬÚÛÉ/MvÓSnÂW¥ Nºµö¾þ~ŸqÓQ_�ÿÁÛ:Åïø(�>|4øsâOøB¯¼@4ïkÚ¦�{}­\D»÷¥Ž‘d’\oÜ£ƒÉ‚s‘ú/ÿ,øuÿBçÇÿü1>7ÿåM|9ôGçÅ犾1�ø!¯bžøfßþÑz¿Ûmã»á­y?ð’ÊKÿfÿdwïÈÙöÜÎð~ZúÓã÷íÅñvÂ/Ú Åßáøqmà¿Ù¢c³£ø‡M½¹ÕÒÒÍ­¤’¿»¢w¿Dùc+É]i§t¯u}Õî½íZµ­©Òë¿·�¬¿j�/L×/t…¿ u™4oìÝoáö­­Áâ¸ïbF+ý½m{�¥\µÃ5¢[]ÄÒ4‹§œ.#Jµÿ¼Öþ-ø“Æß¤ñÿ�´èš?Ľ_H°´ƒB½µ¹±xÖÑ€Šiµ…K@ŽBÛ,@«o4†Ú9ïüGýž>!üT�ÅÚ÷†?kMföû}M4Ëß|TŸÃÉso°Á ÑÚÐéÀÆñ¤Š>Ï…‘€o«oá¿íðáůx×Þý¥t½kÇSý³[‰>|G}2òä¬H×#Om<ÙÅpË a¦Ž‘öüÌrsµ7>{[Ýš¶ûºm=w~ì“Ù+§¶YÔŒ¥Æú§ú^Êiíµù¢íÖÍ6÷›i:[2YÚ,l³²²çrFí1ªø+öÓý´üâ¿øg•ý¥4�ü}gEÑž- †˜×:ŒOí.©sÀpþY’ ó!bBúgáÇí‰ðWá.¥â›ÍÁ?¬n¼k¬É¯ës7ÁN÷÷¯ PXÉ¥±E(pб¨U¹oƒ?gÙ÷ÄV¯„~~ÐzUö—¥\èvnß>!Ü­µ�Åë_Ën‰.œêí Y@»\"mŒÂ’´ö\ÍúÊ�HË×YE);5+$ì�jɾg ß*ë´g /M#'ʴ朮ÚÔÃýŒ¾ø³àwíÉð³Â;×á%ñ�‡?gd°Öu?µKuö»˜õ‹q# ¥I@< p7 Å}Û_,7í�ðYþ3§Ä#à¯�ÿð—Ç¢·‡Vÿþ��8°iÖàÃåÿeùëU[vÝÜcv8®ŸþYðëþ…Ï�ÿøb|oÿÊšÛ�ÊœT·¼Ûÿ·ªN”—Îþ¦Jµ%%³å·ý»Né¦ñ{t·¡ôóÿü<³á×ý ŸÿðÄøßÿ•4ÃË>йñÿÿ O�ÿùSRh}E|ÿÿ,øuÿBçÇÿü1>7ÿåMðòχ_ô.|ÿÃãþTÐÐWÏÿðòχ_ô.|ÿÃãþT×ç�Çßø;Eý“à¤~/øgã/†þ&¿øYkýœt½Z= ÿBñ.œf²·’_´éš”pË"ùÒ9S¶åí*%IûàÇü�‡íUÿe/OÿÔ+Âõê•àÿ±ÇÅ]'ã�ÆÚ;ÅÚê©£ø‡â™{f5-2ãMºòÛÁ^Æû{„Ic>Ì£#dO¼WÐa…Ì­ñ°¢Š+ È(¢Š(¯”n�Û3âŸÀïÚŸáÃ…þð·‹u?ŠvË­]Kg„Ökléq$èNÛtI&yb’GÚŠ›IÍyo‚ÿm¿Ú×Xý¦<_û>Üøà­×ĽN´ñ2†çQ¶ð­¾“>ä -“3ÝËqæ¡�Q%U?;Aï1�e+Y=oo7Ý/D›ídõÑÛIC•]½4¿•ô_{iwÕtgèòŸì{ûjüMøßðŸâþ•âøfëãWÁ�rã÷šF�ªµ¶‘â Ä)=´ðË6÷¶Šd�q&ö]§<ü£Ìì?ࡾ~Óÿ |'ñf_ÙãÄZ?Ä¿§…ŸOð«xÚç…of�ä�®æB.!ù6³,qN@è¦ãQJPŒ~ß-¼ù­Ë÷Ýz_[(òÓ•I}žk®«—âû­óésïŠ+âÏ�_¶ÿÆ~Þš×ÀŸ„MðcÂÚ¯†´+=eî¾#Izóø˜Ýy„&›mk$LñÂ#"Yw6ãg¯Ký¤>#þÐ> ð'…cðžŸðWIÔŸM>-ñO‹5{¤ðþ�pŠ›á··M—+±�«¼‘Tù²N)*‰Óö‰hÝ—�›Mú&šÖߊ½û'ÏÈßKþ ¯½5·èÏ¡.î’ÆÖI¥;c… »c8dšå~üuð¯í/ð—FñÏ‚uOí¯ x†&›O½û4ÖßhEv��—2$‹ó#™GOJùëöý¸|OûVhß<%ã‹_?Œ>^ǧ^êþ ¾{¿ë�ÜÚ›ˆ'¶23H‡nUÑ™°GÞ9 |½ÿ¯ý¶|O¨~Åþ ü Òô|C‹O{ïꚪË.‡à -¯îû¿%Ѥ¼�r µYÏúÆ+ÉpŸ4ùV·PkÒjNïÉ$›í­Èå÷\º.kÿÛ­/Å».îÖ?U(¯šÿlo‹üâŇáïü(�øFÒÒ9nê0Ù]êÞ ¾{¿k±\[}¦Þ{}ìÒ'Ê º36ûÜ�3u¢”Ü´åM¿DÒ‹ùî´5tÚå×âi}é¿Óå³Ôúº¹ß|^ð§Â›�/ø—Aðìž%ÔcÒ4”Ô¯âµ:�ä™òí¡ÃÌ•°p‹–=…~~ü'ÿ‚„þØ_ÿaÛ?�>ø}ðBÇÚV“>©{¤ê—:‰ÔüV–®æéìDrìT¬r$Bw��“sV ^›ñ³öÖÑ>0ü)ý�~ i^ ðˆ4<Ñb·_éƒP¹ðѹµ¸‘¦´|¨†òŒ ˜ÍÇ5w÷Ô:óB-u÷åe÷ëè×¼‘HÊ]£7åîFïîÓÖúkÑEB (¢€ (¢€9oø'gü•OÚ“þÊ­§þ¡~¯§kòGãGüçáïüÄÿ´‹ø›Ã>2ñg‰¼Eñ:Þ]NÓ¬$‚Âᇃ<016£"yò§(žlÊ ±‹k+Yÿ‚QÿÁw-m?ÙPøïâƒ|{¥ëwšåõ´~ ø]â¯i–Ö±²ˆTÞYØÜC$¸Îü89?q8ZùÊÿÄ—«=Z?Eh¯Ÿÿá埿è\øÿÿ†'Æÿü©£þYðëþ…Ï�ÿøb|oÿʚȳè +çÿøygïú>?ÿá‰ñ¿ÿ*hÿ‡–|:ÿ¡sãÿþŸÿò¦€>€¯‹çѬ?h?Ûâþ©â«Mi~ë„<5m{Ï”EÓµk‹¨Q²©<Ϫ,m ÊZƹÀÁõøygïú>?ÿá‰ñ¿ÿ*kÅÿdo‰zwÆŽ¿´ßˆ´›oZiú�Ä»?*-oA¾ÐïÓgƒ¼3ó,ïa†æ/™ dk¹J²åYXõàRuUÌ1Ü=ÒŠ(¯tó‚Š+Êÿk/üSð��m?áSè~ Ôu»ËŸ*ëRñn«%–‘ Û„f73$JeŸ�F…[%ÔQ9r«ÿ_×ທsË–öþ¿¯>ǪQ_~ÁŸ··Ž~/~Ô^2ø;ñO… ×t ÛÅ:w‰~ßM6�}g,ínÐKÒI$W ÿžŒN~^7y¿„nÿÚÃöˆð×ÅíSᯂ~ ÙéŸ |U®èŸmñšƒ?‰E„Î#µµ·‚\Ç(„G¾âY4’X€†sÄBç{rÊWòŒ”e÷7o>—Òî4¥&â·æQùÉ9/½+þ}Oк+óvÿþ �ûHx“ö+³ý¨4/…ÿ 4Ÿ„:~Ÿ«¨øoUÕî®ÿoë^ox[Ä> piÚ…¼R,7’ÁtòH“£pÁö”€kŽøgûwþÒ_µd~.ø‘ð‹À ®þx[Z½ÒtÝ3Ä…å·‰€¢¾ÿ‡–|:ÿ¡sãÿþŸÿò¦�øygïú>?ÿá‰ñ¿ÿ*hè ùSö§ÿ”“üÿ²iã¯ý:x>¼þ åÿú‡öýž|7ão‡ÞñÆ·}/‹-tÝNÇÆŸ üQák+›¶ºyÛë!K�ñE´fC€çË` ^+ö=ÿ‚½ø þ íûd|*ñ/ƒt/ønÿÃ?|gk­iÚ½™òíæ—Rð›(‚ésÂþíó´‡Q´º&ôÎø_âÄηÀϺ(¢ŠúË (¯™ÿॶG�¿cÝá[øš_�5�øÞÛŸÙW“v¹óí®^1ÛÕao6(Á‘ÖET.v1ÅD¦£kõi|äÒ_‹*1nï²oä“oðGÓWÀßíÅûZ|$ýªtOƒ>%øyð_Äþ+ø›¢\j¾Õ4KP±Ò4#nà] HÜ–t…X4 HJ(Uó �Õ?cÚû⟊þ?üSø7ñ{AðGü,/‡ºu޽aᧇKñ …âÉåíŠåÞX$I#1±w –ÈZcZ2³]y­çË~d»µg¶é;^Ìn 6»Zþ\Ö³}¯uæ®®‘õ]ùßñçþ AûC~Èwo‹~%iÿ³”~›V²´Õ< ¢ø‚êiV·w n’¬îËoq$lಬ ¤‡ÀÝ^«ûrþÜ?~þÕÿ~ |7ÿ…aáïxãHºÖ—Ä?'¹M*o&há‘@ñ½Åã SÌ]©ƒ†Î)ª—ååWm¸¥Öés?/‡]ü·ÐnrWÑ+·äݽwÓo=�®è¯Ÿ¼ãÚ;³—‡…–‰ðsRø¥w<�뺜ú¥å�…48A‘–ïËpn¦DcÉ w?¼Ú»�žþÁŸ··Ž~/~Ô^2ø;ñO… ×t ÛÅ:w‰~ßM6�}g,ínÐKÒI$W ÿžŒN~^7]Ó«ì–úüì®íòMöÑ­Õ‰’å§í^ߕݕý^Ÿ‹ÓSéƒü%ûBiºíß„5oí{ k—~ÔŸì³[ýšþÕ‚O%E-µˆ—({1®Ê¿*¿cÿÛwUø7áß‹? >èš�>ê0Ù]êÞ ¾{¿k±\[}¦Þ{}ìÒ'Ê º36ûÜ�<ÿþ áûe~Òß·_à üI×<ðÃ? u; ¸µXî$¿moZ–(æCsgç†Sp«ŽwgtY* )šµÔbÜzEJý,Ójþ¶ÛÆÓ_WÝÇÎéÙÛú·à}ŸðÇ⧆~5ø"Ëľñ‹â¯ê;þË©é7±ÞZ\ìvGÙ,d«mue8< ò+~¾Zÿ‚fþÒþ×?à›? üqâ ?†¿ týzÚpšn‘Z…i/Ú®—o¾ÔÜ#g+¸’Kš?n_ø)—ðÀ¾‡áõ÷�¼WâïŠ:Ûè¹ÔµÄƒÃöofK›»Ë¨÷m†²'ÎÅ•G'#|DãJN=š_6ÒKæÚò×±£)«ù7òI·÷$}KE|Kû,ÁD<{?í¢|ø¯ª| ñ]ÏŽ4»ÍOÃ^ øeªO%ºÉf±´öwvÓÉ+Ç!GgI›X&6ç8æ¾~ÚÿµŸíUÅ~ø7à¦�aðËÆ:ׇQñ<š�ÿ„ˆÙÜ:Åmooo!hÜD#ó.$“i’L,8†N¼æéiKå(ËîoçÒú^ÔûÒùÉ9/Á|ºÛSïú+ã=Oþ »q«ÿÁ2< ñÇþ ŽüI¾°ðî�áëë󭾯uxl¶ÍpŸ"9VGÜ3ª�òÊó>ý¦ÿkŸ ÿÁD|ðoâ;þΧDñ.�qâyµiÇŸ5µ¬ÑEqm�vDs~õJ»£!³•s/mìzó8ù]G™«ùG_Ku"ÏÙûGÚöë¿.ßâÓÔûCá×Åï |^·Õ%ð§‰tÇ¡ê2é‹éwñ]‹ ȱæÛJcc²TÈÜ�†×E_ |ý®o|ûþÔŸ|=àŸ‡^Õþx»Æ+gg£èíec«M§�Éuz‰ in%*<Ù!|q¶¹_ÁF¿i¯„Ÿ³ï„ÿh|>øQeðoT}-õ_ÚÞÞËâË[é"†;á6~Ȥ´É'Ù¶¹U`¦bÙ+ª©ò[¬i¿/Þ/wïiúuêËœ\¼¥Q%×÷nÏ×uêöè�Ðú+äïÛÛþ ­| øÇàŸ„ßdøwkñƶ:Üš¿Žõ,té°2¡šo,¬“Ë$�²8‘”’ fP3U¿`ŸÛïÅ?h?|ø“7ÂÝSÆžÒ ñ-†»ðóS’ïDÖ´é§xOîåg’Þx�P:4�»ÌÈàrS«’å��¼ùoÍ÷YßÑödÎ<‘æ~_ù6ß}×Þº3ëª(¢µ$+Êþ3ÿÉØ~Ê¿öRõýB¼Q^©^û\üNÒ> üuý™ šV�ñ.óÏk6çQœoðw‰£]°[G$Ïó:çj “€ ø¯áHÖ�Æ�º(¯Ÿÿáç?¿çïÇÿømüIÿÈÃÎ~Ïß�ÿðÛø“ÿ�+çÏLúŠùÿþ‹ð>.'ñuýŒ£ïA{á½VÖxÿÞŽKeuõ ƒÐÐ૳d#7>ØÄ>ô׺ì°Çþü’2ªú|Äs@@Q^¡ÿÁNÿf¿Êé¦þпµˆnu¶ñÞ—)AêBÎq]׆i¿†Þ4Ûý�ñÀú¶ø„ëö-vÖ}ÑœaÆ×?/#žœ�Zî(¨í.⿵Žx$Žh&A$rFÁ–E# ‚8 Žõ%QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÍ|_øÇáo€?µ?øÓ^Ó|5á½?6ïP¾˜G@�z³³ªŠ 30U�+¥¯Èø9óâ&©7�> x-ndM­u�ÜÚƒû»›ÈšÎÞÞF±G=ÐõÜú îË0OІ;s;\æÅâ=…VjöF/í_ÿ |Iøåãûþκ.�àÛkv^Ò¼Oâ+oµjwÓÞ]Ek °Ù²˜­�É*‘ç‰�”üÑDÜ#ÿ‚žü ý§?cèÓøÃö¥ñÄ7ñ%¬·Â;‰ià9íÞyf+¦K¨ÛA$¬[„ð’wõQœnøâÊöóFÖô½SLÔ/4�[D¿·ÔôûëF 5�Ì,‘J„‚7+¨# àŒõ×-ñŽYõ„Ô5}FîÿVÖµH量Ôõ©//¯ßû6èošyY¤‘°Ë18¿BÌ8[ê7Äáyy! y’“”µèÕ—M�—Âç?YýÍkóJZr»Y~}ÏÒ…ðÿí�û~ÂV?´n“ûPAã-íztš‡„õ+ãã›'µº¾†Ò7]Ziåi@iѤ[fˆ*«™±“õOü·þ çá�ÚWÄz‚¾+éºoÃê2%®›} ÛI¡kÓ¶Š9$­gfÈXe,•T•Ý‚Wâņ§ªh^×´='\×4]ÅAW\Ó,/^d++q|©\HWÌ\|¬*®£§Ã«XMksÍop†9#aé ÖÐàoi «%ÌÝã(«/G­é÷™¾#å”$íÕ=~æJ?àœ¿þ1þÑ^ø¹¬ø E|¡¦ø–Í ¦ ]¨YÞ2¿hM¤€³ Ÿ—i潺¼þ oñGZøÑÿêø1â_ßOªkÚ—„ì~ß}pûæ¿™#´ò7y$)½�÷˜×½×åMYÙŸj�õ?6~Áf>!ø�ö/ø³âøÀúOÄßŸí¿ ÙÛÅuý™®ø~]Y´è®¼¶›Í2Ç47L©&Ä-ÂÊ«_^|jÿ‚†ü"ýžüwwáßøšöÊóJ[WÖ.­´ JÿLðÚÝ6ØS¿··’×NWûÁ¯%ˆlùÉ óWÈ¿ÿà‹^9øŸû|9ðnƒâ hü¨ê1K¨4³¶—«èÚ†¨÷Wz|²|ÜÓ)òŽÙíPp¤½z‡ÆŸØwâõö£ûCxCÁ³ü7ºø{ûKÏö�WYÖµ+ÛmgÁïs¥Ûé7 (­¤‡PgµŽXwÜÚm’FWÞªånE}ÒküR\¶×§4]ÜžœÑ’¶¨ “¨ÜÝ¢ìý¿6�ZkH¯³(ëtΦûþ maâ�?~hÚµ¤kÿ tAa¯k>×@»˜ØÜ\—ºš;!6èÐ�¬³1ºRÆ äb¸MköÝñÅÚãöyøiâÿxvÏÅ>ÿ„Ë^¿ðo€o¯tßÝ»iŸg�n¯tË„ƒLe¹¹2˺aÝ É4.À®ãö&ø…á‹¿¡ð÷ü!º��>-øÃñÞê:ÅÍ®¯¢ÞØé·vQfÙ-$Šâ<Ø™¤óâdùñàg®øû!ø—áwÄï‚úÕýî‡5¯ÃŸ„ÒøRKy¥i'¾i4¦B0 XÍó1Vù“ääíÖŠ_XjNðV×kÚ5út÷•=÷WæF3•Ob¬½æŸ�¯*6ׯºêk¦ÎÖj償ðP�Jý˜ü⿈¾ Ò5oxÖæþÎÊ×À×5c«Ékq:Kö=9mäÔ]"Ž!湇b6NB²Óü@ÿ‚‰|%øoá #\»×5ÝWOÖ´�íø¿á𦯯Ïk§óþ—u�¬ÒÙÕußp‘�ÑH¹Ý…🄿°ÅŸÙ—Aø-â/ »ñO�þXø›@Õt}_\¼Ó4�COÖ5~³A{•Ä‘\DööÀƒjUÕæ]Ë…-/í}û~п´ž·dóx‹Ã—º^¥àßì{½3Mø‡â_ØøkY‘¤ûF¡zb5˜JøÉoðº öÛÄ:¼w6v©4‰Ô¹V¼ó|‘c‡KydÎõTÿ‚7\xOö_ðŒš'Š|E{ñïÂZ��ŒÆ£©xûÄðŠßx‘nÅæ¡7öo�%¤0ÝI%âžå[–`»³^µû(ÿÁ:<+ðsãĉž'ðOÃ]Oâ'‰üu©x�Eñ<D7ΛasQ$l’š6L¥Šm�€Nâ*¨Ç’¬UG~H»ö“S¤î¿Ä�X«ü1³Õ¥x¬ïéí)iÝ.Z‹^ÖýÛmo+ô¹ÛþÈ_´&µñþïâºkÚe°ð/Ä-KÂvb�Óε¶ŽÝÑåÜí™I™²Wjð0£½M?þ Ëð^Óö¸×¾;\x EÕ>+x€Ûyšþ¤†òkoo¼_eY Kr#‰rñª»dåˆà[ý�¿g½kàßÅwÖ.tˑ㯈Z—‹,>Å#¿“ksº"K¹ƒ d.åäa�oc©‡ð©_~H_ü\‘æùó^þcמ§nyÛü<ï—ÿ%µ��þ ÉØ~Õ_öRôÿýB¼/^©^WðcþNÃöªÿ²—§ÿêázõJú/ð¢yµ¾6QEtQ@5üøâßÁJÿg¿ˆf“öŸøIñ5®·ö¨SìR^[@–ë峉{# ¢°\|Øâ�üñn�ÿbñ÷Ä»�'ËðNµðãIÐlµ/µB|ëØ/®¥–/(?š»RD;™�Ø�E})ED ¢àרækþÞSOÿKvù|ÜŸ4eö¹òW¿ô•øŸžþ%ýƒ>1ø£ÁŸ·>™¡‘àý_ã>·߃5Cª" øÊä v’&É";ÕNœ¯$ðìñ ^ø¥û:Ýøwö1øuðMø_ã=+Qñ³mâû]ÔmâŠE‘„Öà<ÖêpÎe•¦‘Ú2";Y‡ë�¥(J/àö?eð߯ªM&]IóÂpkâöŸùSâòôºv>ÿ‚˜|%ø‘ûBj¾#ð…çì¥à�¾»´ áÙé7þ•áEsqö�·2\o�=›�Ñ…R¡³Ÿ6øñÿýø¿£kÿ³®±âÏiÿµÖ‘ðãÀíáÝ߈­íbþÛ!IÖ6jDAwò‡2þóî¾28ý4¢’Ã¥³Ö÷¾�–Ö¶ÒzÚû;Ý&S¬Þ�ikuëÊûßx§½·V³høcþ cû#øÿà'�h­cÅ |ð‹Lø�w¥^xwþ½µŸM´Ž;ŽHWÉXÂÈŽÀHÞR+H]“rá�‘þÎ?ðM¯Œ_±ÇìóðGâ‡Ãï ¥§ÇiÍ xûÀãU³ŠÛÇzCßLâ¹ó ¸»�$Y!˜ÉÀ�Wê~É'yÿ·K{Þéµ+6’‹Ú\×ÿ·šƒI®Íœ¿µ¯ì‰ñWÿ‚�k¿5?Ù»@ý§ü#âO Øé¾Òkø±ðž&ÿ‚Gjß ,´Ÿ;Ç7? ?á‹MûT+ºÿû0Aäù¥Ä_ë>]ÅöwÝŽk¡ð/ì_ào‰³gÂÍâ×Ã?x¿Yðo†,4ï'ÄZ-–°tÉÖÖçHÚE‘F^0 C†Ø98îtW]Gí*Ô«%¬ÚoþÝ絿ð7ø|ùiÇÙÓ§J/H&—Ï–÷ÿÀâ~~~ß°Žºÿ´§Â_xSà7…¾7|ð/‡ï4Xþ.¥a¢éÚ%ô’o�QŽÒè-”«åî‹a\†àccþ yû(xïövñŸí¯x·á_ƒ~hÿntËýÃþ½µ¸Ól¡‹Ot’%*";!ò‘^Bì›”†?uÔwv©}k$2®èæB޹ÆA5Ç<7îêFYFK_ï>gë¯W{-‡G´Nq“_ [y+~]¬›×sòKþ ãã¿Ú›Pÿ‚JxOá÷€þxWÄúWŒ4Ý;ÃÞ6OC§[hÜÜ\Å#ê6spòÂåÜ]âE(0Œ?Gøëþ çâo†¿ÿcŸ‡þ¶oÚüñÎ�¨ë÷­s ]¥½µÊÜ]‘”°óeá3á†àšúÓàOÀ¿ þÍ tox'KþÅ𷇢htû/´Ísötgiy“;Èß3±ù˜õô®ººä£í]U¿4%w¿îåÍþmß«¾ûïG¢å”tþú´Ÿ¯nŠÚ+hQEQEQEyÇìsð·Ã_ußÚÏÃ>/ðþ‹âŸê¿mb¼Ó5k(ï-.“þ¿ pñH ·â+Úÿd�Øßáçì1ð²oü0пáð¤º�Æ­š·2ÜGm4ä4‚3#3,2$.p00™Á;?äª~ÔŸöUm?õ ðµ};_9_ø’õg«OàAEVE…Q@|oðcþNÃöªÿ²—§ÿêázû"¾?ø½£jß²‡í#ñÆøcÅþ%ð/Å;«-j{Ï èw:åÞ�ªÁao¦ÉÖV‰%ÓE-µ•›$‘Dê&åü†N¬ÔjÞF5ÓpÐôÊ+Ç?Ḽ+ÿBÇÿü1þ4ÿåUðÜ^ÿ¡?ãÿþÿòª½ŸoOù—Þp{9ö=޾6ÿ‚»~Êþ8ý£á&©áÏZ|\ð¿�|HúŸ‰¾ÝkqéPøª&‡d ^b-äû<Ÿ¼òæ!\;�}›þ‹Â¿ô'üÿÃãOþUQÿ Åá_úþ?ÿá�ñ§ÿ*«:“¥;^KFžëtÓ_Š. q¿»ºk¯TÓüϘà�ß²'�>ÿÁCü[ñ7Røà_€×¼‹a¢xjÿN›Ë»�P27ÚÖÍQ>Ðè7–�^0žZù¬À�ë°'ìßãO‚Ÿþ8i&Ñ¿³5|FñF½¤Eö¸&û]•ä€ÛKº7eMãø\«/ñ^�ÿ Åá_úþ?ÿá�ñ§ÿ*¨ÿ†âð¯ý ÿÿðÇøÓÿ•U›T]/cͧ$á¿IÍM¿[­:%¥Š^ÑTö©kÍíÖ0p_+=zß©ón—û|K¶ÿƒ|›à{øk€%Ñ?±´-ãð³Ú<ß#¸ù¼Í¾õÆ~×Öß~~Ùß±T>ðï‡õïxsÁ^ Š] XÔ ¤7â>Ánm’ê0ëÅUÄr0hÃíÝò’GØ¿ðÜ^ÿ¡?ãÿþÿòª¸ï|cøOãoŒžø�©ü9øÿsâïÁ{k¢_ÿ™ñÒ}Š;ÄT¸_-tÑïTQ—V+�—Ö³« b#™'&›Õt�D­ó¨ï~ŸŽP¤ãJ4¹n¢š[õp½ín�Vµµü8/Ùëà‡ÅïÚ/þ oñóâ×�-þi^ ðµÇ…|-ácâ }fþê[™VK›ë‰-·@ˆT,hŠå²¤¶03çß¾~ÓŸ°OÃß|ðoÂ/ øËÂgXÕµ ü@¿ñt6šn�g{,×!5  ì’ÀÎA.%ÜtÉ_WÿÃqxWþ„ÿ�ÿøcüiÿʪŽïöØð�õ¬�Ëàß�íÈQ×þ‡�A?ó ®zѤé¸S’^죾�š\Îý~-n­k´´ÐÚjMÉnÓôq�*·M#¦·ó¹ã¿ðoîGü3ápOÙõ �1Ÿø™]WÙ5óWìÿñ‡á?ì·ð�Hð'�>|Ðü+ ¬‹ccÿ gÇW>@’G•ÿy6šò6]ØüÌzàq�]—ü7…èOøÿÿ†?ÆŸüª¯B¶*”ê9©-|ÑÏSJÖ=ŽŠñÏøn/ ÿПñÿÿ �?ùUGü7…èOøÿÿ†?ÆŸüª¬½½?æ_y^Î}‹ÿÿäì?e_û)z‡þ¡^(¯²+ãï…:F«ûZ~Ñ?!ƒMšâH šî¦Š3(�”È€Nä¡;Xãp# ø§Æßè_ ¿oŸ€¾ðÎ�¥x{BÒþøæ -;L´ŽÒÒÒ1ªx? Q€ˆ¾À ôÛßþ Oð¯þ £àÿø‹â棬h~ñ6¢ÚM¾¥i¤Ï"&”$¢gRÈ’œùmé_.øCþ MðGþ +ÿøayðgÇ–^6‡Ã_ Åa)$ؑԾDSsœð§¾”¢¦Þôeü²Œ¾q’’ù]kä?³(ÿ2”~RN/ðz5üeøâßÿÁP~ üD°Ò|ÿxG¾#ÓumCíP¯Ù..ͧÙÓÊg¾ÿ)ùE m䌌ò:—ì¡ñTÿ‚‹üwñ¶›x{BñÇ» x{Äky`Õ®2DhþzyFD}Åãå$Šû ŠÆXhJƒÙsÿäêiÿéÇo—ÏEU©JK¯/þHà×þ�¯óù~,kðL/‹Ú¿ìw£|4Óc†ú7�ô í>çYø›wâ�"ûWñ#à —6sö¡$à<Œ.dˆ${Ðb«_}ÁD¼=ã¯ßÁ Ã2ø3ö‘øm¨iãu�Ö½c¦êzF¢Pòbø¼¦ŒÂH]fFÁqõmS¤¥ =Û“ÛVÒ[ZÉh¬’Zê©É>x­mnºjÞ÷½õz¶ô?,üeÿÐøÙ£~ÅŸ³î…­xbËã%¯Ã�ßk^$ø[?‹JZÞØNÒ yx|«•Óà 9ØáJ©+×Òÿà�ß²'�>ÿÁCü[ñ7Røà_€×¼‹a¢xjÿN›Ë»�P27ÚÖÍQ>Ðè7–�^0žZù¬À�úET)¨Uö«Í÷ÞÏW»÷zÉ·}]È›æ¥ìÚí¯¤ùöÙ{Ý’ÓE¡ù“¡Á-~(øoÁ¾2ø™á;H<û@xC⯉|YàÙ^òÝíüY¢Þ\$ŸÙ—­²ýžé¶¬…Zï“sê¿n?Ù»â‡Çÿ�~,k?³í§Æ é>¸²Ö>ë(Óc_k3æôùîÖ›WtÜN6°`~†QYG Bâô…­¶–‡#{o%«é̹¢“m»u\§)Ëys_þÞ—?àïo'i]Y Á,düñïí¬x£áOƒ~iŸîô«ÏøwÂ×¶³é¶‘Ç`ñÉ ù+YØ ÊEi ²n\1õ?ø%ïÀþοðMO|?ñŽ“ý�âíI»µ½°ûT7L�spê¾dNѶUÔåXŽ~µô¥r£S•.Ž1�Ê)¥ó³ÖäÆMIItmüÛ»ü�’ÿàŸ°•Ž•ÿÑøWð¿ã§Ã� kº§… šK­_±±Öíì®MÍÃ+®|Øwùrýä$€äg¨®Sþ ÿ¾Ò¼i௅š�Â_…_ õDøAâ+�oþÕö›i¦è*¶º„ÅunTBÐG;b7I$Œ¨tËv¯·èªÄAUnOFÚwVÝ4Ö÷OT´wMhÅNN —uf¾M4ý4ocã/Øà}Í§ÆØ5ۯ؟áìím¤ÙI³\¶ºÑ.µÙ®$ VÃM·!"ØX;¼ÈÌÉ®Ûþ §û?x»öðÅ»/i?Ù7>'ø£â?é‰ö¨gûM…ÜêÖóf'`»Ôµ°ãºŠú^ŠR¤�üâáò“ŒŸÎñ^[è&î­ýå/šŒ£÷ZOçå¡ù û@üñ¿ÀÏø"·À�þ$Ñ/mþ#ë߬ôôðµ–¡kΰÿÚWw«o¨­$6å„”\©b»6c,qßþÇ-âØÓöÕðaøåðçâuÇŽ~-¤ÞðÏŒ¼EñBËÆòiñFŸjkŠ[6·‰ÌEÚQ �Ûw>ãïïÚ?ö^øû]ü6“Â?|+¥ø»ÃÏ:Ý KÔ?¹™C–7R9fуa˜g×û<Á0>þË?Ç‹|à8m¼SM¾©ªßkWÖ‘°*Ë ×³Lð† ÀˆÊä1Îrj)ÂqÄJ³ÙÊÿ.U’èÝšææÚ[5xËlMER�–öüyœ“ët®�­­­}œ|3À?±ŸÄ�þ ÝûXøçÃ~_о&x›Æš‡†ì´-Oö”‚‘fþ`—Ë�ÌÏIJÿZé?mÙKÇßà�v_ |? ý¿ÇÑh²}/íÖÑbk;›¸_9äX~E†S�øm¿)9û.Šºt”"¢º*Kÿ]G�øX�hý¢«ÕJrùÔjOåx«~7>ÿ‚‘þÂZïÄ�Ú_á÷Æ­á7�>=� è7^Öüâ‰-![ûydYaº³–î9-Òâ7çÌ ¹€Àšôø'÷Á¹¼+âßk÷?²ŸÂÏÙµÙ,l΋6—q®jŠX<ž{ið,1Á�»WÏ‘™“%WŠúšŠ(ÒTÝ㶺væ»~z¶Þ­ïÚÉLŸ4R~_ù-’òÙ%¢_xQE©!^WñŸþNÃöUÿ²—¨êâŠõJò¿Œÿòv²¯ý”½CÿP¯W>+øR5£ñ£ìŠ(¢¾|ôŠ(  :ç†4ßĉ©iö:‚Dw"ÜÀ²„> 08®Äß±¿Â:ǯ†ú±yLìo|3e>érçtgæ9<õäפQ@uÿ¬ý™î.dž?€,nfbòÜXxBÂÊy‰9;ä†%fòA$ƒÔSüདÿ‰_‡üEá£ü'þ2Öô2ŸÝÚlîâÛ·ø1�™;qš÷ú(À“þ ÷¤èì…ñGãæ‡´‚€üEÔµm¸è?âb÷;¹çæÎzŽ*Dý’~"h +ö—øºÛ6ì·Õô� ßÛ€2NJéQNÙ8Îfé�1ÁóEx:ü5ý¤<>²ø·ð§]…06k?.㹓íµd�x N-ÎIÚ ñwí5áäý÷€¾x¥@ËImã}OF”à1ÂÂú]ʱ?(ù¦QÁ9çÞh µ_ÄÏ`x‡öpøŒà=׆õÍU¶�œgÞÛ\°é÷`cÉÈfš?à£^ÒPÿÂIáÿ‹¾d¤mgᾺ–± K]Åk%¨ïÿ-º+ƒ5ïtP‘xöüøñGVþÎÐ~.ü9Ô5P@}5|Cj—ñpÛ³‰Tç³(¯\G e ‚2èEa|@øYá�‹GöŠ|9 ø–à ¿fÕtø¯!ÃpÃdŠÃœ ñÍy#ÿÁ2~ØJ_ÃþO¹mû¼ «_x@äŒ�.k~½ýO'&€=æŠð?øcOø@îðgÇï‹z2(Xëo§ø–Éñл^Û=çýñt™ï“‚EûOx KðCâŒ)Æ×�Rð]Ã/»ª#?ü5cÿ<Áàßh¯ÿ†Ëño�É>ø ñOC�1¿Sðìv¾,°>»#±•µ�{!œŒdä ¯ÁA¾ |FñZ-—Ä Øx†llÐõ©Ž‹¬ñÿ7‚+‘Ïêúñ@ÇEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_œÿð[¯ø&_ÅŸÛ«âÇÃo|6‡ÂW6ÞÒ5-?P�XÕ¤°uyæµ’6M°HbÏ#WèÅÑ…ÅUÃV�z.ÒŽßÓ2­F`éÔWLüÿˆ~?j?ú|/ÿ²oþCªzÏü›ûOxÆÝígƒánœŸe½Sâ{‰s#Ù\EàYçæyg°$ö¯ßŠ+ÚÄq^i^”¨Õ©x½»ò<úY.œÕHCUæÿÌüÿˆ~?j?ú|/ÿ²oþC£þ!øý¨ÿèð¿ÿ É¿ù¿{(­ÿ×<ßþ~ÿä±ÿ#?ì ò~/üÏÿ‚vü׿eߨá—Ãï6žÞ!𦇠† l&i­„«’Á•Kœghé^ÑEòíÝÝžÊÓ@¢Š)QEQEQEQEQEPñN§w¢ønúîÃN›W½¶�䂯)R'»p Xù ¥�c�ši6ì�»+—d‘aŒ³ª£$“€|qû`Ác¼ð3íZ7‚¿Ž8 º‘_~Ü¿ðPߊß|Kªxg[K¿h¶rµ¼þ·/äpVéÈ)ö 'Bu¯–¼Azl4k‰VhmÜ! ,ßr6= ðyÇSŠýk,àL& ó<ú¢PŠæjþê^rëè¾ö|cÅ5jOêÙ|[“vZ7&Û²QŠM¶Þ‰Y¶ôJçÛðOOŠþ+øõûhx»^Ö¼iýŸ}â Þ)Ô´XãX—\?f´Ó7Çݾ\1YÙ£>K®#̬Õú_“ßðGû7ãïí~Á–ñÉhúÓø² ™-uý.[X¦DšÆD<·½IÑ¡’=Èñ°![ô®OÙ3öˆ°�ÃañÇáEÅœgÉ«ü'¼¸¾uõ–K}zÞot†1þÍ~z¸¿œJXœ,)ŸÅEYY~o]|ôZiSƒxƒ"¡B—áå‡ÄU�´tê?ÞF2””9ר”£'ïFö’NçmEqðÊÿ´Ÿý¿�ßøgµOþihÿ†Wý¤ÿèµüÿÃ=ªóKUõúG?Õ¦vôWÿ ¯ûIÿÑkøÿ†{Tÿæ–�øeÚOþ‹_Àïü3Ú§ÿ4´}~�}ZgoEqðÊÿ´Ÿý¿�ßøgµOþihÿ†Wý¤ÿèµüÿÃ=ªóKG×éÕ¦vôWÿ ¯ûIÿÑkøÿ†{Tÿæ–�øeÚOþ‹_Àïü3Ú§ÿ4´}~�}ZgoEqðÊÿ´Ÿý¿�ßøgµOþihÿ†Wý¤ÿèµüÿÃ=ªóKG×éÕ¦vôWÿ ¯ûIÿÑkøÿ†{Tÿæ–�øeÚOþ‹_Àïü3Ú§ÿ4´}~�}ZgoE|aûþÔß?o/�Ÿ¾x?ãŸÀãª|××G¸‘¾ênšÄ%6µÜ8ñ ýÚÝGs†"�÷beQôÇü2¿í'ÿE¯àwþíSÿšZ>¿H>­3·¢¸�øeÚOþ‹_Àïü3Ú§ÿ4´Ã+þÒôZþážÕ?ù¥£ëôƒêÓ;z+ˆÿ†Wý¤ÿèµüÿÃ=ªóKGü2¿í'ÿE¯àwþíSÿšZ>¿H>­3·¢¸�øeÚOþ‹_Àïü3Ú§ÿ4´Ã+þÒôZþážÕ?ù¥£ëôƒêÓ;z+ˆÿ†Wý¤ÿèµüÿÃ=ªóKGü2¿í'ÿE¯àwþíSÿšZ>¿H>­3·¢¸�øeÚOþ‹_Àïü3Ú§ÿ4´Ã+þÒôZþážÕ?ù¥£ëôƒêÓ;z+ˆÿ†Wý¤ÿèµüÿÃ=ªóKV´¿ØKÇ¿çûÅߊú_‰<0F&Ðüáyü)ª½â¼žMBöáâ=ã‚X�•÷¡ddñô­ ,4ËðMM>MkDø¹ã؇üHþ(øþ]wB—µÝ�¶‘¥hér¾±Ìú\’ÆÝ)cq•`kéj­£éžÒml,-mìllaK{kkx„Q[Æ€*¢*€TVkÇ”œ¤äú�éYX(¢Š‘…Q@Q@Q@Q@Q@Q@Q@Q@Q@WŽ|_ý¾~|ñkøfÿÅ)­øÑT°ð¯†¬çñ¾qÓ66I,褜o‘9` Øè¯œ�ÆoÚã‚mðGà áV“1!u¿‰7ÂóPUè=N•ƒ×_Û¸€“´?ðO8þ)o—ã/ÄŸüYg~�-ïö†”ƒöu‡”·õùodºë×�€ƒâ_ü;áGïOá{oMã_Ûq'†<a?ˆõx°š 5�ÛÏÏpcAƒ–W=ÿ 'ö�øè˜ð¿€¼)ðWG˜áuOÞ w[GÞ]+N˜[©ô2j9ïEÔW·ü5øWá�ƒ>·Ð<áÍ Âº§ú�;G°ŠÆÒùb‰UGt«~€>ÿ‚…Áåÿ‚‚þÊÞ!𗌾1øûÅ^7¿–ÖïGÕµ›¡m¢hÌ…ä‡I±X-Ÿ0yÑ�e” [‚Å«ÃðN�‡¿ðIíkö�À:Wؼ#¦Úk~ ×õ¹Ô5Õþ¥¬>•5½íì€f¸Òc·S«Ü[Ä Q_¢u�ñáþ‹ñWÁ:Ÿ‡X<¸´¸#>[c]?dßÚ*õÄW¿>Ái!Û,š_Âkè/{˜¤Ÿ_ž%BðȾªkÚ¿gïÙ·Ãÿ³ž‡}•&§ªëÔËs­kº½ÇÚµ=ju]¡æ“B¨á"�R�+h¼VŒl%Xu4¥BJW‘èQEyg`QEQEQEQEQEQEQEQEV/�~ø{⧇¤Ò*øyñ—Lˆem|Mc'…õ§í_Ù$ö’7 Œõ`WèJ(ç¶ÿ‚„éŸ?wñwÀž?ø?·ïêz¾žº—‡Ç«¶©`ÓÛ[ÇßuÛ[œuQÒ½³ÀŸtŠ^´×|3­é#Ñ/×}¶¡¥ÞGykp¾©,d£¡­Šð߈?ðOo‡ž&ñE׉|1±ð¯Æ—�çMâ/Þc]]Ëýû¸P[ãíyÃ�Ç€=ÊŠù½|yñïödr<[¡Ù|tðtGþC~´M3Å6QÿzãKwû=æ?‰í%�Î>KF'ëŸÿh_~Ò}gÁzõ®µkm1¶¼ˆ+Áy¦\½oum Y­§_âŠdG^ê(´¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ªú¶­k éw7×·ÚYÙÄÓÏ<Î8cPK;1à$ž˜©g�-`y$uŽ8Ô³»*�É$öðÇ/‹~#ÿ‚¥ügŸáGÃ[É´ï…zÊÞ(ñ$cäÔ0Ü"7&TìLþð�çäPkÖÊr©cj7'ÉNÎOh¯Õ½’ݳ‹�Xx¤—4å¤Wwþ]ßCä_ø+'í_¦~Òßn‚¿s?੟³ïÀÏÙ_þ éªkZ‡†à³¹ðu€°ðÔö’oï/¥b!‰äÁóCHÍ$�ƒaD¬#5øÛð;övñïí¥âkûÏx:âM* ë-*k˜�6ÐÞܰDG“TÝó(s�Ã?â¾34Ì)ÑÃ`äÖ7Œ)_Y8êå/æ–¾‘½£ÖÿÓD2Éøk9Ì*ñf”TjG1½ý—;�5Bj_Ãç“n3§ñE?i¤y—ëwü©û(Ûü2ý™5‰÷öƒûwâ ËÁg+¨Ý›nåWŒ¯™8•›œ0ŽØWè¥s¾iÿ¾xgÁÚJ…Ó¼1¦[é–çn ¬Qªn>팟RMtõëdø‚ÁS¯²µõëøŸ”x‘ÅÕx£‰±¹íFíZmÆý ´‚ùA$QEzGÄQ@Q@Q@Q@sß¼!¨üAø]â-H×®ü-ªkZmÅ�®³kÉq¥I,l‹q±ÚdŒ¶åÏ”dÅt4PäüKþ9¥þÁ¿·ïÅxcâ׋o[á—ˆÏ�î´û›%±»ðö�ª·Ú œ«%Æ  íïk9Ëúû_?þÆÿòq_µ�ý•[ýB<)_@PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEæ_ÿl¯…ÿ³v¡maã/hÚV·~7Yh‘;^k:�ý{Ø@ê¤Q5zmó¡ý§~0ühÂ|2ø/y i³}ßüM½þÇoüô‹L„M!Çü³¹K3ÏÞä?±‹>/�3ãÆ_ø¦ y—ÃÞÝàߎs·²6£*ôf¾‘S‚Ú|ký¸~þÏÞ!M Ä~/²>)�<Ë i0ͬk÷KÇ1iÖ‰-ÓŽ@ÊÄG#žkŠÿ†‚øçñ·åøyðŠÛÀšT¤íñÄëño!Pp$‡H²in$Ï÷.f³l@ã>±ðSörðìßáù4¯ø3Ã>°�̓ŤiÑZ}ªBI2JÈ Èä’K¹,I$’Mv”ó›þÁZ§Å Ïñ›â׎¾!Å.|ÍG�¼%á°>_²Ø¸¹�=RîîáO¥{Â/�þ øá4м á? ø;EŒî:.› �¾ïï‰T=Éä×SEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEV'Œ¾%xwáͨŸÄ:þ‹¡BÝ$Ôo¢µSÎ:»Ô�ø×“x‡þ {û7ø^óì·Ÿ¾­î7 Hü_a5Ñ\‘‘J\Œƒ“·Ò€=ÒŠùÆø+GÀ5ÿ�ꚟ¯ög…5�CgûÞE«íÏlã88Î U“þ ÃðºøñÑ>5êûøøKâkO/Óþ>lbÝž~îìcœdfX-Ú'ž+v}1^3ûE~É?Ämqüwáínãá§Äí2Ó˶ñŽ˜¨á�,¶úŒ-ˆ¯¬Ç9†¸š'†LH8Y?àª~g&ßá§Ç;¨†Q๡ÿ•’AÿQùUi?à¨qÍ+|øôÊà §Oш#ÐÿÄʳxš+y¯½íaÝ1ð#þ$øWñö�ðwÀkÉ~2ë~"“ÃWÒø2dÔ<.¦52hC|Î7[É$D»å�ÃÆÿpHߢøkâØSá¿Ãÿø*GÃ/ÚKáÀŸ�þOk2ê'ð¤z^ŽºuîûyÐOdQ"ÞO2EÝL¹+呇ý"ÿ‡¤/ý_�ø/Ñ¿ùeGÖ¨:ûÐ{jÌ�ª(¯–¢ÿ‚¥ØÆÙ½ø'ñæÂ,q#hº}ÎO¦Û{Ù×’¸ã®H͈àªÞ @~ÛàŽ{ ·€oîw�\Û¬ Àˆ>ÔÖ&‹ÚKïCö°î�§h¯š¢ÿ‚³ü�‹áñ[HaÃÿh|(ñL#wÎ:”Çý×9ÁÆjå¯ü—öw�ÿ¥üOÑôN>c­ÚÝi3ýÖ7QGµ»í88ãÕª�{1©EìÏ¢h¯&ðGíñð/âiAá¿�? ¼@dûŸÙ¾/Óî÷|Ûxòå9ù¸úñ^§§ê6úµ”w6³Ãso0ݱ8tqêàÕ”MEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEñ?í½ûWx“ã¯Äÿøgÿ‚ÒüC¨‡ÄºÜ/ˆtxÅæº@8‘‡#"5˶ÒÊòʸêÞÊž‰k)=£»Ö»˜Ìd0Ôùå«z$·o²1¿jÏÚÄ¿·§Æ þ|»ò¼?+ãFI�b 0ër§÷­ò‚37׳·ìõá¯Ù‡áe‡„ü/iö{A¾iŸ{éˆç•€�°=€dþɲ—†ÿd„ÖÞÐ#óflM©j2 êwæGô¢¯ErrNüCöÁÓÿa�ÙÅŸnÚÔ,mþË¢ÚÊãûQ”·‹HÝó¶9ÆçµwfÙ¥)Á`p^ík®ŽO¬åú/²Œò¼¶½Jʤ×5iÙ$µµÞ‘�ÏïgäWüqûdÜþÒµŽ�ð_õÖ�ðòP/RÊÝë3( 8>Dd&…žpkõ‹þ ‰ûYþÿ±—„¼!5‡‡ûS]—n}FuV˜·û€$Cý˜V¿?à…²eÿíSûjAãÏ µ4Ó®%ñ%ýÅÏ-u'™»Ìbx--î}TK_Ð=|> ³Æâ%Žû rS_ÝZ·ë'«ûºwÄ9¥<*— à$œ0²ýüÖÕ1VýâO¬0êÔc·¾«6¶aEW°|˜QEQEQEQEQEQEóÿìoÿ'ûXÿÙU±ÿÔ#•ô|ÿûÿÉÅ~Ö?öUlõð¥}@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Wñ»ö–ø}û5è°j?ñ§†¼!mvþU¯ö®¡¼—²vŽc¾YdŒ3ð(¸¢¾v_Û;Æßÿwð‹à¿‹u»i1åø‡Çü¢`ô`—>§ î ØìaŒH2 "þË?>2bOŠ?µ]7O”'‡>Ú Úc¤š‹¼Ú“‘ÿ= žÓ=vô�ŽŸµ�Ã_Ù¢;OøNümáï \ê ³¿ûr³7½wÔPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEyÿÇÏÚ‹À¿³.�iuã-z->}MÌZn›o·º¦¯ 1ÚYÀ¯qpàrDHÛFIÀ×Ͼ(ý²>2|c%< á áfˆü.¯ãUþÔÖ&^æ=2ÒeŠ,ŽUæ»,3óÛŒ8VÄÒ¤¯RV3�XA^Lû ¼Oâ�ükà—Â/O¢j´}GÄV¿ë´?Ç7ˆ5ˆ{ Ö6 =ÀÎ388Î |×âÙ†‹dÉñSÆ4ø¯$Ÿë,õÍGìú.?»ý—h!²u–—$±=ׂ¼¡ü5ðüZO‡4]'@Ò­ÿÕYi¶qÚÛÇÛåŽ0tjòjç°ZS�ÿŠyŒWÂ�­[þ [­ø�rxàgÄ]aå�ÿ‰.,¼5`ÿðe’õO׊ç5?Ú#ö—ñ»~ãþ§Ãx[ïF–º—‹çÑei4ÔR?¼baþÍoÑ^uLç/†Ëúó9e�ªöÐào¼ñgÅùÿ„‹ö‡ø“$mÖÓBÓômØ{‡ŽÍ®�ÿ·Œ{V-çìgá}Oü$§Ä¯nûËâˆ:î© }!ší¡Q쨶kÖ(®9ãqÞlÁâ*½äÏ,ð¿ì;ðcÁw->—ð›áŕԇ2\ÇáËO>SŒeäò÷±Ç$ñ^‘¢è>³û>�eiao�ÞU´+gg èü[¢¹Üå-Ù›“{…QRHQEQEQEQEÏø»á7…|»ûwÃ>Ö÷ýï·éÐÜîão;Ôöãé^'ìðV;ç»±øeá öSºK­ Át{‰«Ikå¹>ä×°QW“�ÂìR”–Ìó Ù�þWßá‰|.ãî¬5=JÿvßP–æì±�ê99ÛÓ¯?hø’üx_û‰ãiú€>ÌtÓ¦±ô9Ç\žk´¢ºa˜bc´ßçùšÇUm#3Lý²¿híßÃO†ž7·_¿sáÿÜè÷mþå�Õ´±ûÞ p9ê:]#þ ¥á'äñç€þ-ü6�pÒê>}bÉÚk­%¯!�;î™ã3´ñY´Wm<î¼~$™Ñ¢ßSÞ> ~Ó?ÿi .kÏxãž2‚Øí¸þÇÕ!»{FèRTF-ƒÁWƒÁÜWÂßÿfo‡ÿµH5xCBÕukL}—T{UMFÏ 7I‰¢>èàÓ|;Ὴ¿ÿ…wñsZ›O‹îh^=…¼S`¢Ý<‘êJHã/w*¯FpCz4sº2Ò¢·âuC0ƒø´>뢾XðŸü©üËkñ£Á�€QN×ñ6“3kž#¦ùgHÒâ͉šêÞ8c{`šúkþ#Óü_ ÙêºMýž©¦j­Å­å¤ë<10ʺ:’¬¤Aƒ^µ:°¨¹ îŽØÎ2W‹.ÑE¡AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEó7üöâ›à…—‚<öçÅom·ÒìaA+X Õ�סlýÅ< ?*�{rülmxáè+·÷%Õ·Ñ.¬çÄâiá麵‹ú²ó9ßÛëöÌ×m¼[iðWámOâw‰q ÕÅ»È$·D”¦X±ÿVŸ1Á*kÔ?aÿدCý�> >ØÇ©x›TÛ6·«²ü÷²õÚ¤ò"\�£êÇ–5…û~ðþÊÞºÖµùÆ·ñ'Ÿ×uYÍtgmíny+»–n®Ã'€ }^¾i˜Q¥Gû3/»_ºÔ’ëþöWÍêpàðµ'S똥ïô_Ê¿Íõ ¯ÄOø-ÿÇ©ÿn_Û–„z5ã7€¾ ZKâ;ˆ$ýÛÞ�‹>XdnVx,Ó#äšiàšý.ÿ‚§~Ý6°쇮øÁ^ñ=ø:W†­_ÏÔ%VØå{¤JV‚#Û�XWå×ì�û"j3øCá—€¯ä¼ŸâÇÍB/xÂòR^êÓKÌ�i9$–0›«·Ý‚ZxƒQM~uœâ%SýŠ—_‹ÑíY?ü•3￵ŸpÎ#�9o‰oØ`¢þÖ&~ê©g¼hósmn{v?Dÿà‹ß³l¿e8µù­×QñÌ‹|¨«·É²Œ¶@:Ay;J=+ëú­£hö¾Ñí4û(ÚÊÆ··…(ÑBª�`…Y¯oEQ¥Qè|C•¬·/¥‚ææqZÉêå'¬¤ßW)7'æÂŠ(­�X(¢Š(¢Š(¢Š(¢Š(¢Š(¢¹ï‹^ÿ…­ð»Ä^þÖÖôøH4Û�(#Gðoá�>'ä•Mfâ×þ¯öþÝ~#{ˆó�¾Ê ¡×Ðá_àwÇ��ŒÇÇ?tφú<À†ÐþؼÚG)&±¨#»ƒÏÍogjã³Íz߯/�~ýž|1ýµãÏøkÁÚQm‰u¬êPÙG+ÿq Œ7¹È®I$9¯#ÿ†åñÅ€cø;ðƒÆþ6�Ž#×|Gø;ÃÝ>÷�{ÛeBz=µ”è@$7Lõß¿ao…éèëèùÿ8Óâ?Û—]?òøo¢ãæÿYnÏðÿ®“§ù&�õf¢øñWýÄ�épþ׋øiMÿÛ�õ±öÕñ(ðwíËx<ßøJþÚùŸ7“äÄ|¬ÿ~ÎÝ>§ëAø ûp]þîoŒÿù]¢²‹zŽäcM?ˆúÑþ®Á|XÊ?øŸåþÕ—J>åÿÉmQ_ŸÙ'öºÔÿuwñ÷E†É{kR$˜Åºüz�øb?Ú‹þŽ/ÿ%äÿ ?°°‹|m?üŸÿ�íý0óÿÉù#íª+â_ø`oÚ?Wù¯ÿimRÝ“„�LøtþF�øwíÿG=âOûóqÿÇèþÅÀ-ñÐÿÀjò!ý¡‰é‡—ßó>Ú¢¾%ÿ‡wþÐôsÞ$ÿ¿7ü~�øwíÿG=âOûóqÿÇèþÆËÿè:ø OþD>¿Šÿ y}ñÿ3íª+â_øwíÿG=âOûóqÿÇè?ðO?Ú¾?ÚsÄ-"òªðÜm'°?¿<~éGö6þƒ¡ÿ€ÔÿäCëøŸú—ßó>Ú¢¾%ÿ†#ý¨¿èâÿò^Oð£þÓö¬ÒÏ›gûBZO7M·6ÌSúÆã?…ØxN˜Útÿùþѯÿ@óÿÉù#íª+â_øfÛ"Ó÷�|tð¬“'Ì‹- ØÇ°9³#�úP~ ~ܶ?½ÿ…·ðÞ÷g>GØâg¶³—ÿB«ô_ÃŒ£÷Íí�ý©Qo‡ŸÝþHûjŠø”øOöåÓ‡�ÿ ?Ã{ýŸòÃÊ„y�ºù õûÃ¥oý¹tÏß}‹á¾¡·þ]÷D»óÇ]éÓ¯Þ? ?ÕÄöÅQÿÀÿÍ þÖïF§þÿûjŠø—þWíË¥þûþ÷Ã}Kyi‰wg¾~Ø�?Þ þП¶Î•ûËß‚žž»¶Úò2ùü58ü??Õš�áÄQ÷?¨kí*Ÿø>Ú¢¾%?µïík¤×ß³þ—p�‹K’X}³?€ Á@h­7÷WŸ³>µ<ã’öÓOå‘Û‰Çþ=Gú«Œ©¿J°ÿäƒûj‚ø”—ý¹/ò>Ú¢¾%ÿ‡¨|R±ýæ¡û,|H´µ<»®ÎÓÛ†±Q×Þ�ø|&«¦�º¯À_‰Üƾ[�ã¹ù¡_ëGú£šô¦Ÿ¤àÿ)öæ ¬Ÿþ/ò>Ú¢¾$ð[Ï Ø¯üL~|G²h¿ãà}–äãï}ç^ƒ×bÃþ ·ðvòb²h¿­@ß.�jAöùnXçðíCá ç¦OÒÏòaý»€ÿŸ¨ûN¾ný¤?jÝVñ~¡à…Siñk:[©v²ôwÓ•¹öiÆ ?‰µ÷rþ\ÚþzqÑ_—þÿ‚�kÞý®þøjÃöªðOíŽüK†õÿØøbÓN�GYÑÙ/,®­²]#pªVI%Ü ää{ïÅ_‹ÿ¿jÛ›Åÿ>xúËáNƒð³FÓµ/ø‚- ßZÔïïo„�¤QÜæ¢&öbŒäð0B–qå}õ×KY»ÝmªÕ]6ÒZè)Ò唢ß“볗*é}ôÕ.ûj{÷ÇßÚoAý�5�Ykvš½Ô¿¼Omá=4ØÅ¬S¤Ž�6÷M±d®æÉSÛÑkóßþ !ªø—ötøeû3ê¼aiãß |_³¿Ôµý3@{&º´ŠÖúA!³„ɉDcæX²  ÷ø'÷ÅωŸµŠê_üC©Úh |_¯�<m´ÓÃdãQ¿ºMÍö©±ÿñÉå¿+“%ZÃ^‹¨žÒi¾Ÿ \W{»Ë§G{$`â£.Ž+æùæ�¼’I¿—V“ôoÚ'öÕø]û&x‡Â_Ä_YxZóÇ—�a¢ ¨&hîæS`Ò"2D Ë^VE‡5Ð|sý <#û6x.ø×VþÅÑçÔ-´¸î>Ë5Æë›™DP¦Ø‘Ûæv8ÀÎIšø¿þ ãð/Cý§¿n�Ù_á߉ è>1¶ñ¦�v¸£Î• Ç*Ï©Š{2 ñ�_´ˆ<[ÿ鏸Eñfo‹>%x[ÚóÈÌ[XµþЄØêˆXe’âO%‘‰ÆáW†ÂF¬i]ë)+ÿ…Õöm¯5¥ÿÄ´²fµh(ÁM#“òkŸ—äùZò¶þòKõÆŠùGöÀøùñ'Åߵgï„Þ Òü¬ëzÏ‹üEâ«­95;�#L†e·Ž;[Y?tÓK3ct¹UPNÖ'|B°ø�cñÂ÷ºÿ…Ë¢¾ð/üÇÅ>ÿ‚Z|Tñ_Œï¡Õ~3üÔµêY²H¾ß­‹�"ÂAj¨V_>Ù°ŠáñÐã°ý¤¾!|Iø û;ü3�Ç_´·�>]�$x£TÐìîu�gRT�Þ VÛjfMÛa‘° 2M[ÃI=Õ®’zÙÝs]iÚÍõ÷£¦£T%ÍÉ-äºï“Ù>¯Nš>ˆû6Šø×þ ûkkŸµDŸ¼;«xïDø¯gðçX´¶Ò¼m§iƒLþßµº·36Ù�$‰•ДN>èÆOÙUèÊ”ù%Ù>»4šÞÏgÔë]›_s°QEˆp:Âý[àˆ® “Ìq˜tûp&Ô®%bOAÝ›¢¨'аøz¸Š±£F7”�’Fuj”JŽÉnr¿·‡í³¦~ÇŸ ’HcMWÆzîmô%rÍ<§�5Ô|ÞR’3ŽX�£©#Šÿ‚}þÄÚŸÃ{ûߊŸ¥mcâÇ‹sq;ÜçF�Çú¥ì$+€Äpªkò‚[“ýƒe¯üiø”ÿ´'Æ8þÑâm[xkI™‹G·çË”F~îþíO+’ç.Ù_µëé3E<ºƒË0nóÅšëýÈ¿å]_Ú~G•…¥Ã¾ÿ‚˜é:ñÇ�>|hø‚­Â\[ømt VÿhI¬Ëc½;ïŒ8#•Ý‘œ»ŸÚßãç‰Xÿbüð.ƒÿ–¾&øƒ ¸OOô{->áö?é ŒäéW¾|lðoÆíûCÁž,ð׋l0 ¸Ñµ8o¢é–‰˜ éëÒŽžíÜäx™žw7�¿jž|cð7Âñ7ÞKjz¼À³+ê–è|˜Xs�¼d՛¿õƒ›ÏÚ [Nâ4Oé–àAö‘rv{[8ù±Áôê+Uƒ¢ºíêw<©þ|G¿çPý¦>7Þ°û›-¼3f±ç®¾�áÓï–#NaÙ‡]»m÷þ;ÝJß}lj!¶ßÿ‚Þ4^?º£×®MzÝV¥ü¤ûY÷<þê×þŠÇßü9Ú×ÿ$Qÿ ukÿE?ãïþíkÿ’+Ø(§ì)ÿ*i>ç‹Ù~Äz^�sw5¿ÄoްK(žéãø•¬+\È#äO–m‘ƹ<íE«ö<‚3”ø£ññr­ÿ /X}§±ÚÓ•?B=Á¯`¢�aOùP{I÷<–?Ù—Ä6M¾Ïã¿Ç{Iºo:ýµ×›.-dOÇn}Z•> |NÓ9Ói¯�À|ÞU͇†/¢‘¿Ú3i .:|©"�LMz­¾­KùGígÜó(<;ñÿFÿ�/�°j~ïöç�,.7g®ï²½®qÛí�ÕnˆŸµ‡[-â€þ-Eè‡Âú®€î= �Bôg�¼7ÝpÞ…ECÁÑ}íêw8Ë_Û㟆›÷À¯ ë1/üµð—Ĺ–Aëå_ÙY*ž¿/šÀd|ÇœjXÁO<'¢œxïÀŸ¾s†—Yð³êVq{Éy¤½í¬+þÔ²ªûò3¿\_ÅŸÚ7áÿÀkd—ƾ6𯅸ò—VÕa´yÉ8Ø3±< ’xšÆx {§cE‰™ôÂ?�Þ øûá5×|â¿øÃFg1ý·FÔb½€8ê…£bêy¨®¢¿1>*üu²½ø·àˆ¿ |â=*þoxOA×<{q¦?‡íõM3P׬4éìÌSªO©¬‘ݸBдdÌ’¬‰Óºó+Rörå½ÎºsæW (¢²,(¢¨xŸÅZg‚4 ­[YÔl4�.Å<Û›ËÛ„···Oï<ŽB¨÷&€/Ñ_=7ügÃÁð‹Ã>3øÝr[b^xZÀ&ƒœãwöÅÛC`ê0K&–@IPÍÿ„'ö�øÞ[ûwÅÞø'¢Mÿ.>¶ÿ„�_Ú}uè’Ò&ÇUO¹`oñçÄ=ág…®µÏëš?‡4Kßs¨j—‘ÙÚÛ¯«Ë!UQõ5áçþ §üJ_/àç€üuñ�¤%cÕtË!¥øls�3ûVøÃñúµŸÚ[ÑMløþ áð·Â>+³ñ.¯£^|Cñ�‹y�x‹Æú„Þ"ÔmdîöÆéž;<ÿvÕ!OEíôóÁøYûB|n$ø³â†þhÓ“ðþÌjº°ª¾¯¨Ååc~ëO�×$¬™ÁÂߨá_ÂÏ[xœxq¼Sã[\ù~)ñeìþ!×"$‚ÞUÝãÉ$ H$%mP{-QEQEQEQEQEQEQEQEQEQEQY¾*ñŽ‘à]MG[Õ4íO‹ïÜß\¥¼)ß—réë_7|Wÿ‚ÂüøeæÇk®ßø®î> :-›J¤ÿ×Y6DGº±®üWŒÆ;aiJ~‰¿Çdsb1”(+Öš�«>£¢¾ÿ‡‰üxøòv|'ø }¬¼Eªx…œA ?Ä òb×Fþ”©û#~ÕÈüg¶ðNŸ7[©ƽѼ‘?Œ¯ý+×ÿVgG\}zt¼œ¹¥ÿ€Ã™ýö8µã?÷jrŸ�¬¾ùXû3Åþ?оiÿk×õ­'DµäùÚ…ävÑñþÓ�+Ãþ#ÁTþ|7Þ’øâ×X¸^�éòÞïúH‹å~n+ϼÿJøai}öïë~2ñ¥ûœÊ×—âäÿ¿j$üä5îÿaoƒÿ Ö?ì_‡^ŠX¾ä÷6bòtúK6÷ýi{,Š�ÅR¥WýÔ ¾öäÿç̪mCÕ¹?ÂËñ>}—þ ,ž;¸xþüø‡ãŒªÂ$1ïþ¥'8úãߟðÒß¶Å#�|ðï…-dãÏÖgýì>‡M?÷èý+íˆ KhV8Ñc�UU @>�íœ?÷l}g)Oð¼Wà?¨bgü\Cÿ·R�èßâ|Iÿ åûbüR9ñÆ/ xJÎ_ùa£Û�:zŒÇ;~ôýGZpÿ‚Ak>1ù¼oñëâ?‰]ù�,�'ÛÍ–oEíü=¸ÇÛ4RÿZñÑÓ¡Mr_�¯ø‡ö.ÿš┟ëcä Á~ hûRx·[Ç_¶ê¡3×þxǨü‡¾}#ÂÿðL¿�ÇÙ>è’ãþdžû×þ{;úŸÓÐW»Q\µø�5­üLDÿð&—Ü�©åX* (ýÈâ´/Ù·á×…ÿäàiØÈeÐí¡ÆNOÝAÞºû+4Ø<«xb·�9Ù\ýMEyU+Ô©­I7êîvÆœ#ð«QYQEQEQEQEQEQEQEQUõ]Z×BÓ廽¹·³µ�wK4òãŒz³>´bŠð‹ðU/ÙÓàƒLž!øÅàxç·½¶°¿�ÄgÐÅl$p}¶æ¾wø…ÿ/~Íþ� ÐÇ�|kpÌR¥h~RÌÝþ>d…°Iþé>Ý«²–]Š©ðSoäsÏF—Þ~ƒÑ_šðýŒ_É�áWì}ñ3]†^!Ôµ3q²ç…,ØÇÉÏü¶Ï\ã7ü»ã�ü‚þ|,øYc0ýÕÅüñKp€ñ–V¹�²:ó ôè{ïý“YÆ>²_ðYŸ×i¿�7è™ú_E~i/üÓöôø°7ø×ö±Ò¼6eåãðÍ“/—è• ¯÷W¿vää’åÿƒyõŸŸø¹_µ?ÆîÿY‹‡‡Oùøžçû«ë÷G¶©a£üJëä¤ÿDXªþoæÒ?@¼qñÇÁ_ wÂIã x{gÞþÓÕ`´ÛÓ¯˜ÃÔ~b¼oÆßðW_Ù›À E÷ƿϷƒý›¨ OÓþ}„ž¿ÏÐׄøþ  ý™|'³íö¾:ñ>Þ¿Úz÷—¿ëöháý1^Éà�ø"ÿì¹ðø/Ø> øV}½?´š}OóûL’gñ£“/Žòœ½_›aÍŠ{(¯›¡æž2ÿƒ�ÿe +/x‡Ä[F@Ó¼;v›¸ÎÚ/§?Ëšó­WþNøeñšÛÁß~.xèÅ!US¥[fP’Ìz2õP~n@¯hø­ûC~Ê_±œ�e¡x7áýÖ¿ �Kð�‡lL‰&O$h±Æw1È-»“…5ÎCâïÚ›öØ�"ðö•kð+À²¨T¹¸,º”±vØv‰G*Q!íšäži–Ó—-*Rœ¿ÅùÙY}çÆf•ý›ÿà•ß¾x–ÛÄÚ´ú§Ž¼go:Ý�[V”â)Ô†G$�24Œà× ÿmÿ‚axÏöþ‹ÃÚ·ƒ>'ÝxGVð”,´‹¤oìË™ÙÉ̑þò)ÁXöɶM†$*²Õæç¥ŠÂµõt¤ž–“r·¬�¾GÕp^WŒÍjû>!« “|¯Y¨é¢›„wrѸÝ$ï­Ž’Šø?Û;ö‡ÿ‚sÞá~Ò¿ umkÃP°‚ßÅúB$»Ç;wL§ìó±ùWheûØ×Õ³×í“ðÓö¥°ó<âÍ3UºU/.žÌ`¿€ n-o Y6‚Ào PžŒkàUX¹r=%Ùèþæ}x{�äÔ¾·^š©‡{V¦ÕJOþß�Òô—+ò=:Š(­‰ ÔôØ5�6âÎê$žÖê&†hœedF2‘èA"¾øQû~Õ°Æ�qàO‚>1ø1âÏ…PÜÏq ÚüB·Ô¢Õ<3²™ªIg•¹… 9ʱ'hâ¾ë¢®p½¶{ùöûº=Õßw}F£ËÓõòþµë²>iý�?b�cörµø‹ã¯ˆ,�Ç¿þ&q¯ëZ}šÒÖbe·±´Œ’Éo$ œ·€käø'—ÁŸÚ‡âgüãÁþðG�~[ü=ñÖ�ugý¥­é×k¯øVÞk‹ˆnc¶ŽÜ]œnxÚV‰•œ«HýR–%š6GPèà«+ †±¬¿xCøeá[= Ãz6•áýOR–š~›i¥­²–,BE ±'€9$÷­Ö&ñššMK‘ZÚZ<Ú[æ¼÷îïq¯8ûËâ½ïÖúkò·¦Úh�˜¾-Á5'ÒþüÒ¾kú‡|kû=KÇ…ou«Wº°ÔÑÌ^"0�Gp ,Ñ�Ê~ízìégûHßøäÝü\¼ø1¦xzÚÐÇ™àصË‹ë†ÿ–²\Ýùb$P8�brÅŽ\` ÷:*^&rrs×™·¯w»^¿wUg©•ýÈò²ôM»}íýç…~¿³½û1xKâE†½w¤]Íãˆߊ쎟,’,V·²«Ä’oD"PÌe£óÏÙ×öLøßû(ÿÁ=<ðÿÁ&øwoñÁ·—cS·¹¼Ðµ»ygº“ì­*¬wçω¼ÔBÊÑck)9ú⊟m+[üù"j?ƒ×¹~ÕÞRjNOÕÞÿúSô>>ýœÿàŸ.Oüqñ�ÄûŸhÞ%øÙ¢Çáë­+À¶³.•gpÏÚ¤’eŽ[‹§óسL'?/+àOø'çÆÿÁ;6þÉŸüûaêÿ¾ë&¿ñ¶�i¢ø§ÃÞ5pÙÜ Mÿg»·¸´G‘ePÛ :*Xç8Ûõ…RÅÔræó“õrVwõKåÓ¡Ÿ=´KNUt²|ËîzŸ+|Oý•>/ü|ðÇÀË¿kŸn�ð»CÖ~,ëVò›y�‡‘[HÓå ·Z›•³…”ýèüÆŸí‰ÈÅz·ÃØSñæ§m®|pÔô›iâÏÁšBIÿæŸ «\´˜“S‘HÊ´É íalVJôp¹ejÎíYwgU%Jšìв›Ï�ß,¾1j–ÓZxSAÓ¦²ð5¼èc›R{œ ­]Ð�ËÄ‘Ål #ÜHAYa#ë:(¯®¡F4 ©Ãd{tà¡…QZ–QEQEQEQEQEQEQEQEQEQEQEQEQUu­jÓÚ=Ö¡s•�ŒM=ÅÄî8#PY�˜ð’O¥4›vA¶¬ÂøÅñAøðÛUñ_‰oÇGÒ!2Ìý^CÑcAüNÌB¨îH¯Šÿf_ƒþ ÿ‚“|qO�¿¬ž×ÀZ4̓ü9/0θ‘Áá�0›½qŽš5oø,/í¸ý·Oøðþó�óDÚýÈüŽæëMü/'? :>�káí&ÖÂÆÞ;+(’ x!@‘ÀªŠ£€=+몵’PtcþõQ{Ïþ}Åý•ýù/‰ôZ#‚þЫíðbôþû]ºwz–@À¢Š+ätÆø‰ñHøQà-gÄÞ ½‹MÐü?e.¡u/Ý·‚$.ì{œ(<Ojþ|~Ùëð]?ø,:†½ÚøN{£¨ÞÚ–ÈÒ|?fwjH?)�”Œ‘ÿ-.Yñɯ®àåÛÃìÚM—À^:›ˆc×¼m4 ÿöªÁ­lØŒ€Ò>É`9û0Î$5§ÿ‡øEü³þ eãŽÚÝœv¾3ø�l/tÔ•~h­I1iÑrvÉ$†sޱºgîW&–­yã'ü<>Þumÿ¸Ö¿âhûÌËK„øNxŠï—Ž„¥'Öž:Éú×’·�8¿æ=»ÂVq~ØðU뛨b�üð"Ñm-Q>Ú„ª€‰¼Â1ÚÑkîÚù‡þ /ð&_„_²�–³©+�ÇÓzöYy‘£�~àÔþïsÞf¯§©`býŸ´–òÕüöüÃ8VkÌ1JÕ±Ru¥åÏðGþ݇*·MBŠ(®ÃíŠ( Š( ¾ÿ‚cÊ6?g¯û&žÿÓ]µ}Ë_ Á1¿å³×ý“O ÿé®Ú½»âg.+dtÿÿc„ÿ5�í?ü9ðfµ«‚Y5;�"íIêÉrL‡Ý\æ?á�¼/£ÿÈ·âÿ‹þôŽÃÇú­Å²D·»šx#ð‘®I9Í{�êòG±ÇÌÏÿ†[ø‰¢ó£þÑ_G݃YÑü?¨BŸŠiðÌA9ÎéIèP(ÿ…[ûBi\Xücømz�?âsðÒây>km^ÝA=ÎÌtÂŽþãE.Eý0æg‡}ƒö“±ÿ˜¯ÀíSwì­RÃËÿɉ·gþŒwÏöÏí'iòÂ9ð:ÿoüµÿ„�T´ó?àa—o§ßn™ï�î4QËæ<;þÿÚOþ‰7Àïü;:§ÿ3”ÂûIÿÑ&øÿ‡gTÿær¼�ö�ý¬>>Kÿ!Ó¾üÿ…Ac¿ÿá7º¿ñ–™¨Ý¹a¨½›D†Öæ  deOGùº ¹ðcöïøŸðÇö¯ðßÁ/Ú3Â^ ÑÛ>Éç¿·Cžì¯ÚOPÿ˜÷Àí#øqýƒªj8ÿo?l·ÿ¾qÛïsǸÑWËæEÏÿ…MñÿVÿ�‡Æ�ÙŸñ#øm%±ÇoøúÕ.¹õãèæ�øek<ë?´GÅ™”ýë}2ÃÃúlÜ2é­8<‘þ»Ç«Üh£‘3<;þýà½_Ÿëßü`‰5ë jÿïZÅq±ãŽbÎ õ9ìþ~Êÿ þ\¼þ øàß ÝK“-Η£ÛÚÜNHÁ/* w$u,I5ßQMF+d+³Ç?n/ù%~ÿ²«ðóÿSM¾Ô3¢Î±—Q#)eLüÌ z �ÌWÃ?ðQ=fó߳扨iúEçˆ/ì>$øâÛK´–®5)SÆ:3%¼o3¤Jò0 Žˆ Ì£$~nÇâ¿ø(çŽÿೞ ø«ãφ>xZúí¼o‰¤'‰t¿è·ÒÇ’l„ÉŽ�äÏ,îZÅ|¸Ö%ò3â/Oó;°¿ýêÚµ®ƒ¦O{}soggj†Y§žAP ä³1Àzšðkßø(ÿ�üUy%�Âý3Å54s< §‹Í- É«Lñi‘�AʵÖþã&“ÿàø{«êvúŸÄY¼Gñ¯Z¶u–;¯ß�VÖ"HtÕXôÛw�ðZÆÙŸ•qï60é–PÛ[C½½º,QEH‘F¨\Ióïö7í%ñ¿?mÕü ð/D˜ C¤D|Wâ=¾¿h¸H¬-¤õ_³^/£šÐð¿ü—á�¯ˆ-5ßÚk|MdâX5j/¯=¤£þZ[[Kþ‰fÝãÖG'Š÷z(±Æ°ÆªªTaT =:Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠòÿÚöÊøqû.éìþ0ñ-��îÍñi°Ÿ>þM°®XýæÂú‘[áðÕ±*r“è•Ù�Z°¥z�%Ýž¡T|Iâ�7Áº,úޝ¨XéZ}²îšêòu‚‡«; }M|DoO�?µÓ4þ>ƒ¡LJ/‰|@¾›±GUóϵhxoþ +¬|_Öá×><üN×üs~�¼i–3´V0ÕUØd)ôŽ8«è?ÕêxmsJñ¦ÿ•{óû–‹æ×¡åÿjN¶˜:n~oÝ�Þõ$u¿à°ÿ ü«ÿcø:ÛZø—¯¹)¶‹nE»¿÷|Öo¬i ®<üAý±jRWCðÞƒðcAŸîÜê|ßî‘^@}‚?¯§Õÿg_~ÏÚOØüám#@Œ¨I$¶€yó�ÿ=&lÉ'ü �v”¿µ²ü6˜2“þj¾óÿÀU¢¿ýG[\Mk.ÐÑ}ûþGÅžÿ‚8iž/Ö£Ö~/|DñÄ�[%š7¹{{qž©–g—o¦ÖNƒ�_Hü(ý“þüXO…|áÍ"xê;5{¿Æw̧ñc^…EpcsìùkÕ|½–‘ÿÀU—àtáòÜ-ÍN ý÷{Ô(¢Šòࢊ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¬]3â?‡µ¯jž³×´k¿èpÃs¨éPÞÇ%îŸÛ¼™&„6øÖM�´°¶œg¼oöÑÿ‚’|:ýƒ|Qà-+Ç0xÂyþ"^ÜØé­¡h°…íâIdóVÏ�ŽG¼¬B¨Ä|AñsÃ^ øçÿо3~Ï?ü1iâï‹ZQ³Ð¼Sar.¬âñ>—m¹ô=jÝsk©iPá¡” ¡“EY# &C~®Ñ\ìÓñ7Ä¿~ iߌ¼©|;ñTžm¾« Þ\Grlî!‘¢�Å4d¬°;!x¤à¼lŒUIÀï(¢¨ø“ÄÚoƒ4+­SXÔ,t­2É ·7—“¬[ êÎìB¨÷&¾"ý¤?àá?€Ÿu#£xFïXø·ây$ò ±ð½±–Ýå'…ûKáÆ/n+£…­]Ú”[2©Z�5y»uÖW�óü4üL>³9ÿ ù½ùþ©|xÿƒ€f/�¢h¢ñ­Ï�µs›O X½îÿ÷g}–çð–¼bø-'íûP>Ï€²¶¿{e7ü{k~)yÊlôÎ<ˆWÿO^Õö�À�ø'GÀÏÙœÄþ ø[àýî wÏb.ï“1s>ù¿ñúöš>±‚§ü*NO¼Ÿè­ù‡²ÄOã�½êĮ̈¿fÿø(ßí0sâß‹Þø=¤\¬±Ðâ�ï`Ï÷ZÙ¸ÿ¯¯ñ«ZWüqáïˆ7ñßücøåñk↠­æ;Éx-ÒGï“9¹“žŽ½~•ÑKû_¿…h…%øïø‡Ôi?Žòõlù3á7üãö\øAå=¯Â­'Zº�§×®gÕ<ÓêÑÌíàjú/áïÁü$‡Ëð§„|1á˜Â„ ¤éPY ƒªñÉü먢¸êâ«Uþ$ÛõgD(Ó‡Á‚Š+ âÄß|%ð̺ljµ­7BÒàá®on'²ŒŸ™�e'°®vÒWaV¬)AÔªÔbµmè—«7j¦¹¯XøcHžÿR¼µÓì-SÌžææeŠWÕ�ˆ{“_xßþ ««ü_ñ,ÞýŸ|ªxïUcê÷°<:}¶z>ÒTí?Þ•¢ VÑ?àš^?ý¥u[}oö…ø�¨êȯçGá½A¥±ì `"œp|¸òç¡ë\OÎí‡�7žËïÿ#ájqÂÆIÑáêÅIiÏðÑOΣÒVí#§øÇÿqð~�¯Â7ðËFÕþ+x®bc† &û&þŸë–“Ý£)Æ+�²ßíûm‘?ÅŸ§ÃoÜr|5 ÿ¯™ðɵˆäcýl’`ÿ¯­> þÏÞ ýŸ|?ý™àßiš ±HmâýõÆ:%9y»15ØÑõZ•5ÄJþKEþlŸõK™ûÜEŠs‹ÿ—4¯N—¤�ý¥O›Kû§�~Îß°wÂÿÙ‚(¤ðφ­ßV�@:½ÿúUûŸQ# Gžâ0ƒÚ½†Š+®ãËd}ž_–á0VN4àºE$¿¢Š*ÎÒ¶¯¤ZxƒKžÊþÖÞöÊê3ö÷ "™YH ƒÜø?ö¿ÿƒxþ þЗSk^[¯„~,ÝçEq¡¦í4ÊC5¡ &1ä4XëÍ}óErâ°T11å¯ÿ®�t{¹æ¹5W[,¯*mî“÷eå(»ÆKÊI£ðÓ⇀¿nø%ô’K«_ÝüMøyhÙþÙ�â;{h÷å¤�KE|„)2DsÎ}‡öRÿ‚™j_´„PÛi~Ñ|gª°ÉÓ|3â+kMoÙ´íQ­CÔ­¥ÍÞ>bÇm~µWÈß¶GüGàoíƒq6­&‚Þñ‹7œšÿ†vÙNÒç!åˆ*SœÅ|Î8q^ \’¥-h>xÿ,´)/ý¹3êgšp®{¦w…ú¥gÿ/°ëÜo¼è6—›tÜ_÷O.Õm�ø ìñÌÚÿÂéAÚÿðœè7ž·ý›«¨ÒÚQþÔRºõçŠô|DðÿÄ�/íÞ×4}~˃ö�6ö;¨¹éó!#šð ­¿n/ø%T$¤�~Ô? ¬(âF×, ÷Õœ[˜ÕGE®Ëölý¢ÿaïø)޵ Z�Ãχþø™3ßO×tK]7[2“ó%½ôaZ\°û© s·,‚¹aC 9û)ÉÓŸi-ýÏäx™Ç…˜êif9eHâðËþ^R÷¹ëä4�7ßš6óg³QZ—ÿðJOY/üRþ.øËà¶ê>ÃãÝGQ‰݇R’êÊ€wÆI'ûþ Ýñ/Eñ ý¢uËÌ}ßøK<¥ê9ÿ{ì+aŸÃ¿‰du—ÂÓ?ês…ÿ�ÅÆßð?�Cÿ w¡ÿБñÿÿ —Œ¿ùWQõ‡âÄ}PìÏÙÇãn£¸nPߨ:~åþ÷úf©;|­†ÿgƒVm®¾;ëM‹?Ù×Ä:vï˜sÅú¸Qèßeºº!½€#üÙÀ6²ìKû ¥…ªþÉÜQ\Ä?iýtüžø% BÝ$¼ñî£}:ö9†=!Übc�;IãR×ö1ý¡|´û?€üà¯ÀT)�@ÐítÔ t…b»Zí§‘Ò_›ü ã—A|NçÆìíûEüR®ŸáŸÂ->L`Ln<[«ïº46vÐIé‰nTpyÎÑÛøoþ cà M–ãâf©â¯Œ÷™ÜÐø®õN�î¿Ù6Ë ƒ¯açC+�Ás–'éŠ+Ñ£‚¡Kà‰Õ á𢦃 Xø[EµÓtË+M;N±‰`¶µµ…a†Þ5TDPT€ ·EÔlQEQEQEQEQEQEQEQEQEQEQEQEQEQEðGí_ñk_ÿ‚ˆü{ÿ…ðÒõíü¤L$ñ–¿0¸Fæ%#ï*°Àý䃲!cÝÿÁFk=rßY°ø#ð·}ïÄŸ�̶탢ڸ勸ì™;¿åœaŸ‚Pײ~ÆŸ²f‡ûü³ðΗ²çP—¶£³¨Ü‘†oP‹÷Q{(îKõ™|!•aã™VW«/áEôÿ§�y}Žï]‘âbe,mW„¦ýÅñ¾ÿÝ_ûwe¡Ù|øG ü øo¥xSÃVKc£éˆ¡AË9êÎçø�˜–fîI®šŠ+åªT�Iº“woVû³ÙŒTb£d‚¼ÏöÁý¨4Ø×öpñWÄo0k/Ù™!¶µïî[ä‚Ý:üÒHʹǖ<^™_�ŸðW?Ú;Ný²¿l�‡SÜK/Á/ÙÆÒ_ü@–Þ]‹ª^Ä-Š¿O1žHìÓœ‰.g8":ó³ DéÓP¢¯RmF+¼žßvìû. È(fx÷+ÿ‚Š~ÛÞðçŠ.eŸÄß/¿á:ñÕÒpÚVŽO� ¼{á*è¹+‹›C_«ðPëhþ?þÒÿgM×GŠXõmja²;KXÕ•Âì�& týäUÄÁ¾Ïá߃^:ý¥|}6~&øµwq}Ï—�£Âìß»_ùgȬBŽ<¨ ǯDÿ‚[h÷?þ5|Tøû«ÀáüI¨6‘¢ Gͪmf×¶Ñ‚?盎æº3,<0Ôèå]Ô~'Ýï9|Þ‡ä~+q~*Íiáj®_®ÔMÅmO FÏ‘vVP‚ïÌü϶ll¡Ó,¡¶·‰!‚Ý(£A…�T`;KE±õ‰$¬‚Š( Š( Š( ¾ÿ‚cÊ6?g¯û&žÿÓ]µ}Ë_ Á1¿å³×ý“O ÿé®Ú½»âg.+d{�Q^¹ÂQEQE|Eyÿ+YÙ¾Éÿ©¬_ø(¯‰,~9ÁL¿d¿†¾»ƒQñwƒ|Quã�%«‰AÒíí¶“s·˜„îá6=F}Óöªÿ‚Yü ýµþ&XøÇâW‚î5ÿéšré×ÐxƒSÓZ;U‘åíµ¸‰OÏ#œ�O=p:¿Ù[öøGûøzçMøYà=Â_“Û#K{|±Q=̬óÍ´³cÌvÆãŒf°£ (Ó�M©ÉËN¯ÚJ¤{ZÍ«ï{5ÖëjóŒ�G·EìÔ%ë¢vÛtÞÖx/öR»ý§ÿà²?¶ vßþ.|0M·ƒÃŸêöÚ{j"])ÿ×™­æ'fÏ�®Üo~¹ô_ø"ÝÝßÁoˆ´ìòom¼Q¤üñ5´ö^+[Xá¼ÖΧ ÜJ—ïË=ì.…$˜üïòîW±ükÿ‚H|ý¡~2ë¦Û.î“8&'�qÚÅHÁ”f½Söwÿ‚Nü4ø5|ºÏˆbŸâ'ŠÝ¼éõty°™O,é%y<æC#Èjú~RÚŽ5T�UU @:²ÄñofðøªûGââžïð^EÒÊ¡Ííq/ÚO»ÙzGeùù�†%·‰QQUQ€ tS¨¢¾tõBŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¼Óö£ý¥£ý•ü gâKÏ|@ñ–’÷«m¨7„´�«\èÐf7s[+‰ÞÚy +�ÀìÆHßøïñNÓà‡Áox¾öóA°·ðæ•q¨yúÞ¤4Ý9Z8Ë žä«y1– íb ä+‹ÿÁ4?à¡Iÿ ømâ=jëÂòü>×4 N;;¯ êw.uÝ&'¶†X¤½�âŒÅç•á+¹d‡ËmÁËÇÌú—ü(ÏÚ»ö—Õà´×´ˆ_jÑWé·å%ð‡�t»oôyAfÓï.¬-ÆÂÁ%Y´¥̸>Åðþ ¦k~<Ó¼GñÓ¾ ñÇÄo…¾ ‚ëÁŸ¬7éþ ñ¬*­m6ª°ÃÜÄKDáÚH¥Úd›Êש|uÿ‚wü ø¹ñ_Gø¥âïhvÞ.ð�ý¶¸ž#¶žM*ᤳ‘g…îå�ãûLql^°¾h´½9[“ H›p¾±Û”÷Üýø]ð—Ã|káÏøHðÎ…d1Ž›j–Ð!îÛTXã–<“É$×oûþžËîŠý_àŽßÕþâüÉœ^ÿ‚&ü^ý´uëo~×õ�v%q<^ðÜÂ++SØnذÆq…o*Oüõ=kîÙ«öøIû éKmðëÀ^ðÜ�<·½ŠßÍ¿�qŒIu&éœ{3‘Éãšõz+›˜W¬¹$íËE÷#ZXZtß2Z÷z°¢Š+ˆè (¢€ (®_âׯ¿ ü ð»ë>/×ôÝN\…’ê\4ÄrV4yý”ûRrQW–ÆUñ¨Su«ÉF+VÛ²KͽÔW%ñ‡ã¿ƒþødêþ1ñ� Øó±®dýääuXãyÙšù#Ä_ðQ�‰µv·qáÿÙÛÀ×rZ#˜gñ^³H-¿ÚUoÝ¡î7—b?å–k¡øAÿšÓµ?|kñ.£ñGųaÞæuÓ #¢`�òªž€ìLqå⸾·*šaã7¢ÿƒò> \e‰Ì¤èðÍmÓÚÎð¢½ÅRÝ ­ýã_ÿ‚“|Eý¨5‰ôÙãáõõì*þLÞ'Ö¢ mmêʤˆÔŽ£Ìvb?å–kGáçü~ãâ?‰añWÇÏj¿uï¼4èn->Û<˜Ã|®S?É}�}‰ ø~ú=¾Ÿ¦YZiÖˆ#‚ÚÖ†TtUEì\¡`¹Ÿ6!ó>Ý>ïó4¥ÀË5ˆâï%ªƒ÷hÅùSZ?Y¹6cøáþ‡ðËÃphþÒ4íK¶ŽÖÊÝa‰}N “ÜžOzØ¢ŠîI%d}Ý:p§NšI-’Ñ ¢Š(,(¢Š(¢Š(¢Š(¢Š+æ?Ûcþ ðSöéIï|KáÁ¢ø®Q•ñ&‡¶ÏQ-ØÊv”Ÿ·úÕbBµôå�|=*ðöu¢¤¼ÏO)Îqù^%bòêÒ¥Qu‹iúy®éèúŸ–ÿðˆ~ÚðIo›C»ÿ†œø=aÿ.3‰¹¦À; Ëθ°ÜF gbWÓ_±üGàÏí¹q�a«Éàÿ3yRxkÄmnÞ\ट.sœáPù˜(µõu|¹ûrÁ þ ~ݱϨëš!ð猙I‹Äº[kâýŒÃ.qþ°À²õ¯+êX¬.¸9óGù$ÿô™n¾wGÞ®'ȳßs‰0þƳÿ˜ŠI·Þ­!>íÃ’^§ÔtWåQñßí}ÿi*§B[Ý÷ú®ž8Ì„“•ÉÌBÉ“�Ùâºp¹�:³ö3N?•ïòèסãgÜ �Àaÿ´p’Ž'ö«KX¯)¯Šœ¼¦—“g¹QEéQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEá··í›cû|%û\)¡âýtµ¦�¦`±¸›€de˜Ór“�¼J¨ ¶G©|Yø©¢|øs«x«Äw‹c£è¶æââSÉ8à"�âvbW¹`;ׯ_°÷½sößý .¿h¯ˆ–m“i1·ðN‘/ͺFÄ,ÀŒç üR—| «_A’à(¸Ë0ƯÜÓéüòéë¼»#ËÌ13MapÿÄ—þJºÉþ�ÙéßðNoØÊûàž�¨|@ñë¾¥ñSÇÝjW|š|rþ@=œœÇG “õ W™˜æ±¸‰b+nþäº%Ù%¢;0¸haé*Tö_�›óaEWÐ|×ÿ[ýº-¿`Ù [ñDÄÞ,Õ¿âSá›VÃ4×Ò©Ä›{¤JFìvªõa_“º�ì™­¥—ÁÏÙ: ¯¿ádümÕ`ø�ñcP}Å…¯ïÞÖV9É‚´\:¹ægCÎ寠õωúGü›þ SâÏŠþ&¸2~βE”×°9ù­õ{ès!uRpæIa/Ç µº‘ûÚôŸø ïÂÍcã÷޾*~ÖÞ7‚A⊺¤úv�¿7ØôÈä]á9MÑÅž YŸïV9 U±3yü¯~rz9]™ú/ÂIC…)éˆÄrÖÄ÷J×¥Eÿ…>y/æ’ì{wüÿÇvŸ³wìQ¦|9ð�·Ù.+~È^°ðïÃ�;Ã_¾h6Ée£hך�Ñ|A¡ÙÆ¡b´ŽfY-oR5ÊmYQT3ÈÀ»}ƒEiN¬©»Å“()+Hø²ûö¾ƒÁ§Žþüaø*ÿ¬{ÿ\ê¶Qz—½Òþ×h€yæö«>ýºþ øúõí4Ÿ‹îïâÀ–Äx‚Õ/ ' ’q"èÊ#־ʬ/ü/ðÏÄû%¶ñ/‡t/Û¦vÅ©ØEv‹ž¸YŠìŽc5º0xXô<‡NÔíõ‹$¹´¸†êÞLì–�ƒƒ‚8<ƒSÔ:¿ü×öoÕï$¹~iײýûÍ+öÚeÓ�0 šÝ#��w±Y²Á*> FÐ-~#è#²hŸ¼O¥F=ËmB4Ú;)8šÕf+¬Hú«èÍŠ+Oø&/†aÿ�‰?´üýð<{{uæzÇÉ—n9û¸ÎyÎ!ø&»ÄØ´øóñòÊ÷"®—qåÿÀ§Óäv矙�§L ¿ívdýV]ΖŠå¿áÝž*ÿ£¤øÿÿ‚ßÿó?Gü;³Å_ôtŸÿð[à¿þgéÿhSìÿ¯˜¾«3©¢¹oøwgŠ¿èé>?ÿà·ÁüÏÐ?à�~'nö¡øþè~òýƒÁ«¸w]0ú‚¡hSìÿ¯˜}VgSEsQÿÁ5D͋ߎŸïáÿžgYÓ­¹õÝocþ±Ï#¥Oü ÂWþ&?¾:êg8Ë|DÔ¬þ_îÿ¢Éõù¾÷?{�…ý£ÌU—sz©ë~ °ðÍ�ºÔ¯m4ûe83\̱F:Ÿ¼Ä€þUR/ø%?ÁW?éú_޵ÿm{â7ˆõ�G`EÝüƒ¨ä`óZþÿ‚aþÎ^¿[Ëo�_ ¤Ô`_]xZÊîð<ùci1‘œnë“Ôš‡™.‘+ê½Ùä^%ý¾þ xOSûßÅ>¨FWN´Öíï/ܳo4­Ôt^¤â–Çö¨¿ñéÙà„Ÿ%||ñ‡}ñ]|)á/ è:½‡ˆ-¼+¡Ý˪__ÞØÝEyd÷wî�¤iÌÊ`‚Üñ 3”’}SEÅ:’›æ‘¼b¢¬‚Š(¨((¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢�¶Ñ“ÀMcüC×uO ø[Ô´MOë:}„÷6Dw1Ú¾©:FͺË'É‘€PÍò®ìž|uûRÿÁRt›ÏØ›Â_ü¯k~ ðþ¹âè|-ã=_û^ëßCrg‚[ #‘Rÿí�ÛXªÍ‘¬—ÑɲTÛ»˜ý¤~0üeøaûAøkÀÚ—ÅwÑþügñd’hß´›}<ê^ßÃ/†Ù¥‚[E3]ÖòHØ”óalËåÈy¿Ù‹öpñŸÄ?ÚÛâ•�þ¿àïj>Öí¼ñ^KÝ*?ì/‹ºÍ‚\[Myg˜!׬£”BåPG,eU¤ˆ#ú_öøÉãitŸøWŸ¼E¿ñ^ÖÒoËiðÃ}¤è“\˜ôä՚Р4Ô]U÷,#c¾Z²Ädo¤«…ø û3ü<ý•ü!6‡ðëÁ¾ðV‘<¿hžßI²Ke¸�(_2B£.ÁUWs@P:_6~ÓðQí_ÆÞ;…ß³æŸÿ ‡�® Eu­B«-†’Ã2±ýÛ•Ï21ò”à|äàz9nWˆÇTöt‹VÞ‘Šï'ÑHåÅã)a¡ÍQï²[·Ù.§³þ×·O�¿cÏïׯ>ݯÜǺÇC³`×—yà?åœyçn88 F+æ­öcøÁÿ)×m|IñŽþóÀ_ Ä‚ãNð�“®.”Ȭ>S�ùi(/×j `kÕÿd/ø&†�ðc]7ñõû|@ø�xÿi¸Ôï™§‚ÊSƒ˜CòÎüµ›�´'"¾¤¯iæX\±{<¯ß«Ö«[×´öÿ×µ�=a+c=ìg»�_ûsëè´õ9¿…?|5ð;Á–Þðž�e¢i6£ä‚Ý1¼÷wcó;žìıîk¤¢Šùz•'RNuÛݽÏb1ŒRŒU’ (¢  ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ªÚαkáÝëP¾¸†ÒÆÆ¸¸žg ¢–gbxI>Õã_·ÿí[qûü‹ÄvèmªëZî™á­>ë]ºkML¸¾ºKt»¿™yŽÚ-å›± €©pÃáÿ�áñÇíÁá|:ý¡¼ ñ›ÇÑl¤ñWÀÿ øŠ{;oÜY¥ïÚîlßM\ÅâHôö,²y ç³’FÒþÔÿµÎƒÿ“¿n%ñÀ_ø¶m?Ç õ_ÏnÚGŽÚÖàÍim¨ØÅ1’8®$v×KŽ#ï¡h‡AûWx›à�ßÂüsý§üâ…4&“@¾Ñ4½râÚïŦÚmæÁ~Ë,cUÓeI¢óÇ–�qéüÅûh~Ó~‡Äþ$ñÖ¥ÿ ~³áω~$Ó5m'à÷мÚN¹­jZfŸmii ¸–O´[èÑ\ ˆ�ªÉ%å½Ç’í»7¼~ÅßðJ¯~Õ ý ¿ké¿·üUq¶}Àó/üKô(sº5ž•s‘oÈ-)wfÒÃ`â¡õŒK´:w—§êÎJµåÍì¨ë/Ázÿ‘Âiþý¡ÿà¾:´:�ŠäÔ~~̦Q-®™nÄj>'ˆ6åc�<ìà#¨…NÒ‰++5~Žþ˱ÿïØËáä~øuá‹ØaM̱®û­B@1æ\Lrò¿^Xàg�^–ˆ"@ªªŒµž':±öq\°[EmóîüÙtpу疲îÿ­Š(® lÓ-¼Lîʈ€³3©&¾Ný½1ñìóã/øçÄŸõ%»¾Ñô›»}A5¿°Ïº÷B¸Q¸Æ÷PÇ<+÷Nç��+œüaá?ø'·Ãï‡^:ð7ÄŸ„ú‹®þ|c“øwÅ®'Ó¼cð®{ò¯ ï·Ë^ènòö×)2س7ÈaÜ#ú{þ íûxêþ"ºð—Ã_Šz�¾±¯ø·DM{áߎí­Å¾›ñSG0,âeUÊ[êpÂÊnm3ØËc$'Ù5â¿°_ì'ìGû:éß'ñŽ¥ãØ4]Jþ÷NÔu;-®-aº¸’qX@Œ2È£bª…!UU@Qí,Á’@d“Ú€³Ÿ¡éV£÷vÖpˆ�ìqÕ�v9'¹5¹J8>wψ|Ï·Eòÿ20üñ•/‰*ýf¢ÕBÖ£ðÓûOûÓæoÈ¥áß éþÑ-´Í&ÆÏLÓ¬ÐGokk à *:*¢€ú »EݶˆûÈÆ1ŠŒU’ (¢‚‚Š( Š( Š( Š( Š( Š( Š( Š( #"¾ý¶à‡¾ øãâqãÿ„Ú”ßþ,ØJníum µµ�Ôü�eŠ"¦7'¬°á¾f,²p+îj+›ƒ£‰‡%hÝ~^�¡îdI™d¸Ÿ­eµ\%³ê¤»J.ñ’òi£ó#áüÛâ—ì'ã놿¶G„n¬gò4߈ZM·›c¨¨Çï%XÆÙKB®T< ’ÕúCà_h¿ü#a¯øsVÓõÍTˆOgcp³ÛÜ¡èÈêH"³¾0|ð¯íðþÿ¾4Ð4ßxSM·WЉ#oFÕÇUu!”ò5ù­ñ ö øëÿuñN¡ãÏÙ£RÔ> ü%–Sy®|<ÔÝ®gµOãxåðE‰€ fUc^g6+ñÞ­.ÿn>¿Ì¼÷õ>éPÈx«L2ŽþÍ퇪ÿºßðdúEÞ›Ñ'ŸªWΟðOïø)ÏÃOø(‚ÍÏ…¯Ž™â‹ƒêÞ¿u]CO<Ê:MHÄ©Ç*#£èºõèW§Z ¥'x¾§çY®SŒË1SÁfÝ:�ÑÆJÏþ }Ñ­V�\7…¿iŸ‡Þ8øÙâ†ú7Œ¼;ªxó–‘_k:­êK{¦C#F•’™8È<�èH×>ûm|.ñÏÅ]&ÖÖËâ²|$øWceu}ãmWJ‡ËñðF¡„V·®Lv0ùbV–p�2„QBK�Ï?ø%gÅ�Ã^+ñŽ©û7|%µñ‡Žþ"_ôáìt†ž…‰²]gSd–FÔî÷˨Mnž}ÓÉv¢]žP5©ç°´QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE�E|�ÿ:ý¨5} ÃJø7ðû}×Ä_ˆÄZbÃéÖnJ³“ü ø`ð¢ÈÙS]ùf]S‰Ž–—ݽ’[·ä–§63 ='V}:w}õ<Óâö½}ÿYý¬“áæƒu<_þ\‹�Q·bU¸—j7C’#öÉÏÊ+ïx~ËÂz –—¦ÚÃe§iÐ%µµ¼+¶8#E ¨£°^yû þÌZGì‘ð7KðŽ—²iâhÔ¯Báµ ¶Ì”ûpGdUã5éõÝ�æ4ëJ8\&”)i>ó~rßÉYÙvtÓ­_Z“ÕùvŠò_ðBŠ(¯ô‚¾&ÿ‚ä~ÚWß³oìÅ�¼f¸ø™ñŠsá½ ÚÛ›ˆâ�¬sÌ£9݉$#‘$ÊGÝ5ö†µ¬ÚxsG»Ô/î`³±°…î.n&p‘Á)gvcÀP$ž€Wå§ì0ÿ‚“ÿÁB¼}ûZxÄý“áwÂ…—GðJ_|�À!��®X�åÄï;gîÉr¸?ºÀòsZ³qŽ�ÇSOEöŸÉmæÑú‡ùn5ªçù”o†Á¥6žÓ¨Ý©Sÿ·¥¬¿¹\óÛ7à]ÏìÉû%üý†¾Ím'Ä_ŒzŒ¯Œî¢ç È¥ä�Œ–?”õò´ã‘Ï?¨Ùð×ìû¼ZMº[øῇÖ×O‚CÌíáVNwùBFTß�»Ž3šð=wâo‚à¡ uo jz¿…ü«xŒi6“Ý_ÚYŸˆPØ_¾µ·ˆJn ¤’ÛËm&䉥�ŸncpÍñü|ñÃxzëÄZWŠ~ x'àWį Sá?ŽzöÉü/©Éví�«ªÌóMá«ùøˆ]mx¼äŠ@Ÿ¹xý·ÀŸðO� öÀ±ÑXî’F<–cIûMþÔÞý“~Ëâ_y*ÙK+(°×ZŒ gˉ3Ïl±Â®rH¯‘~üøƒÿHñ•Ÿ�>.}³Âÿ ídûFƒá[y6¿Oáw<¬:Ê@fì¤÷rÌ™V¦ñ˜É{: íu“þX.¯ð[³ÍÅãù'ì(.j�§Eç'Ñ~/ xŸâ×ÄÏø+‰.ü3ð÷íÞø1m)·Õ|Cq[�`jË%l`æFä%}uû4þÊÞ ý“ü º„4Å·¯/¦ÃÞj.?ŽY03ÔáFrv�ší|'á=3Àž²Ñôk ]/JÓ¢ÛZ[D#ЍàV�–tëSú¦>΂Ú+«þi¿´ÿÐxL½S—·¬ùª>½¼¢º/ϨQEáˆQEQEQEQEQEQEQEQEQEQEQEV/Ä‹ôÒ¾ë×Rë‘øb;m:âWÖd•ÒBÄÄÜ‘(1â¹Ó4Œ:DaOöžŸk ù{m¬¶ô/ +0ÿ‚¸øûIý†Óá­ìÃ_ x#áþ²¾!ð…<=lÑë>&×ÒÞX^(áŽ>´‡•¢idŸǘ·á°ü!à½[âGì¯ðÃâÆßxGÄŸ³‡ÀyÇŒ<;¯Åe©è^=Õ.¬íî´û[-FÑ¥­Õ¼Òì�Á&ë‹‹dRˆ¬á›ÿÉøâø)¯íKyû_|e°òô+9Ú×ῇfùà´Š'`·# ±Û[<ÆI0»>Ž ž"¿ÁÅôŠý{#—VIªTþ'ø.ç¡ÿÁ)à™úþ�ã+�Ú?ö‚i¼Cñ¿Æ o¬í¯Æááx]p€FxIöaBŽ!L"€wWè=W6+:õ=¤þK¢]—‘­1¥XÿÃ…Q\æ¡^5ñ[öðøðkö˜ðŸÂf]|x÷ÇQÃ7‡ì-ôk™£Õci$IÞ9ÂùXµHÌ×aåDÈÇ;ÔÚ[öËøaûiuïįižƒUi ¸Y%’eˆš_.5gDL’�#Œ2—eÈÏÄ7Àÿ|fýª>!øcö…žÒoü_Ômµï‚ÿ<9s$6ÃK²³…¬íô[†’Qc¨[H³ÝÉ 6Û‘rïûøÃ…úÏö†ÿ‚oü2ý ¼r¾5[=WÀ_`M–þ9ðeéÑ|AI£.£ÇUÒMü•7üÓö;Õ?aŸ€·žÔü{{ñKâK[µ½¸Ò ÓŒW— pÖ©'ËTY^Wù®é[jF�Qz/Ùsá‡Äo„¾ Ô£ø¥ñN/ŠÔ÷"H¯áðì µ�ºD¨ª!�ä%Ø«I#™6—s±#P¼+ö„ÿ‚“j~4ñÛ|6ýŸt¡ãLLw “MÒÆpά~GÚHýã(qËçhƵxRWŸüèxyï`rŠ*®2ZÉÚ1Jó›þXEjßິ�wý§l~É^þÑñf¨±ÝL…¬ôË|I}~Güó�#åÏWbw9À¯•#ðÿÇ�ø*!jÒMð‡àõ×̶‘îûv³ äd­ #¶È°A äW¥þÌ_ðLkø«þÏ‹:£|Iø‹tÂw–õŒÖ6ÛËWÿXËÙ˜\ ¨¸¾°®_eV¾µ½Øÿ*ý_è�”Y>mÄþtÝ 3Ú„%ïIÓé®ÿÉ—vÚ<ßöpý“< û*xcû7ÁÚ,V’J¡nµ ±-õñ唌‘žvŒ $áEzEWt!.X«#ïpX,>„pØX(B:$•’ù ¢Š*Ž ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(á?ø(gü§OøÝâïø[?5Cð·ã†—)¾‚þÂCkg­L2Ož~îVÉ̪íÌ$W •«ÿñÿ‚¾]|@ø†~ þÐzOü+�ŽL¢ÉEÜbÚËÄOü&3�©3ðB‚c— ÄNàƒïjù³þ =ÿÌðGüKá§Ù5d]ƺLe´[D>Õ§È2ÊŽF �ûÑ“ÆIR­ÍxØŒJ3xœ’{ÇìËü¥ç÷Ÿ¦dü[„Ìp°É8ªó¥)ÖZÕ¡úΗzoT¾šH÷�‰?´O‹ÿ5ß ø—NƒWðùô­RÆ|ùw–³ÆÑK`ƒ†F`pAç­~g|VÑþþÍ|uà_Šþ-ñŸÁßx4ûOƒÿü ªjž‡[·’Æ%Ôì×KhçÕu6¿{¨ lÒy~J3!ó··Wÿÿÿ‚�øãöuøÛìÏûS¦øÖЭ·…ü]q.ë_ÂNÈVIˆÙñ„˜à¹ù$ (;þæý§4ìõã+O…7º›ñçIž/]ëAÍ…­ë!É.Õc…'?u†@Ê‘‘]Ø,m[‰¸c‘â–iFKš‹¼*Aí(>©ýéèõ<Ïþ «ûHê_¿g}/Iñ…íÅÏÅOØXØxâ ­„siúŒ¶É:ÃpÑ�#í~KÄÓÅ")$*Bð+èzüÔý”o|sûkoÁõø±ðƒQñ‚`moTøUàÛu¾ñwŽ.fSsu5Ö­ª]B$¼œ;ܶ- f\R fm9$\fؾ6²©û¾b… ?†%AŸ�…qÝÿà¨?·œ>·c?Â/„3yÚ¬ˆs«w’¥r8`ì†5?óÎ9˜ž¾ü†¶…c�U#@UF�Ð_WŠÿ„¼ÂGøÕ’sïozËâ—•‘âÑÿlÄûwü:nÑó—Y|¶_6:Š(¯”= ¢ŠÅø�ñ HøMðÿZñF¿yŸ¢xzÆmFþæO» 1!wo~ã½&ÒWeÓ§:“Té«·¢KvßCàßø/Oí=­ÂáOÙÃáë™þ"|p»ŠÂh£$5¶˜òym¸�º³H6éåÇ>}k†ÿ‚ŸAìÿÔøsû,|0çÆå‡Âð­¸Û=ú;¡¾¸aÉÍÄÒ¬X?Ã;€p˜«?ðG‡úÏíËûYüDý±?øF¼Ç-Í×΂tì܉údyöÿݰáŠjµz™Åeî¥î¯$ýßü Zú ø�Z9N Á˜W­7Ï]¯µ^KÞ^j”=ÅçÌ}ñûþÍzgìû1x/áÆ“±íü-¦¥´Ó(ÇÚî[/q?ý´™¤|vÝŽÕó/ìR?á­?à¡¿~0ËûýÂ_ñOxuÏ(x1ùˆë’ÈÄÓÝ{·üWãÏü3Çì‹â½f ¼�Rþì�0ƒ†@ËOûgPÿÁ7>ÿÃ>~ÈÒç‡ÉÕuX¶u Fϸ·ûI—ÿ®už&r¯ŠJZÛÞ~½?V<æñY�á2¸ÿ ½¼ûs|—ª|Òÿ·QîÔQEv¡Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@W�þÑÿ5„^Ñu-:ëÃéæjÉÍ�þæ½ÖàO+Ùi‘«§›¨Éå*2H;_"€= �£ÿ­_~ËŸðT­oö‰ø»£#ø3ß |Gá¹¼X5Ë?‹ïéØ e.¹ –ñÁh÷©¹£‰.$‘6>å*�"ð>8ý·>Ý~Ö>øáã]\¹ýžõÿÛ¯�±Àù•Qû¸AF8Ú àã öãÿ‚�ZþÎ÷þ ðm—ü%¿u°ØiêfLÿrI•yÉÎV1Ëu;W“�ûÿÁ>î¼â¹>)|Z½ÿ„·âÆ®ßi/;‰¡Ñ ,Âe�Ëò cÀ›ép9]Va™üà†Ò©þPï/¸òq8Ê•j<6âë.‘ÿ9v_yÊþÌ¿°G‰~9|B‡â÷í'öLjn—KðÄ«þ‰¤Gœ –>@ÇQ g.YÉíuFpíKEy™žk_QN®‰i­#Ù.Ÿ¯S¯ ƒ§†�,7{·»}Û (¢¼Ó¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¦O:ZÀòHëq©gv8U’Iì+âoÚö‡ñoÇßÛwDø;á_ŠßÀí&÷Ãsëþñ^›a¥j¶ÿ¯ÐÆÞÚK…ž'ŠÐy¦{P"¸“åeaÜ@6?à¯1ø§@ø9}â¹<âxÚ|p*økÄáýF÷ÄÚ…­ŽŸ5åø�¾Ï¦[4þdØ$ÌÒ#¤[‘ÿ‚y|?ñí5⻉5Ö"»ñ÷ÂMNãÁðüSðdöÖúOÆm"-é,WV¿¼"¹plsÄÍm*‡‘Üa/�·ß¶—ì¥vß´� jšžŸ«êþ ñ²ˆ]h#–Âî[)®­–@CÚÏåØÛ¶ñ’Å `ý¸õ}_]™…x¶¨Qø!¢ó}_Ïò0ÂÓ’N¤þ)~—È(¢ŠóŽ ¯œ~%~Ô¾/øÑ៌žøáíZëÅ_­g±ÓÐî5ÛÏi¾—PMKLø{òKäéšl‚1!"£Œ² ažxƒvžÿ‚w~È~ ñ'ÆŸ éS|CðýÖ¯'�t­Eá×Ùø›Áò¶‹ñ�áçüL|-ªÛÉäÍ;F|ϱ´ƒn`6ÏîäÁÈV|øøü-HOë¸Uï­×ó®Þ«£ù£ð–}…Åa¿ÕœúVÃMÞ�G«ÃÔ{MÓ¹=*Gk{ÊÍkWö¦ÿ‚Sßü|øÑyyáï‰:ÏÃïøÛYµñ_�ì4«xN­w¬ØÇk�Þ™zÉæéÒ¼ëÒ£1) „Di%�û¯„%Õ5¨5ÿxá$ºð÷‰|#ªÊx¤RM½æ‰q,Qþì/ÙdÉ t³'£†ÄÓÄR�jN韞d¸¼£W-ÇG–¥7gÛɧÕ5fŸTÓ?Qh¯ý…¾>'í)û8èþ)²ÓüCƒ3=¦‡©ë[VïÅâ(u,a£K­¦Tel½‚·< ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯š¿à§¿µMÏìóð1to´’xçÇ’�#EŠf‹~I”uʆ ¸ç|‰èkéËÈ´ûIgžD†É$ŽÁV5$’xõðwì�cqÿýº|Cñ³TŠFðG€e:O„ �|’J¹Û&uW3áæ�䯡áü-'9ãñ*ô¨®f¿š_f?7¿’g—šVŸ,pÔ_¿SEä¾Óù/ÆÇÑÿ°ì­mû"~Ï_‡ Æúå×ú~µp¸>uÛ�¸ÝP‹ì™êM{5W�‹ÅUÄÖ–"³¼¤îÎúaFš¥MY-Š(®sP¯Í_ø.wÆ?~пþþÈŸæ â/‰—�ßø’TäXéñ¹xĘ?s÷R\8ë¶Ù1�üýÿñËã.…û<|ñ�üMt,ô/ XK¨]ÈHÉT õvl*¯ve­|ÿ/ø1®~Ð>!þ×_¢fñ'ÄÛë‹Ã.XXiÉ W1“ü9�-Ó¡ lÝC׋šÉÖ”pÞ”ÿ~ËÔý7Ãú4òêUø³“ŽÊ’{O+û5æ©ëR_á]ÏCÿ‚¢|^Ñ?à–ŸðJ¿øC<­§ê:…Œ~ð¬1nÊŒ&¹Èù¼ÅˆM&þó2g–¯^ÿ‚V~Ç1þñƒ|5ºCâmÿµ¼Bà Òj7<ªHἡ²{¬+_!x¼ÿÃÑ¿à»nŒ¿éß ¿e¸MÕá5½Þ°$RS=7}¥#R­Á]>_ïWéö­ªÛè:UÍõäÉoigÏ<®p±"‚ÌÇØM}n"1Ãaiác¥ýçÿ¶¯’׿~U,Lñ8š˜Úò»mêú½äÛó}Oˆÿo&ÿ†©ý¼¾|‹÷ú6‰'ü$>!AÊÁ-ý“þ¾…}È|GÿªÒn>9|[ø±ñãTŠA/еGÒ´�3ïCj…]—ÜÈý1a_nWÏ`}å*ïí?Áh�ˆà$ñtñô÷ÅÔr�ýz‡¹M}ÉËþÞ (¢»�¿ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (®'Ç¿´'…þüMð¯ƒu »É|Qã6”éšuŽŸq{7“Á-ÌÞJ0‚ÙHÕ§”¤a¥Eݹ€ ý¡~5Ø~Î?¼Kã­WOÖuM3ÂÖ/¨]ÛéVoyvaLoe‰f¹vÚ ¬@8Å~v~Ñ>4ý©h�Ø"߯×>ðOÅ'ź~ŸñÁ³ü9y_ø{ªÂÑßé CpÛukxäH’Y hgeyÕ`�+â7Ĺ>ÿÁLu›ßŠšÿÄ/xÃÁž&¸ñ…6ŸªjWWtYEÒü9¡éq±±–żÝMö�ÄË›ˆþûý‹?eëÙ³ÂúÄúE¾«á]+ÆóCâð#ÝÅu¥ø'Pž=÷Öö,ˆ6Å$ìÎÈ”$ÞѪ  Ÿ²‡ìë¦xOź—Ž.Çľ¾øÁ£Øk(ð,ïÓìõ†‰^[¶‡ih¯¶‘ Æ7 )ˆ3+8ß^ûkþÞš¶…ãˆþ|³ÿ„“âž®|‰e„,�è@Œ–b~_4/Ìw|±Ž[û¦‡í«ûtkºÏ�GÁo‚ÿmüDÕw[ꕳƒ€½÷Dª3¹�{¿ éß°÷ì/ þÇ^ ‘·�kÆšÂÖµ¹Ai.�Æ8Ër±ü\�ÍÎú¬&†_F8ìÆ<Ò–´é¾¿ÞŸhv[ËÐñ«â*bª<6Ù/Š}¼£ýï>ž¦Wì;ûiŸ²í¼þ$×îÿá*ø›­î—T×. ”ÄÏËÇo˜OÌçæ~øQô]W�ŽÇׯVuñ¼ŸõeÙ.ˆô°Øjxzj•%d¿¯¼(¢Šä7 (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ äüoñËÂ?¼oá¿ k>#Ñ´ïxÊI¢Ðt«‹´ŽïWxcó$ÆNæ ¸ÜØÀÜ œ°¬¯•?hÿÙâí×í_¨|VøGñ7Àþ ŸÅ³ðž½'Šü+&¹.�kgqusƘRæ�Ù®äóc›tNc‰ˆ% ñ×í¹«ÿÁR?fïXx{á^¹äü)ñVƒ7�þÜê–Òêþ6ÓRC.£io0Y¬Ø*´3ˆõÑmHx>þ¿oˆ´máoŠ<û;x·Ã¶×†•«øVëÂÑÙxÉ'`šŽ�ewoks ¾ô¸š(–œÄ36&«¿³—ìäÿ·ßÅŸˆÿð˜üY×ü|>Ý[è:Åÿ4^Ô5Û{¨õ_Í5‡ú=Õµ¼¢¹c’b¬Ñ»WèÞ�a•§Áka ´kaœ¹ £$’O©9 #ö_øS«| øáoëz®�®j² ¥h‘è¶·Q£0‰Å¤lÑÂÞ^Íâ)'6·úf ·LÓZLX5œk"zOÁo�z¿ü×öÒôê:®¿âÝbîêxþt»Ôü+¦K+}‹K¹ºI$7 mÄ2c»1#Ȩ¯]_Æ/‰§ö;ø?àÏük°ð/Ž¿hÏ[jðÞ±¢ZÉš¼ÈÏhÈ ÉA$ð©hÖRV6 ©­Øgö!Ö%ñ„¿þ2çXø—®¸»³´¹Mù>N‹(D œqW­'/cGâêû/óì�†ÏóÜ]l_öGo¬5yÍë1}_y¿±¿÷w£û"~ÂZçÄ_Âæøò¶¼k¨²Üiz%ÀͶ‡X÷Æx¹ùcéVÝ!%~Ì¢ŠÞ…Ò�,~o«õ=܃‡°™FØa“m»ÊOYNOyIõoðÙhQEl{�EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPå§üÇÁßðKOÛ·Â?µ�íf_xÂøhŸ´«a„¸2à´ûG•P¾Nž$“)¯·?lÙ·Mÿ‚‹~È× ²ñ¾­áÏøÍloΫ£GÀÔ¬D‘Ü&FI-çŒu# ­† ¥‘º¿ÚÏöxÒÿkÙ»Æ_õ€‚ÓÅZd¶i+.~Ë>7C8±Ê¨ãÝ+äŸø7—ö€Õ<}ûjÿ ¼LΞ*ø-­Káë˜$ÿ[ ©,ÐÿuÖâ!í¯Šú®9Ñ_[ÉyI|_z×ï?P̧ý½Â°Ì§®#ãJo¬¨ÎþÊOÎNŸ£�Ï(�ö”ð·ìݨx³ÇzG‹þ3ëšÿë�kHÒüGñ^{xÁô–š=_JÓ!Óç‚ÊÖïýa—&ÙŒqʱ–OÓŸ‡Þ3·ø�à-Ä6pÝ[ÚkÚ|Œ]GåÏsF²*È¿Âà0v9¯‰ÿà¥_²¯Ãï�oyñÒÛÂÖ¾$ñ]滣iú>…â-Eâð‰¯_ßÇbž%¾±@#3Çö¤2Ü8g+ 134µë_¡øÕû7üVÒí~+|KÐ~$xÄDï}â+�ËÃ+á½a.mb´µ‰SæÃv·2ª+ù’#Ú¨óÍ{GåçÒÔQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|�ÿløï¨ø+à¦�ðóÃçñÅ[±¢ÚÁ ~÷ìÌUeÇûåÒ.{Jüüµíÿ²ÇÀ+Ù�à/‡<c±Î•l>×:Œ}®åþi¥õùœ¶3ÑvŽÕòŸìà¿ðÛðS|H¹ÿJðŸÂ”6€ÌO>]Eìyç¨ß ¯»«ê3¯ö<%ª;¯~§ø¤´_öìmólñ²ÿßש�{|1ô[¿›ü�QEòç²Q\7í-û@h_²¿ÀoüAñ,†=ÂÖy2©îac…3Æù$dEÏñ8©œã¹ËD�ðØj¸ŠÐÃЋ”æÒInÛvIz³óÿþ Iñ#Wý³¿i¯†ß±ß�îfI|A}·ã[¸>a§Ù¦dD~ß$a® ·R-€9jú»öÏøßáïø&üÏ[Ö4K]:×Áš,Z7†,1•k¦Q¢c«Ä;÷*ŽOs_:ÁþëÞ6ƒÇŸµÄ8wxÛãMô¯¦—Úib\�€ò©$Šªªå�´$pÕÅÿÁV®¥ÿ‚ˆÁN>þË:l’Má� L¾+ñÇ”Çj/—¿ËrÊÂÔ•SýíAkŸ†0ŸZ®ñ¸�¥¯¥8ì¾{ú³îüLÇSËéÑál “†I5´ñ·µ—š‹÷#}”_sÜ?à‚ß²eÇìçûØø›^Kã_‹sÿÂW¬\NwNÑL3jŽÇæ?ºo4†ä=Ä‚»ïø+gƧøMûjÚu“·ö¿�çMÑ#åÙ%ÉŸ©%túȾµôÅ¥¤V±Áqà (#Ž8Ô*Æ `Ú¾"ø÷ÿaÿ\ð/�Óý'Ãß ¬ÿ·uDûÈ.NÉ@aЂßcB÷œzŠ×7ÅN·4þÔÝ—Ïü‘ø'ש‡É~¡…v«ˆq£ZŽÍü£Í/‘ô×ì�ðN?Ùßöoð�„"ÜiZz}´¨û÷R~òvÿ¿ŒøöÅz=UÂ*Q[#ëðX:XL=<-hB*)y%eøQTuQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@WÆ¿ðSOÛ[âÀ¯Øi?­tk:ƒê¾>ÑšÄj·‡tIò�ëVöÂênŒ/øµ�_Îè^"ÌúƵÿ¯‡_´�‡ZÿÄohÞ<×¼±§hºÄj7†BÂ0�³%ʰAŒ¹S·8®7öÄýм7ûB\Åâùlücsâßè÷Zuµ§‡|i}ácâ+Y ÊtÛ©ídB`ycCó}Óžv³†øïÄ ø�ögý˜´Ÿ ø§S{ýR+Ûûõµ¥Æ©ƒosw4ði�ÝÜþþâX¤HY~fg»Q|_öÇý·|Eñ/âOü)�ûKÆwÌÖúη~ãBŒq Y 럞Aþ¯î®d8VþÚÿ¶Wˆþ*üH~H×¾0Ô˜ÛëºÝ»‡IJ3µ”žA÷>êæCòû�ìgûxkö5øjºF�¢÷Y½ &¯«È˜ŸP”~{c\�©ž2IËÇêð¸J9eã±Ñæ©-iÓú\ÿ»Ù}¯CÅ­^¦2£Ãá�¢¾)í±óîúz•?bØ�ß±¯Ãÿ²YmÔüO©(}gZ‘?}{'R‹žV z.}ÎIÍ{eWÎâñ•±U¥ˆÄKšRÝž­ èÓT©+$QEs…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@6h–â&GUtpU•†CÔN¢€)x{Ú„´[}7J°³Ó4ëEÙ­¤ 0¯\*( £žÂ®Ñ_7ÁY?laû~Ã>1ñ�¬â]Â4o�Øcr FëêbQ$Øî!#½iF”ªÔTá»v"¤Ô"ç-‘ò?�Hÿ‚¦ÿÁtu/ø˜|,ý—à¶'‡¶¼ÕüÆ ã¶MÂÈá†A[¿½_©5ò?ü?ö8Øïöðå¶©�ÅÞ5?ð“kï Ì«5©Ž=s"5 “óù‡ø«ëŠì̪ÆU}�?† •|·7va„ƒPæ–òÕÿ^A^{ûUþÓžý�ÿgÿüIñµé±ðç…íMÅÌ� bBÇÀÀg‘‘l(.2@æ²þ=~Û_ ÿfOøoÃ>6ñhþ ñ�oì §]\M­È&†·• §qòP™6}»Ù~ý£¾1ê~?ðŸí3âO|qºðgÄ/„¾­ikðË[û!ð¶« Æ1amw¦K™­nSý!\È%»1ÂG—å·žuNüýµþ'è|àߎ_¼3àè¾,C,žÖü1â 5�?í‘Û½Óè÷­,´W‚Ú9dI43e U”+iü`ýž~ ~Ê*»å¥µúÎ_Ü‚÷¥ò]Nƒöø­þÖ?¥ý¢¾*[–î@|£J †ÂÝIòç …åŸ3—«)¯¸ª+;8të8­í⎠AQF¡R5T€KU‡ ©FËWÕ÷g_ ðý,£ ì"ùêIóT›ø§7ñIþ‹¢² (¢·>€(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+ó'á‚·ìWÿx£ÃQb ~Ñ>:Ô1tE½U’frzn3ZÞàÓÈõúm_™ßð^ÿ ö›ý–~9ǘ`ð§‹•ªÌ8ßnÒE8B{f$¼ð#^6uîR†%˹Eü¯gø3ô¯ ßÖs FK-±t*Ó·÷Ô}¥7ê§oSôƒÅÞÒ|á{ý^ÒôíoEÕ`{[í>þÙ.mo!q‡ŽHÜt`H*À‚+ó{ã-þ“ð{öç¾Ò~!|-ñŸÅŸhóA'ì÷ðïövÇšn“ogi »XC-½•Ä7O2Ëw| A”-Ž>Súe^+ÿý—çý°e­kÁ×,³ÝØjbÊåœXë‚Îò¦Ó®Â:1¶º˜$à ,¤ó�§Ù?5.~Í_´£ã ™<ñãÂÚWÆm&ÈjÚׇôoµý’ÚÒyXÀö³\Ǽ�£Š[ˆA�ÏWR#8Œzõ~Rø=üWû j:Äo‰v¶ö¿b°Õ¬>þÎ~Õ_]ÔôåÔE«ÝZI ,ЫÚ�¸�ìvHÛLŒ«•úûüÕ¿j_ÙƒÂ5¿Cá CÅò]¶Š—²]ɦ'�"¤3;à …EQ*l9Dˆ  @=JŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¼kþ ñÑ¿gÙ#Æ>!·Ÿìú£Y�?Me8qs9ò‘—Ý7>‘šöZøsþ ¹3üløíðGàÅ»3Åâdjº¤jOËnÊWÀô�ígþ^ß`á‰Ìi¯À½é†+™þ Ç�š×•,,¥‰è½^‹ó=þ }ð>~Æþ·’+Tñ_ÛÚ�# d¸UdSèV‘ꦾ„¦Ã [B±Æª‘  ª£@è§WŸ�ÆO‰©‰©¼ÛyÕ†¡cF;EX(¢Šä7 üÂÿ‚Âx»Rý¼¿m…ÿ²„îæŽÆK¸¼Eã««sÿvê¥Ñô ß.ÖážknõúûN~Ð:'ì­ðÅŸ¼Bût¯ éò^È�¶µË�–(Ÿã’FH×ý§ñgücös×5 xÓö—ø‚¢oüq½–îÕ�Hk=3Í,ʬ²UzyPA޵âæ�ל0ûZËü+ÿ’z}çéÜå8\O×ZÐ÷('ö«Í;??eÍùòŸjøïÅÞý‘?gMKXž(t�|;ÐZU‚ÞÖÖ’Çsµu$�Ô×Â?ðo¯Â=_âm·ÄÿÚ‡ÆpçÅ?u«ˆôæaŸ#OŽbdœ§X1f”ÿø8Kâþ¯ñBøeû3x2mÞ+øÑ®[ýµ�îl#™V?0ˆ×rz´“5÷ßÀÿ„:GÀƒ¾ðF�“£xWLƒL´S÷™"@››Õ›‰îI5õé,>ËzŸúJÿ7ù“Nr¯Šs“¿.þrð?3_Æ^+³ð„5]sQ“ÉÓôk9o®¤þäQ!w?‚©¯�¿à�>½ñ¶�ñ ã.·5‰ä¦ÜùvÑ»3?Ýó]“ôÁk¢ÿ‚Áü\›Àß²¡ðÆ›¹õψZ„:-¬1ÿ¬x·—Ü*Æë°¯yý�¾Áð#àg…|!o·›¬¬½%˜.eø…Ûþ_8ÿyŠK¤âÿà[þx²·†Ÿ3ÿ¯µ}ØýÔÔŸý¼v”QEw~QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQ_~Û_ðR•ÔµËo ø7Rñ—„>CâÛ øÓãf…™{¤xUæ¶f�æ3lwi¬`{·µ{h Óq" ‹ûRÁBW@Õ|gá/x7ǿDŽt†—Æzÿ€î´Ë‹Ÿ šæò¡¸—uÕüFÚy ´q» ‰w).‘·Ë—¾/ø¯û`é_ãÑ4-bëã�†o­5?|{Ñ4=þ ñ†gòä¹¹¾+$b1uj<¹ô·!þÒáÀUt£âŸ‚¿´ÿì·ñ›âŠü¥¬øæÇO·ºñˆ4½4ïŒ>WÙà¶- ñU‚o_/åŽî,``ª§èìmð[Aýž?eŸx;ÃÛGDÑ´ˆE»ë5 ŒƒÍy."*ž\ÌîÌñìPŒÅB¨@¿¿dŸ†ÿ³n·â]_Áð§…5¯Ü‹ïÝèúrZ¶«p 1wÇ8Þò0\à4Žz³óÏí»ûhø‡Çß×à_ÁmCÇ:£›}gW·oÝh1t‘Dƒ;]Aùäÿ–tfBXý»?lÿë~=‡àwÁ}Ú‡ÄMlùž£nß&�8ßÑ$ rÏÿ,—§ÎFß[ý‰ÿb~ÆŸ •‘Ÿ‰50²k:̉ûÛÙ:í\ò±)'jçÜä’kê°xZ9mæèóNZÓ¦úÿ~Ýì¾×¡â×­SQápÎÑ_¿öØù÷}=I?bߨ·ÃŸ±§Ã�ìÝ7†½¨“XÖ$LM(ì;¬JIÚ™îIËO³QE|î/[ZUëËšRÝž­ 0£MR¤¬�QEÎjQEQEQEQEQEQEQEQEQEQEQEQ^-ûL~Þ~ý˜<]ᯠjº‡ö—�¼a¨ÚéºO‡ìX=ܯq2Ä’J3ˆ¢ÜÃæn¸;CEc_J„=¥i$¼ÏO(ɱ٦%`òêR©QÝÚ*îËVßd–­½Õ»ÓEVǘQEùsÿ?ðñ?ø,7Â/Ùâßý3Á¿ þÏF¼ÆîBKåJ= _gˆÆý«ô‹ãÅ=+àwÂøË]—ÉÑü-¦\j·Ž:ùPÆÒ0_V!pr@ï_ÿÁ½ß uˆ~ø¡ûKx¾ø©þ5k÷É›Ÿ&Æ)œ¿–O!à´xé‹HëÕËÿuN¦-ïeþ'þJìãÅ{ò�×Wè¿ÍŸ¤ `W™~Ú>ñÿŒdïˆZ_ÂËåÓ~!ßèw0è7&à[4w%P’�DR�’B1V< ôê+Ê;ÊÚAÕ4ÿÙÇà ãÏØúçÀ? ¼{oc½ñ2ÚïÇž¹Ô/íc:¶”mþÒé.ÌR™ìM6m!Ù_ìoÙÿö7ñT¾7‹ÄŸoþ|\ñ'„£¶²ðn»� ³Õtä…¦f¾–âF•Òî2=ënc… EF¯Nø£û,x3ãWÅ ø·Å6:†³}à‰Åæ�e>©uý•ov­º;Ʊ inb9òæ’6xÉÊ�@"Çí/ñûIý™> ëž2Öht¸Ñí÷aïnåŠ÷f#>ƒqè L¤£)lŽ|^.Ž„ñ8‰r ¶ßDµlùÏþ cñû[ñ†¿£~ÏߨÍãíMVhØãN±l–W#îï@ÌýÄJx;Å}û5þÏú/ìÅðoGðv†ŸèútYžà®$½¸nd™ýÙ»v€+ç�ø%¯ìÿ«]ZkŸ|v Ç�þ%»\[WÊÅ›ríî‰0¤Ѥ@c$WØUÇ„‹›x‰îöò_ðwgÄp~¶:´ø—T¬­N/þ]ÑÞ+üSøçê—@¢Š+¸ûð¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠeÍÌvvï,®‘E—wvÚ¨£’I= Þˆ}ã?³çíÕà_ÚŸãŒ|)à;Çñ¿�`�µ=fßÁæ™äT†ÿ–¸òd%ÇÉÀÚ[$�f¬hb)W‡´£.e®«m4‰éfÙ>;+Ä}S1¥*U-rÉZII)Fëux´ìõ³AEVÇšQEQEQEQEQEQEQEQEQEQEQEð�üqàøLÿà—Þ!¿ò÷·…u­3U^2WtâПÊèþµ÷u|÷ÿ_ðl~;ÿ‚lük²‘¬½Ô@=šÖ3r§ð0ƒøWiKÚ`êü_ä}ocž‰0¯å­NþœÊÿ…ÏHý–¼ÿ _öeøwâƒ'šÞ"ðΛ©³ç;ŒÖ±È±®ò¾\ÿ‚+xÁüqÿ·ø9zïæt‰4ðsœ k©í€ü@~õmƒ«í0ð©Ý'÷£Íâ\ Áfø¬ÿ—u'üM~‡ÆŸ·×Ã{¿Ù«Å%øÕà]z ø×✞øy«x§W²‚úÃÀºÛåŒêˆ’(l�µ„òÞAne0I"íG'þ §âÏŒžý¤¼EðãÇ7ŸüKeá? *ø¯[ñEº6ž|@·J–òéB8üëkÛ"×OÞ-H†2UÚE?lx—ÃZwŒü;}¤k}–«¤ê–ïiyeyÏow ©WŽHØte$`AƒSi:M®�¥[XØÛ[ÙYYD�[ÛÁŽ(#PQ`*€ +¤ñ QEQEQEQEQEQEQEQEQEQEQEQEQEQEð÷ÂÄÿ…áÿ¦ñ¶´ÃͰøg �>ÖCÏ—;GL¾ß4÷—½}Ã_Á&Gü%ÿ¿h�>ëž-1FÙÎÕYndÀöÄÉÿ|Šú\�÷X°×½Y«yGìþ¿SïüIÆÑÀ{»ÓÁE©µöëËZ²ùJÐ�eç¿ø&Tmÿÿ‚²|]ý¥¯\ø?ÀøWÁ,ü¡Ê4BHý?ÑüÉvm@`ñ_©uóÏüÃöF_Ø›öð?‚¦·X5Óh5Mxã Ú…Æ$™X÷òò°ƒÝaZ÷_xªÏÀ¾Õ5½FO'OÑí%½¹“û‘F…ÜþM}eˆ�JϓᎋÑW?/ •<õ]·mþ/î>1ø�ÿUÿxð·‡úG‡þ ißÚ×kÕáÙ ôϘö€�údÞœ}¿_ÿÁü+wâÏ üAø¹¬E�[âOˆ&‘¹Ä»1ÚqÓÍ–Eãþy/¥}�^&î«ÞNÿ.Ÿ�ñÞBU°53Š«ÞÅÎU|Ô>kåN1ûŠ(®ÓïŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¡Ôu}#Ožîîxmmmci¦šg (£,ÌÇ€’xªþ$ñ.�àÝïUÕïì´­/O‰§º¼¼�`·¶�ygwbT¤œW‹~ÞŸ®ÿmÏØOÅþð>¯¡ÝÜøªÂÚïJž[�7JÖD7Ý-¬ÒEœÚ],>DŒ™ýÔî@=�|ký¦µŸÚ#ö™ð/Âÿ…ô_è^0ð�ÿ‹4Ïhö–óø¢k[´·{œIhËc-Æä*Èǵ޼£þ ÃðÆ~ ñ¯Ä /¢ø}}àŸø“^ð‡ÆO ÅnߨZÞ±oŒkúLH±›ØÌ1^ÙÈÁ<Ï1ƒo�„ž‘¥xjÿþ =ªèø­û3Þxá÷ƒtÙ.oÇŠ'¶[Ë=y¶MâÂ丂8…Þû¡åoW…Ts"¯Õ? þøkàWÃÍ/Â~Ñ4ïøsFˆÅe§ØÄ#†X³wffff9fffbI&€ð£á~‰ðKáž�àÿ ZÍcáï XC¥é–ÒÝÍvÖöРHã2Ìï#íU.ÄñÖ¾mý¿nWÁõ§ÂO…0¾µñ_Äø€}Ÿ ý‰Œïlü¢R¹ažAvÀÚWöÿýº¦ø Ÿ€ü oý½ñcÅ[môë(PJtá'ʳ:ô,OÜCÁÆæùFÿìû Åû/è^#ñ,ã^ø�âŒÏ¬ê’·šÐ;ÚÜä‘»—~®Ã=�õv Ž ‚ÌóÞÿÃûoù¥ýÅÿ“=6<|V"¦"£Âa�­ñKù|—÷Ÿàj~Â?°ö•ûøW¸•uŸkÀM®ë/–iœ�Æ(Ë|Â5'©åÛænÁ}êŠ+ÁÆãkbëˈ•å-ßõÓ²èzX|=:Õ*JÉQ\¦ÁEPEPEPEPEPEPEPñ‹Ä>8ðŽ“ý£àÿi/0!3hóêMºŸÞÙ^"Ý‚Hùè:WÏ^ÿ‚Ó|Óüi/…þ!Câÿ„~)¶`“éÞ*Ñä‹k„K˜›PìUHäkëšó?Úsö>øwû`ø0èž?ðÕ–µ )û-Ö<»Û?Å ë‡CœÚØù�W›Ž¥�þ&jÿË%x¿š´—â¼�¸á\džy–‰p³tßü½¡>Z±ÿ·gÍJivå„¿¿²:_†¼ñ£Jûw„ÇšéëñöÊÿ‚üPý–5i¼]ðsSÕ¼g¡Ù³L‘Y1ƒÄjÿ»<üz9ÿž` ׊üÿ‚ÇþÑÿ.…˜ñ森ÛÚ9Ž[@º‰Ü§ZIž¸ä`H?JùJœkSSØæ¸yA÷Z§æ¶ü?~Áý°œK‚y—g4±Pë ‘tçîÊÜÍK·4`ŸM5?¢J+òWàŸüß*ùP|Føgôóo¼7zW»m§Ïë=}}ðSþ Wû9ülDž<‡Â×Òã6¾$�´ï/?Þ™³oùK^î Šr¬V”ë$û?uþ6üÊ8›À^=ȯ,n[RP_jšUcnÿ»æi‰#«ÿ‚“~ÝV°ìåqâÆ·¶ÔüA}r–&™3•[ÙÛæbØù„ifb;…\‚±`Ÿø*¿ÃOÛÃI†ÓM»ñ²GºëÃz„Ê. ,ÖïÀ¸Œ`ò 0,‹‘ŸÈø,_íÉÿ ©ûWÞ&ïÏðOƒ7é:FÌwoßÝ�úêê0¹}ó_*éZ­Ö…©ÛÞÙ\Ïgyi"ÍðHc–S•ea‚¬ȯ„Ì8þµÎ^Á)QZ[½·iþ]-Ðþ°á¢.Y˜ðEíiJ†eS÷œë^Nd¹iÊ ¤ÒVrÚJnIJÊÏúĨ¯oaÓl常–8-àC$²ÈÁR5%˜ž’M~:þÀŸðpέðïK‹Ã_íµéöÑmµñ„jÚ’í*\FJ¬Ùày€«÷mä–ÿÿ‚Âøóö滺Ðtã?ƒþ‡Äz5¼ß¾ÔT•ï$ßÓ"1ò)Ç T=}%~>Ëa„UéÞScg>‰yëä~+”ýxןË)ÆÆ4°ñÕâ/Íî-%)u¨Ûí4šoìø)'ü÷OðOÛüð2km_W¡»ñc –ÎÌò´CÄÏÿMXÆ>Q&r>ÿ‚YÙêß´Oü+áµî½}­ê³ëo®^^ÞÌÓM3ÚÁ%Èwv$“˜W©ôòÕ{§üÇöǵý„ÿh˜þ Üx]¼]-¦�qgmd5c²I¶©�ÉåIÑ7ŒçpäWæsâ ØüÊ•|Âv§'ml’zÙkú³ûƒ á]Â\˜e\#‡çÅÕ¡R rqö•'(8ÇšråI&î£xÁzÝ¿ébŠüpñ/üåãk¬ÿcü.ðµ�§Û5)îñÏû+oóÚ¼ïÄßðq¿ÇýpŸ²X|<ч}“I�ÏRå¬ïÏn�º ý>§eødߤ_ëcøSôGñ¿ñhS¥þ*±úG9û§E~iÁ;ìkóËáZÿàà�ø¡ÇÛ<û4i¿Ù6GïBÚžd‹‘Óp¸{Ç ÿNqúWê }>;÷Tiá–öæ~²ÛîV?=Ã{õ'[ä½ü¢Š+Ê;¾ øÕ¿þ Kû{Ú|;¶’I¾ü&“í~ xÉò¯ïÚÑdw, #¸ pÊkÞ?ࣵQý–?g‹Ë­:Oø«jþ2ð\9yuÏÊ.®¬“®ë�>]®Š£$¼rʸ�½+ß(¬1§8Úœù_}ÞŸüæzÙ>? …¯Ï�êôÞñnQ~±”Ziö¿4{ÅŸ7|ÿ‚¸þÏ_|¨ô¿‰:.•}.ØõÒÚTªÇøAœ,nÜf¯¢´ÝNßY°ŠêÒâ«iÔî�ï“Í}}ðKþSøeâÏ&ø;Äþ¹|žÉÓT´ORHòåØFÕ¾ Ž2œFŽ|�ûÊߎ«ñ<ž&ú-xƒ“Þtð«ö¨ÉKÿ$—-Gò‹?HëáÚïþ —áOÙ/öÏ‹áÍÞ‡.»áÍ2Ù#ñ©c(7:mä˜`‘Æ~YU#+¼d¹å ·añãþ ;ð_¿²÷‰|gà�øÄúý¥®Í+FÞÐ^Ü]IòÄZÞP²ùjÄ3�¸ ¤g$gðÅ^(Ôˆ¢Š+éÅŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¸?ڟßð˜þÌ_´‚25O êvxÏ_2ÒTþµÞVO�4ÿíok6§i63Äwtù£aÏçQQsAÄêÀÕt±4꯳$þæ|]ÿçø—ûwþ káK\çûVÕ,ñé›·Ÿÿk~µ÷=~vÁ±Z�¼ÿ‚rêQüßèž3¿ˆg Í½£ñíóÿ:ý¯;$—6_Eÿugâ•K‹ó¯ùý7÷»þ¡EW¨|QEQEQEQEQEQEQEQEQEQEQEQEQEQEWÄßðCrn¾xòíÿ×ÜxÆãÌ>¿èöçù±¯¶kâø#c ÿÂêðŒ™ûG‡|c!�ÈݾGÖØ×ÒeºåÔ»Ò.f¿Ty8½1Øwþ?É‘öÅQ_6zÁ_™?ðYßjÿ¶·í_ð¯öAð…Ô±E­ÝÅâ/]@sö;4ÜÈ­Û)Ë6Öá™­»‘_¡ßþ/h¿~x“ƾ"ŸìÚ'…ôéµ+Çx¤h[jŽîØ £»0ëàoø ×Â_ã&¹ñ3ö­ñÄÿ OÅÝRâ×F|ßcÓ#”n“ÎÃ$i ú-šã†¯5n´¡��ÛÖ_à[ýú#ôÞ„rÊž,¬¿Ý—-õÄTMC×ÙÇš£ô‰úà_é <¤xwC³‹OÑ´+(tûXÇÉoH4ÁT¿3>:ÿ(ÿ‚óøCÀiþŸð÷ön³:ή¿z µÑÈÈ{7ïÍœL‡¨·Ÿ¨Í~‚~ÖŸ´.�û(þÍ^5ø‹ªùmká=*kÔ‰Ûh¹œ °CŸY%hãî+äø7‹özÔ|%û,kÿ¼Uæ\x×㎳6»wu2âY-VIDÿ¿#ÜMžë2zW×`¡B¦%t\±õ{ýËó?(ÆNUë(MÝ·Í/?_V~�×Ë?ðWÿ‹²ü9ý�/tkçVñÍì:´qó##2là¢Ïýuµõ5|CûE·ü4ïüwáÇ�÷ú/Âû?øHu4ê©9Û0 :H³^ç£~??Ž“T¹#¼´ûÿà'â*¤2y`è;TÄÊ4cëQÙ¿”9ŸÈú›öiøGÀo€^ð„aChZdVó•èóãtÎ?Þ•�¿î(¢º£¨­‘õØL5<5a¨«F E.É+/À(¢Š£ (¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+â?Ä} ဵxŸU³Ðü= ÚÉ}¨êrypYÀƒsÈíØ+Îk¯ÚSý›~ÿÂq øBçâ.‡áýUbñm¾�tRÒ4Õ.níàUo´ËnÛíÁG1‰%”#|{ñ×ãÏ‚?iÚ?ÅÑø×ö“ñÁ�]xOL×þkڌLJ´-vÎX¯50ºÁ©OÉT’Öäº$+ ò‡šî@=ö¥ø³íñŠ/ Ťøwâo†WÁ|RøGge¨G.‹ñYÓ㺂}:õÉx®#Š[­2æ…T3y‡q€‘ÿ_ø5ã_…w~)ŸM‡Æ¾ø+­ZÃ}oá¿xcûU³ñ,ÍçjSéÖK°XénÒÌ›D±³BÞY-#ÿàŸ?ðO/üIð¿ÁÚÿDÖ>|F¶·j:G†§—Fðî»}qk5›êéL pµÄ™ˆ�bvóTJdØ+ïJ+À?oÛ†ÇöEð,ºt)¬øÿÄ_¸Ð´„Fwc´M"¯Íå†8ríò�â+ÐþÙÿ¶‡ÿc_„òëÚ®ÛÍVït:>”¯¶]F|tõX× »ã��ËÅ`�ØóÄ%ñÌß¾2¾ø�¯ÿ¤iZ}Âat(|‡Ë?rM§ ŸòÍzüäíú<«/£N�ö–`¿t�£µ%Ùu}§òZžV7RSú¦ã{¿å]ý_EóØè¿àŸ± ÿÂ)/~%üH™µ¯‹^.Ýqy=à �’raCÓÌ#Êð¿(%¾¦¢Šò³¶6»ÄWz¾�è’è—C· …§‡¦©SÛño»óaEW ÐQEQEQEQEQEQEQEQE|ÍûLÿÁY>~É~ “Lñ„>:´»¬_ñKÞCÙ'É–dŽ9l«žøæ¼Å_ðrÇÁ½7réø�ª:ç 5µ¥¬MÓ?hvÇ^ª:w¯ÐOx3Gø�áùô�{IÓu½.èmžÎþÕ.mæ�‚§ññí=ÿù|øÔ./|ú�Ã=j\°:yûVšÌ{µ´��:q‘�jùlâž|¯,à×kYýí´þv?{ðçá5NZ]…ÄB}j*œôß›Œ! ÅyGœñ?ÿÁÏÖ‘ï]àåÌÜ�²ßx�G�ž D¶läg�æ¾*ý»ÿà¢:oíÑru ï„~ ðLj÷øžéóOöù»)c›åfD,?„�Åuµüßã·ìâ·7Ö:$>?Ð`Ë}·Ã¥§�S±{RÀã®ÅuÞ¯�$�¡�«)VS‚Á¿'Îó|í§†Ì®“èã÷;~)Ÿè…¾x_NQÎø*1œãöá^¬š¿IÅÔÒýc(«õCj ½NÚÂX#žâ^êO*’@¦gÁmªS€N`}*zç>+è“ë^ ¸{1�GMxõ0?ŠhXH«ô}¥³šù¬<#:±„Ý“v¿cöÜß[ ‚«‰ÃÞP‹’�ó[V—›Z/;—ˆl4U�Þ^ÚZb3Èf™cÙ8.rx\�3Ò“PñŸ¤hÇQº¾³¶Ó«›©fT„+ï'n #ó‘^_¨ÝÃ㟇¾-ñbk}e!µ²óì°°ƒýé^fô ¯^µ�ãk{‰üâo º2Øø7NÔ/  $-nÿaOB<™é†µ^ÇŸnŽK µI¦�¥å¤oóR’�©ù†gâf#NuèÑŒ¡:nt]ÛçnU=›vû¥NUî¶‚k³=£_ñŸ†4i5 Ù¼«H¶åÕBK0U ª bY€I ±azš�”SÆ%T™¨–&‰À<üÈÀ2ŸbÅ|lÒbÕþÙ,­r¡5-3MÌ�šò9(Àžã=`€G+ñ#Åí¢xÍ,,®n ¹Ònl ˆ\k³¬ÓÄÍb–¡X\ǵ˜<²¶íÞg?(®|&V±4׳ošò¾ÖIrù÷–¿‘ëñÏ%ÆTúäcì)8ë.yN~Ùµ¤Zøi>T윾)¤ôöJζñ~›t÷ao"_°Þ-„æLÆvV0[“æ&1œ–sÅy׉~ ÿeÁ«é�ªÍ°|Mf�[ùÍç­³ÜÚœ�ÔBU™s÷ b¹ÉÅex·Oµ¹Ò|Wqy5޵Ÿ�줚Xîä·X£ÆžÙ‘—…\�Ç@5®&æ·µm'kYwq×]×½ø9ω^Ê2þÏ„e*jNjR²\±®ù[�ùd�»§¤–Û¯k­ox¾ø»OÖm­´ÛË�6aÕ#�…ŽçF‘.ÜÄ3G€:Œ.+°oø9Óâv“áë©/þøk„‡ä’Ùî U“gkHùãi^>ŠOŠzGŒupL~¿‚ÖáH<îÆÁÔŽÁã?íz}=ûqxóöcøÅðƒáwÂ_ÙÏBÓ|;¯|BñEµ¦½©ë6r&¡¥[EUšê᛼²¬�åJSÇ8¯Ô8"®gšc©Òž2ÑNòMÞN+YZë²zßCø»Çîà^ÉëW¥ÃN¥IG–iÖ”jK݇<£4×¾Öœ¾ö×ÕÇÿø»ñkáWÁýj=özñO�§ñþ³&½}ã ­e4›[°ÁQ4ð„u\JùI‹Jß/ Wëß„ïµMKB†mcO¶Òïä{X.ÍÒÅè ›'ׄõªß ¼¥|8øw ø{B�"Ñ4-:ßOÓÑ*–ñF±Æ±Em×êóuªb*W«QË™¶•¢’]²¾‹M[?Ï\À«†¡†Áà¡AÓŒT¤¥VS©$­)Kžn ™ÝÚŠW¶ÁEó�üûö“Ÿàìßqa£<‡ÅÞ8“ûGŠfA‰eP9Ê£`Ñ䎦µUN¤¶GÈçYµ ¯W0ÄüÓoϲ^mÙ/6xÏÃ×?ðQ/ø)%ÏŠYMÏÃ_ƒìúi?47סÎÙcºE2dƒ÷«ïJñÿØWöe‹öOý›ôO ²ÄuwS}¬K"[É/Ïp€,`÷XÁï^ÁXàéJæŸÅ-_ù|�‚òŠøL Äã¿ÞqÚTò”¶‡¤#h¥äû…Q]G×…Q@þ<ñµ¯Ã¿ Ýê÷–úµÝ½šhtÍ6}BêL‰ ò1ú/Ö¾6ø‡ÿ|øwªÝXËoñúöÍÚ9­âÐ>Ï,l¸ùJÜISÏBCœWÜ5æ_´7ìmðÃö¬Ò�¯�¼¢x…¶yqÝË—{óÄw ¶TôV¼ÌÊž>Pÿaœbÿ¼›üSÓîgÝpN7„¨b-ÅXZÕi·½*‘‹Kü‡½ÿƒ"|7â¯ø9³áÕ™ìO†þ5Ô0NÏ·\ÛYîã;\qõô÷¯-ø�ÿ/\xÇG»Ó ø#¡Í§^Faš _\7ÐÜÆNxź)x*IZê¿iÿø6šÊô\j|k%”œ²hÞ$_2"}ê%Ü£° ™~õùÑûNþŸ¿cËñ�ü©hörËäÁ© }Ër@KˆËG¸€NÒC`¨Å~_œæœS„OëG¼c¾Îß;ݾp€üBà²tªWÝB­ZЪŸ”9â¥nðR^g#ñ·â.“ñSâ Þµ£x?Dð5­ßÌÚ^“,ïiä’È%w(Gʤ(Ç+ŠÔ5;m&ßͺ¸‚Ú"ëyd¥˜…UÉîI䊞²ümá„ñ§„u*G1 ûw…d^±1+�ulî+óÕ%Rª•WdÞ¶_~ˆþ©F¦/•,¾<Ò„"”›»KÝNRmêì›m²íÞ©m§º,÷BÒ($�)`£-ŒõÀäú Šé÷:Ôã¾³“M´ÆífSEÉ/¿;vŒœãƒ^oá}b_‰pêšõÔMš.�&’ceÀKÆ]÷{{ D �î·Ðfx6Òx™àÈã+a«%®·¹ –›\GžŸ5 #ºÝJõÿ±â“S•¥sv¶îÝÚVyùãñ¬êR«‡¢¥F¿2¤õæsº�>e´c)©Å»éî>®ÞÅa«eÍ´Ñ\[΂H¥‰Ã¤Šy ¤pAõ zå¬ÚäÚjË›Ûx#¹’=§å�ÙÕ8Ç&7ã9ãÜW�èº�Ö•û)hÓÚ<кébIaÏ™'ËH¸‚±—`qÆ3X—¬¯‹îü1ªßßÚǤiËöÕ¹’ý¡�í7y†WÞdÙv¾#¶Ð©äüî¢Oi8®×æŠÕôø¿ÛZÆxŽððÂJt×ï)F´×7½ÊèÖ©jqvæiÒ³méÍ5ãíTW“Ízn!×�„5«í[O°±Šù]u9/“íqJdòVfv'Ì�6´{ö€Ë�7òØü[{â-n-¯ïE¿�nc¸°e‘×È··•¼óäW‚8�¦'«sŸö<šº–žjÍhž«¦—~‘gSñ"„er¥yKg)BOšT×$칯QS¦�—½V7JÌõª+ÊuoµÛ®­­ WXûM�Š-m ‹í² t…î-£xÌ@…`VFûÀ‘ž¯V®,VØÅI;ßO�“ýWâ}6EÄÚ5jR•>G¤µ½âåR ì¬ïNZk§+½ÛKÙÿd?Û¿Ç?±©¨ßx? ê'—5Ýö‘ÕÀN>D‘†ôLŒ•R<�p1õ‡àäߎZc¾ðßÃMN=ım:î)qŒ Üí<ýÒzûcóÚ¾¶ý�Ê>nÑü™ð>$p7 Ö£S:Í2WŽ«×ÙAJ«²ÓíA»m£mtGÔ ÿàäoˆ^2×m´¸þ éþ(Ô® „´Ñ/îVyÈûÛÊ™¹íÁǽ}áû0þÔŸ¾<Çoqâ/ÙúûáÆ�0ɸÖ|QqÏA€N?¿óÇ©±ÇÄ߀>(ðªYü¿øz–{=†ƒ6—*ñK 0=ó"äó^Ý_³dø,rŠ©_í÷T-÷Ù·ø柉MÂò¯<WÃk5£ujb=¢óö|ðŒ_“S (¢¾”üD(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š)C©d=éh Íoø5¤çþ ùâÿoˆW¸ÿÁn™_¥5ù­ÿ´Ê?|}Ò-$˜ø;Q]7Zòxy-‹"“Ø0{˜òxÌÊ;×Òðͪի€—ü¾ƒŠÿ´£ø«|Ï#7÷! Jÿ—rMúlÿ‘öíGÃ^#³ñ‡,5m:tºÓõ;hîígC•š)20ö*Aüj¿Žüo¥ü3ðF¯â=nî; A²›P¾¹�ámà‰ ÈçèªM|Ô½ÛóicÙ§T’�5vö·[ö?;¿àºŸ5Úâ/ÂßÙ+À÷¾^½ñCS†ÿÄOÝö=:7-˜û€Ç,ì:âÑ{7? ÿ >i¾øÂ:¸´Ñ|5§Á¦YDÊÅ\à ±$÷$žõùÝÿLð6­û]þÑ_?kßX˜® w@›BSû¬-‘†zß½~¡××f?ºŒ0‹ì«¿ñ=_ܬ�ɰžû•~ûz" SSƒDÓ./.åX-m"i¦•þìh –cì&¾/ÿ‚KiwÆÒ?ß6óu<Q1+\;+²Á £)ïlýƒEÂþÑ¿´?‡?eÿ…�‹|MsåYY��À„y×Ó�vA=]°}€œHè>"üCѾøSñˆ/¡Ótm¸º¸”ðŠ=RÄà’@“_ |ð¹ÿ_øø>%øÞÒâÇà÷….^ èspº£«rÎ:0ÈVèH‚B¶=ÜŸ+§]K‹|´)üO«}!ï?ÁjÏ;Œ•6¨PW©-—oï?%øìmþÆ?³·ˆÿlŸ‹iûA|a·ýÃ0ø~@L�©Ìs?À½S#.Ù�ñ·?uSa…-¡XãUHÐUQ€ tS«›6Í*c«sµËé­£‰~¯«Ô×ƒŽŸ*woVú·Ü(¢ŠòÎÀ¢Š(¢Š(¢Š(¢Šð¯ønÔu=V=áOÄ¿XéZ­öŽu 9´H­îf³º–ÖsϨÅ.Ñ42^5$ ãTU«J”=¥iÆÚò’Š¿kÉ¥}žL”Ÿ,SoÉ7ùëExgü6v±ÿDCâÇþøsÿ–ÔÃgkôD>,à_‡?ùm\ßÚyýÒÿÁ´ÿù#Oc[þ}Ëÿ—ùçExgü6v±ÿDCâÇþøsÿ–ÔÃgkôD>,à_‡?ùmGöž_ÿA4¿ðm?þH=�où÷/ü_ä{�áŸðÙÚÇý‹øáÏþ[Qÿ �¬Ñø±ÿ�~ÿåµÚyýÒÿÁ´ÿù ö5¿çÜ¿ð‘îtWÏ~/ý¾åð„õMwYø9ñ[OÑô[IoïîšÈ- ‰ É!TÕÛj©8U,qÀ'Šúº)V£V<ôjFkkÆQ’ÿÉ[!©EÚqiù¦¿0¢Š+AWñãöÙø]û6$ÉâßéÖš„ ¼é–¡¯uOôhCÊè¨_zø“ãçüsƺ˵‡Á_�¾2Õž`~ϪëÚUË$€ZÛ‚Ì1’ ”qÕ{Wé}äcðxêþíþÍyFïïoòHý„ø—…²«VÌr—Œ¨¿çåwwÿ)Å¿INKÈþ}>=ø»öÓý³Ä‹âo |hÔô{§òÿ³l<3{g¥ŽÛZ(¢XÛë&ãêkÎt¿ø%Çí«ïò¾øá|¼gÏÓÌÎzoÆzv¯éFŠù:¾R¯?i‰ÄNrîíúÜþ�À}0s ³°™.O†ÃÓ[F<Ê+åT2ÿ`ÿ‹_²×„lµßˆºðΙ¨Ü Kinn홦”£>Љ#?ÝROË�ßäU÷ü£ö‰ñ/üköןÃôÍ_Å>ð/�¥éÚe³Ü}¦Eewx@~F�*‡û»#Œñº¹ÿ†¿ðC¯Ž^>Ò>×}…ü'¸�k“yÎû6ñÌèÄjü«4Ãaéã%CÛ‚vMµ¯Fö?µòi`¸s™ñÍz8\MHóºqm8©kÈå:Ž\¶æ²Ñ»[M~=¢½çöåý„¼Gûj~·×�*ëör2Ïe¨=äRÍ& Û@c’,&©;Îp<¸+Rt¦á&›]škïZ3ô.â<–ÒÍò¹óÑ«wÓ�ìÜ^’I­SѤŠý ÿ‚{ÿÁ/õüÕ|Iâñ,<°TÑ_RñmÖ‰{£C¸°º‰#ÓîT¼„##3c`ÁR²üÙûcÁ=~!~Å÷–÷#±µ»ÐuÛoªi“½Õ¢9Î!’FŽ2²cûÈ¡°Jç5°N�Wç‹æèšr^«¥ÿáωȼbá¬×ˆñ<1‡ªÖ"‹å\ÊШÒ÷•9m'tÖ�Ù¸ó$Úðšp�Œe¶� €N8çÈþTÚý`ÿ‚1ü)ñ7�ÿd-nÿþ�ÿi/¼é‡‰èÆN™Vž/¨Õª©«?z[~kvtx¯âEÈ¿¶«Qu›œ!'g''®¶{EJ[kk]^ëò~¯x`é«âM?ûeo[HûLn[6U¸0ng–X·8,Î2+õÞÿþ±ðßö̲ñ­Î‡¥Â’ñ�…uÏì{›MU“Ä˵��òÈ«<6ò¨)x‹µ6*•l+ Wŵ'üsã·ìÃö›Æð×ü&šoí? –¼ ¾¯é�É; ïõs̰IUpçƒI©GÞM5tûÙ¯#å¸#éÁU«ÐÅý^»ÓÙÖýÜïµ¢Û䓾Ê2oÈûÁÿðn‡Á�‰ÞÓ ñ•È3ü5Ñ/¼Mᤸr ÜH¬òØ Ÿ¼&*QG8–N�+è_ø7;à}ï‚?aÛ߈:æù|Iñ^º×î®%\K4æ·¼ë<£ÔO_³ðž[”VÊêfT(¨¹ÇÙ»7ö¾5¿eëgæxÍÆ¾!äNáì=·74§7ë6”ßÎGó{£ÿÁ%hýq£ü#ñJy©½~б[àc<ùޏ>ǼgâçÂ?ü ø‡¨øSÅzöWˆ4–T¼³3Ç3[³"¸Rѳ.v°ÈÎFppkúÿ‚ŠÿÁC´OØáäwWºF©«ëzÞ�}q¤Goä t–o3™%GÙæÝÛ‚"YnöÛ…$~x?öJøßûaø«Rñ.›àÏë÷zýÔº…Ö«uÚ[ÝÍ+ÄóŒ’Xœî=«à8³ ÁåN�3rœµwjÉmÑnßäWø#ãvsŸl^uÄp¡„ÀÒj•å*šIûÓŸ-£¯¥Û’³Ñ£Å(¯¶> ÿÁ|wyyæ|EðÏ�tëtoø÷ðãh7�0ÿ®³ê‘?öÍëã¯øJûÀ1Õt-NÞ[MGF¼–Æê6ïŠXœ£©ÚJä#‚G¡=kæ±X ˜xF¤Ü_7òÎ5ÚùØý›„üLáÎ&Åâ0Y#Ú΂‹—»4½ë¥Ë)E)m«‹i]k©™Et >kß¾ ižðÆ›>­®jòù6¶°• !Á$’Ä*€ ’Ì@H¿F¿hÏø%^ƒâïÙïÃÚ_Ãï„?4ˆ^±[yuKÛÏý�ÄRd´�r«ª³FK³tªáJ¸ ·lU<]:•!Räé)Æ-ùG™«ÿ]Ï/Žü^áÞÇá2üærRÄ7¬bä¡öê[XÅËÝNÏ«Ú-¯ÌZ+{âgÃ|ñµ÷‡CÄ „—$2ñŠøïö£ÿƒjMÆ£ðÆ™ºè¾$úá.¢_À�êýëîsÌ(ETõV-'î¾�^ë£O£MÜþ^àÏ¥çæõ~­š©à§v“šæƒÖËÞŠ¼_~hÅ-¹™ñŸüßöRøqûiüy¹ð޼Câ ê:…›\hSi²@©w,yi`q$mó˜òëŒqƒ’E~‚jŸðl§Ã‰�~ÅñÆÖêÌ'·µ˜“í„\~µù•ñ'öpøÍÿûø¡£ë^"ð¾½àý[D¿ŠïMÕ<¿6ͧ�ƒ¯—qhŸ§*œu×ôû~ÔúWí™û5xkÇÚ_—ê–þ^¡h�“a{Ë<'’p¹ä£#zœ—eø¥<aC÷°×[¦×á³ü>é#Æ|_‘Ï Å#š7—â#ätçÔKK;IZqWZ¿z2î�…$ÿƒeü;§ê0\é?üK§Ë’"&‘YeB¸ÿ&¾Šøû üxýŸ¤·ƒIý¨uhð(Nñg„°­Ž€LoRuP8Ú²úcëz+ô 7 åØysáàâü¥5ùHþBμqã<â‡Õ³|T1íR†vôæ¤ìüÖ¥/Á¨[hð&«ugy~«‰¦´µkhd>«I!Qì]¾µvŠ+ÝJÊÇå“”œŸ_—à´AEr¿~'¯Á‚>1ñ›Ù¶¢žÐïu¦´Y|£t-­Þc|»¶c88ÎpkÊá¢>1Ñ3øiÿ‡ ûÿ”ÕÏ‹Æá𱌱5#+Úí+Ú×ßµ×Þ:tçQ¸Ó‹vì›ßþúŠùÿþ#ãý?†Ÿøp¯¿ùMGü4GÆ/ú& ?ðá_òš¸Ö ³þ‚!ÿ�Gü;§ˆÿŸrûŸù@Q_?ÿÃD|bÿ¢gðÓÿ÷ÿ)¨ÿ†ˆøÅÿDÏá§þ+ïþSQþ°eŸôü ?æSÄϹ}Ïü� (¯Ÿÿá¢>1Ñ3øiÿ‡ ûÿ”ÔÃD|bÿ¢gðÓÿ÷ÿ)¨ÿX2Ïú‡þó©â?çܾçþGÐW‚ø_ö¤ñÜ_üáÿøÂZM�Œµ)ô¨otŸ\jRÛÍ…Ýè- šu¸(VÍ×"L‚ËÁǽW¥B½*ÔÕZ2R‹Ù¦šíÐç”e8ÉY®úQZˆ(¢Š(¢Š(¢Š*Ÿˆ5ì�öëpO³[É.â3·j“ŸÒ®W7ñŽÿû+ኮ·û6�w.ãÑvÂç'ò©“´[6ÃÞ¬aݤ| ÿ½X}�þ áâ 6•ûWޝ¥Éþ,YX&ñÜ~ú=_Ÿðm›öø&¢K‚>Ûâ�F`s× 쟥~‚W—‘+eôÂ�¼ñb§?f/þžÉ}ÚQ^±ùèQEQX¾#Ø|ø]âOj«rú_…´«­bñmÐ<Í ¼-+„R@-µ ŽEy_ü6v±ÿDCâÇþøsÿ–Õ�jô(¥*õ#öæ”c{Zöæjöº½¶ºc);B-ú&ÿ${�áŸðÙÚÇý‹øáÏþ[Qÿ �¬Ñø±ÿ�~ÿåµsÿiåÿôKÿÓÿä‹ö5¿çÜ¿ð‘îtW†ÃgkôD>,à_‡?ùmGü6v±ÿDCâÇþøsÿ–ÔiåÿôKÿÓÿäƒØÖÿŸrÿÀeþG¹Ñ^ÿ �¬Ñø±ÿ�~ÿåµðÙÚÇý‹øáÏþ[Qý§—ÿÐM/üOÿ’c[þ}Ëÿ—ùçExÇ„?líïˆþðÞ­ðßâ„äñ]ܶº¤šD¶­ízc†–? ô­%¶ÿ—rÞOÉM&âû»n}7…˜œ&Y™TÅfr_ìpuiFOø²MF”Rêã9EÊÛF7Øýý›~hÿ³À_ ü?Ð.•áM6+œ&ÓpÊ?y3ïÉ!yý§5óÏüö°?²üÃÆ6WxÍG…´�§$ºV:÷-Övt`žµõí~^~Úiÿÿ‚äü)ø9úgƒ~ZÂUâXÇÍ\*-ÇFS‹¹éçÊ=s–S‡‡µW^äߤz~HùŒ×Z¼§Z¬¯R£wow)=_æÏ®ÿà•?²‡ü1—ì#à/Ü[}Ÿ\k!ªë€Œ?Ûî{*7©�rĤK_DQErÖ«*µInÝÇN PŽÈøƒöïøh�ø(?ÁO…þûNÑ¥>#Õã«(&M�Ø~êÙ€ÿ¯�ê+íúø{þ ëÿûtümø½'ï¬mn‡´yO*ñröEo 8ÿžÇÔ×Ü5æà½îzßÌÿ¢> €ÿÚÖ3;óV\¯þ�ÓýÜ?ô–þaEWq÷áEPEPEPEPEPEPEPEPEPEP_,~Î_ò'ø“þÇ¿êG©WÔõòÇìåÿ"‰?ì{ñwþ¤z•|Gˆ¿ò$_õöúEC×ÈßíÙ~q;ú(¯=ñgǨüûCxwÁ–œ-ì|O¡ê­–°×8ŒÏdðyÖ¬…p¤Ã?š­¿‘¿(Û“ødbäì»7òI·ø&}£Ñ_Óñi/Åþ»#Ш¯—þÿÁGfø¹ñçÁÔ| '‡l¼{à»éú›êÞyWº–ù­,Þ%0òYXMp[ÊU£Úvï=ßì{ûZ·íc�çmÏÂ&mO˜ß‹¯í»6³µ»·¿"ùK4WHÂ<¾w8•rìM.eR6åWz­7/ç§~»jgГ²}¿©/Á­~[è{-æ? ¾>ê¾=øÿãÏê^›Ã±øFÒÂúÊò}J+‰uh.f½„Jbˆ…KY3 2³²J…Ö'Ýôêå©NTÝ¥Ù?½]¤›it<ÏöÑÿ“:ø³ÿbn±ÿ¤3WÖ•ò_í£ÿ&uñgþÄÝcÿHf¯­+öo ÿä]SüoÿI‰ò9ÿûÊÿ üØQEúá…C•c5ÍÌÑ[[[FÒÍ4®"Eff<$“Ò¦¯ƒÿl?�¾!ý»~7Ùÿá]Ù‹Bµ“>3ñy0Å0ßaÕð@?¼“ †'ÔÊr¹ã«rß–W”žÑ�WþK«8ñ¸Èáá{^ODº·ýnú¾.ÖõŸø+÷íÿæ�5î™ð#À·bMFù2‡]œt îÃ!ð!.ß3*Wß^ð¦›à_ ØhÚ=”v—¦@–Ö¶Ð.Øà�F@úVÀ¿‚ý�~éžðŘ´ÒôÈð æ[™/,�üNÇ’¾·ÎsHbpøUËBž‘]_yK¼¥×¶È˃•+Õ¬ïR[¿Ñy/ø!EWˆz!EPEPEPEP_,~Î_ò'ø“þÇ¿êG©WÔõòÇìåÿ"‰?ì{ñwþ¤z•|Gˆ¿ò$_õöúEC×ÈßíÙ~q;ú(¯#ÑlÏ kþð¾­o§ø€Ëâ¯Üx. 4ÁÛm5yn£¹Y—ÌØ©²¹‘г.<¨b@?†Æœ¤¯ý3ìå%wæþåwø&Ï\¢¾i¶ÿ‚§ü=»ý„®hÑühÞµÜF0p°qòˆ|ÿ+&"'Ï›�$ï$t¯aøÛñ²‚Ÿ _Å-¡ëž#ÍÍ��¶—¤ý˜^ÞÏyu ¬1¡¹š2N™2JŠNx®Š˜ôåÉ8´ù¹íí4ÿÉ—Þ…í#{_¿àìþç§©ÚÑ^ñ7öäÿ…QáÍ.mGáWÅ�~óJ½×¯ü7a“u©èZu£ªÍupÉ~m˜eÓdVóÍ4›ˆHØ£…ö½]´ñF‡e©é÷ÝØj0%Õ´ñœ¤Ñ:†GЩ}k:˜z��<–š«Ý=´éæ�žÎÎÛ1*‘o—¯ü7ù¦×DÕ÷GŸ~Ú?òg_ìMÖ?ô†júÒ¾Ký´äξ,ÿØ›¬é Õõ¥~Çá¿ü‹ª�ÿé1>O?ÿy_á_› (¢¿B<0¢Š(¨î­£½¶’QdŠU(èÃ!”ŒjJ(ÚwGÍ¿ü¤xö©ø[¥èZV›¢é–þ ñ@ŠÒÂÕ- ‹ý7Ãßu€®ª±ÿh�ù<_†ö&ø£ÿK¼=[ø�ˆ©,Î "üä}–GRu)N¥FÛrm·«z#ãø.wÁßøX_±¸ñ 1n¼ðF© é`2ßg˜ýžEÛ¤‰�´uð?üÛö “öÃøïÚ­»ŸxY’ïXrÛ¶Îc´ÖBïDVèJ×éÇíwñ×I»ðWÇo‡þ(ÑÞÖÏAøvþ$¶ºIüìZËÜoå¡A²hg·UÛ–Ï� à¶+Î?àžŸô/Ù•|mðgUð¼^…º-¶¹ªkm¨‹‘¯Ý6�g{ª9_)<¿²›ëXÇÌû‘“îãñqÁV”\ãæùuk½¯ªWk­�êñ¾|=ÀXŽÂÉýnU\i½}ÈTQæ—“æ~çw5%u}¥kk�´pÃC *8ÑBª(Àvª>0ðv•ñú.¹§ÚjÚN¥†êÒê!$S¡ìÊx?ЀkÎ?cÿÚfïö—ýž"ñÆ»á‡ð5â_ê–ÚD·âõ¬ÆúâÑ÷J#@Iò å$Œ¶7øIÿR‹ã¯Ã]KÄžÓþø¡¯u 3MðÖ�£|GŽÿTiu †ŠÛûj(ìÊé?"™Uî˜åUWt Úÿfâ]IÒQÖ 'ªÑ·d¯{jö?�¡�TÚÄFvi¶žÎñÕ´ü·¿MÏ’ÿoø#¿ð§ÄG_øUkwâ ßÜ*>—“%îŒÎÁG=e€÷¾ò½� ×êgÁ†VŸþøkÂV;~ËáÍ6 = yž\aKŸv ±÷&°ÿfÏ��Æ¿ê3ëš¿†|KáýbóBÖ4Û]Dê6Ð\[É€Ðܘ¢2Å$MªÍl›Y”�èUÉRœ©Ë–[ýþ–kFŸF´gé]â®wÅV,ÍgÏõg/{iMéÏÞP³W²nïšïWSö1ÿ‘Ããý�pê9¡×¹×†~Æ?ò8|oÿ±îýG4:÷:þœË?Ü0ßõê—þ›‰üã[øÕ?Å/ý)Ÿ–ßðr_Ã�ÏðÃáþ“¢xCBo‹üS“§jP[ˆo§�F% 醓2Ml˜}À N0kôƒà‡Â­?àWÁ¯ x/Ji¾Ò-t‹b’–0ÇÜíÉõ$×çwÅvÿ†ÔÿƒŒ¼á Eχ?glÝÇÃ'ÛJ¬Êàô &º±zÛ·qÇéÕ{˜šP¡‡§B &ï9[«–Íùò¤c,~'SÚbjJj S‡3o–1mòÆûGšMÙivû…W”þÛ´4_²ÿìÕâ_nA¨ÃÙt´oùky/É“¼�î£W›9¨EÎ[#—0ÇÑÁajc1Ч&ü’»>lÕPþÝÿðT¨­pn|ð%wÊAÌ7ˆpvžÙ3 t+fÞµ÷M|Ûÿ°ýž¥øû,é÷úš9ñ/Žû{S’^eÄ£0£ÎDxb!ä’¾’®\‡´ŸÅ-_è¾HùnÀÖ†YŽ1Z¾*^Ö~\ß=!Un÷ (¢»µ (¢€ (¢€ (¢€ (¢€>pý©ü)¥ø¯öÃøRº¦›a©  xžêØ][¤ÂÞay (‘7µÀf‡8cë]=cþÑòx¿ ÿìMñGþ—xz¶+ñ¿äið/ÎGÙd5g,+„›iIÙvÑlø—ÿ ø;ÿ «öäÖ¯¡‹Ë²ñ�¤Ü8(v)¹õ2Äìßû ©üjÒ´oŽZW€.¡¿‡WÖô[­rÂá‘>És¬ÐE i·º_‰lþÉáÙümmý£cYëú|×Gkn]ffÝ!²’åP®ܶHbÊ¿ %iÞºµþ^÷ÿ#/¹Ÿ¼x%â„8ŠXüs~ÅÓœ'm^ª2‚^rŸ$WnmZW-ÿÁ¿aoøQÿ ádx’ÏgŠü_n>Á©óéº{a—¯G›†>Šq–÷5y—ìÙûTxwöŸ'‡l5»8¼â9¼1s&¡o)},PÃ0žÛk±kwIУ°RÃ'n0O uÿ"ð¶“­ø…5/ øëLдKvú×_– ,5Á£L!¿KtŠéîQ’BU~Ó*ûNÖ9¥ƒ®åÈ£­“ù5uók[onš3áøÓŒ1|KœVÎqò÷ªJÉtŠÚ1^QJ×ë»ÞçAûf~à ýµ|öÛ}�[´�†—­Û ûUƒ�?é¤DýèØàòAVÉ?à�ŸðLÏüÿ‚‚™¼g¦‰4�ióê:n© —²ÔåÜE±»0îU¾e(8åIûóö{ý£#øö|Ik?„¼Wà�oÂwÑXêZN¿ö'¸ˆËoÌN²Y\ÜÛº´r©ùe,C*ñŸG¬jS•9rË¿}zEÃ^-gÙVEŠÈ0õ9°Õá(ZW÷9´“ƒÝ]]5¶·Jú•?cù>7ÿØ÷þ£š{�xgìcÿ#‡ÆÿûàÿÔsC¯s¯é̳ýà ÿ^©鸟Íõ¿�SüRÿÒ™S\Ь¼M¤Ïa©YÚê7HcžÚæ–)”õVFìEq?ÿeÏ~ÍwzëxBƒÃ~#�.ïtë'd°óÔócƒ;"b»TùaA™(¯A¢º%Fœ¦ªJ+™lú£¦Že‹¥‡©„¥VJ•KsE6£+;®e³³WWØ(¢ŠÐâ (¢€<›ööÿ“øÑÿb&¹ÿ¦ùê…_ý½¿äÆ>4؉®é¾z¡_™x¡þï„õ«ùR>ƒ†ÿ‹WÒ?ûpQ\íñÃþçÀV~$ŸK:–”5­;NÔä[�(é¶×wQÛ5Þ6¶õ‰¥Feù~@í¸mÁñ¯Ú/þ Hß¼W} ø!µ�Hñµ‡ƒ¯õQ«ù@“I—V¿»ù-¹m-R2Wxó H2ž_Íù^_üß~Ý9o¿øã®Úù3ê*UŒ.åÑ_䔟ßh·müµWúŽŠñ˜?ks}ûq§Áëo™ì†nu¹üD/ÆØ¯ –È5€·ØIo"þÚc'˜0%A´ä‘ƒñÓöÛÕ>üWÖtËiÚÇ…|š4¾-Õ§ñدtôÔî^šÖÓìî·"0¾d†Iíþ\„ó¢ÒqI|JëUµù{骶¿ª«B)¶öÿ‡Ó¿Ëϳ>„¢¼à÷í›­üKø»¤i·þ³Ñüãu{o k‘ëÆêöú]6o.Aueöt[e™yb+<Çl@H"v ^ÿYV¡:VSë®ë»]6Õlõ*3Œ¯ËÑÛúïê´8ßÉÁ| ÿ±ÊïÿQ½n¾š¯™|iÿ'ð/þÇ+¿ýFõºúj¿yàùÒÿ·¿ô¦|Foþù?—䂊(¯­<ТŠ(¢Š(¢Š(¯4ý´5ÏøFcÏ‹–íŸÙþ Ö.wgvYLÙê==kÒëÀ?ઞ"ÿ„_þ ½ñ²çq_3Â…¦Få¼-?3Ï‹—- ˲‘ìpíošáhÿ5H/¾IKÿóh¿Ù_ðJoÏ�ù ^ê×?\j7zÿÓ?jûf¾Zÿ‚(øþŸø%¯ÁÛ}»|Í&k¼×k¹æÏþD¯©k ª<¸*1þì$zÜ_Ûq>cUu¯Wîç•‚Š(®óäBŠ( &ý½¿äÆ>4؉®é¾z¡Wÿooù1��ö"kŸúož¨Wæ^(»á=jþT� á¿âÕô�þÜVß4+o‰ðx6Kï/Ä—Z\šÌm ƒÎ´ŽT†Iöì;^HÃ(mÃÌRFâþþÚŸ þ/|IÓü#áï ÿjÚø–ÊÔé÷P‹­9.ä´7 $‘,d¢�(ݹ•w¨(CÉ£B¬—4bÚßnšÿ“ûŸf}KœSi½­�­÷ó+z®èõ:+‹ø=ûCx?ãíÏŠ¢ðްºÃø']ŸÃzÖÛi¡[MBG–dEm'Ïd9ÀbAÅ +ö®ø}®ë>8°²ñ“ü8„\x„Ão4‘X©3© M“:½µÄl‘3²I £q¶“¡U^ñz$öèígèî¬ü×qó.ýmóíë£Ðô:+Æfÿ‚�|&°øK«xÖÿÄ—Ú6�¡ê-¤ßëh:Ž�ª[]¬äÀtéàKÍÿg>~?êCKþ­KdŽA,a—�à ûS©B­=jE¯UnÏòiüУR2ø_õ·ç¡ÇøÓþN à_ýŽWú�ëuôÕ|ËãOù8/�ö9]ÿê7­×ÓUûÏÿÈ–—ý½ÿ¥3â3÷Éü¿$QE}iæ…Q@Q@Q@Q@Q@Q@Q@Q@Áyk;¿Ù×ö(¹ðLJšWñ¯Å«ŸøE´˜-óçùR ]H€rO–Â!ŽC\!í^Oeû|Dÿ‚Ox Áßü¨]x¢=+Fµµøƒ£ÈåÒí•G�2`å-†å¢Æï™ÀËð›ÃÎÿà»÷ú¿ß ?f qoj~ô°‘‚·q»íK# éè{â¿På‰g‰‘Ô:8*ÊÃ!�ê¬øk6©GWnjRýß+øeýïü›gº±÷¼y–QÀ䘷."ßX©+{Ñ�D½œ;®Zi9Gfç©ä~ý·¼ñöcÖ>+Xj`ø{údú–­�·:w‘–HdLðàŽpÙIøëþ äøeªüBð¯ÅOÚGÅ�çÄ¿|Gp֮܈ìá™Ëùg²‡’&I<««=.Hlܹ›ÂGÒIþêUñWüS_ºñ�‡>|)Ó$+¨üAña‚Œ‘ec]ÃÓ̸Fÿ¶GÐ×Âc*:te%¿êôG/f“Ëò,N*—ÇËËñÏÝ�þLÑè¿ðIÿ„?ð¨ÿb_ ™"òï|Mæk×ËE~J¬©MT†éßú[5Ý=Ñè}£WN=Ó_~›ôõZ­ÑóŸí%ÿú‹ãÍïŠ.tÏÞx&ëWÐt HºÓ,®<;&—{}p'…™¶·™óÁ°¨ ¡¹mؾ ý�>论u¿ xWÅš]νa®è:®…Êøaìô› 6!¥ß‰؇(FfßáÜ}¾ŠéYŽ%EÅO}ÿò^»ý•ø÷wÉЦåÎÖ¶·ÊÉ[ÒÉhxßï€Þ>ð¿íaâ�¼©ø{ÄÚu¶•‰gá+›;ËH-&º’Ôµãj2¤ŽÜ¢B-Ô>ªÅ‚²QEsÕ­*�9ôIl–‹m¿=ÍRm®ºžgûhÿÉ�|Yÿ±7XÿÒ«ëJù/öÑÿ“:ø³ÿbn±ÿ¤3WÅ_ðo‡ì™û~üý´]ÛËÄMªÜ|ÅÈ%FK~0ÿ‚wüjý£¿à™¿ðNwÃÿ´­ÏмGñûÅ~4»>°ñŠ#ñE÷ösÙØG­4w….çlE÷3¶€Å‡é˜l]xá¨+Ê_×ܺ³çqˆP¦êÔvHúÃþ %û_ëv:í—Á?…BMCâgŒ‚â[gçD·q’KŒ™mÇý\ysŒ©¯[ý‰ÿcíö5ø=�§ì¼Ö/6Ük:žÌ=ýÆ;w®HEì2OÌÌOäoü¯þ =ûj¿ƒþx³öÕ|u/Äÿˬß|B¼Ð|ooáÛ‹A7ØZÎݦ’êݦä]—ØÌ»°£ØoØ«ÃÞ3ð�ìoð—Iø�-ìß´Ïhöž(’òñon$Õ#±….ÚIÕ™es8“t�˜1É sšöó|m4²ð.ôÓ¼åÿ?%ßü+쯞çŸ�ÃÔ©S똕ï?…*ÿ7×î=6ŠüÃÿƒ�?fßÛ+ö‡·ø8¿²N¥ãÝÜë¶uÓFƒ6�­¦‹|›u;G¹òîÞX–"mVàæ.A*2XôíðŸüÝðOö—ø ûx‹Fýªo|[ñoÝÝéÒx‹ÅQø’ít¶²±X”\¥Äà'ž—DF_ –;@`O—ÿÁȳoí•ûCÛü_Ù'Rñîž4–Ö�ŠÿáñÌ^i<ϰ}�Í/wnfeÞÐ mËg†@?O(¯2ýм=ã?þÆÿ tŸˆòÞÍñ Lðf�iâ‰//öâMR;Rí¤�Y–W3‰7Hƒ�Ç9¯„ÿáœl¿ø‰þö—Ž¿á‘|½Ÿdÿ„Úì<Â3äÈ'í[óý©ógìùßûÌãæ Óšùcörÿ‘?ÄŸö=ø»ÿR=J­Ád~|eø·ÿÜø‘áïÙöç]³ø»¨®š4´mm4[äÛ©Ú=Ï—vòıj·Ÿ1r Q’Àÿ‚.ø?âÃÿø'7ƒ4O‹j—´½[ÄVÞ%“RÔ×S»{å×õ)–å^A3îêáÛqç&¾Äi·ÓN·¶�?Év'ÙF÷õûݵü/êßsâ� ~Ã?~x6Ðx6ßáTwÚ¾‹¯èºÚ•õ®‹á mFõn­“Mdµw¸†Ó2'�ÑÚ¬ ®ÆµU _^ü7ð\_ ¾è>‚i. Ð4ë}:9d<« Kb!rkjŠÇŽ©Z<³þµm_½œ¥®ú»· £B1—?]Tþõ«l¬¬–·ó?ÛGþLëâÏý‰ºÇþ�Í_ZWɶ�ü™×ÅŸûu�ý!𾴝׼7ÿ‘uOñ¿ý&'Êçÿï+ü+óaEWèG†QEQE|ÿûDÉâü3ÿ±7Åú]áêØ¬Ú#þOáŸý‰¾(ÿÒïVÅ~%â7üŒãþùÈúîÿw—ø¿Dx§íCûÛ~ÒŸ¾ø…µÙ4OøDo1©ÛÇkçÂA§›KƱvóËV¹°´bÄ>QeM¿¼,9OŒÿðN/�5Ö5{¯^éë®xâÓÅ7�ÚØ¦n´ôÒlôÛ½Vg;àº[4wpŒ¨ •Ü~–¢¾6†aˆ¢”iÊÉ^Ûu’“^�Å6½{»û3£ ß™n¬ÿ÷Ùï¾Úè­ã>ý‡<áûo?Ä:†¼YâM{_Öt­vóDƒûCE][P¹¼’iŸ|‘m>Ydeß³qUÎÑåš'ü[Å6Z˜o>'é> ð6—£èÞÔbð�†=.ÛMº[ˆÿ´!ûc}¾IDi�Ú®Ýæ4‰›púæŠ!˜â!.hË[§²Ý_nÛ½·»¾áR�:—sW½ïçv›¿“ii¶‹²<ÿörø1{ð[Áš„Æ·‰7ÿØ÷þ£š{F½®ÚxcB½Ô¯æKk>¹¹™þìQ¢–f>À üÿ‚ þÉŸ·ïÇŸø(Ä-cöVÕþ$Ø|>²ºÓ­oãð÷ÄX<9juUÒ¬^V’Ýï`27�ö¹}Œsò�?Qà¸ß¿áŸ?à™Ÿ.á›ÊÔ|Mjž²Áv¼o*\Ä[™Ø»_ÔY%[ „¤ºÓ¤¿òHŸšã*(N¬ßIOÿJg€ÿÁ¼zׯÏüý¢uxo¾'xº[['Þ··�šæE_ö3sÛ°«ôÒ¿?à‡¿°7íßû?ÿÁC¾x‡Æþ:Ð?f�š•åΔ|yk.ŠÐÜiw_e‘´¤¼f÷/nä«�Í´‚Gì÷í«áïø»ö7øµ¤ü8–öˆZŸƒ5‹O Égx¶Wê’XÌ–�ìʱ8œÇ¶BÊà–Íz™•eW9GkÙz-Í„ƒ…§¿êõ=6¾ýµe?¶WüáÿÁ[cö� ø@�{ÄêÈNÐåþÙ@}nÏ¥xüOáŸíuû ü-ý Ÿýм=ã?þÆÿ tŸˆòÞÍñ Lðf�iâ‰//öâMR;Rí¤�Y–W3‰7Hƒ�Ç9 3öˆÿ“ÅøgÿboŠ?ô»ÃÕ±_œŸ±·À�ÚŸáŸü[ã'ˆ>6ßøÆëáWŠl<_qðÖ-WÅÑjÖ0Ø�Kx…­¢ÜHl×ìÏn64qep?Fëðÿ%|Ñ+mù³ì2l3~oòG„~Þ_³GŠÿhh2ø[Óü7ãoßN-5+¹4ŠÒòÒ{Á”G%–+�:5Ûµ¦¶„3(Ëöƒý�üUâêšÇÃgÞÕôo xkGðMÝÊK ѧÓnuE¸ó#E¹{E¡]¯’Kd(Ÿ¬h¯�£�«J µ“¾ÞŽÏºNöOOz_ÌÏbTÔ›oª·âŸÞì®û$º<|ý‚t߇—~%³½Ô5Øt?øH4ÍSÃÉ¡ø§TÒgÙèzfšôÚËŸ™,¤c†XÙYK ’�éðN_‰ÚGÄ¿ˆ¾ Òáç†zÿ‚z~Êš§ì™à¿èÏáßxÃw×ñÜhÞðιy®iú8ªM*ÝÝ[ÛÈ^wÚ1E*Xy×дQ\جLñ]Z›»wè­Öïñ.�Ò‡$6×ñwèTýŒäpøßÿcÜúŽhuîu៱�üŽÿì{ƒÿQͽο¦2Ï÷ 7ýz¥ÿ¦â~y[øÕ?Å/ý)…Q]†aEPEP“~Þßòc?ìD×?ôß=P«ÿ··ü˜ÇÆ�û5Ïý7ÏT+ó/?Ýðžµ*GÐpßñjúGÿn9Ÿ� ôߎ|QàÍ`1ÒüW¥\é7E~òG('¾ð¯‰ux†þ}3Ë*¸¾²ÔínÇæ·�‡ûHí¤Ú�$|çpúŠüžž&¤ éÅè÷ûšýuï¥öVú¦“i¾—ÿÉ•ŸÎÚ'º»µ®ïóÀßø&F‰ðÃMðÍŸ‰µÕø“m¦èºî�®¯ˆtˆgÿ„¤jSi¬¯p¤˜ÊÁo¦An‘÷¨RH*wZøÓÿÝð¿Åïˆ>dðÿÁýÁ>b±‡áÕ¼šõ’Ú܈íìu#8ŽÆÕ¤XÃG¡m¾hY¤ ŸJÑ]?Ú¸¿hªóê¶ÛM[Ûo´íÙ;+#«Rön—/ºïøêþöxÁØË[øeñSKÔ5?Ùë~ð}ƱuáMÙÝéï©LÒ?Úï>Ñ º£É[!ƒå�™<× Ãß袹kW�VœúiÓ½úo«ÝêkF7åë¯õÛÑhŽ7ÆŸòp_ÿìr»ÿÔo[¯¦«ó'þ ‹ðãã/Å¿Ù{Â~ýŸnuÛ?‹º�Œ! Ú6¶š-òmÓµ¹òîÞX–"mVàæ.A*2Xé_ðowÁ?Ú_à/ìYâ-ö©½ñmÿļgww§Iâ/GâKµÒÚÊÅbQr—€žz]|‚Xí�?¼ð?ü‰iÛßúS>#7ÿ|ŸËòGÝ”Wæü�û6þÙ_´=¿ÁÅý’u/éãImhø¯þŸÅáv“ÌûØüÒ÷væ`6]í¶Ü¶q¸gîÿØ«ÃÞ3ð�ìoð—Iø�-ìß´Ïhöž(’òñon$Õ#±….ÚIÕ™es8“t�˜1É sšúÓÍ=6Šüvý®dÏÛ÷Åð_}Ç~Õþ$Åû0Áã Ý\ÛYüE‚ÏFþʆÇæ—»·3²ïh¶å³�Ã=´!ÉJ0ì’>c6Å}k[üó”¾öÙúyEy—ìUáïøGö7øK¤üG–öoˆZgƒ4{OIyx··j‘ØÂ—m$ê̲¹œIº@Ìä†9Í~aþ×?²gíûâ¿ø/¾�ã¿jÿbý˜`ñ�…n®m¬þ"Ág£eCž5Em,Þ«²—KÍéä!$€Û†u<óö&Š+óþ Çýœl¿Ùûþ7ü5¾¥ã­CûbMü+ÿ �¡ñ/–Síÿmò|»©ü€wÚd�°¸iÀÜ··ü˜ÇÆ�û5Ïý7ÏT+à¿ø:gàGíOñ‡á€¯ÿg›ÿÙøGÃ:g‰.¾#&‰âè´8f±0Ù4Bæ'¸„ÝÇåG{ûµY8, üà½+óäý–6ëSò¦}/~«ò�þÜ|ñûü&øƒâ}3ž/øKg ßÄ? O{§[¬“Å›FÑí¦fid3‹+¶Pw0²*¡˜¨>[ñŸö2ñ÷ß|Ð,.5o‡> ð®‰à‘¨Eoo|Ö“ë×Ö“1%•E•ürneÚÎ#Û¹”ãíš+ó*�JTÕ$“_=UïÊìÖŸþ-uVúYÒS½ú«äÊWõvŠ¿D•¬îßËÿ³Gìg⟄—2Ó ñ—Šü§�éš…¥þ”ºUÄž*·ƒÃúMœæä][\˜ÖK›k�û2’ �3Zx§âG†¿jß‹Þ(²ø#ã{ë ¼'e§è3ˬè0[x‚óN›R�D„j4)qö¸¼·šÚ™<ßMQELÆ¥FÝD�⣵¶QWÒÚû«Ë{!F„c}-׳¿ãÕïæ|I¯xgâ_‰?aÏ‹ö³|ø†ÿ~+}¦=RÝïü7¹¹¸±ûßÃì–†cœFXß÷z Ö«ÿ^éûÍÛÎÐ^r=÷þ_û#Éû,þÁÚæ­ ‹â߈Ïÿ N³$ÜÌ<õÞ6'œ¬ �òIÜšû"›k jˆ¡UF@ÀÐWûRüv±ý˜¿g/ü@Ôv›o h÷ˆ�Ž>Ñ"!ò¢ï&Äì+³„T©Ã K¥’þ¼ÏŸâ,ò¶m˜×Íq_Y9?+ì—”V‹ÉxxÃ}ÿÁú•ùÿKðgìÍ£}–.wBú¦YpGiâi�ý¸.zb¾”ý¯à›Ÿ|Sÿ á–¤|ñFÊCuí«mµ);ùÁ~ã· È îɯœ�)ÿƒu¾_x/ö3Õ~&xƒ|Þ*øÓ®Ük÷—®%–Ý$xáÝë¹þÑ(=ÅÀ¯¿ëéªf•ð8¿öI[‘rù>é­šnçÊCOC÷ëâwó]¬úY~Ï?ðSkÏøÂ?‡?´’þñµ¸¦­2ôíOœ ‡Éì}õ&"Crœ-b¬éûLÿÁg!x™.ôO„šuu;¡y™2=7 nÇ×Èö¯¨?i¿Ù÷ÀŸ´Ã[»ép^i–1Ir·yòî4íªKIƒæBç±Æ#ŠüÆý€uo‹Ÿ³7„µ�‹ðrøËÀ—÷ͦj¶çÔ|›pdR«¹@�Y.Q·&Mœ_œNµF½îé¿‚VþI?…ÞÞì´è™ðÜOË�Ë159 ª{m›“�-lí¥”å~©=×ê+Å?eoÛóá×íod‘h¯Ø<@3èzŽ!½Œ�ól\âUÞBp1�§Šöºò1x:øZ®Ž"2]úEôëAT¤îŸ`¢Š+˜Ô(¢Š(¢¾kÿ‚ÀxßTøqÿÛø©­hÚ¾½ ê6:| þ‹uqk¨[ƒw¹†Kr&W(Ì–ws�É©œ¹båØkséJ+óŸÂÿ´æ¿û‹¿ü!៎þ.ø#§Eám+FÑ~#ßëš¾£â íPY^6žþ"Π¶ËÕ‰Äå-šdq.eu÷-_öÑøÇqãÝGÀ>ø;àM{âW…<=‰¼Q§¿Äk‹M"ÂÞêk˜´û{[×ÒL—7S‹K†e{ha‹` 9Ü3NÊý5½ôµµû­fŸfº»+×¶šßMmúÉEÿzë¡õ5òG‚঺Çí4Þ�àgÃk\_x&ÃÇš¼^%ñ(ðáÒí/žhí,ã1Û^ ¯�­n¾F1@¢MÆWûRÿÁqü-û6ü_ñ¿‡gƒá¥¥¿Âè¬åñ=ŸŠ¾%ÙøÄ×M5´w’Å£éf†£$Vò§Þ¸·If&(ݰ^›V—#Ñëø;?’z_mµÕ &ôIôéÝ&¾m5¦þGÝÔWËðñ«­{öÖ‹á.�¤ü6Ó¢°Mm/‹¼y&…¯xšÖæßí/y¢iƒO�u+xãÞ…ÅÌG΂taMíkà'üSøïû\ø§á´z'ç„õë+Í3Uñä�øìCm…[ÿììý¿c�ž'ŠqzQàš)3¹¼ ã6¢·³(ÚÿuЛJ<Ý4ÿɯo¾ßÕÑôíñí=à}ãü àæ‘­|=øsã?xgÁú¶½§�{\‘ä°¹[ý!$Ôâ²m:X…í¾U Äë¹f•¼ØH û_ à¬6?lýá¡kðº¿ϪYYÚø{âm§ˆ|M¡ÜØÆÓ4:Æ™¸ŽÄ˜â˜ÝÈIQPýíË4ýøFQÞ\ö^P“‹üSÿ‡ºN£P”“Ú<·~rŠ’üô¬ßØtWç7ìñã¯øXž7ý‡õ�µø²ÿÎÔ<{ÚHóóö//�nì�»ZH«¾ˆQw‹—D›ù%wøKQ_ZÿÁfµm'öEð/Åïü<ð?€t_‹W¶:gƒWÄ?#³·óåŽyg›U»k#O´Hí¤xä�®f”4j`ŠF1¯»~ÀŸ·v‹ûsø/Å76ø>]_Á:Ûhz³xOÅ1x£B™Ì\Å-ž£Pùñ´3G�ÐÄÉ*Ë_“skÉ+Ê=c¿–ßü’ûÇQ84¥×þá£×m{¢Š*@(¢Š+å�ÙËþDÿØ÷âïýHõ*úž¾Xýœ¿äOñ'ý�~.ÿÔ�R¯ˆñþD‹þ¾ÃÿH¨zùûïý»/Î'€ÿÁK>-|SÒ~,ü5ðÃ{¶³µñ.�¯kÚçØõ(´ÍJæ ô˜#‚ÞæK{…�qÔ™˜�Ï–»f�?ΗZ¬Ÿò:|#ý¥5‰G->§ã‰|I½&¯)Q“ŒP�Äí“_HþÛúý�„nO‚·ú­í¦™cuàïépÜ]̰Å-Ü·¾–;uf ^;yÝP|Åa��…8ߪàŒå4ªò.gÍwe‰­ý8­SëR�Ý´ü‘òð§À˜Î.ÿgÏÃ?Vðúúå�¹’4u$ÿ¼O­ðŠ|“‹_ÙïÆÓOÕU~ß[·Ô<¨ŠÿxNkëÊ+ìù#ØònÏ“ít+(¿äKøEûJiV}/ÆÒøn÷iõx‹ €hœ�ªv�DÿÁ4~+üQÕ>-üKðÄk©®¬ü5£h:ö„·Ú¤Z®¥küú¼Cqu¼ðtÕeJ˽³;‚>ž°b=rËÅ?·7Æ«Ý2ò×Q³¶ðg„4Ù®-eY¢Šê;ïÉ%»2’¨“À̇æ 4dŒ2çã8߇þÉ«W‘s.[;+üIoèzÙ=ZŸZŒní¯äÏjý´äξ,ÿØ›¬é Õõ¥|—ûhÿÉ�|Yÿ±7XÿÒ«èK�Þ Ô´�_Ãâ=1­<q5®»/›�¥ÉÝ"Éž˜ç¡ �È"¹¼3¥RymG·ïôóQ±\E8ÇgowõeÚcö�ð÷ì¯ð‹QñˆåŽ òí­‘€›P¸`|¸#Þlz( Ç€kæ?ØösñÇÿŠþÑŸ!ó5½cçð®—"Ÿ+KµçË™PôN#œ!Ë8aËü)ðŽ£ÿeý¥_âŠ-nm¾ øå­¼?¥N05‰”‚Yס ÒvÆÈ†pä~€ÇÃUPª£€~׋’ÉðÏOýâ¢ýãþHÿϵæþÛÿ·OŠ¢ž:²ÄOøQøWó?æ~_Ë÷Ž¢Š+ä�p(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+å�ÙËþDÿØ÷âïýHõ*úž¾Xýœ¿äOñ'ý�~.ÿÔ�R¯ˆñþD‹þ¾ÃÿH¨zùûïý»/Î&'í‘ûexSö$øi§x�Å~|£\գд»h¦··ûeì‘M2ÆÓÜI¨ŠÞg/,ЏŒ�¹Š«y›ûj|gø•¦-ï†üðŸLÓfæË¿ÜêÎã¯1[ؤcŒ}Û†ëíóoÁG`K¯þαJ‹$r|K¹WFVÂ^$ÜW—k¿°ïÁÏêR_\|0ð xŽ4e�_ÔtC�Õ·5­Øç®ãÜs¸t¿²�üs@ý¤>3j? /´ËmÇú^•.·-•Ž»i­ÙËiÐÃ#,Ð0’6Y.!.!…˜I”öø¼_°/Á“ k¿†þÖ œ¯ö½ ÔÀÇL ‚ý v<Žk¦øàí#ÀŸðQ/‡:n‡¥éÚ6ŸÃ?yv¶6Éo ÄÓ½QùWÎñ' etrú¸šùeufÿ&Ú;òüÓ:ñ§9]3èOÛGþLëâÏý‰ºÇþ�Í_ZWɶ�ü™×ÅŸûu�ý!š¾´ªðßþEÕ?Æÿô˜“Ÿÿ¼¯ð¯Í…Q_¡QEQEóÿíÿ'‹ðÏþÄßéw‡«�ý³~.j?cïŠþ<ÑÒ]gÁ>ÕõûºBð=Å­”ÓÆ$PA(^5È3È®Ëöˆÿ“ÅøgÿboŠ?ô»ÃÕåŸðR�*ë^ÿ‚t|}±±·žòöóáLj ··†3$“ÈÚeª*ŽK@rI¯Æ¸ê1–wF2ÙÆ?úT�«ÉXIµÝþHø³QÓ~'\ÌÍñ;Ãß|{xãâï¿£·Òä>¿c‚çJ]¾‹öf#ޤn¬É|+ð�›:×ìýñ2F_”¾¡á«�a¢nê K9Æ{¦TõÏC_SxCÆš?Ä WAÕtÝoKºPÐÞX\¥ÌŒg*èJžè{Ö�~¹G‡¤­J +É$|ĪÎZɶ|‡ÿÇÀú >3ÿÃi©ÿñš–ßÃ`?ñO|øËc ù£þƆ}¨?ƬomÂÀ<† àŒg[Ñ[J”$­$ˆRkf|¿áωÿþøëÁZÎ…eñ/Þ¾ñ—‡´gNñÿ�cñ+ÝZêZ½¦œâ8ËßJ$Qu½Yo¢ÚPæ(1·éÅ|!ûVk–_ÙþÑ~Ùký±©üIðT¶vjý¦í!ñ>™<ÍyÜâ8b–F ©±ÀRGÝõø×ˆ˜ZqT�¨Þ.öV¾½l}nCRs¥.g}JŸ±�üŽÿì{ƒÿQ;8ÿ‚ñ9ý¤ÿj�Ù‡öv�ÚKox�kzä yKUu�dÇ´&üÿÀké…¿´—�ÿeÖøÇ¯|@ñ—…ô[ï‰VšdW·�ü�´Iá­‘Y•H@Doó6c“ȯ˜¿eOéÿ·ü)ñâ.—}g®xGá…ÓJÑ5 I–âÞI¤�`Ìr)*ÊÍ6 Á”à€=k÷.„¡‚¡ŠkHP¦ÓóöqKñgÄfrR«:=e9}ÜÍþ‡ê,q¬1ª"…UUAN¢¾aø‡ÿVðÁ_Ú_ø{ã[-gAŸEš$]N8~×i*I r«°OÞ§Ë à#t<×Z𦓨ís‹7Ïrü®«˜ÕTã9r§-ìݯ²Ñ=ìŽ?þ óãýCź/�þ øvOøžüKÕ¡ Ú¤Šxþ锫gÒ¯¬¾xOøSðóDðÖ•—§h60ØÛŒrR4 ÷8É=É5ñ_ìi¬Ú~ÙÿðQÿüY3Çu¡x&Ùt� «pJ°h„ʧæ PNç#ƒr:c÷…ràÿy)â;è½ù³äø-¬Ë‹â&ï²öt¿ëÕ&Õ×øçÍ/¸(¢Šï?B (¢€ (¢€ (¢€ (¢€ (¢€>ý¢?äñ~ÿØ›â�ý.ðõs_´ïí!á¯Ù#à~·ñÅòÜE è_gI¼…V’Yn."¶‚5ÞÊŠ^i¢MÎÈ‹»s²¨,:_Ú#þOáŸý‰¾(ÿÒïWˆÿÁP¿äÔm¿ì xÿRí¿ ã<<1A‡¡Si('èæÑõ=G IÇtÛÿÉQÃxwöþø¡ñ›KþÐð7Ãÿ†qhó Ðßj^?7Ó(=7Acg,$÷ùnÈãœäK'ÇÚ>í·­çÁ+ÓÉ:.©w�3íq~[8õ5ËøÃö4øIãíYµ cáŸ�/õ'$µìš·ÚŽzþô ~{óÍdÿÃ|/‹þ=ôMVÀÿ±ñ§h_ýã–ükî)pfOMYQ¿«oõ<‰fØ·öÿ#½ÿ…ÏûHÿÐgà‡þ§ÿ,*{oÚ7ö‡Ñˆi´/‚þ$ÙÁDÔ5=Îï�Æ+Íž˜Úýsž6Ÿ;ÿ†øsÞÛÅÄzkD¨û]ðÀæâóáöƒ«'üòÕQõ±Ül��p{Œ`àg8«—dòVtÞÿÌK5ů·ù§ðCþ s¦ø×ö„оxËAÒü/ã�›…Ò¢ÑüOk¯ZÜ<òÜÉàCuòa•ƒIl±�…w‡dVú–¾ƒá—†þ~Òÿ³U‡†<=¡ørÄü@¿&ÛK°ŠÎ"G„7ÿØ÷þ£š{�xgìcÿ#‡ÆÿûàÿÔsC¯s¯Ü²Ï÷ 7ýz¥ÿ¦â|]oãTÿ¿ô¦QEv…Q@Q@Mû{ÉŒ|hÿ±\ÿÓ|õB¯þÞßòc?ìD×?ôß=P¯Ì¼PÿwÂzÕü©AÃÅ«éý¸üñý¦~)übø¥ûV|SÑ,GŠõ‡_õ›-ÓJðw‹#ðæ¥,’hÚn£,Ó9Hä”–¿*¥o¢P±�åg27uá¿\Ÿøª>|{Ôn‡þßÕ%ñ3Íê¯ Ôÿ3I$dçßá�~ÇLý´ÿh½æöÒ bûÆ:~©mc$Ê·7‡ÂÚ"á#'sDe·�7�·t2 åH‹_[ø,42ê„nmÙnÒ»<Ü}j�4ÛÝþgÈð‹ü~xÌãþ®¤qøˆHü�9<-ðT6ý/öø†’/k/Þi’ÇìÌ.Aï´‘ë_]Q^÷$^�WgÊ–Z7Š-Ùá^øö�ð…Âÿ©žóâh°Ò¡nÙ´“P¼L?åɆ��ö—ü›âß‹þ5þÉzF±ãÛ‹+ÏYë:öƒ©\Zmò®_MÖ¯´å“+JÌÉj¬Ì±D¬ÅˆŽ0B/!ªj¶ÚŸ-ÝíÄ–°.ùfžAq¯«1à­lÁ,çKïÙ;È]f´Ô¼kã;ûIÐîŽêÚjÓC4l8xä‰ÑÑÆC+«A¿8ñ‡†a¥Î•ÒW·,¿àþCV¤«8¶íoÕ­ãOù8/�ö9]ÿê7­×ÓUó/�?äà¾ÿØåwÿ¨Þ·_MW¹Àÿò%¥ÿoéLáÍÿß'òü�QEõ§šQEQEQErÿ|syðÃà׋|K§ié«_ø{F»Ôí¬^SÞI /"ÄX·qP¹ÁÆzWâüWþ á£ÿÁD|=ð·G»ðuÿ„l|+â¸5½i úߤð¨ØDD$d°G“‚£9×îA­éw6w1‰m®âh%CÑÑ�Vˆ&¿•Ï|8Ô<5ñWSðŒvóÝêº~­.ްGif�&1U–,0©¯Î¸û1ÆacJ4%îNé«'ªi¯3û7è�Áœ9ŸÕÇVÍ)´a\%NjR‹QœjFZ'Êídï(»6Gÿ²Wü'á_í¹&¡ÃÝróS½Ò!Iïí§Ó.mžÍ\áw; Œ’AÀW$í>†½²¾pÿ‚\~Ãv¿°Ÿì¹¦è7BÞ.Ööê~$¹L6ë¦QˆU»Ç á8$; o5ô}}¶YL(¢ŠòoÛÛþLcãGýˆšçþ›çªööÿ“øÑÿb&¹ÿ¦ùê…~eâ‡û¾Ö¯åHúþ-_Hÿí´d×âÏگဤٮ|HðŠÛ‚m¿ñ¥¹É€ùä‘ÏÒ¿:.¾øÏö—ñ§ŽücâøWŸí_Çž(Ò¬´?ÙÞÜA¤Ûiúíõ…¼0:[XãZÇÂÙ+nÜìÎÄšì|9àoø <«Ùçà�¡ÚT6�â�*"¤ä‚Hˆ‚HÉPéóœpà|8ö´£Vµ{]'eþmþ‡Elÿ–N0†ÞgÙ¿ðÞ_¿è³|(ÿ»OÿãµgHýµþ k÷~E�Å¿†W³í-åÛø¢ÆGÀêp%'ñïü$~9ÿ£{Ñÿð¢ÓÿøÝWÔ ñ÷‰m¼§ýŸ¾¼r‘êÞ*„mn€¸�O˜;gŠíÿˆiC¥w÷/ó1ÿXgü‹ï?D4½^×\³[‹+›{»w$,°H$FÇqV+òÒ_؇Ä>&Ô~×aào�?µÀ:¿„ÒþçVˆŽæÕt·v±$.~ë ƒ÷ìãÍ_âŸì!ðSÄþ ¾—T×¼Gà- TÔ¯eÇ™ws>Ÿ²ÊØe�™Ž9¯�â^–QTöœêNÛYþlõrüÍb›�-š:¯ÉÁ| ÿ±ÊïÿQ½n¾š¯™|iÿ'ð/þÇ+¿ýFõºúj¿QàùÒÿ·¿ô¦|Öoþù?—䂊(¯­<ТŠ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(™øÏñ_Iøð“ľ3×eòt i—¥ãÉŽË•_V8Àɽ|ÿú|'Õ¼}Έ¿´Ÿ‹âÏŠ>4ë× hÌ8ŠÆ)˜¿–O!àºc¦ÛXñSÁÂ5oü=øuû;xA÷ø³ãv¿om4jOî좚ûTVõWšÛnìëJÊÈðoø)ŸÅ/øTÿ±'ŽïM—Z��ÑíÀ8,×L!l{ˆÚFÿ€Ö—üÓáwü)ÿØËÀCÇåÜ˦.£rù„·$Ü0ouó6ÿÀkÂÿà¯SÉñOÅß>[;gÆ>#[›°‡8�¤ Ǿ1<Íÿlý«í{khì­£†$Xâ‰B"(ÀU\ýüTåü©/¿Wú–ÿ¶qf3ÓN—¬ÿy?Ñ;~Õ?ðLo‡Ÿ´ÅûëpG7ƒª¶W7ZƒtçÝlýVÏóó<—ömý·þ~ÕvIÿŸˆ`}Ofùt‹Ì[êàdæ"~p;´e—Þ½j¾iý£¿à•ß ¾<ê/¬éÖ×ñP95M “¨y!áç’˱ÉêÕäç_ýª¿a#�BÖ޾´ÿ—ˆ·�V‡©ÌRK,êñ èþÉÀãuËkrËþ}Ô²~‘ŸÂü¯ÊÌþ»‰Ãé‹…×óGUóŽëåsîê+ç?ÙËþ �ð£ö…’+«Ÿ x…›Ëm/]ÛlåúmIIòÜç€7?ÝôZ¶á‘È<‚;ׇ�Ëñ8:žË y¯Ë¿ÈôpøšUãÏFJKÈZá?iŸÙïFýª¾ëþñ Χg£xŠ8â¹›N‘#¹AÉ(ØÎŽ£æŒg*x'ë]Ýy7íÓûM\þÆÿ²g�~&YøoþûÏ Ù¥Ì/ö€°þÑ‘æŽ%�Ï) �&Aó#�Ƹ*IF.RÙ]…gËÜÜý¥?g�ö¥øO7ƒ¼AuªYé“jZn¨ÒéòGâ[û{è@.Ž»L¶ÈmÉRÀ$0â~9þÃ_>.ÜxïAø‡ñ#ágŠ5] |7­ÞxBêÁ?·ì#‘ä‚9ÒöÒåHZk�.xS¨¸�y˜Úá�üONø±ð=7ÓÆ>5&¯ äw¡.<){¦[<—vSF‰%Žâ9mÜnM­›¥zF™ûc|"Ö¼uâO ÙüSøqwâoÛO{â "ÙI}¡Ayn o�#,¡Ú@¡w ‘š¹ÅFNœ­¥Ûê¶´Ÿf¹t{«y r�»¶‹ç{¯Gtšêšî�8Õÿà™¾Ñï¼??ßxÿàËh^‹ÁR�]Yí]"/oo9¾µº!ág˜¥Ä&+€n%>i,1©âoØD'ÄýsÅ~ø¯ñKáMïŠÅ«ëöþmþÛZ¸¶…m⺔jÚ}ó,þBG_Ûé>‹Ä––ήtÿ?U—U–øÛ[ZG5ÜðkOš[mžps²œ¥;-ýæÿòY;ßuz‘}UäßI52”cg·º¿8¯� ×{E-¹S÷OŒ°çü/ÿZÞø»â—Ä�_ÁöZ͆»‚ nleŠ{cçG§.£…¸†9±öÎX$ÆLt­ûüxð÷Ž|añCâ7�ÁºµÎ·áÍ VƒD¶Ót+¹¡šÜIYiÖ÷Rบ%Yî%R.¸ï¼kûK|9ømñ3CðWˆ¼à�Æ^'Ûý� êZå­®§«nb‹ö{iK6Xä‚:Ô7_µ?Ã/�±|3›â7€âø�pžd^}~Ñu¹Ê3n[3'žG”¦L„û ·AšPvK—»kÖÊïÕ(­wV*]T»ké¯àîü�É5ï€:/‰><韮fÔN¯¥ørûÃÛ,ª-$µ¼žÖyY—nÿ05¤aHpgÈ$‚Eا-ÀçŠ)»(8m%îù©^Zwø¤×kÉw¬ÜÔ·OÞò²Q×ä’}켎á_üßÁŸ‡ÂìÝOÄóÿÂ�¸Ö®toµ\@ßjmWÎû@¸Û î ç>Í›1�»w9ç>Á.<-ð÷â�5¥ñçÄýWKøY¬_jþ ðåî¡eý‘áÁyoyo5ª,V±ÍqŽñ„fêY¥ˆD‹¨�*Ëêþ9ý®>ü0ñÅ—†Ò®µ½^óÉ’²YÛBóO/—´�¶4fÚŠÌq€ À¯ˆ?â(ߨSþ‹—þYž ÿä÷ÿø+ü¢Ëö–ÿ²Uâ�ý4]WàWüþ ‰ðöÁý…­ZMÙJð’Z¥%»RV÷ŸNÚž…l.c”â¹q�*–·-HÉ;;=Ÿ+èvÞ4ÿ‚Û~Äß<7q£x‹âÏ�5í"ðm¸±Ôt«‹«iÇ\4oVüExÅßí_ÿÈvÿ@ñw�ü6�£ðÛêú J=Y”œŽÐc±ÿˆtÿcú#ßùuëŸü™GüC§ûÿÑÿ˯\ÿäÊñ¨p6YCøkÇÒ¤WåLSÎ1SøãëÿÉ!ý«à�=¾<øÕeOŠž:U_`æöÃVÿÁ:{üyñ³á¾*xé�úƒy‚+»ÿˆtÿcú#ßùuëŸü™QÜÿÁ»Ÿ±••´“Mð…"Š%.îþ-ÖÕQ@É$›Ìv«ú ÄÿàåÿÈ,«vTéÿàÿ’9+OÚ¿þ ’�þŸâÿx‘;Çâ9u}v&Ý)z%R½~R1ó71ϲøþ cûü1ðÕ¾�Ὴþðö�h‚ÇLÒn--¡Ï÷cŽÜ(ü|ÿû<ÿÁ$?àžÿµµéðøo®tù.,åñV¹ Ôjh“Ëk̘ۂdrÁÈ™ÿéþÆÿôG¿òë×?ù2¼ßõ;(ÇAUuëT‹ÙûXËÿqž¶dóŒ£,>‚£V;ÆTå/Täž»®çOñ»þ û;~Ð_¼sá_üCÓ|k¯kZÍ‚éö×*ê—`iYž5UUg“Î095ñ‘ã­FÎ[øgÔµc§k²Gý±7l‡RE�I‡'!›pÈ, Î)þ>ý’þü ý u¿üð1Ñí£Óå[[ëÝR}Nâ-ûŠ™å•À Ì !ìÏ9®ãIJµ÷ÂOÂOñ7W°ðŒÜA Æ.u;öÆvAj‡,Þ¡Ù6õlkúK€ò�‚r9UÅÕö~×÷’ö’M¥ou+(ìµÑ^ï}ùFd³Ž*Î!�Ë(ºÕ/ÉÓ‹nMï¥Þþ»véðpìIû1xDð5×Ä;¯˦X@�h²x?\™í•”¾d6’G+n$3£¸gÝ󚵫ÁÎÿ°îƒ¨Ëiyñ²Kk¨l±I࿆Œúöµ~&|nÖü=ñ3ƶz…†ƒhÞbi÷7»f¾ÚøÜÌà\ã…Aòä‚Í÷�éG„?àßÙSÅ>Òµ/ü+:—‰/졸Õnωµˆ�ÍÓF¦i6%Ú¢îrÇj¨Qœød8“™æ¸Šy[”èÃU9häÛí÷êõ{ÙÒ¼sà¶iÁ7€Çq£ f&R^Æjœ!~i-¯(éÆ+íJú}ÿFþŸô\¿òÌñÿ ÑÿFþŸô\¿òÌñÿ ׆ÿÄ:±¿ýïüºõÏþL£þ!Óý�ÿè�å×®òez§ã§¹ÄQ¿°§ý/ü³Ñû¥ÿÉ'kÿuðn²Ûføçû6øpãF×õ½¹ÿdÙÚg>ðÝœü¸ÁôÙïþ Yû+ü7ø±�¼kûTxWÆ^,´Ò.ô+Ó¼9u£izu­ÔÖ³Üà"y™Ýì­¾inh‹ së?â?Øßþˆ÷þ]zçÿ&QÿéþÆÿôG¿òë×?ù2¼ìÃ'¡Œ¤èÖ«W•¯ýÅÄÞ†*¥))Â1ºò—ÿ&i~Ó¿ð[ÿÙKâì×ñ @Ñþ4øbóW×<3©iö0y7QùóËk$q¦æˆ*å˜ ±g’{üEû ÑrÿË3Äüƒ^ÿéþÆÿôG¿òë×?ù2�ø‡Oö7ÿ¢=ÿ—^¹ÿÉ”²Œ— •Ñt0œÖnïdºF=‡‰ÅÕÄOÚVµím_›g¹ÄQ¿°§ý/ü³|Vÿ„—ÆÞ$ûGön›ÿαgöŸ"Þ[™{qiK¶do™Ævàd�É¿ñŸìoÿD{ÿ.½sÿ“+áoØOötðoì›ÿ‚x[áÿÃýûÂ:ÚþÁaö¹î¼�;ÁSÜIûÉÝälË,�ó1Æì õsúZ¢Š(äÚ�þ Õû'þÅߵφŸþ+Â5ão ýŸûKMÿ„gX¼û7ŸoÌ_½·´’&Ý Ñ·Êç°pAÏÿâ(ߨSþ‹—þYž ÿäü¤ý»?gOþÖ_ðx'Š~ü@Ñÿ·ü#¯ý“íökž×Ïò|ļ�ÒEıFß+ íÁÈ$ºâ?Øßþˆ÷þ]zçÿ&PMñþý�>*þÒž ×ôo�:cé†uÍ>ú{ÍT°ò§»ºÒ$�g¶F}ËgpI@Áv ÅK&î—þßû ÿÑrðÇþÞñšóOø‡Oö7ÿ¢=ÿ—^¹ÿÉ”Ä:±¿ýïüºõÏþL¯�ÍøO-Ìë,F+Ÿ™+{²ŠV»{8K¿s· ™bpÑp¥Ëfïªoÿn]Œ¯þÝÿðMO‰¾!¹Öu|#“[¼m÷¥¶…qg¨\ç縆•ºž¬~ñõ5Í¿íYÿã÷üC§Žë§üEñ�’7ÕaºPOÔWsÿéþÆÿôG¿òë×?ù2�ø‡Oö7ÿ¢=ÿ—^¹ÿÉ•�.ÁR\´«â"¼ª¥ÿ¸Ë–i^ZÊßýºÿù#„ÿ†­ÿ‚uÑ{ñÇþoÿòmIíYÿÛÏú_Å«�gÔk>3ñ^¨v .g�0@9ò0k·ÿˆtÿcú#ßùuëŸü™GüC§ûÿÑÿ˯\ÿäÊÕð¶«ÿÁN¿àž³íü·žñwÂ_ ê R{Ý3òÛÝÜ�úi2Ûù�Й�ÕèðþÿÙþ‹—†?ðóÿŒ×šÄ:±¿ýïüºõÏþL£þ!Óý�ÿè�å×®òeyÕ<>ÉêKš¤«7ç8ÿò³xç˜È«ECÿü‘óoüßþ ƒðö™ýš¾#øÀ¬|Mâ�|KÓüWaiicw²}6ÊÊIŒÍ‰JÜC"ylâNÚCƒ¾~Óþ7ý”õɼGà�ë¾ º�CÜÜi÷¯L‰’ªÙdü®sÒ¾ïÿ‚ªÁ%ÿg_؃öa‹Æ? ¾ÿÂ3â‹�jÛLŽóûwR¼ÌR$®éåÜ\HœˆºíÈÇ»/ØþSðûâ_ìóo­üwðõLj¯¼R‘_Yhëª^X&—lFèüÃm,lÒ¸`ÅX�Ÿ(À`köLŸC“F�®£hE=[åŠJú.‹Weè|^2…LF9´õz¶º]·§è}û"ÿÁÍŸ²ç…ÿgŸ Zü\ý¤ì|Kñ ìÂMbòÏÀ:å´ +så*ǧ„mƒ \‚@€>býµÿl…ß·Gí¬üCøEâuño…u­­^øi×v¹†�ã1ÜÅœ(C�¸;¸'š÷Oø‡Oö7ÿ¢=ÿ—^¹ÿÉ•å^!ÿ‚t|8øKûMé_ > hðŠè×P¦£¬Çöû»õµ™²e˜µÌ²8ýÂC… ’¼ÄŸÏ3ÄêÒÑjä´^}�Îüm˧‹áêxZ+š£­MAur|ÑKÖÍœWì_ûnþÍß²/í;o¬üsøžžºðô }¥é�é�ü—“9`�!³·”"&ÒÛ\©bS‚¹Ïߟñoì)ÿEËÿ,Ïò xÿ‹¿à€¿²wÄÝk:÷Âû�[W¿o2êîk!çl’íUz *¨P0Y¿ñŸìoÿD{ÿ.½sÿ“+|aé(-úúŸ_À\G†ò˜`a¬ß½9w“ßä¶^Jû¶{—üEû ÑrÿË3ÄüƒGüEû ÑrÿË3Äüƒ^ÿéþÆÿôG¿òë×?ù2�ø‡Oö7ÿ¢=ÿ—^¹ÿÉ•Ø}¡î_ñoì)ÿEËÿ,Ïò ñoì)ÿEËÿ,Ïò xoüC§ûÿÑÿ˯\ÿäÊ?â?Øßþˆ÷þ]zçÿ&P¹ÄQ¿°§ý/ü³*þÒž ×ôo�:cé†uÍ>ú{ÍT°ò§»ºÒ$�g¶F}ËgpI@Áv ÅK&ìßÚ'þ õûþÒ? ¯|'ª|Ò´Èn.¬µ {ë {�scwey í¬éæ[‚½Ïþ!Óý�ÿè�å×®òeñŸìoÿD{ÿ.½sÿ“+ا…„�Z�ÕÓý)#–U&öŒWÊ_ü‘áðú_ Ðáû3áÅñÿ355¿üÁúÃ/Æ?Ù¯ÃÁ¹,š—ˆ5Ÿ+¶1ý›i¿×9^˜Ç;‡·Ä:±¿ýïüºõÏþL£þ!Óý�ÿè�å×®òe\¨A«)Ô_8ò±)Ï´~éòGŸ|'ÿ‚‹þÌöß¼1ã/ˆßµ‡‚|K{à˹µ-KÐ|-y£éÖ·RÙÜÙ4²™MÌó2ÛÝÜ ýähL…Œy ·èÏøìƒÿEËÃø yÿÆkÍ?â?Øßþˆ÷þ]zçÿ&QÿéþÆÿôG¿òë×?ù2¾g0àÌ»WÛâªU”­oŽ|©… ÛF<””ô—ÿ$u_àâ/ØÛ࿌~(ÿÂAñªÆñ7ŠaÕô×´ðþ­~—ÃFÒí –·µ�!ó­g]ŽC|�±µ”ŸHÿˆ£aOú._ùfxƒÿ�kÃâ?Øßþˆ÷þ]zçÿ&QÿéþÆÿôG¿òë×?ù2¾š•8R¥ 4þF1WÕÚ)E_E­–º/CÏmÊNRݶþ÷síØ«þ 1û6ÁD~)ßø+àïÄøL ¯çOþ ‹øu£|ÿƒˆ¿iï xrÏû;ÃÞЕ"¿*gLó|Lþ8ÁúÅÿòG ÿ [ÿêÿ¢÷ã�ü:Þ;ÿäÚUý«?àœ¤þû㇊o¼WŸümuú¤—l¤þWuÿéþÆÿôG¿òë×?ù2�ø‡Oö7ÿ¢=ÿ—^¹ÿÉ•Õþ«áÿè'ÿƒ—ÿ eý£Wþ}ÓÿÀ?ûc›ðïí‘ÿÉðî£ çöÿ½^úÙƒÃw®iwšÍÄN?�d»ŠW׿wÌÜüÇ>Ùü£ö=µ�"‹ãw…£Ž5 ˆ¶—�TÉàW›ÿÄ:±¿ýïüºõÏþL£þ!Óý�ÿè�å×®òepVà<ª³æ­R´Ÿ�H¿Î™¼3¬\  ¿í×ÿÉŸ?à¼ÿ²=—Äÿ…šùøÓ¡M¥øWÄÓê›Ûé÷÷[Á&‹ªZ+ˆ’$Ÿ¿º�pŠÄ,@Uf­ÿFþŸô\¿òÌñÿ ׆ÿÄ:±¿ýïüºõÏþL£þ!Óý�ÿè�å×®òe}&]–áðhá0×å�þ&›Õ·ºQ]{}jõ+TujÚï¶�-ÝžåÿFþŸô\¿òÌñÿ ÑÿFþŸô\¿òÌñÿ ׆ÿÄ:±¿ýïüºõÏþL£þ!Óý�ÿè�å×®òev™žåÿFþŸô\¿òÌñÿ ÑÿFþŸô\¿òÌñÿ ׆ÿÄ:±¿ýïüºõÏþL£þ!Óý�ÿè�å×®òe{—üEû ÑrÿË3ÄüƒGüEû ÑrÿË3Äüƒ^ÿéþÆÿôG¿òë×?ù2�ø‡Oö7ÿ¢=ÿ—^¹ÿÉ”î_ñoì)ÿEËÿ,Ïò ñoì)ÿEËÿ,Ïò xoüC§ûÿÑÿ˯\ÿäÊ?â?Øßþˆ÷þ]zçÿ&P¹ÄQ¿°§ý/ü³x’ÙeƒãGÃXceÜ æ¿mg‘ô™—Ÿn´ŸñŸìoÿD{ÿ.½sÿ“(ÿˆtÿcú#ßùuëŸü™^´)œ:w²I+êôù/ÈæmÉóKsGþðþ‹‡Âü,´ïþ=Q^ÿÁE~ØÁæ�Ÿ eãl*±�ÿï”�·éTÿâ?Øßþˆ÷þ]zçÿ&QÿéþÆÿôG¿òë×?ù2¨âüëögø}¾/øY6:Ý𥮕m4æB1æ•Xÿ¿"×sûÿÁg¿e��ß±gÂx‡ãg„íõÿø'FÑ58¢ŠîTŽæÚÆeUqFŽZ›þ!Óý�ÿè�å×®òeñŸìoÿD{ÿ.½sÿ“+ÈÎ2iN4ñ|Þëºåi}÷ŒŽœ.6¶NTm¯tßäѹñþ Ïû#Ù|OøY¯Ÿ�:Ú_…|M>¡©½¾Ÿq%¼hº¥¢¸‰ 2Iûû¨¬@rÄVaêßñoì)ÿEËÿ,Ïò xoüC§ûÿÑÿ˯\ÿäÊ?â?Øßþˆ÷þ]zçÿ&WV]–áðhá0×å�þ&›Õ·ºQ]{V¯RµGV­®ûiÒÝÙî_ñoì)ÿEËÿ,Ïò }ÿ_ÊOü�ÿéø7ûÿ˜ÿ…IàïøDÿá,þÜþÕÿ‰µõÿÚ¾Ïý�äÿÇÌÒìÛçË÷1�üçÕµv™…Q@Q@Q@Q@Q@Q@WÎ?ðV_Ú§þ÷ö ñïŠí®~Í®]Yÿch„6í·_º�ÓÞ5/7ÒYb+F�)UžÑMýÇ£”euó,u¿ ¯:²ŒW¬�¿áÏ“aà¡ÿðYÿŠÿ.Û<ðz/øE<&Çæ‰å>d"XÉà©Aw6:ƒwâ¿P+äÿø"�ì­ÿ �ÿòðV›umö}{ÅQêà®ϺUhчPÉn°FGªú¸2z2†N§Å?yú½eò>¯Ä|Ò†/;žÿq†Q¡Oü×-ÿíésOþÞ8Ú_ãm�ìÝû>xÓǺŽÃkáçT1±ÇžÑÆÌ‘wp¨=ØWÆ¿ðnÁ+ß ~Æ¿ÄÍ{|Þ(øÍâ ­vîæQ‰f‚9(·wæO´Èq?çOþ9ø±¨[þÊ^øGáüËâO�>(´Ò ¶S†ždIüÜ5šýýÜ¿>iÿ¾ xSÁzPMðž‘k¤[¸Ü�D±†>çnO©&¾£øX:�ð�üø›üxŸ(¯ÅÿÀ:š(¤fÚ2x’OjòÎÃâ;Óÿ Ïþ co3Xü*ðÖöòÌJñç>›ƒß/ãµ}»_ÿÁ&Çü-�‹¿>*¿ïcñ'ˆ�•„ŸÜ…ZIŠƒþä–ãþ+íÊáÀkMÔþfßè¿|‡¾ËëfoþbkU¨¿ÃÍÉü–((¢Šî>ô(¢ŠñßÚ3ö ø]ûQG,¾'ðÕºêÎ05{ô[õ=‰‘F$Ça `=+çoøa�ß²yÿþ%·‰´y_ x„¨{"oÌYé–S¯ºè¯sÄXÜ=?`ä§Où&¹£÷=¾V<ìFW‡«/inY4t†ÿ;ŸxCþ éwðÃ\‹@øíðãÄõRvý¾ÖÙæ²›ÕÂ7ϳ¯1´Ý+´ýµõ�?öúý�|}áÏ„®�ã}sYµ¶ÖVš„K•»‚F Y<¢XâM¿™ô—‹¼¤xÿA›K×t½?YÓnF%µ¾·Iá“êŒ?•|³ñcþáðßÄÚ·öÇ�õ {á—ˆ"c$:MËÉnˆFÌ~‘È€zW[žKŒþ$e‡—xûðûŸ¼¾Mœî…hµV>~ì¾ýŸÜŽÆ?ðO߈þÿ‚µxoƾ †Ü|ñë~#ÖeK¸ãŸÂÞ#ºÒ[O–hâ.®ñÝ…µ�ùA¶Í ÌÛ|ÌœßÙŸöPø Úì³ðÿ_øRÞ—öhÔžûXñÃêÚeÕ‡‰¼­>îÂOìÅ·™¯‰Ôd¹[©�ÜØ ûü×À=¶ý°¿dO¹&�ñÃÃýœ“©,cë²fsõžº¯…¿ðY/‡šÖ«ý�ãý'Ä_ |A =¾©hòÁžÛÕD‹ÿ‰@õ¬ÿÕŒ\aí0N5á]`î×#nŽ’\·µ­f’R½‹þסyBºtå.o‰[ãINÏTù­vï{¶Õ®y¯ÂOÙwâÍ¿ìµð‡áΧðãUÒu?„ßìõËÍB][K–ÃXÒ—W¾ºkëCÓK±b–"cž(¦Ëáclu?´ì…ñÆÿ³íeáÝ/ÃßiÖ>&xÞ cÃVÿo¶Oí+U³Ñc2niÇóÚ\ ²•oÝôÃ.~ÃðÄ¿|RÑRðÖ¹¤ëö �ô�>í.#ö% Áö<ÖÝ|ì)ËÏM«7{ß}}•ÿôÔ~ùy[Ñœc[–WÑZÖì½¥¿ôì¾èùßä ;áwŽ~þÖ?oƒ2|VðçÆèºÕ§ˆ›SÒ¢Ó´h-í-mÓQŽêatÕí^æ³[\†7?Àûˆñ‰³Wí#ñãö‘þñ^ŸáýãM—Œ_éþ›G‡RY>ÛëÒêm y—g�O´•,¾T2~—QJ—îêS«»‡-¯ý×úr-wÞúêkW÷”çIí;ßæ¥o^g嵕´Ÿ¿eß‹:ïìýûY|1´øqªÜÜ|@ñ¤Þ-𾸚¶–4Íf ‰tÖû8t·0ÜF!›pš�÷,ŒYA—ö�ý–þ'êµo€¬þˆ6?´ÃÂt_�[K�NðÜRiVúp‡SŽêe¼U±š .â�]ûGÊ#“p TT*qä�7²‚§ëʵó÷VªÏïc„œ*:±Ñ¹9úI¶î¾rz;­vÑ[à_Úþ ýãOü ý±ôý;Ãz÷‹þ'øcLÑü1©É5¤w~ ’ÏD‚̲I˜‚^,Œ¾s¨W%ÁçqûËJ‰àÒí’_õ‰«äç�y«V®mîB‚Œcön—ÍEk袬QEIAEPÏÿðV/ùE—í-ÿd«Åúhº¯Êø4£þQÇã_û)ßúlÒ«õ{þ Åÿ(²ý¥¿ì•x£ÿMUù+ÿ ø‹OðïüsÆ’êÖv1ÿÂÈ¿;î&X—LÒ³Ëê?:M¤®Ë„%9(A]¾ˆèÿgÏØGÁ?´ßì—ðóÆÞ+»ñž«ã¿ø3GÔuPñV£©Ü5ĶqJî±ÝO,H¥ÝŽÄEP(Æ<ö�ýŸ>=þÍÚ#&�ñâ=ÿ‚í2ÉuáïjVÙ?é¬PL­’ QýîÕôÏìCû@xÀ¿°¯Á‹MgÆ^Ó/,ü ¡Ã=µÎ§ sÅ"Ø@¬�mÁ•²ÆA5íÿ3ðGÂÍÊ_ ˧øëR»¾KIm ¾khí"e“ý&I|©3º `ŠÏµË*¹ä3¬UZ^Ù×TjÿüjðÖµeáOøCþ&}µ¡6š=ã\hw±ùŸñðÒ¼¸R™n-ÒMü20ùë«àªñVk–VöJ¼+Ç£ÒKïVwùŸÖ¸8�òÿ¯K)Äeuö”m*M?îÆJTÜ{8Å;n•Ñúµà‚ÿ þ&ZyÞø“ñSZ�?cøÕ≊¼SÊþ"¼#þ 9‰û>ø*ËBмUñfox‡q’;ï‰þ$Ô-â²Y<Ë{‹é!�IÊm‘H߯@¯ˆl¯¦ÓnÒ{y¥·ž#¹$�ʺPG"¼ïÇ_<]ã G[×5?ßj×z«™ �ю᥄¼J±Hï™rD„&Ö\qÃW¥S�qަœ&ì¯wm]´ÓFößæ|VèÅ”ðga3Ì^*x¬57)û>H)^œ\Ó“u”cnf”nínV›=;Ã~)Õ<¬Å¨èÚž££êVùòo,.¤¶¸‡ ‚RD!”àžAkéŸÙ?ö�ð¶­qk£üIñOÆ[íÀC®EñcÅ?f•»yñh^{…)Ï* __xÃ\°ÐüW¬�`ùzn¤úuµ»ÛÂ-­c2Ʀy±Œ9o¾«µ9äÒøÃÅÚÏÃöÕì“Z}Oe¥¤Ëwy>nœfºo) - zÆÙ$p>w%­˜åÕ”°•®ÜºÙ¿w}:s-wísö_2îã¾TóüDá(Õ�³U ¿{g¦ÛRöS´Z”vsQÝ~“þÔž9ø+ð^ÛÁÄ·þ/�4±Í øëY°ŠÏÌù·6·i#©È>J¾ß”p�OÇ~,ñæ»ãÛ¨g×õïx†{xü˜gÖ5[�NxãÉ!<ë‰B'«ò?ëZç†õ-/E³ÕumbK뉚ií£°[øU"F0$%·ä¦í€à¼PÔ|c„,î.—W´KI® ëZ‹¨¼ “ …[}¿ÝÎð‡$€T•®Þ#ÎsLò¤kc1 Åü)Éì¯fï«ÚÊÿ$®y>pW ø{J¾+ËëÔÄS_½­ìã&Ûän”dá)©8ÅÚÑ|Ò”¢zÇ„­¬¯|U¦C©Íö}6[¸’î\·î¢.·Ëóp¹á¾ã–¯s.ãÃ_—‰�u|©¿E£×Ôü»Œ¾�ü#�ån¾K’ÕÌqHÅ×”bŸóM©Ó|«´nÞÚ|Kõöjø«?Ço€žñ¼ÑØEŒt›]rÒ;7‘ãKk˜Rh~i±GRr‹‚qŽ2|³þ Öµ}ðSÃÑë>&Ñ´ßøú[PèZíæ‹swo‡u˵ˆÜZKÁ<ûh…p Œ‘‘^ûÿÁY~üý”>ø_‡Å6Ú·‚ü'¥hÓÇa¶ÒOie2’ PÏ8õÆx�¾2�þÎ?~0ø×ö·ø‘áŸh~ ²ðï‡mô·éÞ$šõô¶žÞgIm6¹óYA5ãò±ò‡É¯£¨îrZwí ñ«áÞÄ ð—Ä{4ûמ¿:. ãÑl/Yà'Ü߯=½=“à/ÆÝ+ö‡øckâ�ßR²´¸»½°–×P„Eugsgw5�Ì2*³.èç‚T%Y”íʱâ+7þ ¹ÿ&Ñ}ÿeÇú–êô ›þ 5ª_鿲ó&Ÿªk4š§‹¼'¤Ïu¥j3é÷‚ÚïÄše­ÊGq¤±‚iP´n¬œ^qÿ …áßú~6áßñgÿ,kÐà¤òm?öP<ÿ©n‘ZTåŸðÈ^ÿ¡§ãgþÿòÆ�ød/ÿÐÓñ³ÿÿ‹?ùc_4~Ã?ÿi_ÁM~2xkâN�⻃ºGöÇü"³ßxQ4ûòµHb³òogÝjÒ™p¹ÆkÝॾ:ø—ðÛö%ñ¶µð~ÛU»ø‹f,¿²"Ó4¡ª]6ûët˜¥±ŽA!4¤å-Û4ÑÃ!xwþ†Ÿ�ŸøwüYÿË?á�¼;ÿCOÆÏü;þ,ÿå�s¿ðM/|Kø“ûø'ZøÁmªÚ|E¼¿ÚñjzPÒî—eõÂB^ØGŒ˜"0ƒ †ïšð¯ø^´¯ü>Óþ¿ìïÿÃ:ìÇÚ¿áOì¯ùùùþÑò7gíß/úï½òcµ}/ÿ …áßú~6áßñgÿ,hÿ†Bðïý ??ðïø³ÿ–5³ûOëÞ%ð·ìÓñSðdW3øÃNðÎ¥u¡Emkö©¤¿KYZÝR­æ1” ´î89ÅxGüÏâ·Ç‹ÿ³µ©|}³ñ ŸŒmüMqkfºÏ‡×D¸{khѰ…aˆ2ù¯8·’ÉÛÀ³X|×¼þw‚>+üOÐ%˜5�n_ÙÜIT7ö†h�q�w´ßÚãWì.¿àß |H²O½yá{ã¢j.=�ë¼û›åçøpxëh ßà/ÆÝ+ö‡øckâ�ßR²´¸»½°–×P„Eugsgw5�Ì2*³.èç‚T%Y”íʱü¤ø=ÿ+´ißö×ÿP7¯Ñ/ø&çü›E÷ý”ê[«×çoÁïù]£Nÿ¶¿ú�½D”QE;åv�GþÙê•ö—Ço7Æ/ÛËÅz«âOˆ6:7‡üá»ë-Æš¾�wZ�ˆyY,.a;¥¥ºæMÄ€ÉÏÅ¿åv�GþÙê•÷_ˆ¿å#Ÿ?ì›øCÿN~(  _ød/ÿÐÓñ³ÿÿ‹?ùcGü2‡èiøÙÿ‡ÅŸü±®Çâÿ�¿áTü'ñ?Š>ÉöÿøG4›­SìÞo•ö�"“fü6ÝÛqœg¡¯Ó¿níGÂ6ú�÷ÄoÃàý*ÜxîÁìuÕÕn¦²·hÄÐÜGäÅ7 M9f�‹�%ùrRwn+u¯á'ùFOä>Wdû»/¾+ó”WÌîá�¼;ÿCOÆÏü;þ,ÿå�ðÈ^ÿ¡§ãgþÿòƸ/þÚ^;øS¤ê âï…všœž½ñV‹e¤x¤ê?oŽËÊk»IÝ­"òn–9‘‘cÇ!Y˜6‚ݵûeÚ½÷‹ßDÓ´«ížÓµ™5íG\�KÓ<ëâÏRÍ"Š(íÄwË—eŽUÛŒUKzoÿ ¾ý¾¿aGÞµºíç¶Ýþ%~××fmÃ!xwþ†Ÿ�ŸøwüYÿË?á�¼;ÿCOÆÏü;þ,ÿå�qßà£>ñ÷ÃÍW\ñwŠþø=4�IáÓy�-¯´]Ju‚…6—Ò,pb�2<µeeu#åÉßµý¥¼Iâ�‹¾%Òü;à«KÁÞÔ­ô�w[º×¾Çv³ÉWµ­¡·dž(b¸…�žâ"x\¨=7퓳»íñ-öº¸-vïoW®Ýö{v4¿á�¼;ÿCOÆÏü;þ,ÿå�ðÈ^ÿ¡§ãgþÿòƹ†¶~½ã+¿ë§�-4�|T˜Áá�Z}¯PËÛËsj×¶gHíÒx!r W•v�FâW�ð—íÝñÇ1øm;á”-~'Ár<<÷>2¼wéæIöÕ[FòmÚ4™£’<‡laጻÛM;?øgÙö~OPþ¿=»ìÎßÇ?ðO߇Ÿ4OìÏ]|Oñ›æ ¾É«|Nñ-ôÀ!\$·ì†Na†HÎ ^/Ø÷ÃPD¨ž'øÔˆ€*ªü^ñ` @ö�púíɯøûGð$øk¨ø›ÆrkvsZ^x‡ì–EÖ“t¶×^eÈ·y$€¸�$‘À]±bPìÑï|ý²Å/x?GŸÃGJÕ5éuë Ví¸]"÷Hž;yâFTxÝØ”“÷g`RP*®í®[íý?—žÂ²^÷}>zèüôz>Æô²Ž™¦¿›¦xããV�v¿vsñ;_¿ ÿl®îæ„ÿÀ£?γ|7ðÇß ¾ _ø¯Â?mµT‹É¼¸ñŸ‡“Uk´Ê�…­%²Ù�ŠQü °|sçš—íiñ'ÇŸ~·„<;á¨4kÚþ‘©Ûj> xd¹þÏ{Èz­„¥F-„êU�g>Sas!©wÿXðÌ%ÐÒO¶‹mâµð„±¿�¡�ÅmrnÐÏŠaÜÖâå¶–3‰<¥iDe@ Ÿ"©8Æ×}?ú£“‚Ã×q­^ N“¼[×–Vz¯;_ÌúÓö”ø×á< gá§‚üWn¿zçÞ)’ÒêO¥¥åºÆ=³vzöÆO þÏßµU‡Ç¿xƒÃíá�x;Äž´²¿¿Òõè­¼Õ·»{¤‚T’Úyá‘YìîWå��cä Œä×'û6Éý|[ÿ²àïý8ø¦¨ì>”¯ÏÿØÿà-§ÅßÙ+áw‹U.öûßOë¢odÎÃþ ÿô4ülÿÿâÏþXÑÿ …áßú~6áßñgÿ,kÅtÿÛN{�nûâDÚ‰-¡O„‡�lôñüKn- ¼g·wƒìãË»š-š\·–®cÚûw›~&ý¦þ9ßümø5ocàŸèšŽþßptÛ¯I%Íä d'EºdÒÝmåˆÛ`–ew ›~z´›_Ÿ—½(þqq2’�ÿ«û±—å%ê{ _²žŸ¤È'Ò|uñ«K¿Nb¹o‰šî¤#?õÂöê{wúÆ>–Þ$±:¥'¹¾²G·ü‚óßÓ¸¢‘Cþ þ×Oñâ„^ñ'‚|Aà�\i7ZÕ´w7·Ö�µ´ÖÐÜ=½Ä1ùòÜš8œù™ @&½–¾^ðïü¤sáßý“ÿéÏÂõõ ~þÇÿm>.þÉ_ ¼Yâüj¾×¼Oá'VÔ®WâÇŠ [‹›‹8¥•Äqê wcµTg¯Eÿ†Bðïý ??ðïø³ÿ–5Kþ íÿ& ð?þÉþƒÿ¦ëzù×þ SñÏö•ø/ÿ ËþãNñ]ÿö‹êgÄؾMwo—ö?³ wA/”ûŒcnì~^>—ÿ†Bðïý ??ðïø³ÿ–4Ã!xwþ†Ÿ�ŸøwüYÿËõ:øWöøçûJøãþ kñ“Ã_tïÛüÒ?¶?ážû‰§Ø?•ªCŸ“x C>ëV�ŒÈû€-Î3@KÿÃ!xwþ†Ÿ�ŸøwüYÿË?á�¼;ÿCOÆÏü;þ,ÿå�yü3â·Ç„³‹©|³ñ çŒnÿÙ7ñþœü/@ÿÁºßò³Oí{ÿ^^3ÿÔ«O¯èF¿žïø7[þViý¯ëËÆú•iõýÐEPäü­ÿ(²ðý•];ÿMÅO¦j—úoü àiú¦±£Iªh¿ t™î´­F}>ð[]ê%­ÊGq¤±‚iP´n¬œPÁêßò‹/ÿÙUÓ¿ôѬRÚÿÊ?gúöøKÿ§ŸÐWÿ …áßú~6áßñgÿ,hÿ†Bðïý ??ðïø³ÿ–5êuó¯ÆÛcÄÿ üUñtï‡vz·…þ5¤ºö«qâAi4–ó[Ã;›[qo!–X’G%$x�…M²b¨uQ—;ød/ÿÐÓñ³ÿÿ‹?ùcGü2‡èiøÙÿ‡ÅŸü±®Sâw푯økSñýç†< gâ ü)*¾(¾—_ûùqmäñØZ‹yáâ¶–6ÄÓ[†wØ­Á`º�íÊ–ÇÄ–6þk¿i~+Ò¼9¥é«�í˜uàšÞø?”vF!’áÜmm¿c”n8Í÷šK­¿òf”~öÕ»ê։ؖŠïÏðWw^Ú_VŽ«þ ÿô4ülÿÿâÏþXÑÿ …áßú~6áßñgÿ,k‹›öúÓôŸÚºËá¾¢< z�ååŠ}—ÆÖ÷zöœöֲܙo´Õ‹Öï22Éö‡ 4[ÑÊ®ï�mMSм6~]ø[⮯âÝtøN]7ÄQfC:[Iw;]^@—JÇo67rZ0æÜªR]mø»~`Õ›‹éúÃÿðÈ^ÿ¡§ãgþÿòÆ�ød/ÿÐÓñ³ÿÿ‹?ùc\Ýÿí7ñQñl¾ðÿÃMûƺŽšÇˆ¬ïü`m,,Riç†Ú;k˜ìæ{†Ÿì³H¾d0@»Ê;lZ‡íë­ø»Lµ¹ø}ðò?-Ç�àñÛÿmx�t³Û»Î�hÛ ¸?j b$IºHö¯˜œ’Õùþ ¿ºÑnûhR„›²òümo¿™[Ôôød/ÿÐÓñ³ÿÿ‹?ùcGü2‡èiøÙÿ‡ÅŸü±¯>Ñ¿o­Sì7Wš×€ãÒ-u^üCðÒ&º.noìmDEá¼U€%¤ä\[�#’åpíóepfÛ�Ä^†â_ü<µÐSRðn¥ã?G‰ökèlR)%µ¼Q­µÆÙá#Êk˜ù“÷‡`Üçî&奓oåÍý"Zo£&ŸïPÖûyß•þR‹¾Úú�ßü2‡èiøÙÿ‡ÅŸü±«–ü#o§Ãm;Çôq¨¥Æ»ãQ¢è�m´%Ä×�d6O#O-¢·p ¶b¦ÓNIôÓñœtôp•ü•ö»R¦�¹zßòŒ¿5ù;= ìþ+þÐ> ÿ]Â?ˆ1'·øRv_sÿ/ôTV?Ü�M;öó½ðÞ¯£ÙxóágŒ¼%³ªØè±jö×–®�·W·QZ[#¾±²ÐÓâæþÍM}öí_ø”®Ø7ý�Ëÿ�ƒ»w™÷Ý�ë·Òlßü&ð›§ë_ mÿ´.<qâO YÙø™n'ÕâÓã·7× [µ¹ò§�ÕckˆØî_4 tTQ„§ô�õïiTOÒÞÍ¿Ÿ–¸Q“«8­eÓÖ4佉òµß—¬ÿÃ!xwþ†Ÿ�ŸøwüYÿË?á�¼;ÿCOÆÏü;þ,ÿå�y7íWûbXø‹á‰í<<šüºFŸá/Åwzæ…¯e^Û‹ÛØÅ�¼2eÁ–8çwcÑWiów&ÇÆÚËâ-Æ©ñ“Cð�ü>ÿð«m¶ÜëšÇ‰ÚÉ]åÓ#½G·�,®L’GæÉ.Ä8�ÛÝcδ½”dæ¶MÛÊ.ÏÓ]5õÛSJ ÚË–_ù5šüØôød/ÿÐÓñ³ÿÿ‹?ùcZw€þ&|4þ¯‹zÄö©÷4¿دˆ­ØN ìž™’ê@8;zæÙÆþ.ø‘û8øC]ñµ¦�k®êÚM¥Û¾�~׉v’[Æâwͽ¸ŽG,Å¢DeN�ØT¿iß ü2ñîƒáI®¾ÝâoÞÁgo§[Ò@²¸_6^È€óÉÇòAŒ©O ß·’VvùÿŸ–çnI•ãsŠŠŽ[JU$âåd¯h¥w'ÑE-\›²ZÞÅýsöéñ—À� jZ×ņŠÞЭ%¾Ôuÿkê0Z[D…帚Òè[Ίª¬JCö–q»·Óµñ¿ü'þLã‡ý“ý{ÿM×öEQÂ|‹ñÛÁ�ñ‹öòñ^‡ªø“â Ž�áÿønúÆË@ñ¦¯ AÅÖ£âžVK ˜DŽéin¹“q 2rŸðÈ^ÿ¡§ãgþÿòƶ¼Eÿ)ø‰ÿdßÂúsñE'í?¯x—Âß³OÄ=OÁ‘\Ïã ;Ã:•Ö…µ¯Ú¦’ý-ekuHv·˜ÆP€&Ó¸à`ç�ÿ …áßú~6áßñgÿ,hÿ†Bðïý ??ðïø³ÿ–5ä?ðG?Šß>/þÌ:Ö¥ñöÏÄ6~1·ñ5Å­šë>]áì­£F† Ëæ¼à>ÞH#'o7ü§ãŸí+ð_þ—ü3Æ�â»ÿíÔψ?±|(šîß/ìfî‚_(÷ÆÝØ<ü¼}/ÿ …áßú~6áßñgÿ,hÿ†Bðïý ??ðïø³ÿ–5êuð¯ü[ãŸí+ñ£þoü4>�â»ìçÓ‡ÿ¶¼(šï3íŸim‚/4 –ùÎí¹üÜ€}/ÿ …áßú~6áßñgÿ,hÿ†Bðïý ??ðïø³ÿ–5ä?ðXÏŠß>~Ì:.¥ð ÏÄ7ž1¸ñ5½­âèÞ]ná, ­ÛHƆP«æ¤¾Þ #w>ïû0kÞ%ñOìÓðóSñœW0xÃQðΛu®ÅsköY£¿{Xšá^«å°”¸)´m9ÅVÓ¼ñ3á ð…|[Ö'µO¹¥øÖÅ|Eh¾Âpð_dôÌ—RÁÛ×7µÏÛ§Æ_<)©k_>+xwB´–ûQ×ü¬G¨Áim–âkK¡o:*ª±)ÚXÆîÝ­xÿü'þLã‡ý“ý{ÿM×ð¯ü'ÿ6ëÿs/þâkúJ¯æ×þÿ›uÿ¹—ÿq5ý%PEPEPEPEPEPEP_˜_ðUÇ?·ü÷àWìÏjMχt ‡‹|aœ§—´¿—'÷X[Fê§Öùzô¯Ó-w\´ðΉy©_Ï­ŽŸÜÜÏ!ÂC)gb}Ÿ¥~lÿÁ|9yûLürøïûSkðH.ü{®I¡è>hù­¬c)# õP¢Î éÙ‡5ãfß¾•,ûnïü1ÕýîÈý3ÃßøN£Žâio†§ËOþ¿V¼ ×øcÏ?ûu¦‚$  *¨À`KEgø«Äö^ 𾥬êS-¶�¤ÚË{u3tŠ(л±ú*“øW²µÑ™Ÿšÿ[þoþ.ðÖ†?Òü1û9x{ûNå:Åöö `zo\Ú ÑO¡ÇéÅ~iÁ¹Þ½ø§ |oøû®Cÿ¯Šþ2™Qß–Xb-;ì̺dÀÿžvú[^¦mîÕTÐI|÷‹g X:¯í;ÿ—à翵—ÄøU³'�uõ.m7B»{v¾12Åÿ�²×¡WÉßðZŸþÄ÷ºldùÞ*Õ¬ôÅUû̃�×þXcþŽõâbª{:2ŸdÏ'‹³¨d˜¼bÞæ×­�¿?ðH¯‡ßð�þÂÞ‘ÓeÆ¿5Ö­0Ç]ó2F£Œþ5ôÅrßü¿ þ øOÃJ¡°t‹[ÇvŠF?RA?�u4ðôý�(òFü5—gå8l µ§N~ª*ÿˆQE±í…Q@Q@Q@rÿþ øCãV“ö/xkEñ ¸R¨/­V‹=ш܇ÝH5ÔQWN¬éÉN›i®«FL£.Y+£ã�Áü#g®oዼYð»[^b{¹. �ØeÖaÏý5#Ú°ÿ¶?l_ÙD¥Yh?<;nÏ‚§nŽVø Ýzô¯¸è¯¡‡bå j�xÿ}]ü¤­%÷ž\²z)óaÛ¦ÿºì¾í¿ã߇ÿðY¯\kÙ|?⿆zÜd ¡Ô¬Þxb'Œª%ð(€¯¦¾|lð‡Æ�3í~ñ6‰â†sax“4Yþúƒ¹³jÏÄ…~ø±£�?ÄþѼAfABÎ;…\÷]Àí>ãŠù›âgü«áˆµ?íO^x�áÖ³/ úMëI n‹d„°ú$ˆ*¯‘âwS¡/üÒKñü(ÑþZ‹ÿ—ê¿#ëŠ+áöøGûa~̃>ñ¦ƒñƒE€|–šÐÛzàwf••ù—-ß�Y4Ïø+Ö©ð¦ö;Œß|aà™÷ymµ„Ëm1þò¬¡>_÷N”¿Õ|EUÍ�© Ëû²÷¿ðZ_ƒíŠPÓSõZ}êèûrŠñß„·ïÁÿŽ"$Ð|w¢}®\³¾�Ø\–þèŽ`¥�û¹õì*Û†G òï^' _?gˆƒ‹ìÓ_™éR¯N¬y©I5äî-Q\æ¡EPÏÿðV/ùE—í-ÿd«Åúhº¯Æoø6göbðí!ÿÓñ�¿�<1¦ëmÄKøa¹‘ ]['ön–Ûc™‘I8 Œ“Å~ÌÿÁX¿å_´·ý’¯é¢ê¿(àÒ�ùG�ì¤_é³J¬«Q§Z�X©'Ñ«£»-Í1™~"8¼YR©¥ 8Éz4ÓGÍ>øUðƒÇ³Ç�üEgûBü'Ñü_ªøsO¾Ö´=wÄöÍk}%¬oqD›â+)uòÝ2¸Án y&«ªiw‰J¶ñ7„u»¥‡ö.½gª#/¨ky~Ô ýãøåûkü/ý�XüSâí2Öþ â]nÆêôžÀÃæ\ú°Þ¾0øýÿÔþÓµ¹Ó¾ø2/"ehÍ÷ˆH“z‘ƒþ�Çýõ!«_–ñ&IÃÔnÕ_e>Ñ÷¿ò^ŸzG÷Ÿ‚~(øÇ˜û8Oõì.Ÿ¼«û�<«Y)ùû•$~wQZÞ8ñ•×Ä^j÷°éÖ÷7Ò; l­Ôÿ³J¨¿€ÉêrI5“_–J×÷v?½¨ºŽœ]T”­ªNé>©;+úÙ_°W=¢ü5ÓtÝnëRžÖÊöþ[É.๒Õ|ëPàŠç$t<‚:ô®†¨øŸ\_ xkPÔ¤�¥M>ÚK–EûÎK=Î+Z3ªŸ³¤þ-=N Ï �œV/ÕÍ7ª�•Ü­ÝZëM7Z“&�i½ÄKkn±]³<è"ffá‹ |Ä÷ÏZ«¤ø3GÐt¹ìlt�6ÊÊë>u¼©Sdm;”G#¥yö¡âc@ñž“¬k�§]‹o êwÊ–IM›¼xg}Ýc9?(Ç:Óx»ÅZb[Ç0Ðîîµ�>{‹(ííåAmlEà+(®ZŠÒ×wk¦ï÷%{»vGÅÓâì²uª{l$”©5†.Q�HÓqº[{IO‘F.W·4¬¯n›þþƒý�ý•ý‰¤eïó>Çö8þÏ»9ݳsžø¨n~xfóN¶´›Ãº¶–[ͼaGò ìR¸]Äq×Ë]üp•Ḽ³·‚kt‘qb92KxR)$ç2ÜCއ*üú?ÅÞ>ñ6ŠÁ'þ%|wøg¤x·Ãº—ƒ.t�j:}FUt •dqå:°e#'•<šêÀà1©òa æÖ¶]� Šx³'È0«œâ£‡„ß,e'eÍf쮚½“i>Ìõ?ØWþ GàŸÚ7áo‡¼a®|A{ïím>ÚþëAÑŒ s¤¼±«›k™ HVT-±Ô ÁV÷¯Wý¬?g/‚ÿ°½ïÀÍn?ø;Hÿ…�%¶¯­ëWách›Ãzë"OqpûUá-È\…2,x‚×Îúüãÿ„5”½Ò›HÓïmÛ1]YëžLˆ}U€ ?JúàÖŸûo|#híï,´?iѾN·©ZË!È�dYI÷vo¥~¥‘JŽʶ[(Ëù”\ÿ=WÊçðwŠôsÿžywЯIÿ˙Ն[·îß$ýf£æußððŸ€ô\>áe§ñê?áá?ÿè¸| ÿÂËNÿãÕ0üCñ[Emã?„zÇ„®N\Ûëšn¡f=I+:Ê3×6úúú�~�B¼kGž)¯Tâþæ“?‹3\ª¶]]á«Êk­:”êÇÿ§)Çñ?7¾#|Cý—>'ÂÆûÇ·NƒTû?Ž4•û:ØçÊò7´œüÛ·ç¶ÚÆø­¬þÌÿ±¸ñ �½¦¯£'�4GÓ5#n²$8pÓ¤ˆ²‘û©‘NÕÜ­Î}þðþ‹‡Âü,´ïþ=_dQGK­Ï�ÿáá?ÿè¸| ÿÂËNÿãÕèðKÝ~ÇŲOö®—{i©iz§�üiygyk2ÍÜx«Vxå�Ô•te`ÊÀ�AúŠùëþ …¯ØøSöIþÕÕ/m4Ý/Kñ¿‚ï//.¦X`´‚?i/$²;¨ŠªY˜�“�^yÿ øÿEÃáþZwÿ¯²( �ÿáá?ÿè¸| ÿÂËNÿãÔÃÂ~ÿÑpøAÿ…–�ÿÇ«ìŠ(ãøxOÀ?ú.?ð²Ó¿øõððŸ€ô\>áe§ñêû"Šøßþðþ‹‡Âü,´ïþ=Gü<'àý„øYißüz¾È¢€>7ÿ‡„üÿ¢áðƒÿ -;ÿ�Qÿ øÿEÃáþZwÿ¯²( ž¿à—ºý�ŠÿdŸí].öÓRÒõOøÒòÎòÖeš ¸$ñV¬ñË©*èÊÁ•� ‚85ùñð{þWhÓ¿í¯þ o_±•øçð{þWhÓ¿í¯þ o@Ñ%Q@Î߯ù]£Qÿ¶_ú�¥}yñçö‚ðÀoø(Ï�OŽ|oá [á¿…E‰×u›};íž^§âo3Êó�wìó#Ý·8Þ¹ê+ä?Œ?ò»F£ÿl¿õJýŒ ‡¾#þÚ³¿Äÿ‡ºï†¯þ:|(†ÇÄ:}Æ›s%¿�4Å•#š6�Š”€À1ÆAìk�ñ'ÆÙcÆwÖ¯«ümø]¨ÚÛøNïÁ’YKã}0Awar`2ù›d棠 ¬ nn3‚?D(¥evûÿ”—å)/˜îôòÿ4ÿ8§ò?9>|aýž¾øž×YÖ?j/ |AÕ4Í-ôm.ãľ7Ð�´ËI fhÐ[$F”ÃùfJ|¥ùùmÜ×…£ý“|ðóÀgíà‹{9õÛ[êgÇš<º†Ÿwk$fb/˜Š[­­¼q¤‘:ùpª°~Iý@¢ªî÷¾¿ðy¾þozûß]Ä’JËoÃk}ÖÒÛ[CóëàGíû;ü‹Ä�íà?ê>-ÕF³ªj×�ôwž{Ÿ³AlH˜£EÙn‡b U$… ¸Q–ÿ?g;_Œ¯ŠôÏÚg¯ˆîíõwÃö:ÑSKÖ®¡�"IåÜáÇ*ë ñ¤« ‰Ápߣ4RݧÙ[ä­§¦‹O%Ø:[úõõóÜüÒøgã?٧᮹ ?ü4ׄõÝÁ³M?…ü=ªxëC};Ãlé$Kä4j—ˆ¡–Hc3L�¤lø+âÿìÃàM?áŵ§Çχ2'Ãp4£7Ž4’n<ø$�üü0Ý…‘ˆÙ³�3‘Å~ŠQEß_ëüÁ«»Ÿ“ž"ñ7ÀÝâ/Û_ ~ÑÞдÜxŸU¸×¬¼¡ í>ïSºŽëg—)hf‰™î$�Jª¡IùÕ$®ÒçÅÿ³M¯…<-i¢~Ó~ðιá[›ÛȼGaã� ]Rþ[íÍ|×hYmßí7˜Ø…vº§—å…¿Kè¡moëúòëÔww¿Ïïÿ‡~—vÜüÍÒ|Oû3x[Â>Òô?ÚkÂú<ÞÖoµ�7ROh·w³5ã\5Ä3µÈ•e�ÅË©f_7…>fì±ØÐ>6|ðG�æÔ<7ûXxcú Ö©&¯qá‹_øjm*iæ�ËqóO—Q¬Ò3»,wHÅ6f¿F¨§wÍÍ×þðÑi±6Våþºëë«×}O�ÿáá?ÿè¸| ÿÂËNÿãÕ£ûüað—ÆïÛcã¯à¿øsÅÚLð…œ·º&¥ ý¼s­ÿ‰�¢2DÌ¡ÂÈŒTœ€êqȯ­è¤0¯Î_Øgöæø'àïØŸàö‘«üaø[¥jÚW‚4[;Û+ÏØAqgáe§ñê?áá?ÿè¸| ÿÂËNÿãÕöE ø¿à7íà/�?ðQŸxßÂ3Oà ã¡k6ú�ØüÍOÃ>_›ä»lßåÉ·v3±±Ð×ÚQ@œ¿°ÏíÍðOÁß±?Áí#WøÃð·JÕ´¯h¶w¶Wž+°‚âÎxì I"’6”2:°*ÊÀAq^¥ÿ øÿEÃáþZwÿ¯²( �ÿáá?ÿè¸| ÿÂËNÿãÔÃÂ~ÿÑpøAÿ…–�ÿÇ«ìŠ(ãøxOÀ?ú.?ð²Ó¿øõððŸ€ô\>áe§ñêû"Šøßþðþ‹‡Âü,´ïþ=Gü<'àý„øYißüz¾È¢€>7ÿ‡„üÿ¢áðƒÿ -;ÿ�U€ß´€¾<ÿÁF|| ãxÌi? üT/Ž…¬Ûê?có5? ù~o’í³—&ÝØÎÆÇC_hQ@�ßðn·ü¬Óû^ÿ×—Œÿõ*Óëú¯ç»þ Öÿ•škßúòñŸþ¥Z}B4QEùÿ«Ê,¼ÿeWNÿÓF±Uu]~ÇŸðBïÙÿUÕ/m4Ý/KÓ¾Þ^^]L°Áiz¾€òK#± ˆª¥™‰I8kþVÿ”YxþÊ®�ÿ¦�b½ëþ ¥ÿ(ãøÿdßÿúl¶ $ÿ‡„üÿ¢áðƒÿ -;ÿ�WšxÓãìÅã­âe�ßÇχ1ÅñV(âÕŒ>7ÒC[„µKQäeˆS±;ÃüÙíÅ~‰ÑG˜š_üiû5|@ñWˆî×ö›ð¦‰¢øÚ8cñO‡´ßhk¦ø�ˈ@Æc"½Ä-$ �Èm¦„ºF™än­�oâ×ìµ®þѾøœÿ¾C­økO:uµ¤>8ÒÖÂPtŠi#ß¹¥Š;«¤�ƒ«q'#¢ddW�|cø/ñ ÓÍÕ~øò}QvÑuÔþÑÒoe ᦷÿ¶lP ƒ­a^´éENr·Ekím/kéÒútG­“åØ|uoª×ÄF‡6ŠSRäÝ;IÁIÆí-yZ¿ÄÒW>+ð~¿û2ø/â.‹®[~Ó~—NðÖ¿¨x“Hðü¾:Ðÿ³4û»åºDmU¸•Y¯&`&šB„€…W*{oŽ_´‡ìçñÏNÑ|ïÚáׇuŸ êK«hÚÖ�ã]!otË�ÄÌ‚c,.¯²ÆÉ,n¬²2Çü›ÇŸ³wŠ×@øÑðš]2ìò·ºEÉXnTp^“rH=Ön:z{¯ø*ßÁŠž\ð•Â9w&?ÑõÈÏoÖ_šÿ+ɡęuY{kË%ÒW‹MŠÚŸ ç ñ¦‡×þ¥*ô&®ªPq¯NQwÖôœ½×w{¥»¹òöµñàî¯i«i¿µÖ— x‹û0é:¶±eãÏÉuâ(<ç™~Ò·K 4rK9�­£„ij:&Ô «·áï‹?²·„&�i~é¶gÂPø.8¼q¦´6Ö4¥ —‘œËûÖ™›8É? žñN™ã)/´�FÃU²“î\YÜ$ñ?Ñ”�:¿^ß»8÷NÿŠkî´¥÷»n~Y8T¥QÆi©+^ûÝ4õ¿Tâ·×EØü¾ø»ñöt›ál¿ð�ütøo}¯øwᦩàÚ_èëäW@ÊK¨óKÚBnDŸ+È+cá/�¿g D·~Ѿ ñŒÇÃ/á¨,5ßèóE¡Y\"}ªÚ„Ç$›ü¸•¥¸’iH…1'Þ-úsET½înmy·ûæßßÏ+úÛkß5éË·þJ—ÝÉ™¿ ¼Wû7xÄ©«j_µ/‡ügw†çð”Ûž>Ñ piÒ˜O–©n°®ñäÞ�e}çÌy6DZ4þÏ~ðG„tí?ö³Ðlõ¿ZI¦hþ%Oxiµ84ù%k'V€ÚË „æHLÄ­¿9'ôΊnMõþ®ßç)zó>ìVKeýY/É-<—d|CàoÛ›àw„¼-k§ßþÑÿ ¼OwnI©ê~.Ñ#ººË ­·“À ‘¯g'$ól_„4èø§ðãĺí÷Ä�›m7Jñ-•åÝÆÏiR>È£‘�¶¢;I<_ TRnîãZhñÇŸÚ À_¿à£>5>9ñ¿„<5o†þ']Ömôï¶yzŸ‰¼Ï+Îuß³Ì�vÜãz種Kþ ûuêß uÍÀÖ®´­nÖHõm^òÎR’À0[çý¯õŒ;�9 E;ö*ÿ‚Îi¾.[_üYò4}O8¼A m³¹=žƒýKï/É×!¯™—eðÇËRVkK¿†ý¯ÒÞz¸Ðú=q†'„èñn ‡µ§Ròöq¿µPé5{ÊZ´£yZÒ³ONÏþðþ‹‡Âü,´ïþ=Y~7ý·g¯x/WЯ>9ü&ŽÓZ²šÂw‡Æzh‘XÙ©2� q�F{û[OÔmõ{n­g†æÚá±M‡ŽT#!•‡Њš¾’QŒâã-S?÷©Ï³_ƒ?6eñ·ì³-Š@~?ü?Ù€[áÐÿŠçHÉÓØ(2õÿ��”|ßs¯ÉIð—âìÕð»Çv> ºý¦<%ã+­I“AТñŽô[„ЬehÚX¢hÄrJ_Ê…L·/4¥b/»ôžŠ»»¹w¿âäßþ•/ü ÷3QJ*+eoÁE/Â1û‘ù�áxÿd¿ |ñÃË_ÚÀ§Eñ•ê]\Ï'Žô†º²Ž?%`µ�³µ-áH#Ž5eb9bNk¿Ðþ:þÍÄŸæO�ÿ 5 ~+Íö­£xÛM+•²ŽÄ$$»¢�s’Çq$À]~Ò_µOƒ?eO�gÅÚ¢Ûy�…�ŒX{ÍAÇðÅrzŒ±Â®FHÍ~KþÚ_ðQ¯~×ú„¶íáÿ¤™ƒE¶”‘.Uîƒ+tã„\ò~[ˆ¸Ÿ—AÓ©ïÔjܾZ|]–‹ÍŸºxAà7ñÅu‰ ½†?z´“¶úªkNyzZ1ë%¢}§ÄÛ­gß…öß þø£_ñe¾�ØCâ}v;C=µ¼h±Ç¸‚QÕp%‘ “–á‡�þÇ¿´-ö¯ð÷‰¼}â­'DÓm®'¼»ÕµíN;Xšo&M›¦™€.ÒêrkÈëîø!/ƒ¿µ?hïël»“HÐ ºœ}Ù'ž<ûæ'‰¯Ì0™¶3:ÎèO+ûÉÛ¢¶¯òÕ½_Vyq7p÷†Þf´²*\­Ðœ%QëRnkÙ§)y9{±VŒz$w_·7íÍðOÆ?±?Æ#HøÃð·UÕµ_kVvVV~+°žâòy,'HâŽ5”³»1 ª ’Hs_£TQ_»ŸäÑñÇŸÚ À_¿à£>5>9ñ¿„<5o†þ']Ömôï¶yzŸ‰¼Ï+Îuß³Ì�vÜãz稫ððŸ€ô\>áe§ñêû"Šøßþðþ‹‡Âü,´ïþ=Gü<'àý„øYißüz¾È¢€>7ÿ‡„üÿ¢áðƒÿ -;ÿ�Qÿ øÿEÃáþZwÿ¯²( �ÿáá?ÿè¸| ÿÂËNÿãÔÃÂ~ÿÑpøAÿ…–�ÿÇ«ìŠ(ãøxOÀ?ú.?ð²Ó¿øõyoíÍûs|ñ�ìOñ‡HÒ>0ü-ÕumWÁÕ�••ŸŠì'¸¼žK Ò8£�e,îÌBª¨$’×èÕø›ÿ‰ÿͺÿÜËÿ¸šþ’«ùµÿƒÄÿæÝîeÿÜMITQEQEQEQEQEQEñoüÃö“Ù÷þ ×â]>ÂFïÄi£ð�„qó#-Àcs…�mÒTúȾ lÿ‚zþÍiû"~Åÿ<ä¬7Ú.“jayÝ}6fº9î<é$ØÚ¾.ý´ü7wü3áÂ(óyá_‚vð–øŠ1Ê-Éòît*vééÏüü8ïÏé½xØ?ßcjâ:GÜ_-eøéò?Kâ_øLጿ&ZNµñU?íïrŠÿÀ•¿¾òü_ãßü(/ø&GÄI¡˜Ã¨x®ü1gƒ�æí¶L?ð\¾¾¯ÌOø.Oüdïí§û/þÎÑ~ú×_×á"× ë›E(6ßhc¿ëþ5õ9U5ÿÃ4Á>¾xJH|‹ë} +ûøÈù’î웩Ԟåd™—袽¯Äþ7Ѽ¶ZÕôÍ j·±i¶Föé-þÙu)"+x÷‘¾W …EË68´ÕB(`«æ/ø)ßüƒ~ÿÙið¿þ�’¸ªÕu+ÅËþ^N ÿÛóQoåsk*t&ר„ŸþÿCéêøŸþ ~Ÿð²jÙÛÀ#÷–÷þ :…ü>±,¶ëŸûà\W®þÑ?<.×_åø­}w¢iÿ"øqƒ‡ôÈô»�ã_M,ùóùñ®cã–)âLA¼R$�ãW¾7ñî±ÿi^ø­ãhÒíM3Á³hvšLÚ"Ç-�Üö ki.ãºKiD²°ñ˜%šŠ3Á^2¯‡ƒ¦¾;[ÿ§;z¸ÍYzö>CÄ 7·À¬µÍ/iZ”ÞñUeÏn®Ê”¯¦Úí{~¸Ñ_—¿±w�uïÙÓþ mðâÚËâŸÄýkÄ?]üH¿û`øì>ÂȺÒtµÕí4Ù4µ¨³ Û.ŸöÙ%‘lcshc;ÕŒLç�Úoöƒø±ðËâOÂ_„ñ×�~#Üx’OMªxŸÁv^‡Ås¾šÖþ^œÃUxtu™Ó´æ8¬¶ÃdþñÃÝE­y’kÑÂ3ùZ2MÞݺ«�ZNuÂR�âàíoSïi¦[xZIQfc€ u$×û;þÓýª¼Þ%ðEε¨h_* ûÝPÒ Ôha5«]ÁÚmØ0+<;âoásƒ^_ð¨|Yø™ûk–?ÛÄß¾ -ž©cý¤«¢É«Ëk’‹KÙc€ÞiÉs5²ÄÒ¤aâYMŠƒh_™¾øûÅ?ÿ`ßÙgῆ|ñ—]ñg�¼o®YéÞÐü%ý¿�m§YnŠ �R;m*fž5+4S^KçG¶L$�F‰ÔO^^K[¯7;üzh®Ý­f?°¿›ž÷éÉËù¹$Ÿ¥¯{¯Ñ_ÇÚ‡üS¤hWúÖ“e­ø‡ÎþÊÓî/#ŽëSòP<ÞDLCKå¡ ÛÚ'³~ üeðßíðæÇÅžÔµ¼?©¼ñÛ]}žX<Ɔg‚A²UW’7^TgnFA¾ý’?koþÓ7±_Š�Çæ¹´^¼®ß��Ñú(¢¤AEPEPPjZe¶³a-­å¼VÓ®É!š0ñÈ= žúÔôP›Z >{øÃÿ¶ø%ñ‘d’[h²dý¯B°2“Ôùkû¢}Ú3^='üâÏÀOÞ|øå¬ØÚÃÌ:>¹¸Û.:ghx›Óý@ýxû–Š÷°ÜM™Q‡³u9áü³Jkî•ÿ m\£ R\üœ²ïuþ>oÚÓö«ýœ0¿>YøóJ‡ïj>'Îuîíäù�GÖ®Çáwüsàÿ�.~Çâ×¼ ©+yrÅ«X³Ä¯Üo‹~>®©_Z×ñGö|ð7ÆËS‹|% xƒåÚ²^Y$“F?Ø“Óê¤WGö¦Wˆÿ{Âò¿æ¥._ü–\ÑüŒ¾§Œ¥ü ×]¦¯ø«?Ì¿ðóâï…¾-é¿lð¿ˆôOÛ`1}:ö;€Ÿïm'iö85ÑWÇŸàŠß 5mGûGÁÚ§Š¼ªFKA&Ÿ|g†#ë‰3'å(®sþËö½ý�[> ø¡¥|KÒàû¶Zï3Ðf}ÅGÒàQý“–â?Üñi>Õ�þL¹£ù×±t¿�AµÞÿƒ³üÏfÿ‚±Ê,¿ioû%^(ÿÓEÕ~>Á®ÿ ô?ˆ¿ðLoCâ 5Ó¡ø�¨<˯u§ÄÉý•¥îó|™P:c¨|ŠûGöØý·~0x‹ö5ø·ð×â_ÁMCú‡Ž¼¬xnß^³2ÿeÛÜ^XÍlŽçl‹±Z@[lÌ@ñ_ŸŸðM8þÉ¿ðEÿÚSá—ˆµ[;_kÐx›PÑ-,ÚIZüM [ÛÁä°Q™h]U8r@㑞\_ æ¦å*>ÒãiÇÿ%¿âv`8Š•ñ«‡¬éÔ[já$üž�î>¬°ðŸÁ J/µø;öXø�ã­ëZëV£Lµ¶¿B2²Dºž©mpѰå[Ê À‚ ÐX7�´Ù GûxÁ‹ 6ÏÁR�ɵ’{¶…qcq¤Aý›%´–(�!6ì  `#ŒX2øéàŸ‡^'Ó´OxǺ³¬KŽ­­Õô’³$Iná��‘‚…±Rpj×eT–´Ò~‘Zýdz_Å+Ä]ÕÌ+IyÕ¨ÿöã…Ó~'èš+Flÿc_ÚFØÌ6ž �`Æ01¬qÅx¿üöç�Ã/<aðÄÿ µ¯ÆQn5a¡û&q9Œi÷×,®Ã —T3á‰Zú*óö¶øU§ü;‹ÅóüMø}„ç¼:tZÜž"³]:K�˜àÉå™V;nùOW–üað—ìé{â­7â_Ä�ø2ê@ŸØWz÷‰í­ô»˜AQh ‰Ɇ y|™3Þ¼¬ÿ…=®X|­¨ÎVWm%ý#{µ¤}n}o†\}ƒÀq%Û‹¥Vµ š§ÊU*Eû‘|óIEKÞ“wÒ<¶w?1|àMoâ¨,t#SÖoü±±µyÜ{áA8®·â¿ì{ñá'ƒ³âÏÝiÞºÛ\Ísk"n|â7‰eiW ¼�xÁ9 Õï|døQàÃðãJñWÃÍ#Å­iâê¨ÉÆÉF7JöêßÞ�êšMÌ~eÄ<%¾0U*Ó…G9:•9&õjÜ‘�»w¬d´µÏÈÝ áN…áËĸ¶³‘¦ŽÝ­î.¥¹)mÌCÌfÄ(ÂŽ8'>ƒû+þÈoñÇñÃáÏ ø¯Äºd(/£Ò¯ šîÆÌ°-¿´.¡·�$(Ø$L„kl°à„ÜL‘‚€»Ø"‚}IÀÜœWêÇìàŸ üøs£èæ›â¿é#ÄWž Óu+;«;óæ¶DRc3ƙºÆbá¾pí¶¾'€¸{Ÿcù1—±�Ç«×v£ó³~—êÑûŸÒ+ÄL›Ã®Œ²¼=(ãêÝaÒ§Ýß•N­­§/º—yr'x§oÌŒ_°®¯ðÚÃþ?ë~ÒµÿK}Fky¥V%ZCisq ¾a•€Ú¸QŽ?Qðn›ªÅª¤öÞbëp}–ôyŒ<èö²màü¿+0Êàó_µŸ>,ü#Ô[Pð?Ž<[àžæÚi.ômWYµ†ãÉŠ´K!‰Ü:ˆáÄ¥À�9¯ÍßÛGöVÐ>i:7�<#âýÄüe³ŠWè>"¾ð©HA<‘q°Ì@ì Dÿ´kÙ?à—^&ð'Àƒ²j+ñ‡„¼9¯ø÷R‰,muM^ÞÎæêÒÃl¨’8f2MØPÌQ±�¦¾�øÓû`x7özÓõ»ßÞÁ¢iÚ%æ›b÷W:•„i<—®6«\@ƒs¿š±ü‘»&ð¤×Õp§‡¹V+"¥[1^ýOzéÙ«ü*öþ[;=5ÛSñŸ~’\Y“ø�Œ£Ã8žZ~Z>Í®jrpøÛƒÑ>w(óFÒåŠ×Cò‡âÇìïã��— ‹<3©èÁÈUšD[¹ôY�´l}ƒú_þ [ûzß~Ï—Z‡€î<5â?Zx‚à\hú~�=ŒW0Ýíýà {smGEfw ¤¹¯·/h_†ï‰4¿ \xãÀwšÇЬã»Óti5›I.u‹iU™$†å¦�ÕX†U*B’:WÏ?fÙÛX×õ�_Føƒá/†Þ%ðc-þ£s§ë֑â:Í夷6ï XL»1�ëŒç"¹çá†'*ÆG”ÖSŒ[¼$ÔdÒø£Íð·nê6Üõ1J¬‹Žxv¿ qÎ áåV+–µ$çÔÞtßï"”ºEÔrW�Sé?ømïѹ|lÿÁ‡„¿ùwGü6÷Š?èܾ6àÃÂ_ü»¦ü"øÝáoŠú ‰Ñ hzÆ­®Ï:.‰áéðë}Y‰>äžõùéð{þWhÓ¿í¯þ o_+^Ÿ³«*k£kî=ºræ‚—tD”QEdYüíüaÿ•Ú5ûeÿ¨Wìe~9üaÿ•Ú5ûeÿ¨WØø=á?ÛóÅž.ø�áÍÇSÅã AÓluÛUÔ4ý"ÓLÕn´ÈÒ iCDŒÿd3;…ÞÏ)˨¯ƒ–&§³‹·S EuF<Ìû2Šù3þÝû?ÿÑ ø;ÿ„^›ÿÆhÿ‡w~ÏÿôBþÿá¦ÿñšõ¿ÕêŸÎŽíX)õ�ògü;»öÿ¢ðwÿ½7ÿŒÑÿîýŸÿè…üÿÂ/Mÿã4«Õ?�ö¬?”úΊùØ?öo¸×®4¨þ ü}NÒî§³_ ifxa‘¤Xädò·*;E(V#Æà}Ó‹ŸðîïÙÿþˆ_Áßü"ôßþ3Gú½SùÐÿµ#·+>³¢¾Lÿ‡w~ÏÿôBþÿá¦ÿñš?áÝß³ÿý¿ƒ¿øEé¿üf�õz§ó¡jÃùO¬è¯“?áÝß³ÿý¿ƒ¿øEé¿üf�øwwìÿÿD/àïþzoÿ£ý^©üè?µaü§ÖtWÉŸðîïÙÿþˆ_Áßü"ôßþ3Gü;»öÿ¢ðwÿ½7ÿŒÑþ¯TþtÚ°þSë:+äÏøwwìÿÿD/àïþzoÿ£þÝû?ÿÑ ø;ÿ„^›ÿÆhÿWª:íX)õ�ògü;»öÿ¢ðwÿ½7ÿŒÑÿîýŸÿè…üÿÂ/Mÿã4«Õ?�ö¬?”úΊù3þÝû?ÿÑ ø;ÿ„^›ÿÆhÿ‡w~ÏÿôBþÿá¦ÿñš?ÕêŸÎƒûVÊ}gE|™ÿîýŸÿè…üÿÂ/Mÿã4û¿gÿú!ð‹ÓøÍêõOçAý«å>³¢¾Lÿ‡w~ÏÿôBþÿá¦ÿñš?áÝß³ÿý¿ƒ¿øEé¿üf�õz§ó þÕ‡òŸYÑ_&û¿gÿú!ð‹ÓøÍðîïÙÿþˆ_Áßü"ôßþ3Gú½SùÐjÃùO¬è¯“?áÝß³ÿý¿ƒ¿øEé¿üf�øwwìÿÿD/àïþzoÿ£ý^©üè?µaü§ÖtWÉŸðîïÙÿþˆ_Áßü"ôßþ3Gü;»öÿ¢ðwÿ½7ÿŒÑþ¯TþtÚ°þSë:+äÏøwwìÿÿD/àïþzoÿ£þÝû?ÿÑ ø;ÿ„^›ÿÆhÿWª:íX)õ�ògü;»öÿ¢ðwÿ½7ÿŒÑÿîýŸÿè…üÿÂ/Mÿã4«Õ?�ö¬?”úΊù3þÝû?ÿÑ ø;ÿ„^›ÿÆhÿ‡w~ÏÿôBþÿá¦ÿñš?ÕêŸÎƒûVÊ}gE|™ÿîýŸÿè…üÿÂ/Mÿã5£ðö_³Çí�¢ø ÂPdø3Åþ Ö5ÉtX¤o°é÷zuî“ Ik%`&§ ‘c ¬aŒã9'›“Ô¡IÕrM#jøÕŸ"GÅðn·ü¬Óû^ÿ×—Œÿõ*Óëú¯ç»þ Öÿ•škßúòñŸþ¥Z}B5ã�áEPäü­ÿ(²ðý•];ÿMÅ{×üKþQÇðþÉ¿‡ôÙm^ ÿ«Ê,¼ÿeWNÿÓF±]gÁo‹Z¯Áoø#?Àý_Bò\¾ðO‚Æ�.dµ‹ý6Õé¤#$�ýäÈà’¿&Îxˆ2ñ¹Ó]`Ý×ø£ú«®ö?½8'Ç� üLP˸« JŽ-è•xÅÆOþ�ÖkFúE¸Ë[G›s韅?ð^]Pòáñ¯‚5M1º5Αr—hÇ×Ë“Ë*>ŒÇùWÒß ¿à¢~0ùi¥øïGµº“�kª1ÓåÝýÐ& ÿºM~‘´àÒW‰‚ñ3£¥kT^jÏïVü�£âo¡÷æ7žZêá%Ó’\ðùƧ3ù)ÄþŒ-®c¼·Ia‘%Š@r°=#­I_Ïw„>1øÇáå„ÖÞñŠü9 ã.“¬\Ønç=bu=k¬ø{ûUø£Âó­ø�âŠ!-—3üUñ]¬ßEhu%Aÿ|úüˆ¸¯—OÏâ_†¿�üïÅBþ(Á§S$ÅRÅEtw¥7ò|Ðûæ�ÞZâ?hÏŽzWìÝðc^ñ–®A·ÑíËÅí­w9ùb…}ÝÊŒö“À5ùŸð×öÂø;â/./Ý~Ñ™¾ô±|`ñ]õ²ýYu%“òŒ×”þÜü3âïÚhÞñ7Ä w¶�¤óɯxÏ\Öỹ`Hd‡Q¹”FcRWrª±.àäb½Ìã‰òú9lñxÓá�GÊÕ`Œ=î�vDw¶}‰+Ÿ�2G΄¯#8ýŸ ç†üö?øÊ<Å4Û·éúSô"FSûÉþ8;˜´þij¿´×�/ü3ecŒ>$EsIíÔß¼Cz5DpAYmî/¤·Æ ÈXÀ>ÕçÕô¹÷ˆ<ðöYf—ZÉî¼’î»ýÝÏż)ú,6)ãøÞQ¨¡'ËF ¸ÊÏINZ>Wº‚³µ¹žð:/Š|Gñ³Æw^!ñV¯y­j÷‡÷— �£²ªŒ* 쪅s´Q_—NrœœæîßVuáp´pÔc‡ÃABIF1I$–É%¢K¢A_¦ŸðAý“á×Ä=|§ü„5+]=\ÿÓžBþ þù—]†þ/x³ÁºéZGŠМq“$Ê@üÒ½ÿökøó©þÑÿ„†OkÑn€m=µi‘n¯�ô�B¹Ú‡±fç‚5ðÿìÁÿŽÑlü/öÿŠ?m½Õ®•^-;MÕ.,?²È!ƒ}¦ÚD”Ê£…ýîµôü1Ÿ…ÿègøÙÿ‡ƒÅ¿ü²¯Ö¸s#â E5ŠÌå'´-gÿohíé¿~Çñ‡‹¹—†™mg“ðM)×”]¥ˆ�Fã§JQ\ª^sk•ý”Õ¤}SE|­ÿ gáúþ6áàñoÿ,¨ÿ†3ð¿ý ÿ?ððx·ÿ–Uõ?êþ#¼ò? þÔ¥Ùÿ_3êš+åoøc? ÿÐÏñ³ÿ‹ùeGü1Ÿ…ÿègøÙÿ‡ƒÅ¿ü²£ý_Äw�ãþAý©K³þ¾gÕ4WÊßðÆ~ÿ¡ŸãgþÿòÊ�샣é®&Òümñ¯L¾�˜®OÄÿj&ì|‹Ë¹íßé$L§¸#"�ì Guøÿ�jRìÿ¯™ùéÿ‰ÿͺÿÜËÿ¸šþ’«ùdÿƒ’~+êß>~ͦ¼ñO­iÚ·�|;}sb$¾›LÕ-4×¹8O4Ú6ŽÌÀàWõ7^#M;3ÑNêè(¢ŠC (¢€ (¢€ (¢€ (¢€ §â~Ïšö©¨N–¶m¼—W3?݆$RÎÇØ('ð«•ñÏü‹ö‰ÿ†{ÿ‚køØ[Íäê¾8òü)b`¿Ú³ö�ëÿ©qøâ¹ñx…B„ëKh¦Ïg‡rz™¶i‡Ë)oVq�¥Ý›ù-Oÿ‚xvïã—‰~:þÒZä.§ñCÅ3Yi¾o-oeÿ¹ºX£ÿ·Aé_¤õâ_ðNÙÛþSöøiàg‡È¾Ò´X¦ÔS"ö|Ü\�^&–@3Ø öÚçʰÅk¿W«üYìqþqO3â N&‡ð”¹)öTà”!oûv(+ó#öv?ðÔ¿ðqÇÅ_—GÁ_ � ÂO½äÝ2%»¯±ß.¡ùb¿I¼]â›/øSSÖµ)|�;G´–úêSÿ,≠»~ ¤×çOüiá{Ïüø¿ñ“WŒ.±ñgÆÓÜHØÉ’8wJ[>ž}ÝÀÿ€×Ò`½Ì=jÞJ+þÞzþ Ÿˆ÷ªÓ§ç»þ úOY~'ðN�ãu°Ö‘¦jãJ½‹R²¶©qö;¨‰1\G¼’¡$«®sÁ©_,Á[þ)|Wøeð ÁÑ|ñ-�…ú6žšöÔíJêMì”›ôI·ø/NçÐW_<}¤5„ÞðÔÖj믵³ép4-©,âáoJÇÚ겉q¼H¡³¸f¾ÿ‚vþÏÞOø*gíã½Á^ѵ-&íô¯¶iÚ5½´Þd×'rè�·Èö®Îs—fbrI®«á/üÇâ'Æ›/jšïü"ß ,¾_øÃĺ]õ´~o‡5kYm/¢�¼Ûb“ÀÑ©*Ì™UbTŸœàœÿðPåð�ƒÿhŸEðÿÆÖþ<×àÔügá½7S¶³HüE˜ó.$ûZÄØß[É42˾\àÆƒãÅÔT�9ßEí�¤9 ím׸ڵýÔžËOŒÏ#_žå˜H6£í%)kk¿dœS[»J´o¦’zôoô_Sý‡> kz‰´Ëσÿ nôßê «x†Òo XI½z¬Î·7hbÛ<Á�˜I f‰ÎI­ßþÍŸ¾h¶o‡üà­ NÒ¯£Ôì­tíÖÖ;¸àÑÜFˆ�RU�V%u„`(;F+æß…Ÿ¶ïŒþ+Éû3kzÆ‘ã_|HÑõKÛϦ•£]ÅâÙ"Ñ¢¼ŽXîÆ¡)²‡yf€3ùŽHYÄ@n¯ý“?h/Ý|7Ñþ øŠÿãG‚5ÿür>¼}nòËÄ:n±bÚÎ¥m“icý«=¾— 1Ç\[Å…áÜ‚áHzôÝ9S¯ìŽñ�ÍÊ1�ÜìôÕ$´½‘õм'Cë Ufþ\²œŸÍ]k£o{]Ÿ{ø·örø{ãï ø—C×|àÍkEñ¥Ú_ø‡O¿Ñ-®muÛ„X•&»‰Ð¬ò*ÛÀH� `‘q™¯~Ç¿ Ò$ØxbëÃVShÖN �ÑZ4f »�UG.Þ¦¹oþÜzÅ?‹wz‡¼ñW𽎹uá™üqmchúz•°aqß´ý»dr£Â×dû8•Jù½ë?Oý¿ ›â¾—áKÿ…_4ñTƒøJÿVµÓ- ñtÖp™ÞÖZ÷íV³I » Ô µR#m̧ãtâ»4ŸÜ´ü-Ê·i®]7ÕI§ºm|Þ�ñV—fšz£Öþüð‡Áχ¶þð‡…|7á_ Z,‰‹£é�Øéð¬ŒÏ X"U�C³»6’ÌO$ÖÄ�ÙáGÆOh~ñÇž*ðφ.�¤k³¾°ÒBGå ·‚XÚ8¶Çò €axWÊ?ÿà¢Þ>ý ÿf/ƒþ6ñ�â„WÞ%ø›aᡶÒ4�NËÅVò_^[H—ûFymbýÌk5ÃbUu&$‘#×u?ø)÷„t¿’|ñ‡qø¨x*oˆðÙÙ7†mõcqöO%‡Ú¾Üb¤Z•´6Ë1Á”*³Š‹u÷nVõiE¯ý8’ëÌímUã™GUÚÿ{’k¾¼�»hÒ¾¶vö½ à‚¼,ú+ižð¾œÞšêãH6ºT�.K¢ÆåàÚ£Êi‹¹�¦ ïmÙɬo~É_ ¾$|4±ð_ˆ¾|>×¼¦]›û= Qðí�Ö™irLŒfŽÝã1$„Ë)ÞÊüüÇ>KÿKð²|Fm&_üQ¶ðÝ¿Žë¿ϧYG¡YëFèÚÃ&ëíRG,ÞR,ÐÛÉ´è²™B��©jó‹ÿ±GÅ/ˆ«â»-F_ øšoè0øjëT¾#÷6�M5Ä(Ù@ày²±l+–Êç;WšŸímû@øBþ}Át¯�:w‡å}.OiÞ1O Á©M¤o"úÛipèÊæ §�z°ÁømÚÛþŒ§ÿ2þ�ÿÆ«ôßõ’UéÚ¼}¢kíSmê­½¯³î|öR§&©¾OI[fž×î“:ÿ‹ß²¯Ž¯µÜøFóM‡MñŠ­u«�ÛÄú‡…ŸSµ‹HŠÈÄú…Œ/qlË'ѵÛk©¼a¬YGckfÖÏ-šhq!°‘ÚX¦‘nf•ß3ÀØŒ¯ðWìEãï…Wßõ xŸE²Õþ$ǯn½¸igŸAš}FòëN’ÒFŒ²Ç²ï[ÿ«IA–0ÌÒùØÿðÚ?µ·ýOþeýÿ�Qÿ £û[Ñ”ÿæ_Ñ¿øÕ£ÊÒRû_eý¤¢þÏd½ww–£ýå­§Ùê¾ÍÚëÝ·øm¡çŸ¿à•ÚíÑñ=׉t½Ââ VÊ=Æ]B[ûAw°ÏrL¯¼Ê`F…ãS!g,ˆ5õŸÄ¯ÙÀ|VøÑý±©ÞêVºx{û)†‘¯_èÚ�ž.ÖpDörE � AQž…H¯ÿ†Ñý­¿èÊó/èßüj�ømÚÛþŒ§ÿ2þ�ÿÆ«ÌÉr¬¿+ ¨a`ì¥)|2ÕÉ4ï§geÚ˱õüyÇYßæ?ÚyÍU)òB ÞVŒak%¯V¹¥ÞR“êvž,ýˆîüAâmnëo‡®`Ô~$xűµô’Ü\5Ž›ecnc•Þ6gŸ}¼Ì›™�ó. 6)ÛþË^>ø[ãKøWMøyâ[Èî|Mnt�kQ¸Ó­¢´Õu%¾Ycž;K‚$XY"òv¿˜O˜6üü¿ü6�ímÿFSÿ™FÿãTÃhþÖßôe?ù—ôoþ5^§5²—_³.±ŒN±Š_{Üø÷퓵—/UöyšëÑÉþ…—ü³SÓ>øç@7Ôuü)ƒÀ¶W³E"ï|ÍNiØ‚²4v­-å¹YÛ÷<®QsÝüUý™üMâˆ>!\iShR^x‚O]éPÝÝK O.™2LÑÏ"Äæ%�Æ:¤„ÎÓ�§Í?á´koú2ŸüËú7ÿ£þGö¶ÿ£)ÿÌ¿£ñª9¨((B.)mhËMöÓÍ—Z¥zÕ}µi)J÷mÉ]¶¢›nú·È¯ó:þË?¼Yªx›I“Nøs‡~ ø“Kñ^¯ªÿj]¾©¢Ïl¶^eµ¼BÑVë d¢–šÝ�KŸ'1€ý.¯û.êVŸ|Y E xwÄ:¦¿ã+ÏGü$×Þɨ˜&û}�¼—1\F«Ê'ðmß¶¼çþGö¶ÿ£)ÿÌ¿£ñª?á´koú2ŸüËú7ÿ¡NŠÙKk/vZ%Ëd´éÉ=úÞú™Ú£Ñµ½÷[ÚJûõæ~^G»þκÄß ø4ψ×Ú>½~�Ë)Ôíï„×%Þâb–Ωek‹HûBÇ‘· íÞÿ9|ý’¾$xûöbð^—®éðŒzÂëï éq$—K¨]\êVp+‹èÝ>Æ"x‡˜ŠÓ´�óŸ,®Ã­ÿ £û[Ñ”ÿæ_Ñ¿øÕðÚ?µ·ýOþeýÿ�R©*5ŒÔšjß ¿¼»v—á­ÝˤêS|еîžë§•ígÛ¦ÊÈõˆüaá�x#ľѼ­]xcÃW>“H×/çÒ­a†cjâ[yᵸ+´Úª¼�|é³krÿeŸˆ²ÏŒtIt|âÍ*ãÂúG‡5w¾Õ.´‰´Ÿ±Ïu#µ”kmr²ÂÂíöÀïß)Fò+ËÃhþÖßôe?ù—ôoþ5Gü6�ímÿFSÿ™FÿãU¯·§Ïí=ëÝ¿†]y›[uro¿fŒcJJŸ³Vµ’Ýtµºôå_­Çø'þ ÿ«xFóÅ–Ú¯†4ÚÝE®.—©|Tñ l]JáåxdÓ¾Ï-­�)&Ö¸¶g}Ñ,ŠŠÏòkMû9ücÔ~Ãoª®‰­x‡H×ÛPð»¿Äkë[¯Û‹O"0º¢i&MHîisõ¬�Õñ;ÝsXßðÚ?µ·ýOþeýÿ�Qÿ £û[Ñ”ÿæ_Ñ¿øÕ`½ŒcËe¢ZE­�Óøw]N–m³Wí¹�·ouÕk×o.½tÐÛø‰ûxÓâÏ€¼c‰õO xƒÅ,øy¢xR}FâO{kwyqtî©Õ†C<$lP�Ÿ.0Vߎ?e_iŸïúúg‹î¼a¥Ï§Â.¦˜\Ú�U4k{!'—6$rŸ;c—â·í±«Üüø}á�‰Þ'ðäú¼:ÔßÛþ/Ö¼5¤A¬µ¥ÂD°­Å¾‘¨ZÞÌųo„h%‰]™ÙÀ÷í�L5YEÅN×ý>a Ñ‹O–öÿ/éŸèÿ´'Œo¾<øKÄ·WxwGñŸ€<sâÿÙZ¬Ï£´×đưJb‚âv½Ão0+��o7_øSû_ø»Ä?·��‡"ñµ¾£áÝc^Ö´›¯ j:¾�q¬h‰kËÇ,–6ÚTY#=¸òšæús$2)(Y‹Göý¬éIÔrŒ¬¯'oñ—O¿{ÞeQ8µmm~¾ìyoóÑü¬|•àïø¿Zý™¾ø«Äž#_k¾-ñ>�z‘jºN� ¦š_~Ô·òí·D~îf&I‹2 Òøõãߋڇˆ4MzÃáf£/„üKâ ÅðÌöþ YžÅôáj÷ö€ ¹“zùŒ€Àá×ê:*^ÎPŒš»my^ +ÍÙ®m]Ÿn¥Æ´UUQÆé+[¾²ûwnŸ#â%ý¤þ%xWáµÕî³ãöh¼Gá¿ ø¢ïZmÑÀöú�ä±j@‹Smo ‡F»óÌ[åyP.xkã§�¾#j:'†ü#ñ‚øgTø„º—�ítÍ.êçU±þÁ¼½¹†7ŽÜXJðÜBgŠªË±ÑÌrûFŠÕÒ¼›»µßWµÓK}’V½”ŸWfâg–�šÖÖé¿//6ÛßÞµíÙ_SåO‡3ñÄ+ÿ†vz®Á®xÏçÄH±ŽÛnìáº�t-3²BŒþR,nÙ=‡eyÁ�ñ|>ø ð³ÃÚ—í?�¼0º5èÔüK8Ð|Ý?W¶û*.‚ÓMhÖ°²$“Ê`–3tæ>$ÚŒèMŒ°ÒrsRÝ[¯ktk×KkmmtïÛE][K¶¶Ñ]éªwÓMz_gªùgàOÅ?ˆß¼káË¿ø¾ XéÞÓüK©éÑiÖðjSO{–ãí14ðBb…¢G�Ôà +oÅýˆÿiSñ/ÄSè¾"ø×7‰õý>îÄÉw¥k>ÕÍý–ß骖ÂàA³ç1ç õ/ÅÚkÇv´„¼=់>¶ð»éz-Ö�©x›Ånž¿~Ñ<‹pñ,zT«zÅQ%Œö›T$‘1öíQ¡$Õäôiú¥[ѽ>gï*‘i¤º5øÞÿvŸ•–‡Ä¾ý¬5‰~!üUÑãg‡µjÏHÖ¯ô›Û]sA»ðw…£Žác¶:ƒGj—ú}Ô{ÕYnZâÜáÈ’WýÒIð³ö‹ñ7Æ/ø;EðçÅ/\^ëdðÞ¯â�ø§u¸d‰4K«×].îÎÍ,æ‡Ì…6É5¸�X°–ÿU_kQJžQINWÑ.½%{ïÕié÷*Ñwj6½þW�—ÜýãáŸþÑ¿|5ð¾ÍâÙümªkžñ<öÚdž¶ZÞiPÛAsvѤҼ¨^I£.Êò7î–:�þÓ~(»Ð¼Ecðëã„_4Ÿ?ÂÐCã'Óô»ßì{Ûýj;‹<ÙA ¬ Û8“Êeó¢Þ ÞFWî*(†JQr“i[MuÕîïçë¢Õ­ÛFß¿.ë˲·m^—»)GâïÚ_ÝxgÄzêøÙüñsHÑìumcH°7²[Íagx ˆ HhÞêEYaŠ& «üYcæ¾øŸqð_FÖôK¯ŽÚ‡‡¬®>'jÖž+Ö/£Ñ>Ñà¸Kû‹gmÖ‚_·H°�-är# ˆ!u#ïz),;_k_ŸjK¿_fßý½®ÎêUSV·o½{O/ïü­§—Èß¾-xÿãV½ðãI·ø�‡qâ9á!Óô›´x¿O°ºµŽÊäy¶ï VVÝ$ˆåB^0�ÐÇõÍVôàáîû÷Ò×ù凌úIÝÝyþmþ°QE¡!EPEPEP^ueÿ)øoÿd߯úsðµz-yÇ…%OÁE|9&žévžø}¯Zë |º†£¡½œrcî´©§Ý²ƒÉ“Ò¼¼áÿ±Ïåù£·üxüÿ#áßø7[þViý¯ëËÆú•iõý×óÝÿëÊÍ?µïýyxÏÿR­>¿¡øsèŠ( Èø=[þQeàû*ºwþš5Š“Mÿ”*~Í¿õÃá'þž¼?QÿÁêßò‹/ÿÙUÓ¿ôѬTºzÿ‚"þÏ·$~ãKÒ¾êwoÚ kmOB¸žVôTŠ7r{&µ£üHú¢*| úJ¾Xø¹ûP|WÑ~+ø‚ÏÃcáäz“ã}3ÀÐC©é÷’ÞK.¡§ÚN·�,w�`–íI„FLÈ¥<Èï+êzƹøuáûÛ©¦›BÑ¥šâþ-VYÊ6io"DH®X•É™8Õd?2ˆÔŠý Pr’wÓKù®hßï�2ònëT|´f£i«NÞO£ù3å_~Ù߬< ¦Zh1xsPñå„Þ!ݵ§†–êÊ{]*ðZµúµÞµ`–‘e£,�5Ä™—å ±³ž†_ÚßÇ×· xÒÇŧƒ<¨Ýxv( Ó’ð?í›ã­{ö¥Ò¼?qc§j_<]¬kZ.‘ª®‰–æK¸gØçW¸¸¹ØöÏ 3X[#X)E“‚ý’ÿkÏiÿ°Îœ,WKÐ5 è~ðæ‰£ÞhϪjÚ�åÔVé ËEöÛHM½ß˜ \*.ÇyfFYmâøvojMñóÅÑζ^+Ô$inõ‹(RÒþéÙJ3I4A]‰Ve9'†aÜÕ9µ{»›ha’æáá·�-b�¤%b…>ìj3€ƒ 8ù¬¼f¡ÚžNé-d–×Öözêíue{´ì�êlÐG5›\ÍiBí?v”§µú9Sò¾×³Úç识¿noˆº×ÃË{ Gþ? øæ?ƒõ�nÙE¥¥ºØµÓ]=¥®¥:£¡)Œ_:³)âgÛÖ§û~xoá?ÅÏ¥äëâûy›J¶Z¹möxØÂâyĺ‚Åhø ¶¡¤äo¿1è¯2¯�Æ—²ÃÅ=7“{E.Ê×wnÚëkè­ö/ nM|nkV{üã Û}e=“I^ûy³ßi‰ý þ&�Fî÷áÿ‡íõ cT´’ïHÑugiË;Û–ó 7fæ$ å$q¼fT�ÊUL'À€!vÝÅA`¤�¤Œ‘’N�;QE~cžg’Ìë{z”a uqM_Öí«·­íåµ’þ²ðëøp~û;�Äb)}˜×”&¡å©ÆJ6²Qri[EvÛ(¢ŠðÏÑBŠæ~0é÷z§Ã­B(ç�ØÄe†‰g€J†x×Ô´BEuÎ;×!ªèš#\i2Aá›áḸ7v £L#yÌH"”ÙíÞPmuÿU�ì­�ã¯K ��hs¹[V´WÙ_ºÕíÕŸžñU\¿ðТ¤”iÊò›�Ç7ô„½Úi)Õ•ýÈÉ]j¯ê´W•è¾þÕ“@·ºÒ®?°Î¡,6w6È-Y”’FxT'•�ÀÚ ©PW·…þ7‡W·–ºLö÷ñxŽòÞy„-æÇ` êÄŒO+ÕƒhûŸpŽH5»Ë(¤ÿ{®½÷¿½ýß=Öç‘KŽ3Ô‡û¸ùnùåuav—²Õ/mxÝÆê¿.Ë×h¯ø{¤[_Máùtý"þnRinõµ�+Ú†˜2›�ºÈ~U$ƒnEý&›†ðŽ‹.�«yšV¹u%óÉbâÚ8ÌWª§Ì#c«ù‹‚¥€È ´� UÊ%(©í{éÛ›]ö|©/Uå|rÿjb)R©,2J£Š�¦ä¯'ErßÙ¯z*¬¥$“²§'{s8zn‹®Zø†ÄÜÙËçB%–ÛJüñÈѸÁðèÃßqVëÅ ðAÓþÝiÚv�ý�%¿ˆ%“UC¢ÈñÝÚ}ªvˆlM†æ0¦/’6?(Û‚>SÔ|<ðùÓü â%¶ba»ó 6pèWLVíä€DPLKaŽ Ûò–-ÆI¥‰Ë)AJP©¢•–�.¬ÛZmªîµëbòN9Ì1S¡C„Š”©)ÊJ¢Ò|­Ê1„’›JK’M&á6âïÊäýŠò}?áåφÆ�t Óµ ß ^Csp#`Zïm·�fsÉpÆLn9À`8¡Ò<;æÇxKEÔ4-F=æ+én,¤³2Ü4J!3… Ao03�†%þpIý—I®hÔÓ½¶Ý{Úév´Þãÿ_1Ð’¥[ïéî©¶åÌ¡/Ý^šçqS½Dù9Rní&×§]ø– ?Øé,²›‹ûyîc`ÅXZ%`Ns“ç.8ìzwЯÐ|4ŸÛ–Rø_Ãú†‰<^¾¶’y¬Í>ØßfòÃot™RL˜!±÷›iÅ¿ƒþËñ=¤«‹à²h®â‹Ãzy»o“™î$‘ãžE`H`I;œ‚A9ºÙ](ÒsSwKkk{½ìß.Ê×ül³�3 øèá熋…I¤¤ª%J_œ`êµ)MÉE&­eÌzÅQ^ú¨WÒ±÷í±áÙSKb¿ ÛT×î­Î´Ú¸ó™OHãC#OP'¹8ù¾Šôòœß–â+ÒšÙ¸ÆVôæNÏÍj|·pfUÅl²Œê� oÔ©Nöé'NQr]y]ã{;]#ôsHÿ‚Ïø"b>ßáO[sÏÙͼøÿiÓ½uZ7üÃá&©·ÏéÛ±Ÿ´iÁ¶ç®|·~�ñøf¿.h¯»£âçCâ”%ëò±üó�úøm_øTëRÿ Vÿôµ3õÃFÿ‚”|Ö€ ãH­Üã+q§ÝÅ·ñ1mü�u:Gí•ð£[¹ø‡áêÒ5(­ú×Bµø÷áë_5Qc¡i–³xå…•³Ï'× Å} ð«þ GñOÇæ)µ[}7ÂvoóÔ.ÎWÚ(÷û9Zứľ&ÇË—‚�_8©¥ónM/™øïý¼*áØ:™ž{S Õ*“£)?ðÁSŒåòLý+Ò>,øWÄ ø~ô’@úŒ2d�“÷Xö­èäÆHea�AÈ"¾IøUÿ|ð…<¹¼Oªjþ+¸R Dì6­ÿBdÿÈ•ôŸÃ_ƒ~ø;¦5§…ü?¥èpÉ�3ì¶ê�6:¼ç݉¯×rl^qYsf8xRôŸ3û”mÿ“żs“ðNnŸ fU±mu–Sƒô“«Ï÷Ò^§KEWП›„ÿð_�ù6ÿ€ÿöP¾/êZkúÀ¯äÿþ 쥿f_Ùöaþª÷Ç/mß´ÐMâ‘,R/ª¼nŒpÀ×õ�_›Öþ$½Yõ´þQEfXQEQEQEQEWæ_üÿÃcÁZ¿g€pŸ´h¾•¼câ8‡1²eòäô>M«(ÿ¯Áë_¦•ù‘ÿ¡ ûTÁZ?j/ŽRâ}?F¹Ñe<¤‘ aÓ°"‰ÿ¯ƒêkÆÍÿyì°¿Ï%HûÏòGé~ÿ±¬~~ÿ愹jµu»šRù¦ôQE{'æ‡ËÿðYïŠÿð§?à˜_5!'—.¡¢0,o¥K2üf?@jßüëá8ø1ÿÎø;¤¼©n´õ™�m÷Ìׇw¸óÀç¦1Ú¾rÿƒ–¼Mw­þÌŸ >én«ñ+Ƕ±¡ÿ–±ÄŽ1�úí=¹ü=ëôKÂ^µðW…tÍÁ<»&Ò++tþìq E’Šõ*{˜Gù¤ßÜ’ÿ3Ž>ö&O²KïÔЯ0ý§¿gøhûoGý³ý�ÿG�t¿gìŸhûoؤgû?ßM›÷cÍ·t×§Ñ^_ÚŒºÅ©/X´Óù4ŽÆ¯Aí$Óôi§ø3á�ŽŸðH¿ YüOý¥>&Kâ}Z |døs©hš¿…íà®�w2$—Z�­Áb¥ñ3Fa ÊBͼ¥y_üþ ÷¨üLýœbñÄ?Å­½Ö�¯øgD·Ò4A¥.žš�‚ÛP¼•šyÌóȶVêƒ÷qFÿvÅ÷/ÝŸ¶¾±ý…ûüO¸Îü"úŒj}íÝê¸ø%6‘ý‹û|>ŒŒ4°ÝÜ2öáÇèEyó§U†¯v0’K²“i¥Ûâ’Ó¦›%o‹ÄbªKŒhA?†�J—²¿3�(^öºº‚½�¥kÊìÊø+ûx§ÁR|ŸÅ¿´?^|¶¾ÓläÒü+&”ºÅœÚbið¬ª÷×&Œ!w‘NÙ À޼¿7s«Ý³æÚ>”¢½9ÎS©íeñs)_ûÑjIýé?>§×ÆŒ#MÒŠ÷ZjÞM8µ÷6¼º:|ý�¼sðÇz®�áÏŠ:u—Â-OÄš—‰×àë–Ó_¼·©ª5шZ›É¥�@²(`‚l Ÿý—¿àŠ_ðÏ_´_ïˆ7^,øªj?nµ¿µ4ï‡é¾'ñˆ¼µ¸�å×5v¼š[ë eI<ÕH•ØJZ&.�Þ4TS÷qÝ+|­oËü÷5Ÿ¾¤¥ö�ß®÷>GøsÿÐñƒ¾x;Àš‡Ä�+SðÏïˆÖÞ9ðá‹Â¯m~–Ñ_Ý^5�Ü¿mxçr×ñÅPœÄŸŠ/ø&&º¶÷ž‰¶rü¼ñËxñ¼.|/�ynR¹²þ×~_Øÿ´³6>ÅçùgÊóÿ޾½¢Š»ø;ßçh+úû‘û¯»wR\Ûú~2jÝ­Ï+[½¶Jß5xŸþ ãÿ 'ÂK¿ Â_äý«âÝ¿Å/µenÛåk�êßaÙç çÊò¼ìñ»–q°ý+ECܤ¨ÇáV·Ê1‚ÿÉaò¾íÝËÞ›¨÷wüe)?üšR>Ö (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€>ÿ‚±Ê,¿ioû%^(ÿÓEÕ~PÿÁ¥òŽ?ÿÙH¾ÿÓf•_«ßðV/ùE—í-ÿd«Åúhº¯Êø4£þQÇã_û)ßúlÒ¨éOø'�ó?`_‚Ržd¹ð‰q+²I%„.î}K31'¹&ºŽµ‚gI¬bñ^£¨ZÜjv×7–¶ö:5î©q,ÞY¸”Ek ¯²1*31U%�‘ÎÿÁ;¿åßÿìžèúm·®ƒâŸÀoøY¬µïí_±}�Ãφü�²ù›ÿ´ɼíÛÆ<¿²}Ü|ÞgÞ]¼þ�zª‚t•Ý¿ö×eÓweå{Ÿ1MÖµWes?öÃßhþ¥¡jZÖ»oá´·šétÏ êw“ÜÁpì�\ÚC»Iym!GÛ=²Ë܇!I oÛKáðð]¾¸·ž$š;�MôuÓ¡ð¦­.´·i™á}5m�ê0ˆy‡t e|íe'‡ñ§ìÞ*øw†¾*¶ý×…4 •¼Ñ�Í�ðÒî^sö«q:­®7yr[ù‹”Ü<ŠÁð÷üŽûÁÿ5 éš�ÂXŸTñ;øœøu-¾�¢Ìmb�™ok©A%ŒÑ˜Ë¥ÂNeRä†ßóš©*ÊRQWKgßÞK¿XÞ^[zÄ6£Í£Òþ^ííꥧš=ÆßµÇƒ>E¥Í­¯Œ´ë=ZÎõ½—ÁzÏØ¬a�àË�jb²+ÕÖå¢hÇ.sT<ûPØÞ|jñoƒµ«‹H/ìüJº/‡ílí¦šêö¤X_Í4ª›ðˆ÷l­1 ï�IÞë¿Ç~6ÿÁ-n¾5húe¦©ãmÄÛx^ÏÓj¾.ð�ñ«g%¹‘šóO¸–é>É4ÆAæ»,Ò?•ó7 zô½7ö,O~Ñž!ø™£ø–m+_ñMúil†ÛÝ5tû[Ae&_æt–ÛíÏ€bid]¬¯ wÍ[�»iªù]r½÷¶ý“z6”]%JÏ_³øé×ÖÖº½×C ~Ù×—]ßuâMü9¥Í­^ůxSUÑf6Pÿ­žï-¢{„NCí.€àºæÏ…¿i]â¥àöÐwM¥xºK´·ŸR³¿Ó.'X!óD–ðÍjѰ ‰ãFB6—¥xgÂø%˜øn|D[Ä> Ž}oÁWþ kÍÁ+¦__ý§Êÿ‰†£pn¥’úï1åÝŠ.Ä*Ù÷?ˆ_³n“ñJûÁé¯&�«èþ¶ºµ»Òïô亶ÕãžØ@VDrT(Æì`z{Ñ]Ó¼’æ²ÓÏšW×]9T~oÊÄÔTùýÍ®þëFݵ¿7ܽH~0~Øÿ~ø·ûÅzüº]ò[[ß\¥Þ\[éö³ÌðGsuñ}ž‰âvñ Zöœ÷Ú)Ô,`Æ–ºl°O ÜBó+ÆÃ$±2¹O¼‡M×MéuÿÛzÿ/ãéfíJíy}ï—U·óiò󺿭~Þ_ ´-BÔ$×u;•ñ,W²éÖ–>Ô¯u ¾Äéâ5¤6ïq°4Š$�ãW�Y@G+Å/Û+Eð‚µOéÉiâmÓÁRxÎÉ´×»¸¹Ô¡ÈlŽiÈpÊL¾adÉ&=€¸‹áì�?ÃoèÞ%¿ñ2kÔÚñÕ¤�L‘j7z­Õ•Ä’Æ‚F0ÇØÄi«.é RZ�†ÿbô_†Z†¦ñ#ÜCgð×þÛΖ6•J*›° Œü¿êùë÷ªeõ‡NêÊV{t|µ4×yS³Ówu½´‚¡tåµ×Ís¯»Ü½÷×mµÑ½ý¾~èZ.•w«^x�LmGNMVâ|)«—Ñ­YÙÅøû.l`,’m–èDŒ±»‚UX�Íoö½øáïx‹F¹Ö/ ß„¬´u™!Ñïg´Ó 0,êd¹HZÏoÝ! 3)ϼEûüAÖ¬µƒâo‡,.<]áè|5â§‹Á²:]A �#šÁ^ü›Iü›™ŒÍu`Œ#\nÛLý™‡…¼3ñNÒ¯49áñ¬vðAk­h§PÓíb‹O‚Çʸ·Gö˜Ù Ë.øò¯l�*Jµæà–—åóø­×O³×]n¢aN0´Tßkÿä¾_â饖çCðçãæ‡ñJKuÓ,|klnZtOíjú8>ãwm@D«°¶„Hq�ÂóÚ÷íyá_‡Úmí׊ob±�|Q7…´øôËkýVâþí iÖ v¢O´2$˜Ž5‘I «#³ª_²÷ìéâ�ÙêÎâÊïÆ>­£Þ]ÍrÚUž�wic¥'•p[i±Ïrlí“Ë�˜x³"ˆ–R­bÛöZû?Œ´­_ûw?Ùž:¼ñ¯•ö/õž~›qcömÞg~Ñ¿ÌÁÎÍ»Fw ýã’è¹ònhyÿ/?_ÑÜå}õ·þ;}òä_?[:ÛsáØðÚ_x�Z‹ÄqÜKgo£xSVÕoQmåÜyö¶ÖÒOlc˜ˆÜNˆUò‡ ªZí± j^>ø�¡Üh>9‹þÕÔ6óOiá fûíë$VìZ%ŠÌï`óà$fFhÔÊ?wó/âoì ­xÃÂPé:O‹|+¤Í¯®jÐk³xbíüA£>§¨MvÿÙ·öÚ•´–l«/”ÄY‚|êP´GcâÏìY¬üH_YGã--t/K¦_Ïe©øy¯Œ×vbÕ ]”º‰.lgŠÔ$¶Â8Ëy�ûЧeb爵Òï§å×§ãu¶¶Ó–Š•¯u®¾WýV¾^gSyûoü6Ó¼g¯Üjú½½­þ°Þ†Ò_êIªDBó‹6°6ÿkIš$,ˆÑpÑí æ&çÝþÚß ð‡õ¨5=kV·ñC\®�k¤øoSÔõ) ³ùw;ì­í乇ɓËæÄ¾[²£íbâ>þÀIðRf±á¸²|q?�ËÃþMLS.‘.šmmí’g ó|ÝÅ�‰6K«P~ÆÞ&ð7Štßø+Ç:.—â+¿ŸYðÔš��ͦ¯ªh´^LW–ò,‘HVQ.Ì0Û­êû­­í/u_®¾õ×’×^²ãJ×Oú¼üº%÷o¦¶ô7ý¨<Åü×ú’ëRH¶ûŽ‹|,#¸h|õ¶{ß'ì©rbÃù (—|œŒð°ÿÁM> \Y}¥xŸU±Öµ½"oùÚ”×Ël­½ïÚqA/Øàܯm+ŒË¶EÜ»:_þÊŸØ?ð‰ÄûÍÿ„[ÇZÏ�?ãËoÚ¿´?µ?ѿ־_ö—úÎwy?uwüºÃÚ_ÞÛþÍèµ×wo†=s´=›kþŸn­EvWÝî²tÏÛ“K>>ð‡ï4‹ùeñݦ£r·ÚE†§¨éöFÖå PÓý‰),ÆG—ÊXv�Ä«£¶×…ÿno†>0‡T–Ï^¾K}/K¸×>Ñw¡jvú……¿úë«)f�úI{S*âHÎpêNN›û!ê·ði¾)±SáDÕ,oþ×£<ÃS±Ô&IfŽ0·`˜yhReQóf6ÈÇ7§þÂ^#Õ<'ƒâoˆZ~±¥x{Âz‡„|.-|3ö;‹n­ÒÛí®ndS$1¢�%m�“!)ó(Ne,RƒÑ9ko¹Û¯{-µÖü«Þ5Œh9FîËKýúô×Kúiñ=¹ü,ø©£üfð|ö€Ú”ºUÓך]ÖžncF—Æï È £‚ ±×EUt=7ûD³³ßæ}’‡~ÜoÚ gºUªô%dìŽ898§-Š(¤PQEQEQEÂ~ÏÇgíýñmG |àé CÅŸ®F}zWç÷Áïù]£Nÿ¶¿ú�½~€üÿ”€üZÿ²{àßý9x¦¿?¾ÊíwýµÿÔ ëàs?÷©úŸOƒþ Oè’Š+ˆø³ûJxàU³?‹¼]¡hN«¼Aqt¿iqþÌ#27üMpJJ*òvCÄâèa©ºØ‰¨EnäÒ_{ÐüøÃÿ+´j?öËÿP4¯°¼gðÁ¶ísñ;Rø�á/xÁ~jv~Ðm5kî­­a“IÓõIå1¸*ï$š€R\«Û¹÷|�ñN¹ñwüs¬~Ó^ koønñâ‡BÒ"YâÔu—_ &›îP޲IÐ’‘’Bç#ìØ»ÄÞ ñ�ÄO�ú—Š4…Ðu˯ˆVíq`¹ÿGÂþŽI91„'ÜžAÝ�ס_ÉzÉú‘òÔxÃ+̧<6[WÚ8«¹EIÃt­ÏnVõÙ6ísWþÝû?ÿÑ ø;ÿ„^›ÿÆhÿ‡w~ÏÿôBþÿá¦ÿñšÒý±ãEÓ¢“Rµ]6kèî¥{{xØÞ,›#W— Gå¥?dåEIÅÃo%Þ óœWϲgW-Ogí9»÷é/É3׿áÝß³ÿý¿ƒ¿øEé¿üf�øwwìÿÿD/àïþzoÿ¯–gÏÛ[Ç^6Ôüamÿ gÂRØÜø û_²¾ñˆt»ÇðuìrÁ)©=ž“eŒª.�¦ð£F;.%Ô¹ý´5¯Ùš;�;⦾5OøLF‡qã=SÄž·ðäL¶áÕ5˜4‰ìÚňÛ5’ݴ̉" QŸ·Ãòsòék쿟“ó_q~ʯ3�6Í­ûG›òüO¡§ýƒÿfû]vÛK“àÏÁõ;È%¹·´o ibyâˆÆ²H‰åneC,AˆS"g†nû¿gÿú!ð‹ÓøÍ|¤ß´¿Ä?xoàß�Vî ½^O x¶Ç]ñ …°½›LÓ!Ö´›yuX-Œ¥Ü© k!LKËÊ-Ø µ~£â—í“â}öÇÒt�üCµ›Fÿ„“DÑ%ðéŸiÖ-®ÒØI}i§Ã¤›·µ+q¸]6£~r8QV7Ò3 år«Ê\».²i>öóït®Ó´Ê3Tý§7Fþçkzù|Ý“WúþÝû?ÿÑ ø;ÿ„^›ÿÆhÿ‡w~ÏÿôBþÿá¦ÿñšókþ)ñìéñÄÚÿ‰ÛÄ6�x†ÿHÓt+íN—LÓííuo&2Q­ËÍ&ÕÁi]—0�Ô»v?ügñVëYð7‡äºÑþ7‹†÷ü;»öÿ¢ðwÿ½7ÿŒÑÿîýŸÿè…üÿÂ/Mÿã5óç‚ÿhߌ>ø_ˆæñlþ6Õ5Ïøž{m2OÛ­o4‹¨m ¹Š;hÒi^T/$Ñ—ey÷K �Hi¿]è^"±øuñÂ/ŠOŸáh!ñ“éú]ïö=íþµ �Åžl †ÖPmœIå2ùÑo¿ï#+0©FrŒ# en‹«{öÛ}µJ÷ЯgR׿ÓçÝ/Íúîík_ßÿáÝß³ÿý¿ƒ¿øEé¿üf�øwwìÿÿD/àïþzoÿ¯5�ÅÞ%´¿ºðψõÕñ³ø;âæ‘£ØêÚÆ‘`od·šÂÎð@�,ѽԊ²ÃLWø²ÇÍ|#ñ>ãà¾�­è—_µY\|NÕ­”ÿ‡w~ÏÿôBþÿá¦ÿñš?áÝß³ÿý¿ƒ¿øEé¿üf¼§áůüj×¾i6ÿïâÐïî¾ðþ‹nºœ[Ú\æÌ^Ù^ɽmÌW 4oæ¿—9˜ªÅP•J1å·2ºº]“õë¶÷V°œj$ÝövüZý>ãÙáÝß³ÿý¿ƒ¿øEé¿üf�øwwìÿÿD/àïþzoÿ®?ö¹ø‹ªü3ñwÄ-gBm>ÇZоêÚ­† tëynmî!”´¼t.ц¼¢LdŒ•'šã¾%üQø‰ð~×â‡õ�:¤öz/‡5Û?ø±ôÏC§É{{{ ݤWd–Ñ’Ï÷}½Ë«HC´¼2®zJ nkÑt”£ÿ¶·ÿ¯gW¤¿«AÿîH¯½ì{ü;»öÿ¢ðwÿ½7ÿŒÑÿîýŸÿè…üÿÂ/Mÿã5óÿƒ¾,ÚüDø™ðÇ7ü}a¥ÝZø‡J‚MlxlIâ;´¼¶ j’Ú[5µÇÚ2Çö'HàB„?šÍÄü,ý»>!êÞø™qiñÇÄQ[|0Ö#]êÖöú…uI|My£ØA'… Ôõ+«MBâ4ŽÙàŠÛ͌ܤ¦,±–I‘H®{ö�ý¨|Qá-3ÀV~øïàë¿ jú~¥x>!ø›ÅZ&™c¬_CmÒ}~×5ºuÞÛZïMOsÿ‡w~ÏÿôBþÿá¦ÿñš?áÝß³ÿý¿ƒ¿øEé¿üf¾{׿hŒž)økâOÂksàû¿|%Ó¼púU¿‡í|½[Pi5V?h[¨žh š+8·ÂŒ’)a²T*Ûöÿkæ›^Ò>/Ký½ÿm¾“¨x7Q¼Õ´ë>)’sI%ÄóÛÈ8‡ïCK‘‹¨Mêά©SWpëm’êõ×mžö}Ò%Nu-þ}"ÿ)¯/3ÚáÝß³ÿý¿ƒ¿øEé¿üf�øwwìÿÿD/àïþzoÿ¯ñ?íU­iŸ¯`Ó¾4­çˆ¬5­Oð·‚ZÏK�x÷Kž;Bú‘Ùoö©üÑ5Óý¢Éã¶‹ìÙ1â9{Ï쯨ø§Æz—�uïx¿QÕì×ÄÚ¶�¦èæÆÎ =2ÞÖúX£!£„O$¥WžR»Bü�ƒ;]/eR\±�Fö]9yiºûIi|çí#fú¥÷ó~ë×gѱ¿ðîïÙÿþˆ_Áßü"ôßþ3Gü;»öÿ¢ðwÿ½7ÿŒ×±Ñ[{ʾã?i>çŽû¿gÿú!ð‹ÓøÍðîïÙÿþˆ_Áßü"ôßþ3^ÇEÆŸò¯¸=¤ûž9ÿîýŸÿè…üÿÂ/Mÿã4û¿gÿú!ð‹ÓøÍ{{ʾàö“îxçü;»öÿ¢ðwÿ½7ÿŒÑÿîýŸÿè…üÿÂ/Mÿã5ìtQìiÿ*ûƒÚO¹ãŸðîïÙÿþˆ_Áßü"ôßþ3U¼-ðÇÿ²/íð¢ßáÎ�aàýâ7ˆ/|;®hºT_eÒîUt]KQŽàZ¦"IÒM9HФ®¤‘·Ù^añ£þNCönÿ²…{ÿ¨—ˆë‡2£Oê³|«nÇN¤ý´u>%ÿƒu¿åfŸÚ÷þ¼¼gÿ©VŸ_Ð�=ßðn·ü¬Óû^ÿ×—Œÿõ*Óëú¯†>�(¢Šü€ÿƒÕ¿å^ÿ²«§é£X®ïàÄ+/„ŸðCÿx¯SÒmuí7Ã?luk½2åÁ¨Ã�¯©uB¤A x®þVÿ”YxþÊ®�ÿ¦�b¯ι÷mßû¬Pÿ ¿àŸ†¼em®x¿âÖ§©ˆÃÜOˆúþ™eo#rÑ[[ÚÞGVêIXãÁ*Š£q “¿ÿ /à¯ú übÿ÷â¿þX×±×Ìßµ¯íIñá�ü`<'oà©tO‡þÒ¼Ko«ZÝ=Ö¯ö«ÛËwµŠhåT¶ù-r&hæÚǘœ—ô)•8¯wðì¯ù/W²»²>Y:“¼¯·êÒ_‹_ðÇmÿ /à¯ú übÿ÷â¿þXÑÿ /à¯ú übÿ÷â¿þX×›x¿ö¤ø§áM'Tð¯™á}WâmŸ‹bдåѼ,÷6Úä/¥.¢Á-®µ{E�ãC&é$½*D?*î•QrÇí¡ñKÄ?³¯ƒ|{ †…á½Å«ëƒÃRøž"K+£l�ö-M'KåÊåíä½cæo-LõÔ:Ç·EÕ_}´ë碾…òUºIï¶þvÓ}m¡ÛêßðJÿƒZÅÍõÔÚOŠ®5M@bMBûƾ§r÷�»¹™sõS_<|kÿ‚8kÚ›uàMzß\€d­†¥‹k =Aû·>äF+×¾#~Û^5ð×íei Ûé^"øoÿ F“á[ûÁ¢Ãj-n¯c·>Z^É«‰fu[˜æ-1Ыl2¯"ó_¿j»ÿi_´ÉåÒ¼)£øóÆ 7Z¦�5ýÖ¶[^Ô6ÜÚCѬ–Öî¾\©æyÏ$Џ·S³ü®sÁÙi*ô-/{ÞŠå•┺hïšn÷[²p�—áÄÝ↼‰|3&„m-µý6ÞÖÎæFäÆÂâÒÛTÔ€òã�åÝö€ÅX …ÌŸ0þÓ7 YŽ/ ´…úi¿éwú„úf—s¨y¼”¶¹šwŽ𫟛£b¿ âî¡”Þ¥\ZþYé=›²J÷µ¬ôŽ­#ý ðKé-ŽãiÇ ŒÉ«FW³­E9оšÉË—‘kt¹¦ì™âÔQE~dZQ@Q@Q@eôχºkÿ–):BŸ~LI4ÁTw<É ƒ=€­JñOj¶Ú•Åê_j·ÃÄ1øžÖ4ÓEä¥Õo`òØÛýÁŒ+ù»~ùÆþv›Ú§ˆwjz¦ýjþìù6b_H¡íüÄ�k�’FÐ’Í!FÛ¹Îá³å÷ÞO)ÅIÉó6ï§øuÿ Û¼¿ò:~$P¡Vt)Ò‚¥Ç•)$Õ�Tᵕ^XEB…“½Ó•µ^»EyF±âmGEÖõ ½¼ á›çÖ¥�ÊØ–ÉÚ6ØäžP,× À6ËÐ(«PÞø�<)ö­KW‚/Ý^_Ì�]É]HaÈ!�*lAÆ2k”µ9OGþN_’üO^>!S©ZXZ4©“M¤®êB‹W×j’ié´[]éôW/ð†î{� MóÏtlµ;û8åžC$­WrÆ›˜òÄ*¨Éäãžk¨¯6½/eVTÞ¶mi”f �£�ŒyUHÆVíÌ“·È(¢ŠÄôBŠ( Š( ¯á…¼-âߥ¯‹¡†¯®áž"Áe“¾+ þr½ÿhÿä·ó?ñ[Ã,óЍ:yF{_¥¹`£Èý\}�]zþõ®ÑïúÓá¯ø'_Ã_i‹e£Ü|RÒ¬ÓîÛÙüSñD/ÑWP´?á…üÿA¿Œ_øvüWÿËüþøMÿ2ø³ð´ÇºâxšÅ1û�f?´1ÿz ËŸ«‘í_N|&ÿ‚ÇøGÄ>T/Ðu?Îß+\Ú7ÛmGûDad_ Wú×ï™7‰\9ŠŠ§/ܾҊKïW�½lœsôOñ'(œñ4é¬t7r¥7)¿XO–£áRõ=—þ_Á_ôøÅÿ‡oÅü±£þ_Á_ôøÅÿ‡oÅü±®Ïá‡íௌöË'…üM¤k Wq†À¸AþÔM‰ñQ]…~‡BXjðUhrÊ/ª³_z?šs?/¯,.>œéTŽñšq’õR³GŽà ø+þƒ¿ðíø¯ÿ–5_Wý�| ¬ésÚK­|_1\!�ÃüTñ4êÊx ¤·í:†Rq^×Emì)ÿ*ûŽi>çágüQâkÅð‡Âo]Ê—Qü#ñˆü'cv¶ñ[››?ì¿ ßÛnŽ%HÕ£†þ8NÄPÞFì Õý]×òkÿÿÉPû)ßþ¢^¯ë*¿?ÅEF¼ã“™õ[tâßdQE`jQEQEQEQEygí¿ñÍfŸÙ â?޼ß&hW6mëtc)n¿ð)š1ø×Í¿ðoÀ“ðsþ ­áýRâ¥ãýFïÄW‡ÎQœAO¡†Ü×CÜšåàä_‰Wö¿±ç…¾è Ë¬üXñ]ž–��>O›�üû(üM}Óð_á�—Á?ƒþðv�ƒaáM"ÓH·!vîŽR%8õ!sø×�Þæmô§|äïù%÷Ÿ¥Õÿ„þ§ §�®åëN„yWþTœ¿ð¦¢Š+Ù?4?4à¥Iÿ ›þ �û!øÿ}‡Rz„+$“îÿÊXÿ¾kôº¿4ü+ÿ‡þtñÿ­O…ŸQˆÈ�¥‚õÿ‰“ãÕúY^žcîÂ�>ÐOïmþ§YT—÷¿-Š(¯0ì<'þ g¨>›û |G‘:¶œ‘£ÏÑ�jÿÁ?tõÓ?bŸ†Q§FÐ-åéÝ×yýXÖüþL+â/ýyÁÿ¥P×Gûɘü0ÿ±nÏÿE-pÿÌgý»úŸù-%ÿ`±ÿÓ²ÿ#Ö¨¢Šî>ð(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šùÿþ Åÿ(²ý¥¿ì•x£ÿMUù)ÿ§x’ÃÁ¿ðLˆº¾©u Ž™¥xÿR¼¼¹™¶Ço zN–îì{PIúWë_ü‹þQeûKÙ*ñGþš.«ðÛþ¦Å?àܯÚÌ‚AÆÛþ)‹:ûös½øÇào„Ú^�ᇾ ¼ð6›ÃágñO‹n4=^=$ôH®-íôûÔ�ìLùªÄ(Ý6åÏü&ÿ´ý/ƒ¿øsu/þP×±*„PÀ;W™|`ý±þüñoö'Šõùt»ä¶·¾¸#K¼¸·Óíg™àŽæêx¢h­ 2FÊe™Ñ�¹†FAåT £)Ùm­¿Èùk¹¶Ô3+þÚþ‰—Áßü9º—ÿ(hÿ„ßö�ÿ¢eðwÿn¥ÿÊÑÓ¿lë~¸ñ&ž|c©XÙê_ÙVö> Önµ+;Ÿ)f -Œv­u˜Ù;Ĭ‘�Ä:åš�í£ðþÇAÐ5oxêûÚ޿}mªé—)et±hz…Ì\Én.!¶iâ�¢ûDѲù0ïó&b5w;iÚ—íÁðßNðŽ‰¬®©­ßÃâ#uö+=7Ã:¦¡ª²Èbº2XÁn÷Pˆd\†X”FåU°Ì�i^oi§Ëü‹ä•ÒäßÔ­ÿ ¿íÿDËàïþÝKÿ”4Âoû@ÿÑ2ø;ÿ‡7Rÿå /‹o¯„Þ šÉo ð‡„|9}âk×·Ô|Qüú}Žƒ¦jzëÝÇg2Ç;D�Ù‰ßËŒù…v�änDg¢rŒ5œí¿n›ô§'ËÝüÇÂoû@ÿÑ2ø;ÿ‡7Rÿå ð›þÐ?ôL¾ÿáÍÔ¿ùCT¡ý¾<7Å+ý ^mÓÂ6þ1O[hú…Ι5œ‰<¥„ñÛ˜Ba ¯æ�ìÆ5Эjj·í;Âú>¬o…áÝGW³K{¦e·–{›X$‚ÙkÓº ªÌp š‹Ã¿¶_ï|@³ÖoŽ u ½!f›D¿ƒO{ëV™n-ì�­±�<‰›Êï(…•ÃU;'g=u]7[ôé×°µ·7.Ÿ>»~Oî3á7ý è™|ÿÛ©ò†�øMÿhú&_ðæê_ü¡«z_í¹ðÛV𾵬&³©Ãa¡ÃÄ­u j6¯{òyPIg�+ß$Òa#kU”HÌŠ…‹(9òþÛ¾“â×�<1o øì§Žíon ººðvµfömÐÄh¥³V‰Y¤b^SƪŒß,¨Å'ÒSßm¿ËÈvvo—oR_øMÿhú&_ðæê_ü¡£þÚþ‰—Áßü9º—ÿ(k[Á_µG†5ÏGqªêzf�z4‹Ý~híÚæke±µ¸h&™f–Þû]ë°2—*Í6‘ûXø\ñý�†­õ WûOQ,/&…’Í$á-¤»hE¼WFìòH³a†S‘DZ’N3ßm»µÛºkÕ5ÐOMãùù?ɧó]Ì?øMÿhú&_ðæê_ü¡£þÚþ‰—Áßü9º—ÿ(i·_·‚ü!á_xšõíõE>Ÿc éšžº÷qÙ̱ÎÑ$6bwòã>a]£y‘ëCWý´<§]i±Z]ø�ÄgVÓmµ{y<7á]W_€Z\†6òÉ-•´É�#óI Hæ§ÚC—›ÚiòóòòsìW,´÷wõ(ÿÂoû@ÿÑ2ø;ÿ‡7Rÿå ð›þÐ?ôL¾ÿáÍÔ¿ùCY?à >ñ—À¯xãPÐ~"é¶úü #[Úø_Ô¾ÈȈ﹡±'ÊălÅV90ûIØØÚñßíÛð¯áÆ¡ko©ø�É»Ò-¼@&²Ò¯oí¡Ó.DŠúY …ã†×1¶é¤e�>Rì¡—7uüý»uÕtê“Y/çÑÛóiz»ÿÂoû@ÿÑ2ø;ÿ‡7Rÿå ð›þÐ?ôL¾ÿáÍÔ¿ùC[^)ý­¼ àïjZö£ªý¯D·7:¥Õ¶ƒ¨]išJùã7Ñ@Ö¶ïäáÂK*¹WŒ�ûÄÝ›7íÃðâÓ‘ê÷�ˆ-mQtXì.|+ªÁ«IxÐ5ÂBº{Û ²Ï3¦"Ã�ò’x¥Í®ê~^�»ŠÏùwõíË_B¿ü&ÿ´ý/ƒ¿øsu/þPÑÿ ¿íÿDËàïþÝKÿ”5/�o…¢½›Lñb‹M?H—]šò÷N»°´û;ìóÄ‘»@Ò"O±xì•QÁZm·íãð½ü¬ë·zæ©¢Øø|Ù}¾=kú–“w^L µ›ì×6ñÎÐÉ)زªò¯ó|�ƒš;ûO˽»wMz¦>YmÉùùšûÐÏøMÿhú&_ðæê_ü¡£þÚþ‰—Áßü9º—ÿ(jäµÿ…Õ4‹–Óõ¶ BæÖÖÎ}Ä)3$3“*G·Í@¯³®ì‘\˜ŒÃ CøÕ”}m®‘zi®’M[tôêxù¦{—e±æÇÕ…?&õê´W»Ö-h·ùü&ÿ´ý/ƒ¿øsu/þPÒ?޾?Æ…�Ã/ƒ¸Q“ÿ7Qÿå yŒà¡Þ&×õ=ÇÃÚ"i¿ð’^\iúsÇes¬\Ï5»ª\ŽÞ6#Êfýæca X€¬G~¿²о$0“âÄ Fþ69{?å„zà�ߺàY½JßîTç?7hÇï’¿Ü�Ÿ|aW¦S�©WûÒ^ÊøìßÊ,wìÙûOøká狾!xÏâ]Ü:ÄYìô)ô]8¶£ioaeö‰­Þæ1¶ãsßÝ;Jë þ[DžX-ñÿ‚-n|Uÿ [üzøs¨éš¿�u’éáê8îïöøi¬&’\N¤*F“OòœmQ–ŠûSöZýŸ|#áŸÛ/â‡ÿ±­5-7Cðg†5K$Ôc[¦‚êêûÄÏ*— égl8ÀPÀœü_ðf%ƒþcÓQHªª0¾òXÊXÉW—¶š‹¿MþG»‡Ëx£M—EgciÃoh‡À¡ÊÇ€ª 31 –'©¯¾>Ⱦý®>‹$Œ¾³û»h™ô¸ØS§‡T⬶I/ÐúŠñÏøwwìÿÿD/àïþzoÿ£þÝû?ÿÑ ø;ÿ„^›ÿÆkìoS²ûÿà¡ßðÿ‚{ãŸðîïÙÿþˆ_Áßü"ôßþ3Gü;»öÿ¢ðwÿ½7ÿŒÑz�—ßÿ-ÿ‡üØè¯‡öý›î5ë�*?ƒ?_S´‚;©ì×ÂZYždi9<­ÊŽÑJˆÁ1¸tâçü;»öÿ¢ðwÿ½7ÿŒÑz�—ßÿvŽ×wüØè¯ÿ‡w~ÏÿôBþÿá¦ÿñš?áÝß³ÿý¿ƒ¿øEé¿üf‹Ôì¾ÿø´;þðOc¢¼sþÝû?ÿÑ ø;ÿ„^›ÿÆhÿ‡w~ÏÿôBþÿá¦ÿñš/S²ûÿà¡ßðÿ‚{ãŸðîïÙÿþˆ_Áßü"ôßþ3Gü;»öÿ¢ðwÿ½7ÿŒÑz�—ßÿ-ÿ‡üØè¯ÿ‡w~ÏÿôBþÿá¦ÿñš?áÝß³ÿý¿ƒ¿øEé¿üf‹Ôì¾ÿøhwü?àžÇExçü;»öÿ¢ðwÿ½7ÿŒÑÿîýŸÿè…üÿÂ/Mÿã4^§e÷ÿÀ C¿áÿö:+Ç?áÝß³ÿý¿ƒ¿øEé¿üf�øwwìÿÿD/àïþzoÿ¢õ;/¿þZÿø'±Ñ^9ÿîýŸÿè…üÿÂ/Mÿã4û¿gÿú!ð‹ÓøÍ©Ù}ÿðÐïøÁ=ŽŠñÏøwwìÿÿD/àïþzoÿ£þÝû?ÿÑ ø;ÿ„^›ÿÆh½NËïÿ€‡Ãþ ìtWŽû¿gÿú!ð‹ÓøÍðîïÙÿþˆ_Áßü"ôßþ3Eêv_ü´;þðOc¢¼sþÝû?ÿÑ ø;ÿ„^›ÿÆhÿ‡w~ÏÿôBþÿá¦ÿñš/S²ûÿà¡ßðÿ‚{ãŸðîïÙÿþˆ_Áßü"ôßþ3Gü;»öÿ¢ðwÿ½7ÿŒÑz�—ßÿ-ÿ‡üØè¯ÿ‡w~ÏÿôBþÿá¦ÿñš?áÝß³ÿý¿ƒ¿øEé¿üf‹Ôì¾ÿøhwü?àžÇExçü;»öÿ¢ðwÿ½7ÿŒÑÿîýŸÿè…üÿÂ/Mÿã4^§e÷ÿÀ C¿áÿö:+Ç?áÝß³ÿý¿ƒ¿øEé¿üf�øwwìÿÿD/àïþzoÿ¢õ;/¿þZÿø'±×—|L• Ót{T³°¾†=SÔ¡˜ÛÆbhå°UWU ²yA$Ž ÍÔú¬ô_ü«Éí£©òGü­ÿ+4þ׿õåã?ýJ´úþ„kùîÿƒu¿åfŸÚ÷þ¼¼gÿ©VŸ_Ð�|9ôaEPäü­ÿ(²ðý•];ÿMÅløA¼ñOüÍe¦iöò]ßê?³´v¶ÐF¹y¥ DQÜ– ­cÁêßò‹/ÿÙUÓ¿ôѬWUðoâÆ­ðgþÇðGUÐ^uÝCÁðö�q,bD³¹ÔÆ›¦ÅpÈxqݬ›O ³­4›v@Ý•ÙìZ·kâmÏQ°ž;«B¹·š3”š7PÊÀ÷£®|:ðÿ‰ßPmKBѵÕ­¢²¾76QÊo`‰�ãŠ]Êw¢4’2«d#2Ƽ—ÂßðOOxSDŠÊ câœqÆY¶Ù|D×4›pÌK1K[¨-¢’vÅ(Ï +Cþ_Á_ôøÅÿ‡oÅü±¯ÑSªÖ±_üäýÍ“wü¸ñßÀ�üRÒoìØÌ×gÌ‘69f2�K–bÄæ¾É±ÿ‚S|´V£ê×9<uI†ß¦Ò+Jø&ÀëˆÂ·‚¤ãœ®¹¨¡?ˆœWçY¶GÆÙŒ]?oJŒÙƒ’ßÏ‘¿]UÏêã�¸eƫ˱xêÊÞýhR’ºíMÕŒ-Úð“[\ü�Òô›MÍm¬m-,m�’�[B°Ä™9;Q@QÉ=X¯Õ{¿ø$ßÀ[ÐÛü©�Ý|¿kþ[n†? ¢ÿðG¿ÙùçÂ)âDuÁ?ëé‚:/E|güAŒÚr櫈…ÿíçú¸ÿÄó𖚣‚Ë+¨ÇD¿u—’Ri–ÔWêå—ü¿àΛ»ìúw�`ß�Þ_Ä.ìzâþ¶ì?àŸ_4¦Í­ÿÅ‹c·fbø­â”ù}8Ôzp?*ê§à–%üx¨¯H·ú£ÇÄ}=ðÿwɧ/ZÊ?•9’'G»[/´›[Ÿ³ã>o”Û1Ó®1U«êÏø)‚øwá_‹lüá]gâ ÜɹÖ×Wñö¹­ÛÄ40˜/.æˆ0Ú$ݳpÊ`Žkå:ü³‰2š9f>x5}§&�ÚË›ªZ½¶õ¹ý�áWãø³†èq?õ_oyBüïÙý™7Ë‹t­ðò»ëdQEàŸ¢…Q@VÇ€< ©üNñ®™áýÙ®õMZámíâØ÷'²�’O@'¥]:s©5N íè—vÌ18šXz3Äb$£&äÛ²I+¶ßD–­˜ôWOñsàxÖç@ñ6�6�¨[œ�Ã1ΙâHÛ£¡Ç{ƒ‚®bªµ ”j:U¢ã%£OFŸ™ž†Ça¡ŒÁÔU)M)FQiÆIìÓZ4Š(¬Ž°¢¤¶¶’öæ8bF’YX""Œ—bpµ÷§�ÿà�÷Þ Ófð牤²ñ YD/mõó-'¸Ø<ÂŽ€~Ì>´1X|l»Õ¬"6šö€5ltO´,ªãÅlÿà ø+þƒ¿ðíø¯ÿ–4à ø+þƒ¿ðíø¯ÿ–5¾‚²¬ý¦�³Ý©R?“Gyã×çT>«œâ㈇EW†©köæ¤Ú~kUÐõ­&+¸tøÖúk{‹ ?y$ccì…Ü�ûèÕšñÏøaÐoãþ¿ÿòÆ™sû x2{wE×þ2D̤_‹>)b¾øm@©üAÕõKÚ%d—ÞÿÈü†rŒääÞý’_‚Ñ|�È?ø8ÁLßbº_šÚïâGˆ »/—áŸA&}²Å*F�‡jþ²«ù8ÿƒ‚æÿ„cÀŸ ¾(ŽKo…)ñG‡­o1Å.¡ Ú†õT–`ŠªÓãRÙ$˜G�¤o™Ø×õ�_Šm×›–Žïó>–�½œmÙQX…Q@Q@Q@Q@™ÿðPQÿ ÿØý˜~ô‹ÚIâû±Õ!”<— 0÷þÏ·ÿÏEõ¯Ó üÑý�Gü4güƒö›øŠÙžÇáý”~´ÈÊÛÌ;bTúŸ°\ÿ߯¯Òêñò�Ûb?šoî�º¿#ôŸ¿Ù¿³²…§ÕðÔù—iÕ½iékî (¢½ƒócóSþ NŸð°?à±¶�Фýìº>©‚²cî(¸ž-¹ÿ·ÿ¾kô®¿4¿à߆ÿ„«ãíqãó|Cr_®í³ßM׿ü|þ¾õú[^žq¦%DzŠÿÉQÇ�þ û·ù°¢Š+Ì;ÿ‚�Z=ïìñd‹¤?E¸‰�è mþÀ—iyû|1täZÆ~ª�Oê Yý¹4oíïØãâ|É¿œ g&8Aÿ ×)ÿ´Ö?¶ÿ`�‡sg;-nmÿïÝÜñÿìµÅÿ1Ÿöïëÿø?‡�÷°¿úMUÿÉ@QEÚ}àQEQEQEQEQEQEQEQEQEQEQEóÿü‹þQeûKÙ*ñGþš.«ðÛþ¨7Á¹?µ õ8ê1g_¹?ðV/ùE—í-ÿd«Åúhº¯Èÿø5kÂ:wÄø%¯Ä­XµŽûHÖüyªi÷ÖÒgeÄédr!Ç8ebÖ€?AÁȯøåû%Âç½øƒ7öÿöoü'~Ó|;�°ùßaûÕåÇ�þ±wïû^Ý¿.ß/;Žìö~o�ß¾Zê~ñ÷Ãø<p¹ð�߈ü!w«kz–˜>[{‹·ƒQ´‡|¨©DÉFBÀ9`;ŸøBhú)¿ðÙj_ü¾¯ÐãSÚAK•Ùúu^§Ë8òóC™k¿É§ÛºG;ñïöoŒÚŽ·v5¯κ¿‰-¼@ÚOˆü6u­çÊÓÀAwh.aûH|ô>b‘Pím€×+{ÿÞÕìþøSÁ:/‹<¤¿†?´Zß_‹Á“Ûk:CÞ]<îÚMÅž£ltàü²ˆT++&c>™ÿOíÿE7àïþ-Kÿ—ÔÂû@ÿÑMø;ÿ†ËRÿåõgìa{¨=|û+w+ÚÏO{mãåæÎÇ¿ðMóã¯Ú"ÜxƒÂ÷m¿¥ëéy©ø>;ïÛ›!n>ǨgQ¤†Æ8íÁ4§qÊttŸø'¨ð¥—Ä´ßèGâ€ÖÛÄ-gg³í3_]\OktŸ¼Ìw6Ép`iþþ$@Á Fcë¿á ý è¦üÿÃe©òú�øBhú)¿ðÙj_ü¾£ØÃ•ÑÙ߯ó$Ÿ^©/¸I+Z[Yÿà7·N—gŸ|'ÿ‚m§Ã�#ÅЮ©àM!¼Ys Ü5¯„<žÓ-²ï È"Ýneg’lígy dƒÖ¾$þ˾øËñ/ûcÅÚV…â�´_ì™4=cIŠúÖG+:ÎD»”•+€6q×=«þŸÚþŠoÁßü6Z—ÿ/¨ÿ„'ö�ÿ¢›ðwÿ –¥ÿËê¾XÝ>G§§Tãß³ðæ}Rß×¥Ÿo$Aª~Ç6÷Z¾£5¦± …­çŽt�Ckœv‰§ÙÙZ­š� afpPøØväæMû!ø§Â'OxÇzN‹â/¶km3êþmRÆ[MJõo ^Lwvò b‘# (›i÷Fw ›_ð„þÐ?ôS~ÿá²Ô¿ù}Gü!?´ý߃¿ølµ/þ_QȺEýúëÅë{ü1Šùy»Ó“jÍ«iÓùon�9Ÿô•¹Ý?öµÑ>øÇÂö~'œCâχ¶þûLöòÀñ�Ežù‚2+´�¨³Ô _ ó|½Ä�ÙRãÆ¶¾8k?C§ßx©´ií$›M7ióé’,±4ˆ&C2;¢îPñ�¹�9ü!?´ý߃¿ølµ/þ_QÿOíÿE7àïþ-Kÿ—Ôå%nGo+.ûY黨nrrRrÕ”WnÑ�ÝæÎ{^ýޱmáÙàñf£3ý¢{wÔ ¾Šk<…™í¥´–¹%‚²ÃðÏö9ñ‡ˆ¼ËOx†Ö×Á–¾4ñˆ­¼?˜Ÿo˜ÝßjC=úÜ2}� ºó|¡Ê€i@:ïá ý è¦üÿÃe©òú�øBhú)¿ðÙj_ü¾¬Ý5gMÚíÛK]´ö½·JÝB½¬ùyy»w¾ŠK·i;õó<¿áßü†¿ n·¤ùbòþ×ý£{kuægÌ;<¿³mÛƒ»~r¸Áóë_Ø[ÓGÃ;{xoJ�áé­iþ¹´ñ>£odUžÛûB=ASì“2å­f·ž˜’®ÁY{¿øBhú)¿ðÙj_ü¾¨î¼)ñîÆÝæŸâ—Á˜bŒeÝþj*ª=I:þ±QK‘ÙkºéwßûÏïôU¨´ä­m}>ãÌügÿØÕ¼uðóÂ^Õ|UàYø2ÎóIÓí¼Aà«X%¤ÞHгI}åÿiD"e[£”Û+�n¡Ž{?ØLYü'ñ/…—Å_ü1Óþ “¦ÿǸ´Šú?¶lá·}·>VF<¯¾w|¼�Ž?h¯ˆ²|jø/¨Ü/ü±Ó¾jW$ÿÀ†½°~,+'á¿í#ûDøãZ?a°ðoˆô™"¸ƒÁ·šAÉîd“Tž!Û½y2Î0$éE¹IÚê>óÑ5Ѿïï>cÇ™V¯³Ž!T¨¶…4êKâRµ¢�µIëo¸îþ þúÏ�cø‘ Eã-‡ÿ \kZ]׆ÚëQ‚鬢µßmwö´‰cͼyrÛK’wma· ¿³÷„fÕð´ºÖµðs—^ñ3x‰í¼à•ðÜzš�2îÄG-²ÝÜ;H>Ö_ÍŒ(Mƒ;«sPøñ‹âÍ”ßð‘øþßÃÑÊ„%µ„-"§³¤maõvú×áOø'G�|+xÓ|*Ö_ydmSáÞ¥1=Mu¿¨úÖ:ræÂá¹tJóvÑj—*méÑ]vØó£žgØÈòeø5JûÕå˺³÷!Í+µÝ§ó<ó]ÓôøLð_‡$ñG‹_OÒ|C¥4ö:`µiF­v—dÜe*a)€2©ÛÒ»Û¿Ùßâ÷í âhõﮋ¡Ý=�…£ÈcàYÝý¶)52H�ö€’\ Æ£��}_FøWñËÖ+m§ü@ø%al¿v+…š„H>ŠºðkþŸÚþŠoÁßü6Z—ÿ/«8åŠ�KVZl¡h¥¿[¹ukt_ú¹™âÕ³<ÂM-%죺vrW›WKí/Ìãíà�ðê:¿ö–½âëÝfþãP†îñäµ ]ª‹$NÞarì(—pÚ  §ÔÖ¿°þ½áOíþñ�†ô{ý_—Yðä×>{¿³C4SÃ5•ñˆ×‘œˆŒmnÑ”BLŸ6î«þŸÚþŠoÁßü6Z—ÿ/¨ÿ„'ö�ÿ¢›ðwÿ –¥ÿËêïÃe8L;æ£FÏ¿^�o~‹ñîïêeœ-”åòçÂÑ‚—ó5Í.¿jIË«ëÛ²³~þÉÓü7ñvƒâÿ&±­Y6·w«K˜-"Ô®õ9­å‘ãA#c�ìá¥m»wHÄoe¯ÖôŽ^Ó$½Ô¾-|Óìá’{Ÿ†ú„QF=ÙµðxÅÏø(æ§ð�fFø÷ð[ÄWѶ×Cø]©Þy„v.½ä�Åÿ^¥5% pvZ-´Kn§½+7yI~=]ßNçÖ?å ?¿ìžø7ÿN^)¯Ïïƒßò»F�ÿmõzéÿeoø(—Æk~'¸øyðxøÛâOˆ£´}W]º/›o`�rº|QÛ¢ÞMã/™w!iàù�Œ/ΞøcãÏÚkþµøâ�rçá�ǯoþÓñF‚K 4ðÛN&uÉ{ÖÚã>ke›œü¶7/r¯*•§&ú»¿¹\ö°ø”©¨Ó‹—¢Óïv?¥Œß´ÏÃÏÙÛLû_Žüoáo C·zÿjêq[<£ý„f çƒÂ‚kã?�?ðr'À?êÙ^·ñ‡ÅMjWò­áÑ4¶‚ÞY?»æO±ÏÖ8ß=²9¦üÿƒmþø/SþÕñÍ猾*kR°–â]kTk{y¤Ų ’£ÊùïÇögÁ�Ù‡áÏìë§}—Àž𯄣۵›KÓ!¶’QþÛª†sîÄšåÿ„ú_ÍQÿà+õ‘·ûLûE}ïôGóa㿊^;ý§?ààûŸˆ~Ю~üwñ^Ã¥ø_^Ë 4ðÒÀdŸÎ�3¾Æ6�w ÍMªß)?¥ðM­;Ä?ì¶mvÇ/ÅoÛcW¹ø ðþûüOáÉõxu©¿·ü_­xkHƒYkK„‰a[‹}#Pµ½™‹fÞ;ÐK»3³€+靸x�ìÿÿEÓàïþšoÿ£þ#û?ÿÑtø;ÿ…¦›ÿÇ«:‘拌jÚöþ·IŧÉ{—ôÏ�4ÚÆ7ß|%â[«ˆ¼;£øÏÀ ¹ñˆl­VgÑÚk�bHãX% ±Aq;^á·˜ÇÈ7›ˆ/ü)ý¯ü]âÛÆÇÑxÚßQðkZM×…µ_N¸Ö4DµŽåã–Km* ¬‘žÜyMs}9’”,Å£÷Ïøx�ìÿÿEÓàïþšoÿ£þ#û?ÿÑtø;ÿ…¦›ÿÇ«i¸ºŽQ¨’¼�¿ÅóéÓïÞ÷™s8µË­¢¯×Ý�-þz?•�-ðwˆ|_­~Ìß üUâO¯Ž5ßøŸF½Hµ]'N†ÓM/¿j[ùvÛ¢?w3$ŠÅ™ i|zñïÅíCĉ&½að³Q—Â~%ñâøf{,Ïbúpµ{ûÀÜɽ|ŽF@`pëèðñÙÿþ‹§Áßü-4ßþ=Güoiuw»ï&ý•–Ö²ÚÎÄJí5˽¾VI~.íõ×{êyïÇ�ø�Áþ,øªÞ¿Ó|;®K­x'KƒTMÚyQ/oᶔ˕ 8ÊáDŒvvë^ñsYñ ¦½§ØøÃã&»¥i þ-A¦ÂSsm¢ÙJm®|>—QµÛ½ŸÙC¬×m2E²Ê «IµÇÐ_ðñÙÿþ‹§Áßü-4ßþ=Güÿái¦ÿñêŽUÉȪôJ÷×N[õëfÿ›Þ·7.Œæ÷Üœ:·nšÞÝ:].Úlž§Îß¿l�IŸÆ/xßþž�âO Xë6“ý§â�^áõ%Ó©¶d–Y¼ˆ[Yli02®Kkø{ö¡ø«®þÚ7Z%áØ4ûÔ‚/êž$·µÕ5›?ìÔœÏkbÚk]O#Jï¶T¿%Ѣݓëv¶¯ìÓaãÍCÄÑ|iø:ºæ©cm¦Ý\ÿÂq`|Û{y'’ØgØ6µÌç wòH �øx�ìÿÿEÓàïþšoÿ¦Òsrö¶»z_m[ýRí¦ˆ$Eÿ¤Å?Å7ß]ú3kŸã×ì;{¯]þз÷þ"‚ëúNJ´Ë3 F<ÿÚ0½Ä2Cö#5´p6ò~ØÒ:ýŒnb¡ý7Ä¿µ�MñÅ~ÖluËo EámfÿT†ÆÁçñ.�±3^É$ð¢Rm|Éc)€ �Ub§Ó?áâ?³ÿýOƒ¿øZi¿üz�øx�ìÿÿEÓàïþšoÿª—&ñšNé÷W^¯UµÕõÕ6ï¤ûÍûÑmk壷d’{ê–—ÒÖ>køåûZüS‡Gð^¥aãïü>зcáû(\Bºm�ž}2ñu«‹†‹O&ù6N«Qè¿>#ø·Ãw§ÐµÃþ)ºÔ¼ §VÇJ·º@o¯ ¶œ–tsÉd æ¶UOÊPœ×§ÿÃÄgÿú.Ÿð´ÓøõðñÙÿþ‹§Áßü-4ßþ=Dc”]K¤Óß[.—óNÍõÝÝ…åΧÉê­¦éÿK[^ËM ø³ñçÆŸ ¼#âÍUø×¦iCÁž;M0ëzÞ£ ø\Öô×Ñ­ošÚÚK›6Ód»Yn°ªÖð«¢(ia;¥¿‚¼7ñoÅ0|ZñìZ׈ÓÃú��†½'‡4›û[»m2%Õ®ÈÓeŽ xÙšnRÛ¬k!Ty6½}]ÿýŸÿèº|ÿÂÓMÿãÔÃÄgÿú.Ÿð´ÓøõC‚ßÚëd¾è¤úõjÿ]M9íkCþ ½ûmå·ÉXùóà�íñ3ã?€|/£Eñ.ËíZ‡Å Ÿ Iâ�ìÙ¾ÓB¸Ô3 ÃX[ZÊþj€“¥š'îÓ+2†2Ý—ö¡×í`ðf‡ãߌ¿ð¬´o¶xªÂóÆï‘a&·u¥êÿa´¶‘ïm岊I-Ä“:Ç4�1ˆÑYO»ÃÄgÿú.Ÿð´ÓøõðñÙÿþ‹§Áßü-4ßþ=ZÞ>￵¯®öŠ]ôÕsz½o­á¶×Áý^môþò^J:[K|½áßÛâÅÕ¯Æ Zoˆ_ÒóÃñ5ä^YÝkz#Ùy‚ÆåtÕÓ!™ˆãwy®î"•gG@ªêo©xÿâ¿Ãψþ(†çâ¶«®iþÔü-9µ¹Ð´¸¿µ£ÕêÚgŠÝYa�UÚ+dªÒ6YÀUÕÿýŸÿèº|ÿÂÓMÿãÔÃÄgÿú.Ÿð´ÓøõDb¢ÝKµn»Ù½Õí­×Ümí +¿¹ÚËÊÖCƼûPx�Xøñ¤X�‰ë«x·Pñv¥¤kŸ šÂÉ?áÒa7B+݉¿‹Ãk/Ÿq3Á7Úpª¢X¶Öø/ñSâ5ÿÃïÝø»ÇãX>&ü#Õ%»ðÇŒ->ünOŠZLxìþ26UÙѵ ÍQ-f´Ýeo¤¹·a'”ÉçE¼nb$M¾õÿýŸÿèº|ÿÂÓMÿãÔÃÄgÿú.Ÿð´Óøõo7 MÉNÊòv¿óJo¿Neµ�âºha%œnýÞŸÊ »uåë}þoÔ<¡]xgÃVºÎ¥â«XÂK©j n—W‡ûò-¼QBýÈÔqÒ¸?�òr³wý”+ßýD¼GYðñÙÿþ‹§Áßü-4ßþ=U4OŠÞý«iOƒíðßÄz�ì¼âÏkÚ†‡}ýŽ›nú«§D’O4bi&¿ˆ¬[·”I[TšÃ2­Má§i-�ptä«GCäOø7[þViý¯ëËÆú•iõý×óÝÿëÊÍ?µïýyxÏÿR­>¿¡øcéŠ( Èø=[þQeàû*ºwþš5Š“Mÿ”*~Í¿õÃá'þž¼?QÿÁêßò‹/ÿÙUÓ¿ôѬU�&ÊY¿à‡¿oR7’øwá·ˆ¯Š.L6Z}æ�}w.=ÞÞW>ÈkJ:T‹}Ñ5>}_,|\ý¨>+è¿üAgá±ðò=Iñ¾™àh!ÔôûÉo%—PÓí'[Æ–;„@°Kv¤Â#&dRžd÷•õ›©[ë:|7v“Ãukr‚Xf…ÃÇ*�ÊÂèEfÜü:ðýíÔÓM¡hÒÍq«,�e4·‘"$W,Jä̉j²™DjE~‡(9J.úi5Íýñæ^MÝj�•Œ”c-5iÛÉô&|«ãïÛ;╇�tËM/j<°›Ä#[¶´ðÒÝYOk¥^ V¿V»Ö¬Ò,´e‘¦¸“2ü¡–6sÐËû[øúöãá§Š.íôoü3ñŽ�¢^ÜjrøvãÄ ÷÷ÎX4ö÷°½�¥ÌÖ²Ûåòò+m‰½»ÆŸ³‡Ãωv¶þ"ðƒ5è,oeÔ­¢ÔtKk¤·º•·Ëp�Ð…•Û–qó1ä“PMû.|2¹ñ¦“âI>xOhC™ª6�hotèá…!›Ë߯8P¤íŠÊ•:©§7}bþäù´óº´ok«ìùKœàïʬ½ë|ÝÕß–×ÞÎÝ.xW�ÿlßkßµ.•áû‹;RøqâícZÑt�UtHt·2X%Ã>Ç:½ÅÅÎǶxYšÂÙ�êÁJ,œì—û^x“Oý†tábº^�¨xOCðχ4MóF}SVÔo.¢·HnZ/¶ÚBmîüÀ�báQv;Ë22Ëo×zOìëðû@ø‰uâûø:ËÅ—ò4×:Ü-´z�ò²³½ÀA#1V`Il�Äw©µ?€ÞÖü:t‹ÏxRïIm54Se6‘o%¹±F –žYM¾B°G� €@¨…ª6rÝ%¿kõ¶ú´›VW½¤â¯§¶…åe£iý×é÷_½º);|éàÚûâÇ�|g¦�;Â:�?áb?�ïfÔôíö¶ñ�=ïLæÒÓR¸U•Õ1-ó†(rñ"?GÒþ-ø‡Ã°º´š F†k ››ÛY#²�ZÚ{’æâT!r¯)’Bì9rí¸œšóOŽ_°¯€>?OáeÕôȬl|! ¶ú}¶™m®È¤Ù˜D�7¤?"þî2€� ÎÑ�71§Ž†kïT·»wd�š»òR|Íuµ¶²>Ÿ…^K<æ‹Ïœ¡„ç¼ùäàŸ3„U×½(®E+¥ùŸVÿ%|mãGâO�5-sT”ÜêzÅÓÝ\>>ó»@‡8°WKðÿömñ—Ä›ý. âõ}WûÞâìyý¯ÉyŒD· ˆÑ˜ñÐäúËà_Ù+á§Ãk Ò<áØ¼™"™%žÍngDë$oæË¹÷+ª²�Ù  ŽFk{żã¿Ýé:焼3¬éW÷ƒQº²¾Òื¹º;ÆêU¥Tï#wž+ò,»Ážiûl×ÌÛ»P몿½+î¯öw±ý»ÅNiR£õ>ÊÕ8ErÆU�ì’j6¥M¤­¦žÑ«h~f|/ý†mõK/êšþ¿oqgâÏÞxFãMÓ&T¼¶’ØÞ$’+È¥X«Ù±hö#bÙÊì=÷„¿e�i?b­#ÄšW‚<­_øÕ[ë« ëS¿ðÞ˜öÒ–ž8¦Ôî'Þ±ŸôU&òÚè�›îí+à�t/‰·ž5²ðW„¬üe¨Fbº× ÑíãÔîP…R¯rJÃ� ¾‚«øãömøuñ;ÂzfƒâOø+Ä:Šs§iÚž‡mwi`víÌQH…#ùxù@ãŠû̇Ù>‹…*r}]Ûé}]Þ¶oK;7¥©üÕÄ?H¾?ÎñQ¯ŒÌªFi¨Sµ8]6Õã^mÒ÷¹¾»½ÄôpêèFE-zÿíßðŠ‚¿µ'Š4‹Xlô¹æ]BÆcÅ3(}ˆ£…UbèàãŠò þO̰3Ábêá*o 8ýÎÇû9ÂÙõó&Âg8ƒN’œT­ò½˜WÓ_±×„¼ið,x×Ç–ø H½ð„!ñ;ÚøžÊâyï¬'7LŽâe½,Ø}¡’㉓�¬$Èÿ‚xþÉOûK|Z[ÍRÜ·„|8éq¨–-ãõŽØzîÆ[ÑAèYkôÏÇŸ<ñOYѵø'Â>#Ô<:Ûô›­SG·¼›KmÊÙ�äFhŽäCòÊ©ì+ö ¸:uŸöÕud®©ß¾«žÝy^ÉèÝû#øé�ãdp”Ô<ªwœÒ–%§´4q£}ÓšÖ}TyVªM4|k:Ÿí%ñKLð¿Ä½3MðŸ‚|Hѯ„& Í{¨NúwÚcÕ¡½1YL’ Õš,Â-±4Ç-çï‰>êžð?‚µëó§½��4hõ­6KKµ�] §›èCÄò*>G _²v?³�ÃÝ/âmÏ�­< àëO^oóüA‹m«.ôØÛ®By§+òœ·#Ž•Íü2ýˆ~ü*øn|5aàßÍΓk£êz„Úmºê:Ü6Ѭq5ÜñÆ�,€(!Ž6žWm~‘Å܆Ï"êéN²Ú[÷Ò_ÍÓ]-öU•Ÿòï�ÿHœãÃúË ;â02kš“v¶üÓ¤ö„º¸ê¦ïÌÓiÇñΊú—öÛÿ‚oêߤ»ñ'…çYðnL’¦7Ýi¯ï1÷â¤Þé¸üµ_ËÙÎKŒÊ±/ �‡,—Ü×tú¯ësýpà~;É8»*†q�×UiK~’ŒºÆqÞ2]Ÿªºi¾ëöeÒïõŸÚ ÁÐiPY]jm¬Z›X®ÕžâUùÝ•�#•”0%"nGZûßáíYñgã Ç�µ;߆±ZëÖö:ˆû=œÖSXË,xuY.¥ighж0í�ÇŠÿ‚;üÓæðÖ³ãýOL²º¾ÂËF¸žyl¶DÂi!b2…ÄÛ R á9úÞËöwø¦x÷UñU·�¼oâ�v¶Ôµˆô[d¿Ô"p¡ãšpždŠÁTÌAÚ3Ò¿£|2ȧ‡ÈTí*Üòù4£ :ÚÎ_ö÷‘þZ}/xÞ9Lj500w¥‚Œ)/ñk:žŽòPõìùÓÇßµÄß øCT¹ðÄ~'€þi>:Ô¢Ö­ïïï5V•oŒ¶i7ÚAMÑÙqq/œÊßy&ÞJx§íïð,7ŽÏ1ƒ'ÊAnWƒÅ}þw“a³8Êž& q|ÖæÖ×onÍ+$ï¦ölþyá>3Î8k fK‰� ŠÉ¸IÇšÊ:Il×2“i§u+]tü°øïûk_µMUm/­ø“âê? >_èÒj2kasáË9mZúA¶K£FTÌÀ�dÆâ&±•œÎIî½:~ûÚon]›µª�Õ%¥Û]l®ôóÓMź<Ûàí7ã¿Ú3Ç^û ~м;wàûêé%´ú…ÍÔ—P˜­&Y¢�ba‘%ts·Æwå>�¬û ézV¥öË]6ÂÚóì±Øùñ[¢IöxËáÜ|µ.ÅW ,p95¡]QMFÍÝÝ»ú·oK++yJÎWZ-4ôI?½ÝßÌüÿƒ�ä¨?ý”�oÿQ/Wõ•_ɧüDãY×ôoZ•¸Ñ|Kñ Ä—]ägt7Ñ[è°™ãaÃ*ÝYÜÅ‘Æèš¿¬ºüû]ßæ}VøQôAEW1¨QEQEQEUø‚ÛÂ~Ô5[ÇòìôËi.çîÇ–cùW«Á¿à¨á�ü»ã6¯žTËá;ëHŸºIq·B=ÃJ1ïYW«ìéJ£è›û�C)À¼n:Ž;Ôœcÿ�4¿Så¿ø6«Ã“ë?²ïĈڊÄßâŽ.®f“¯™qÆAÏßMq_£µòoüçáÐøiÿ¶øQm³lÚ��Ư+Ë››©¦Cÿ~ÝÐ úʸ²j~ÏJ/ùSûõýO¨ñ/±œU�­…U”Wø`ù#øEQ^‘ðçæ�ü1ûÏ€dndˆw;˜õoôxO'ñ?�~—Wæ�ü+ÿ&ùñ‹þÊÏþ“A_¥Õêg_ïµ?®ˆãÀ»Ä(¢ŠòÎÃâ_…�>kúWYÓn,H=–&Nïªù{þŸâã®þÆGK�‘7†õÛË�¾òÙ?O¬Íøƒ_^WÄ?ðN×ÿ…'ût|}ø_7îc¸¾þ™ à,BÜzæ+«ûâ¸kû¸Šsïuú¯Èø,ýýWˆòÜcÒ3ö´[ó”Tà¾úoï>Þ¢Š+¸ûТŠ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(çÿø+ü¢Ëö–ÿ²Uâ�ý4]WåüQÿ(ãñ¯ý”‹ïý6iUú½ÿbÿ”Y~ÒßöJ¼Qÿ¦‹ªü¡ÿƒJ?å~5ÿ²‘}ÿ¦Í*€>“ÿ‚wÊ?¾ÿÙ=Ð?ôÛoZ?´/íQeû>êÚnœÞñ‡‹µ=OL¿Ö#´Ð!µgŠÖÇÈûDŽ×‹´\F@Ý–ä(,UNwü»þQýð3Ûáö€þ ­ë±ñ÷Àý'â/Œmu»Û�F+«M Sðò$"ÆmïÚÙ¦b “æiÓœ Í•lŒ~�ûß`½–öüy]¾MÚþGÌCÙûoÞü7<çKý¿¼=«x Äþ!_ øºÎÏÚUŸˆ!]QôÝ+ûcJ»yødº¼Š¢")­Ü�J�FèÁe �¡ÿÁO< âÏ„úOŠ4m+ZÖ$Ö¼Oÿ…¶—i©è­$š�”fX–ðß�:Bñí+åݹfp€"ôþ ý†|/®øfÏN]_ÄÖ2éš6‹£Ø^Á5³Ogý“rnlîUd…ây„¤Fñ¶Ñûº�bo'áçˆ4þ&üBÏ‹õ)µ?_ÜÛhwÓëf[hí¤‚h®té-„8—÷qƒ¨û¿-]Oj¥.]WM¯ñ+kçói£³WøTC‘¨óo¥÷þ]WÊ[wZ>ï?ö…ÿ‚ˆø_öY¾ðä>8Ðu� x‚Æ+æûF· E5†çÛ$MlúŠÜÜÉFñgÀ9 ‡Š±ð×ö©þÓý§þ!|<�ïüCªiÞ!‚;++ xâA¥¶�apnnœ”ÙÜË2¡rÒÈìÂ5dŠO/;]ÿ‚løZû‘è:W‹ü}á�_ ÙxKS³Ó.¬±¬ØYù¿gIdšÖIcÛçËŻ„9R¥pµÙÍûxUþ+ÞxÖ;�j×ÄWú“_ÏuÂFÒBöÖ3Xœ&ZÖHìíÝ‘‰a,jêÊUv¯ßs·ÓÞüÕ»é¦úÊÜÊ×±IÓ³ÿã§ã¿–Ý.Žá×üçÀŸõ_éÃOÕ,5_è·zõýŠjÚ&­3ÛÚ�³¨þο¹X¥V*¾]ÃDÌ[åÎ×ÛÔ]~Ö–º| Ö|AouðãÃúúßÜ\Ai­´âd¸’xïLvˆA ‰Ußø]!nkÁ¿ðO}ºMÆŸqã_kšpð�×�ôëKé¬JœD Vë¤YuÆ“yŽB�ìøõ[ÿ„šv£âoê¯5è¸ðbN–J®»%Áä·˜6äü¼�¥yõéN «§ï?zËÿJ–­'ü¼º'kójôdÍÞñZ]ýÖ�’oÏ›[^ÖóG—|Lÿ‚ˆx+áõÓËc§x�Æž´Ò,¼Aâ %®¡¥éúuÕÌÖÑÜ™êóöòe-ÒYTl6-CûqØO£ø„<_ý»ÿü~h,­-ÇÙ÷vÿµý�bû+y»Þå@ÁOõŸ»®—â¯ì±áÿ‹÷^1—R¼Ö`ohv:ð¶–5Ai=Ìñ´[£l9k©Ü €$Òñ÷실øÓÄz†»gâ?øgÄ—zÔ:í¶­¥Mmçé· b¶ "Ià–ŽKpU’hårÃi U5U7Õ|´÷ºÛ½ú·ä“ýÝÚ]¿_þJÿ$¼ïÎÜ~ßzLñèvºG€¾#x‡ÄzÊj¾f�cid·Ú\ºd°EyËMuº2„*Ë3G ÿVî^0õ~&~ÚrY|4Ô|YàÛI|Ci'Ãy˜"–à¦9$–[¨‚¢«ðùað¬VMØŒöŸ ¿dýá~½¢êÐjž!Õ5m"ÓV·šóP¸ŠIuIu;‹k›«™öÆ«æ-cÚ"ÆŠÌ«P¡kh?±·…tYx|]kw}—‚?áiãß.ŸµT»Œ~ø…ûà Éùje ޳ۣ娴òæäµüúšBTT“{&·ê¹Ó×Ï’÷¶�œæ¡ûvè±KðÏâDךv‰¿âhl¡Ófÿ„^ÒC Ig"ô‰·,3H#´7„O™2«jk¶îƒ¤ßxÕâð狵ÀPÕµo[Ciýš±µ’^Æ‘—¸Ydw‰ÇÝ�jc24jUš†¿û ÛøŠÈÅ/Äω°¾££Ç x†ky´È_Å1´¦8®BÙ„ªM4bK1o!GåË*²÷ºoÀ‹? [x»ûYÖü;wâç‚F»³ÒI¥´6°ÚÆmÖxd�ˆàN%IIãJžÕ¹¸èµ¶×û_+[–Þw¾–0§È”T¼¯¿÷~Í+[[•þ |v¼øÏ¡éZ¤~ñF‡¥k1=ŵõæ¡£^[ä²µ?½0yLae2´ ŠîþþÊ6³Î©ªÞiÞ*ñ.§?ˆµ+�c[û]¶—o³y2Bž|±ÚYÀ‹"ˆ~ôK�Èí/šv•µgû*xnÖËC·7´Ðè>(ÕÐ0ÃlyfÁÜOÚ´œ7¶Ï¾›úk²_v�Dér8˽üì£?ÍòéÛÍ\ã,ÿࡾ‹Ã·γáøgF›@»ñ>‰}©ÚZüKal‹,’[,W.ñ7”ñÈímßcî “eOþÛ~#ÿ„oá¶µ¡|(ñmÕ�޼@ºrÄÚŽƒu%Í›YKsͼ°j­nD…AF2�V)r£tE÷<û[øKv´ø�ñwÑ4™´_ ›©´é¿á¶�Ç‘j ž&p‘D�ïEË…�Ë9j¾ý�´O xvî+/xÆ_x¨xÊÓëê>A·y Ø­�Ïusömî\³1p/Þs­ùtÞײž©Û«Žö²ívôW‡+¾ö}í~]<ì¥óù-ißÁCtÄñgöE‡ÃŠ:Ì×Z–§£iSYÙØu»í:vŠîKÄ)±c–A$â(YbeY ˜ŒÜÖ?oÿE¡Újº'„|{âÍ$xz×ÅZ½Þ•elú}»G%ÊOq�&ئc ²O(¶S%tVŸ³�„¾ßøX¸Ö5 QámoZ×íä¼»†8ž}Uî$¸Y Aò)¹“`î-ƒŸø‰á/„ Ð Ñô‰,í?°`ðγgáÉ­%$°‡ÌÇq,ÖÌ#`³Lžm¤�I¶Só|¨W‡Žú¬/Z¤SÒ×it}7·5¶×•uwoÎÌ3¼§yc«Fœníy$Ú¿Dúòü¹›éd½’÷öóðí¿Ä�" øÚÿG°ÖtÍ ÷ÄöÖ–Í£ÚÜj0ÚÉe–3‰äY äLP¿–Ì ›«¶?ÄÚÿľ ×ô‹]#š��Vÿâ Ï….SN´´²’Ò¬ÞpŠnuY%%AüªU$$m›üÖêkâŽ�¯YxÀ> ‚ÃÄž#Ò|J÷7Ó(†)ôѧ‹xã%"4è7#±Ý&r»z­ö1ñç�nå¹ñ‰¬ôH®µyuÖ‚ÅLÒEy"ÞEÆÐ¬P”Èc€O©ÏÎ�Y/ªS�EÞÜ«znו—JŠêïUÒçÌ®ÔqÒÊpµqku wå’¿=N_´ÓÒû_±ÚY~ßÞ›ÇNºÐ¼W¦è/yw§Zøšæ+OìÛË‹bë*¬ip׈¡â•²Û$NÑá\UŸüWGñ$>V‰àßÞjlqœñÀ¦NÎa’cÔã§o¦oøOþ áàßøªßT»Ô¼C¬¤Q²¾Ÿw$b•Î1! —#3o' ׫|&øW¦|ð&� i~t°ið,&æãa¹»+ür²ª†sÜàUžqY.yF’ò\òýàô%ljñ­ÞT°±}¯VkæùaøK_-ÿ„«ã¿Å~4ýKðEŒœ ®Àó€÷¹³ôŒT¶°íÏ‹îçÇ^6×lέLKÿ§“|¿(G–?zg àoÙ«Àÿö¶�áÛ=zOr¿h”PÏœ~®ä Zãþ'~Ð>ø/ndñ_‹Š ùUÚ=¤WÃåov|Q¡ÿÁ­¼s©Ç©üYø¡ã_ß©Ýå‰Ì1)?úV•ÊÿºS·J÷ß„ÿ°ŸÂ/‚~SèÐbº‹nîáûmÊŸQ,ÅÙOû¤W­QIÕ›Ý�A#„ýŸÆ?à ?¿ìžø7ÿN^)¯Ïïƒßò»F�ÿmõzýøÿ)øµÿd÷Á¿úrñM~|ÿ•Ú4ïûkÿ¨×繟ûÔýO©Áÿ'ôIEW Ò;åv�GþÙê•÷O‚l`ñÿíyñ¯[Õíà¿Ô¼â  è³OvÓl°´­EÒGÈ^ãP™œ® íŒùk�…¾0ÿÊí�ý²ÿÔ +îß‚ÿòr´�ý”+/ýD¼9^¾H“Å+ögbß±g§Ñ^]ûbizþ¹ðOì>¹ñž£}¯èvÒÜho"_Ahúµ¢Ýº2Ê¢ØÌYº*'€kÃþ|+ñÇÉöú�¾¥ñsV¶Ò~!ßhV–:¶»¨^ÙËáæÓf� ‚áØL¿lq²òbò© ›ËQ}k¬Ôœmý^ óšù)>–~²ýß=ûéé/É5ënú}ƒE~{~ÏŸð²[Sñ‚_k?ü9£ë~¾{ï3Ã>0ÔÂÚ¹–Š+wÔ¯.§¿š%–q¿NKt˜&åÉ bÔ¹Ô¼Y¨~ÌÑévúÅM"Í|%ÔþÍã››ýÃþ(³ñÆÊæÃ_¹Ò¿¶tЖöÓO$²[ßKgèŒÒÉ4‹€L²°º�sâ•÷ÄŸÛIÔ<6Ÿt‹;h–ÑX�3ÅWöz¦�él.o&žKÑ£Û.É&�à’ÑîU¢2®ÛÒã^òŒZÞVí¼šNݶóÒnÖ‹dÊ’Tùï­›ù§·Ïõ�YløŸÅZ_‚t)õMgR°Ò4ËP ÷w· o �£s¹ 2H'©~¾\‡Þ0Ò¾üXñ7ÿ® ðÆOü/‚ÿFÕ>+ê^.Ö¼#â{;„Öoçº\Z]C’É×î ¹6êÛ$!Zà³I+JĽHtoø‡Bñ�ÃKßÚ Nðmìþ·{Ÿ¶¶šÍ½ëëP­ü–�ªx±-‰ÌØSkŒ•é6fŽyF)k+|¯}ü´ß»K­Êö:^úÁKõ¿øSzl}§¯x«Kð¨³:¦¥a¦�Bê;Sup�ý¦âLˆáMÄn‘°p£$ã�Rhúý�ˆ –]>öÒú8'’ÖW·™eXæ�ŠI'¬ ²žAkæÏxCÆŸu [ÃÚu×Ä;ÍIø¥¥¦—6£}¨ÜM>Îk�nægš{Qs%È-$ŽªAL€�G˜é?¤øY¦jÚCÛ||·ðòüGÔ®|aý“sâk«¹´éZú]>K9cfžHšW¶3¶˜ÆC‘ç�»éGÞ­iç§JOÿr;¿îì)Q·^ßsöŸü‚ÓÌû²ŠùáÃß|F×¾Zkš‡Å»?[\xŽòÕnµ=GLÔ'±K«_ì¨õ9UÖà¿–de[†Y™WlÁ�œ­Cá5äö´OŠíu¤øÏâ­þââñüE³ÆÚ2iLu8 µ‚éÒÿ¢N€�>×§”S r `HL’Ép¯Í(¦­Ì¯®û'f»êïÑ5kݤ'JÉ»ÞÎÞ[µtú§m;ßcë ?é7þ<Ô<3ÖísK±¶Ô®­¼§U½Ä“Ç ï#aÜÖÓŒHÙÈ®v+äŸÚßà·‰|Sñgâç‹4�ˆšv©¡|2ÓæðËøzöîÒ-CW‚}fXЬŽ„Ä>Í'™ ‘¾&,„eüR>2—ãnºPüi_ˆ-â=/þ§.©ÿ�ÑöÚ}£í",ixÿ�ÿ4^bçîù|ù*Îê-oÿÉ[NòënÉ»ô*­x½4ÿÒ#'~Êòµûén¯ìcX´ðö“uuocce\\ÜÜH"ŠÞ5™Ý˜€ª$’p© �. I#u’92:œ«È ÷ðž¯àÍ{â£ñ¯Â±Û|Zñ’x�úüÐk—Sø·Ã�ØJÒ†¶ÓÆñ–ÂbxT¹ÓÙ7¢25ºÆîò¿Ç_¼Y®ø[Ô| ¨üxÓ-|)à]ëÂpIy®Ápú·öŽ¡ö¯:Þ÷÷×r*$ Át²GåºR2RO(Dzå›!“å%K)IÊøÚ Ë_ôôý¥ã´�ÁÖëðí4Áâa"ëBK‘p5ÿ8o;ì›¶?ѼŸ¹û¿2ªuÜ9®¯kí»´ª-wȬ¯«’Úáì=åÙõì´¦÷í﻾Ñ}�¼(¯øð÷Ä— øe®ÚÇ2æ9%µðúN�‡pY}«â¿ø8/ööñ·íËûøfÛâ/Àÿüø]cãK]BÃ\ÔÌæ}RùloÒ(}º ­—v«ªûÞ­×~~Òþ8ÿ‚C_kw4}á“ð}ït�Ê/ôËý-¼0NR9’ÝQ[t­Ë6W±æŽq �*ç/>[/¾V=ºÞb0tå<ׇ Òo‘ÕS©éËKŸWæÑõß�?à˜ÿ|)áÈmõ…?¼M©¸óoõMkÃÖ—÷Z…Ë|ÒÌÍ,m´»–m©µ8UU �øwwìÿÿD/àïþzoÿ¯c¯“m‹ÿ¼㿈^ñ¼¾±øwá ÄVúhÒlîíõ[«�Bþ)#ºic2ù ²©Xd‰òAY�߬N©ÅZôKµúúˆÅÎJRrÚÝú´¿6�Oÿ‡w~ÏÿôBþÿá¦ÿñš?áÝß³ÿý¿ƒ¿øEé¿üf¼/ãWÇï|ð—ˆü3¨üP¿‹PÒ¼mm¤ÙøÇV»Ñ|9moo.Ž—Æ=BêM.êÖ$óDŒ¥§˜îÐ!o¾íÉkß¶÷Šm¿f+ñÅ­7@ó¡ÖV³Ðu� ÓÄÞ&6ׯÚÚ{5koz ÆK¤idÌdÝ’VÚ²öØkÚ˧EÕ_ðëø]jiìªé®þ~¿åý3êøwwìÿÿD/àïþzoÿªz'ìû7øšÑî4߃?u"ž[W’ÛÂZ\¨“E#E,d¬DIÑ—ª²°8 Šð¿‹ß¶G‹<;ûiéÚ>‡ãU²Ó‰ô]çÂ:æ§§Ã}{Ú[ nmôÅÒMñ· qŸ´I¨Æ¢hä ªÆõþüñ'ÃkŠ�ê·:Þ…¡èÓxßUðͶ‡imssâ‰S[ÔæQ5ÄR,w6¤&Ëb›6Hfv�KEn{j®|º.n‹ì¤ÿ'§[ù&Æ©TqV–­ÁoüÜߊk_#è�øwwìÿÿD/àïþzoÿ£þÝû?ÿÑ ø;ÿ„^›ÿÆkæ�ƒÿµ·ÄŸü1ø�eoñ7þ%¼Ñï<2šG‰t�FÃıZ�GPû5ÄhƒLÓíf «÷ ÈÎÀÊN?yÖÛÆ>ý¡l4ÏE¢ø÷Ä–¾$ßx¢{}æøh c-å–Ÿ#(ÁÈŠ8"À üÕiÑsŒT7òíK§ømó2|üŽWÚÝ{¸¯ý»ð7¿áÝß³ÿý¿ƒ¿øEé¿üf�øwwìÿÿD/àïþzoÿ¯ø©ñCâ½×Å}ôÿˆsøfÒÇâ‹àˆô{M*ÂöÒÞCK±’âo:k:I£–íž%P22)Pùc?Xý¥5ýßAÑ»ðGÂ];Ç¥[ø~×ËÕµ“Ucö…º‰æŠ ¢³‹|(É"–%B­¿oö¾iµí#âô¿Ûßð†Ûé:‡ƒuÍ[N±Óâ™ 1´’\O=¼�£ˆ~ô4¹ˆº„Þ­UeJš»‡[l—W®»l÷³î�rTçPrßçÒ/òšòó=§þÝû?ÿÑ ø;ÿ„^›ÿÆhÿ‡w~ÏÿôBþÿá¦ÿñšð�þÕZÖ™ñö ;ãJÞxŠÃZÑôÿ x%¬ô¹�t¹ã´/©–ÿjŸÍ]?Ú,ž;h¾Í“#�7G⿉>;°øñ'Æ?t É‹¯4=$ø†}+CÒ4{K}U¢Ú·rÙNEÔ�£¢É2Nœ òw�¯iG~N—Ùwñ÷–ŸûIiuÉSO{v—^¼Ïî÷^»>�ž7ÿRý“¼!ð/Jð–·à_øoÂMÔ³XßZèš\:}»Ï€ñ9HUT¹U�#8E¸ùKá·ÃÍWâÏŽô¿h–æëTÕç[x°'«1ìªb{M}ÏãÍoMý¯?a-zòËÇ>(ñ6­¥i�«Å§kòhoy²Öúå[P_츖)c•hcšh!\0JO”þËÞ ñgìà�iv6Z‡ˆ<[ðïÄ:þ�lú{ÞÏkö9tÑo",lüź‘š5™D`KøWpDñüV£MZ�HûI5²QMJÏkË“O9kk;£>xÿ†á¯%S%<^¤ðô`÷œ¥ûÈ6·ä‚›æþì,µq¿×þÿ‚l|ð÷�ô�;^øgðÿÆ:¥…°Š}[[ðå�õÝÓä³$±³Ü͵s…µáÝß³ÿý¿ƒ¿øEé¿üf¾}¹ý¨µøD4ê ´Ímþ0jhÔþ7ÄÇOøGt­�ÞÀ¨Eš£çì’‰‡š´Å¢SÐÊz߈µ‡ˆí>.êÉÄÄÑ<]i¯é6>øbllŠø£N¸KF–ëkÂ×óçÝÿ¤[J�ÃöO�—.î´ésr¸kè¿™Ãçï'¢¼�®“8*B¤N[zöRùi%«²Ö×¹íëÿðøŒøðxrðf›ÇþA¯žÿm¿ø%¼:”w>)ø_eµÂƒ%߇â"�wkQÑOý2è‡)í¾üPøŽÿ¼%­ê~=ºÕtOüIñ7ƒ_ÃÒiQYÚXÙ�a­¤IR!pnéñ©v”ÆÑ¶ {Á”ñ>Ô¤ÓeÈR BË#y%“kyŒÞä™vi„T1”´{5nh»'uoU¢½ö³?BðçÄŽ"à|çûC$¯i/Žî�H©I8Íi¥ã+KGÓG¯|ÿ‚pü!Ó¾xrü'øoâ?¦Ÿö–¡«xbÊòîiʆpòÉ;$¨ÉáT‚º_øwwìÿÿD/àïþzoÿ¯œ¼û`x®÷Á·×Ú/ÅÅñö½/Ãíc_ñ>’tÍ=á\ê°[¤�Â#‚’ß´°ý›Pieo �ÙŽMßaüÑ5�ᦘ5ÿê^.Õn¢[©ïï-mmŽé1Ž8íâ�%$… ÀÆçsóWµ€ÂáéQŽ”=ÚqIl×X«5tþªÓ¥ït¾/<ͱyŽ>®g‹•êWœç-×½&¥'gªÖ{=|¬q?ðîïÙÿþˆ_Áßü"ôßþ3Gü;»öÿ¢ðwÿ½7ÿŒ×±Ñ]žÆŸò¯¸ò½¤ûž9ÿîýŸÿè…üÿÂ/Mÿã4û¿gÿú!ð‹ÓøÍ{{ʾàö“îxçü;»öÿ¢ðwÿ½7ÿŒÑÿîýŸÿè…üÿÂ/Mÿã5ìtQìiÿ*ûƒÚO¹ãŸðîïÙÿþˆ_Áßü"ôßþ3Gü;»öÿ¢ðwÿ½7ÿŒ×±ÑG±§ü«îi>çŽû¿gÿú!ð‹ÓøÍðîïÙÿþˆ_Áßü"ôßþ3^ÇEÆŸò¯¸=¤ûž9ÿîýŸÿè…üÿÂ/Mÿã5ßü—ö}¼¶x�Àï„q‰ÒÐøFÂ'GXƒî¯g¢�cOùWÜÒ}ÏÂø8jò]Hø}àDšy´O…þ/ñ‡´!<­4°XI£øWRŽìK0‰µ "BÄ‘q‚N+úƯä×þ5ÿ’ ÿöR5¿ýD¼ _ÖU|.*5çµßæ}EÝ(·ÙQ\æ¡EPEPEP_ÁÄ~5>ÿ‚YøÆÑ[kx‡RÓ4ÐG\ ÈîüD} ¯¸kó{þn½}Oö6øá«vÅω<g ŒõU´»?ßxëË뾋ۻ×òµ÷è}÷…˜u_‹²èËeV2ÿÀ7è}µûxþ—ì•ðÃûB� Âz]ƒÝ£´‰þ$ø×£ÔVVqéöqA 슢�áP0åR×£N P]ŠÆbeˆÄO-æÛûÝŠ(«9�Í/ø7¬Â'ñö¬ðoÝ |D“1ÿst—Pÿí±ü«ô¶¿3¿àŸ��¿ð^oÚ£À3£¯�m!ñU°?vá‹Epqêq¨Jà-é_¦5éæúâ=§ó(¿¾(ãÀéK—³kñ (¢¼Ã°+á¿Û¢_øeoø(ŸŒ€ùZ¿ŸëÏÑ"§˜çþ¹K¸×­}É^Qûl~Î~Õ_³‡ˆ<%û¤Ô¥Œ]éSIÒ È¾hŽ{åÿvF®\e)N—¹ºÕz£äø×)¯�ʤ°ǤãRŸøé¾d¿íë8üÏVV ‚# Žôµóüçöž›ã'Á‡ðwˆ·Úxïáà TµŸåžH£ÌqÊGRFÓ÷Þ™?xWÓõ­±«R=O["Îhf¸ Y†áš½º§Ö/Î.éù ¢Š+SÖ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€>ÿ‚±Ê,¿ioû%^(ÿÓEÕ~PÿÁ¥òŽ?ÿÙH¾ÿÓf•_«ßðV/ùE—í-ÿd«Åúhº¯Êø4£þQÇã_û)ßúlÒ¨ë‹Ùâ'Ãm#á¯Äh~ ‚G}?Jñ‚n5©ô¤w/öx® ÔìÇÙÓ8�^6tPÈÀ ?þíÿEkàïþÍKÿš ú.ŠìŽc‰ŠåSv9Þ‹wq>tÿ…û@ÿÑZø;ÿ†³Rÿæ‚�øP´ý¯ƒ¿øk5/þh+èº*¿´ñ_ÎÅõJ?Ê|éÿ ö�ÿ¢µðwÿ f¥ÿÍð ?hú+_ðÖj_üÐWÑtQý§ŠþvT£ü§ÎŸð ?hú+_ðÖj_üÐQÿ ö�ÿ¢µðwÿ f¥ÿÍ}EÚx¯çaõJ?Ê|éÿ ö�ÿ¢µðwÿ f¥ÿÍð ?hú+_ðÖj_üÐWÑtQý§ŠþvT£ü§ÎŸð ?hú+_ðÖj_üÐQÿ ö�ÿ¢µðwÿ f¥ÿÍ}ÌI$I=«Ï|{ûWü:øj]uohé4z y~Õ2ŸB‘aøŠ‰æõà¯:–8±Õ²ì?kŒœ)ǼšŠûÛG›ÿ€ý è­|ÿÃY©óAGü(ÚþŠ×Áßü5š—ÿ4ŸªÿÁJôïÞ½—�<âŸ^ŽØ|¤9èp‚GÇÕEV>!ý¦¾0�ô-3Ãßldáe¸ÚÓí÷ æ°?öÍ­r¬U^”ç)z/×cä%ÇY%WÉ–Ó©Š—ý9§)/ü Úù1¯?À�¶Ð´’|]ø5h737ÂÝD¤ÿÂA^kãïˆþ5ønuÚ#àD³§ /…ú•ä™ô"-}¶Ÿ÷±]¬?ðNýWâ«?Ä_‰ž$ñ$3ZÛ¹H�ú)�¸Çѽ+À_±Âÿ‡…Û–7× ÿ-µ"oŸ]²€ýQý¯šOá|¾®ÿ‚ÿ2}¿c?Ýp4°ñïZ|òÿÀ)éòs>7°ý¨þ<ø¿ÄIoàÛïøÒÝ$Û7•ðÇQ³8õ ºÜá~¬{—ð¯öŒø·hßh´p<Á<ÖªÊàûë_]YXæZ$ðÅoCjGD€KS*¸ÚŠÕ«Éù/wò×ñÀ˜¼SæÍ³ “_ËI*1ô÷o7ó™ðï�?à”<ñGˆb¿ô¼ÞG®x>óRg=ÿy«bº¯~Áþm:GŽ>ÚÊŸvføO¨Ë0ÿ¶�âÿ­}qYÞ)ñ†“àmMC[Õ4íÂ/¿s}r–ð§ÕÜ€?:0Ô£F\Ô—½ßw÷»³ÛËx Ëß> /ækš_ø¯/Äð_øP´ý¯ƒ¿øk5/þh(ÿ…û@ÿÑZø;ÿ†³Rÿ悲~0Ác?gÿ„lgÆ‹âkØ¿åÛÃöí}¿ýÙ† ?÷ò¼‡þåñ_ã÷îþþÏ~$Öm¦ÿQ¬kaÖÌún!ÿ�ô4ãšMs]¥ÝÙ/ÄödðqvÑ¿-#Ý?á@~Ð?ôV¾ÿá¬Ô¿ù ¬?ø?âç½#íþ&øõð ö<Ÿ´j¯mc8ëó?ˆ@¯ñ‡Â¿Û➃&­ñ?ãGƒ~øXÉS ¨í¤´ydXã_:29gtEï–e“]€¿àƒ¿ cÕ†«ñ Å>6ø“«¾<ù/o�¬3ýB7þF«öΞµ±7òŠæütDû5/áÑûôÿ6yÆø)»ü*ób´ø÷ð·Æ‘ç÷ÁíRU'·ïeñqî®ká?í�ûf|sÔ$ø_á­cG¸µÔî<%{¢FÀ�½æO©IFs€í_¢ÿc¯…Ÿ¼¦ð‡€|1¢ÜÃ÷/"±G¼üg}ҟūҨþÜ�?áEÉ÷“ý¿0þÎsøÚ^‹õgçZÿÁ<ÿjßÚ2ÙÛâ'Ç ÙÜ)҇áiZ1ýÇXMº°Ï¬¯õ®{Àÿðon«ánKÛï‰Þñl�'˜»àJL#]„?ü×é­ÉW=ÆÏiÙy+Ã-ÃÇxßÔùCáßì=ñSá‡Ù¼-ã_€„®Ö]?àýí¿˜?Ú+¯‚ÇŽ§&ºOøP´ý¯ƒ¿øk5/þh+èº+›ûOüì×êt”ùÓþíÿEkàïþÍKÿš ?á@~Ð?ôV¾ÿá¬Ô¿ù ¯¢è£ûOüì>©GùO�?á@~Ð?ôV¾ÿá¬Ô¿ù £þíÿEkàïþÍKÿš ú.Š?´ñ_ÎÃê””ùÓþíÿEkàïþÍKÿš ?á@~Ð?ôV¾ÿá¬Ô¿ù ¯¢è£ûOüì>©GùO4ýŸ?gÙþÞkºæ»®/Š!k¶^$Ðõ}nñ,4ëÕ]NÓe·[‰JÅöˆäÓ™Ì[·ìš6ƒšø·ãü®Ñ¨ÿÛ/ý@Ò¿c+«Š–¯´Š¹�z*¬9ògüÿái¦ÿñêúΊ?֟ȃû*Ì|™ÿýŸÿèº|ÿÂÓMÿãÔÃÄgÿú.Ÿð´Óøõ}gEë OäAý•æ>Lÿ‡ˆþÏÿô]>ÿái¦ÿñê?áâ?³ÿýOƒ¿øZi¿üz¾³¢�õ†§ò þʇó&ÃÄgÿú.Ÿð´ÓøõðñÙÿþ‹§Áßü-4ßþ=_YÑGúÃSùeCù�“?áâ?³ÿýOƒ¿øZi¿üz�øx�ìÿÿEÓàïþšoÿ¯¬è£ýa©üˆ?²¡üÇÉŸðñÙÿþ‹§Áßü-4ßþ=Gü(ñ×�õWÇ�%åø‚)ï�eõÿ–¦…Æú�÷XM;ÊJ?‚æaSÃîÂEý:‹ŸòÐ¥:¿ù4�(ýÌëáâ?³ÿýOƒ¿øZi¿üz¼ƒö›ýµ,|iñá&£ð.ÒÛãž»à¯Ük—–ÞœêvV�É£jzpÜÛ #‰‹ß«$#nƒšúÇáwüÿà·Á±ðÿÃ_ Á48òî.¬ÅõÊ}%Ÿ|ƒþú¯_†·…c�UUU @tbsœV' *2Šƒ’µÓæ·Þ—ä|Í S‚Ì)â(¹b)ÅÝÆqTù­ÑòNnÏ­¤�¶ksð‹þ Yᯋÿÿà¯ÀÏé^øÛ¯[ë÷ž/:ÌAí4[y5«W½¶�}¼ß:]´ Vl#|Ó_¨£þQñ_ãðßñÓö¨ñ÷‰­ç?¿Ñôƒ*Z/®Ã,†!Ÿkqø×Äðn·ü¬Óû^ÿ×—Œÿõ*Óëú¯–þÄ£?÷‰Ê§¬�¾åd}ÿüEǦQ‡¡„ó§F<ßøNyüî|Sðoþ ÷ý˜~,o?ƒ/ücyKŸê³\gëF8ã}Oð¯à�~X}—Á~ ð·„àÛ´¦�¥Adµå¨ÏÔõ®¾Šî¡�ÃÐþ }>S7â¼ë5oûKR¯”§&¾I»/’? ?àõoùE—€ìªéßúhÖ+§øuà-Sâ¯ü'Bð¾‰l÷º×‰?gû}/O·AóOq?‡(Ð{—e�sðz·ü¢ËÀ?öUtïý4kï_ðM/ùGÀû&þÿÓeµuÿ‡_´ŸŠÞÓ ÿá ¦ñŠ?áÚ_³—ý‚øCiŸübŸúÅÿNÿøþÉþ÷áÿÝ¢°¿áÚ_³—ý‚øCiŸüb�øv—ìåÿDàŸþÚgÿ£ýbÿ§�üþÉþ÷áÿÝ¢°¿áÚ_³—ý‚øCiŸüb�øv—ìåÿDàŸþÚgÿ£ýbÿ§�üþÉþ÷áÿÝ¢°¿áÚ_³—ý‚øCiŸüb�øv—ìåÿDàŸþÚgÿ£ýbÿ§�üþÉþ÷áÿÝ¢°¿áÚ_³—ý‚øCiŸüb�øv—ìåÿDàŸþÚgÿ£ýbÿ§�üþÉþ÷áÿÝ¢°¿áÚ_³—ý‚øCiŸüb�øv—ìåÿDàŸþÚgÿ£ýbÿ§�üþÉþ÷áÿÙ»µŠþÖH'Ž9¡™ rG"†YŒAà‚;SÕB(`«þ¥û9Ñø'ÿ„6™ÿÆ(ÿ‡i~Î_ô@> ÿá ¦ñŠ_ë·ö�ü¿²Ý¹yôôÿ‚nÑX_ðí/ÙËþˆÁ?ü!´Ïþ1Gü;Körÿ¢ðOÿm3ÿŒSÿX¿éßãÿŸìŸï~ðMÚ+ þ¥û9Ñø'ÿ„6™ÿÆ(ÿ‡i~Î_ô@> ÿá ¦ñŠ?Ö/úwøÿÀìŸï~ðMÚ+ þ¥û9Ñø'ÿ„6™ÿÆ(ÿ‡i~Î_ô@> ÿá ¦ñŠ?Ö/úwøÿÀìŸï~ðMÚ+�Ñÿ`�ÙcÄ:ö­¥X|ø}ªh2G§goá "Yôç’1$k2,E£-+¨`2¤Á­?øv—ìåÿDàŸþÚgÿ£ýbÿ§�üþÉþ÷áÿÝ¢°¿áÚ_³—ý‚øCiŸüb�øv—ìåÿDàŸþÚgÿ£ýbÿ§�üþÉþ÷áÿÝ¢°¿áÚ_³—ý‚øCiŸüb�øv—ìåÿDàŸþÚgÿ£ýbÿ§�üþÉþ÷áÿÝ¢°¿áÚ_³—ý‚øCiŸüb�øv—ìåÿDàŸþÚgÿ£ýbÿ§�üþÉþ÷áÿÝ¢°¿áÚ_³—ý‚øCiŸüb�øv—ìåÿDàŸþÚgÿ£ýbÿ§�üþÉþ÷áÿÝ¢°¿áÚ_³—ý‚øCiŸüb�øv—ìåÿDàŸþÚgÿ£ýbÿ§�üþÉþ÷áÿݤf¤“€9$ö¬?øv—ìåÿDàŸþÚgÿ¥_ø&§ìæŒøðPrð>™Çþ@£ýbÿ§�üþÉþ÷áÿü8ÿƒˆuÍkDñ£-Ɖâ�ˆ^%¹Ò¯#;¡¾ŠÛBð†Ÿ3ÆÃ†Uº²¹�#ŒÄÕýe×ó[ÿ}éžÒ¿fË [{++8üG ½½¼B8 �F�Tª0+úR¯�­QÔ©*�«lõ¡X¨ö (¢³((¢Š(¢Š(¢Š+ócþ“ûoâ7ì§áÁ’u¯ˆ‘�ƒø¶ËiOûoèzþ¤õù­ÿ×ÿ“Áý‰ì¡ý-ÒkÇÏ¿Üeëý)¤xKÿ%Nö�g÷Q¨×ä~”ÑEì›…V'ÄŸÚ|-øu¯ø›PŽæké·¥ÌvêgŠšW€X…8�œdŠŠ•#NsvKVT!)ÉB;³óþ I'ü1×ü§ösøÜÿ¸ðç�¡oërž"�Éx<ÙO Žò'õèzâ¿Nëã?ø*ìùmÿ=ÿ‚\¶§á�>íµ‰ô›?xR ˆÕnüÏ L-Ø)aæIo,‘m W{¯$ ×Oÿzý¶ ý¸ÿb/ ë—WK7‹|;è^$�›2}® &aÿM£Ù.zeÙGÝ5íâéÊxhÎKÞ§îKË·ê¾G�‡©jù_»?y~¿£>¥¢¹ üeðߌþ&x§Áún£öŸø),äÖ­>Ï*}�nãy-ÎöP�½�ÈÍŒ|Ø$WQ^9ÞQE|_ûu~Í~*ø5ñ~ÛöƒøCo¿_ÒÐÿÂM£Æ¤®¯l &Á÷²«‰rv«¯Ì¤Ÿxý’¿lo~Ø>MWÃ×k¥n‹ý¥¤LãíZt‡³âBs¶@0Þı_œÿgo|rý±~%ø—à&…¥|;¿øQª óXþÞ’ –M¯½/6ÒÝŸžcò¼~GŠ©›dÐö”j;Õ¡³r{ΗNv“r†Ó³z3ô~Šø÷àGü6 7ÅIàoŽZÇÃO[퉮®P®›xzoÜÕ=-äï öïÚÿöœ‡öYý–üIñ6ßL�Åú ¼1ZG|-Òõež(��‰7d)Î=ó]4qê«Áÿšõ>Ÿ$âL¿6¤êàª_—â‹ÒQ}¥¬_ªô¹ê”W/ጾñŸÄÏø?MÔ~Óâ?%œšÕ§ÙåO±­Üo%¹ÞÊ÷¢1ù±�›Šê+cÜ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€>ÿ‚±Ê,¿ioû%^(ÿÓEÕ~PÿÁ¥òŽ?ÿÙH¾ÿÓf•_«ßðV/ùE—í-ÿd«Åúhº¯Êø4£þQÇã_û)ßúlÒ¨õŠ)‚)$€É'µ-ÄøÃö‘ð€·�[Åú¬±ýèEâI0ÿ¶hKþ•å¾+ÿ‚›ü7Ñdò´¿íßNÇlkgbcV=¹”¡ü”×=L]|rGÍæ\c‘à4ÆbéÅöæ\ßø »ü¢(¯—�í�ñwâ1Ç‚þ^ÛÄüEu¬;¬rg¡ù¼•÷ÙúÑÿ Ãö”øšÙÕ¼i¡x>ÎO½‚ƒ4BˆOþE¬~½2—Ëüìxßëý úe˜ZøŽÎ4Ücÿ�Tä_™ôÖ¥ª[hÖmqwq¬ ÷¥š@ˆ¿Rx¯0ñÇí¹ð»À;–ëÅÚ}ÜËÇ•§î½$úf Ê?+Îtïø&v—¯^-ß�¼oâÏ݃’Ï7”¬}Ë™òa^Ÿà�Øãá—Ãý�aàý&IS‘-êÇÔK`ý1G>.{EGÕßòÿ1}{‹ñÀÃQÃ.õ&êKÿ¦”òsË®ÿऋâû—¶ðÃßø®à»Œ^Z)õÄbC�®ß¢:çí9ñkþ=tï ü?²“îÉ6ǘ)õ æ°?ðþµôõ¥œZ}²CQà c jT{À©hú­Y£ùiÿñõK5ÅÈÏ3¨×òÒQ¢¾õÍ?üœüæ_Ùóâ§íQñ§âwƒWâZJß ~Ãmuu«Auui¨ê76ÿj6±Ä²Æ"‰"h38ÞwLq Ù—é¼ð_üñ¿ƒ¾h? ´eøY�¦·¬xßJ¿ñGö¦©ul—BÚÞŽbt-3ogi@(L·¹x›öBñ�†>>øÓÇ ¾!èþ “âLƒÄ¶:Ç…Û\�®­¢ò"¼³e»¶ò&òp¬$ÆÆ4c šø½ðn÷ötø�âŸø_ã‚~Ïãë H¼H|m¡C©A{ui‘ ý¿—yb"œÇòÈ›”�¦Ò¨à){¾åݽ_7~ºoeþ ¥ï�¸.áì%W^8xÎz{õ/QôëQɧݫ}«}“„Òà þ5ñ†�û?é?ü+àoëµí{ÂÚí¦ª—2Yøz÷J†à\I“噑%¶‘–6 ç.Ô2@XÈŽÛö‡_|y‹ì¿Ž¥û<Í$·—ÿÙ—þO�cûê 6¿kΚþK¢™žâìcû ŸñWíû3~ÍzÏÀæðïĽoÅ ðKTÖ5‹˜4íí·^'ºÔ­®"¸šK’Ööñ»Mpò’›—øB¨Áæ‘ûNüTøÛ¨üy_…Ÿ|]­é?gWmCS¶{h´¨Î—œT8ý˱XŒ�Œªìm dûÊ1Î3œ#ouÙ¿v<Þå­{iñ5¦«I]YSK„ƒ§Nþê¶‹WkÎéÛ¯/%í×XÛSëÏþØ¿>%üzøUà߆Úwƒ´«/‰¿&ñ¼ú§ˆ­î/_@T–È/ú<3Cö¬­×—å‰aÃ0“ÌÄf98ßÿÁM<]à…äk¶ž ·ñ‡„¾1i¿ *ø·Zñ΢ÆtÖn­í Á*L“Û™ Ón ¿ÒQˆÎÛ,””çY8Ý»E9^Õn•ôÓ‘8=zß[#šñ�/#R²ÕÙYºvzköÚšô¶—f¿íOÿ—ðç‚>(ø»Ãñ‡…¦ÐÇ„!ŸG×,t«�i¿¶Þæd–ò® F�mÖ&R$L<™ÞØÙ_5üÿ‚×|føû/øoMñ�¢x’qog£XxÏÄ«s¦Y=ÚÂÛÖîîâíÒòWØÍæ™m‰ħ?/Ù²�üÿÃ?³·ÅM[Å:¦£ øˆêúÒ•máϲXiR‰™¾Ñfg¸º¸FhÊ«§™Ëa"¡X“gÂ?ðNoxönð€.>%XIwð“P´Ô>ë¶ž0]híkÐÄš„ow$wêÐÊb“Ë»”±«®0­‚£©Ósn×r}9å}µQwZ꬛Mk´¡ˆ©+ÊJ)^Ö]y#m×MÚVz|qá�Ûkã§í�â?Ûê´7Á„þð�•…ö³ªé:턚m ½–h¡‰oá’e„ƒ]!ãêXc;â§ì£ø»Çn4¯‹°þÐWõûýõë½i§Ò [{ ‹‡�$‚Yd˜ÄðÑ ÓÌØcó!-æ§êÁß øëÃñê;ñ–�â›û²‹:7‡Ž‹§YF›¹H¤¸º�¥rß;=Ã&6G_ãGìçÿ {ãWÂolgÿ®Ö/u_²}“ÍþÓûF�qeåïÞ¾Vß?~v¾vc9ój«Ý¥h->o]~/-£êJÁA©9^NÎ××^]<¾-u^§Å¿ ¯<û x'ãU¶­ð‹á®¹ãß„¶‘¦é7Ú6�=ºø‰µe€iùkÉ/n-�)ŽLM.n�µô]¯ÇÏŠŸ>9|8ðÏÅ›ÿ‡¾"Ò~(5Ö›iá} ÷H:§«Ýù2‹‹Ë¡q ‘E8Y�ÊÑ ¡vZø™ÿîÒ>0jÿ¤ÖüCz,þ1�HÖÊÕ"¹ðõÆ™O®]d5RA˜ÀpCg5wŸ²WŒ¼Yñ›Â3øµãýÆ×b�¼=a¡xQ´Xï'… šöäKyvóMå3"h£O2C±‰RžTªÕ©ïT•åe«Õ|:üïvÞ×q¶œÇW$a A[}{éòòì¥×”ùSö½øýñSö¤ÿ‚vŸ‰‰€ ø]ãè­a¢­¥Üzî�`¾ ³K{¹/LÏòHÑ)kamEŸ‰Üʼn}?ö�ÿ‚šÝx/ã¿Ä_xwâ7À?‡“|0°€Éoñ÷eç‹5 ­ÅжµQ{lm¡HÚ%7.7I1?ÝúZ÷üçÅZŸÀøTVŸWNøS¥êðêº&�…µ[t‡PŽú;«Æ¹Ù=ª2°Q¼ŒCºVTt—Ô°ðÏö_Ù1ö²ÜÝOçùÛþmßiÛ³`ÆÌî9Àùkâ?ì9ã?„VŸ²o�|â-ZK¯ø§\¼ŸÅ–¾óítá.Ÿ¨Í]Û—u[w’e·`eFpä$‘»+)�)îÚ¿¢œ¿[Û^·R»¾9¸ì¢íëìÓÛÊ|ÖÓËá²5~6ÿÁC¾$þÎ ¿h�?^¶ð&µñà¶�§xƒL¿±Ónít�nÊùŠÆe´k™%†HÞ)‘”]>ì#2T{wÆÚÄþý§¾ü>ÑWA‚ˆÚˆ/n/o¬å¹{;‹(mÜ¢$ц�uÃoBrÁ@WCÍs7ÿðNÈþ'ü8øÉeñ#ÅQø—Å?tè´�_UÒ´Ÿì«M6ÖÚ'K8­-^iÙM#ÊL³HÏ$ŒI µþÆ;›öŠø}ñ'âÅ[êt­GGµÓ´¯ .�§ÜEwÓ•k«‰EÉhríæ™B*CVi¯$¥µµó÷eÛª|·é}S°=qï/ý²ß+óµÖÖOSÁ?gÚ÷âGìÑû|Fø©ñÆñÕ–‹âÍwHÓ-.l®´éÅññÖPyú�ÅõÂEb€ ä�³ÂóËù½ösÿ‚˜Xêï|ã‹?üy¥Íâ³âŸÞ­¦� ‹Y£ŽîÞüI}vqò;¯#Óo™åíß“·9ÚØÅrÿ?dÿ øããW‰|sã ~Ò x�áìÿµ2æ4… 3\´Ï?ÚöƒµŠ„)þÖîÕ”ù•7Ë£¼÷íì—'þUù÷÷Ma*‘V¿Á¢êý¯½ÿ”þþš¥o?“ö§øËðƒÂ þ!|E�áÔÞ ø�¬iºUöƒ¢é7ÐꞤ‹›µô—/ï—$�Ç(–ÙÞ̸Ú¸ÚÛö„ø¥ûFþÉ?´Þ±àåøoðëÁ–^#ð„šf¥ktÚ¾µöKIb¾¼Žö9„6ÁŸÊ„ÚÍæù4±y ÇÌxÇâ‡Ã_Xx¿i¯xóÀ ï­5 +Fðÿ…�õ]V{>À5+Ø.na›ÉÚ¬Â{_2DVmª gÀ~0~×ÒüIÓ¾/|>ø7Ä_Á_îu CSÒâð|rêvw—Е¹ûâÏ Ki¤Ã¼rÙ<˜y‚KuxüÜÇ5ÀÆ5iª‹^u{µ~NTÒ»òòûîµÑ[írø£éW§‚©Ê¹¥%Én~f¥>X¯ùwmRÑí­þ´ñ?íü¿¥ø[ð»Oøƒð{ámÚ|:Ó¼I©xƒâ,Û­$I#[ÙÚÛý²ÌË+´r»Éçb$�FÇ2 ¾—û6ÿÁP¾|Aýšü'ã/ˆ>=øeà-S]’úÁ£¼ñ=­µŽ£sctö·XÉ4Šg¶g@èÃ?$±ää×É¿ |+û`ø·â~‹âÿ|9°øsweàÛO]\ë«klåà–Xg&A,lò•)•ƒÂãÕáÜß´¿Æõ?ð³iÝ6Þnf±ðÔr,R�î‘Ù“óFt­kgNµIËFsæ”�ÚåVæ—.²·Ùk§Ê÷o¦�‡40tâ³|ÏC•E8Æn´×¸¹—-%ñ_y~yí‘ãøQÿðVoüqÓµ¨Vïá—†ü*'Ó ê¯ü$zì·ñê6°!`%™Ãu®Ikp%ÅtÿਿÿgßÚ«ö�ñ>¡ªêZΙã�I¼ÐΓbÓ5ü0é0C#åq"²áÙNAã×£ü-ÿ‚ |3ð¯Ä]CVñf¹®xûJ¹¶„[Ø_³ÚËÈ.fšIá‘LŠàÆ6®Ý�–}ß/¢þŸðK? ~ÄÞ,ñþ 5 ?Câ�kûGC]CFŒ\ø^ÐÛd“³»J£#ç´dÍaMær\©Bš´·¼šæœem9WK®Ÿõ’KiÒà\¹§Ý~ÈúgÆñ¡|&¿Ð›Æ áWð¼qßÛ^êq¶³&•ÐÔfŽÍ„¦<Åö[�UÄ€È"�Û4Ú{âG€¾=øïáçŽÁšÅþ—à#ãÍSÐôë› tEšh$²¸Š[™žFGHˆ™0êíû¸ÈªÚücûöÓþ ÿÂiæýƒÅ‰â�í�ìŒoÛ¯lyGŸÇüñßæ··ø+²øÉû3Ï©|eñoÅ mJ[››¯†·~ ‹EŠÄ»Èæg¹Y–@ù$’F#÷ÝÚ¸ê¹Âƒ}m?_á{¿ùU|¼¢ìuEFU´þhÛµ½¯ÿ*¿ÿ´x§ÃÏÛ»ãŸð'à—Æéÿ müñZóDÓoô&Ò÷ûOFþÔTŽÞõ/^vŽeiž6ko³)�&ÛçÈc.ý–“ûrx²ÿàwí]âgÓü:/þëZæ� Æ ›É»ŽÇJ‚ò#t<Ü»%`ÞYŒ§šâ¿b¿ØwÇÞ(ý•?gÝ'âO�¦“Â^Óô?GáK¯ gë–ú�µ´rAi{vfÚÐZÎIX…¤Rþæ%’W*æNÇâ¯ü—ľ.?ôO üRƒÁÞøç$׺ý„~KÍVÖòk´™í¯àF�Ì"„ÉÛHøóDrÄÎ;qðq•xSÿ§Š>_Ãå¿¥ªk¯Äºm†Q~ÊU6÷¼þ>kz§ í³{Þün»ñãOÄÛÏà�ö�|- hÞ/øg¯M£ÜxvúòËz¾”fócMNš]Ó·“) aF‘JË¿påþ4ÿÁdO‚¯¾$ø�MñßÀ�3Bøa¯Í£·�5­@ø®+9DwÓ[7ÛcÎÇÍ[xZÒo3É ]D 'Ðþ+ýŒµè#ø¼mrg†Î½j5Œ´B`¦ÓÌó·yG~6ço=9¯•tïø(Ƴû/Ïñº÷Oøa‚ü?âû�RТ²¾}KS°‹\}1ïb»7;iT`ù áü–a*ù¢8þìQ�ëî{×ÌÓÁ:|ïØ7\ø#ÿ Ž?¶u›Í_ûkû'ýOÚ5§Õ<¿#Îçnÿ+w˜3�Øv²VRRíoM×éuóïf©ó{;uý9eÇ—þåÍ[ã·ÅŽ?´_Äü,¼ð…ôß…Ëigªê¾(Ðï5—Õu+«u¹Kx!‚îÐCP¼eåi%,Ó#P…›‹ð·ü;Å<7ð“ÃÞ Òü7ᯉ?¯u»=Uõ¸gÔ´Ï �S¢Ë r[Ëuºp‘Â<Èx“{�£z'Œ?d¿xsã�Œ|sð§âƒà»Ïˆ–ÖÉâ+=wÂm¯[Ism“å·•yhÐÍå|®$3#ìŒí]§v ðN¾ø7á€øqã <=ã…—Z�Õ¦·­i)«Ã­H–Ô…õ´R[óä>fa–-މ�”m1†<ÞWï}uÿ õj÷å²KšåÏwÏÒÖÛüWµž×æ¾–<'Áµˆ?cŸ~Ù~=ñýŸ†/øsûiþÃv‘á�øoÆÞ×>#øg@Õ®|5­Á},Ú•¸xŒ°;}ެ9SŽ•ç5Þ~Î_ ¼kzn¾&|øÛeáoI !õ8K¿k HÕn­|¹3ýÿ0v¯¸kïÙ“þ³áöƒ¡mÿ`ÿbx§Hñ/Ÿö?µyß`¼ŽçÈÛ½6ù›6ïÉÛœílb¼ûö–ÿ‚né¿ÿlÿ…,üI/†õo‡wi.§d–â?E˜m‘ÛÌO)á3Üm|?°ÇJx{BQOgV2~J*“Rûã8Ûû×ÖÊåz¶þÎQ^²ö‰Çî”]û¤´»>—¢Š)QEQEQEQEø›ÿ‰ÿͺÿÜËÿ¸šþ’«ùµÿƒÄÿæÝîeÿÜMITQEQEQEQEWæ¿üÝ“ñ»öD×ze|CA¿û»®,_¿òÇ¿§Ö¿J+ówþc´}öXøcã8½Ï„þ ZJ09Ukk—Ïý÷ cñägßîk?¹¦~�á#ÿŒ· çç�þNqýOÒ**++ȵ8® q$3 ’7HÈ#ð©k×?8wZ0®ö«ÑîüEû/|HÓôû[›ëûï jvöÖÖñ4³\Jö’ª""‚Y˜�$‘]õ†&‚­Ft^œÉ¯½XÖ…WJ¬j®�?¸üåð—ì£ü}Ö|+oñKálþ%ÒôOÙË@Ò­ìõí&il­utûRÉ“ ò¾ß ° Jùðù­°§˜Û¿/¾9þÏzçüž4ßü-¼UðóD¼ñ׆~#|3¿ÕtíX™ 7*/çƒìöÓN®wi"va$;+ÿKu‘ãÏèß|©øwÄZež³¡ëVÏi}cw’˜œa‘”öÅz¸ls¥Rr¶“—3^³«=<ïV×Ê®pO ¥N¾°I/”)Ã_/ÝßN²gæ÷›/]þÔßþ/øàŠøÇ^ø•àK|?Ö¼;áÙícÖ.³oSR³�Ä6¶ûtÛÙ& ždÐÎÎT…fÅy§ÀOƒ>0Oü_o ü5MÞ.ø®XßCá�ºßÛ]O_ ‚Öâ Bâiõ+ôK‰‚\ì_0M(F”‰D}ÄïÙOã'üÓâ£ñàö—Ä?€×óý¯Äž»•æŸHO╊ªŽ.P•dU.~Îý�ÿà­ÿ¿m+MÓÃ^?Ð4ÏkYH|#¬ßÃe®´ª…Ý#¶gÝ8UV;áÞ¸SÈÁ}ª~Êö³w¯ÚçÄ^ ýŸt›�wÄ~:ŽÎÎ-7Ã3ZO©è“ZhÆâDKŠïP±Žcw<–VòvÑÏwHÕú9ED½é9uÇ^ÎQ“ìõååÞü®É«"––ÿ—ÍEÅyu¾Û«´õ¿ã^ƒûj¾ ~Ót c>�ü#â_ Ùxkàþ¡ðêÖúK[¦}VM/G¼š[”¼h-7I. Pb"¯Éæß?bOŒw?´f¥ð/Hø±{ðãZðß…mìì ø¯âž�km§xÓž ‘µ -žÊæßR»²¾ŽÝ¦ŠâV‘\„óFU$H1Ÿ¨¿à‰ß<ý³ã øBøE‡ck¦˜¼Yá?†Ó|5ºñ-ÈY–Kk­&웉¤�U$ûZ‘ ý±‘7ÏÞÞ&ð¦—ã]];YÓl5m>~$µ½·Iá“êŽ?•|Ùñ_þûð[â]Ì—VZN¥á ×;¼ÝðÄ�»�ãQ쪿‡Z A5NÒM%g¦ŠöïÕ·¥µòÐñ±¸—N1ÃJž)-}÷ìê^É6¥Ê-ÊÊîJÛÚÚ[ê:+â!ÿøøùðLÿůøû{qjŸê¬½áOˆZ Ø´rØ@àc‚?×+g9w·áGöެ­êšüÊ^%ðÚv«ˆöoûðœ?ô¨£í +å-;þ ;ð.ù±.¯®Ùü¹ÌÚD§'ÓäÝÍmÁÿrýŸe…Y¼zÑ–”mQÊû@GäjÖ7þÚûÑßO�xnzÇGçR ógÒTWÎðöïÙïþŠþPµ/þG£þÝû=ÿÑ@ÿÊ¥ÿÈôþ¹‡þu÷£Oõ߇?èaCÿÓÿä�£è¯˜n?à°ßᙕ|W(S€é¢Þa½Æcó‘«Áj¾iÛü›¿_í8F’øÞËúâ“Ça×Û_y…O8f ïKå8¿ÉŸZÑ_ÏÿÈøcw$‘i^ø�ªK?»Óí‚•õÿ�‚ÃñZkÁZ|Q¯çþ¯ÙãâV¸y r¦Hëþ®ÞNÜÿœÔháúJÿ{8ÿâ&pÓø1J_áŒçÿ¤ÅŸjQ_ŸÛ‹ö–ñY?ØŸ³�ÆŸ¸�¿Ú—R wçx‡·ÓŸÊƒâ�ÛÇ||7ð`n2Å)N£?ë§úôü;Qõè?†2öëýCþ"î¸lE_ðШ—ß5}©E|Rß²Çíwã¿ù>:h:4rWGµÃF§°)o È ‹°ç¸OøtN·ãsŸüyø‰â€ü¼hïŒò@óe˜c8þÝY¬þ OæÒÿ1­9Åo÷<¦¯ýÄ�*ûtßà}gâïŒ~ø»û{Å^Ñ6}ï·êpÛmë×{CùW“xÛþ ƒð+À›…ÇÄ 6úUèšlßoú4HÉù°®'Â?ðEŸ�ÞÛöËøƒo_·êΛºuû8‹ôõ¯YðWìðoáþÓ¦ü6ð–ôû²]Ø­ä‹ôy·¶}óEñrÙE}ïüƒÛq•}aKAzU*?ü–4×â|�ûlÁP¼ûUþÊÿ¾øÃþ7Õõ�ˆ^Õ¼5ovºjyÏ{g5²ÊTHd`¦MÄlôøþ Ëðãö…ÿ‚O|Ô<¤|8ñŸ‹t]oX—Ä2Î>ë÷—ymíá*�j̾^ËxÈÊg%Žp@Б¢Yø~È[XZZÙ[©ÈŠÞ%�nŠì*ÕW¯/ޝܒÿ0ÿVóüF˜ÌÒIv¥JÿÉ¥í$~ü[ÿ‚ˆþÖ·ûâÒ¿gÏÚ~ebH]3áEýœ_÷òX<Ñù}kåÿ‹Ÿÿn�ˆÌD²¿í r3�uÏ ëú‚ŸøÁ_ 5ýIQKû>“þ%åêØÄ9Êjë˜J®!ÿÓÚ³’ÿÀSQÿÉOå{Áÿmïloøa-ST™?妡ð¿Åónú§Ú„øíz—…à¦?ðQ_óEý†cÒÅ—ÁŸÁŸûæq_Ð?ÅŸÛá?À…—þ/‰>ðÔ°œ4†µo Á>‚"ûØû'ƒ_/|^ÿƒ‹¿eÿ…­$v&×¼ks;¢Ð4YXgÐIqäÆßUr=ëÒÂåS—ð)}Ëõ=ÜK“eŸî”)Òÿ bŸà®~XÿÃÝটôfšŸþ_ÿòEð÷_ø)§ý¦§ÿ†—Åü‘_ ‡þ “ñ£ãÈh¾ þÉ~>×!”~ãWÖüèìǦáB.zÿÇÀïõ¤oÁL¿i >Õªü.ø§Îy¿“<á=Š‹× ÿmú‘ҽ욱þ4£Y+ýÊìôþ»ü4åè¿ÎÇçÜŸðWŸø)”1³¿ìm¨ª¨Ë1øKâ õ?éæšÏü™ûeøg^}/Vø3ðßEÔbåíµ? ëR'QÈ–ýHè*ýOOø7ÓÅÝ'øïûN|Nñï˜wKae#EoªÆ×L˜Ï¤*=«Û~ÿÁ?e߃â)õñ=ì@sâB{ï3ýèw?òÇŽ£—øU¿þAí12ø`—«ÿ/ó?| ÿ~×þ:Õa³·ðÁu9Â[A¢ê×WÙHïÎ:öËÏø)Gíëo¡Ÿ´~Î:ÜÍshnto„ž%�€pC)3L‡�QŽk÷7áÏÁßüÒþÃá xsÂÖX ö}#L†Æ,ƒlJ£ô®’�­áaü*7}äÛü�{ Òøê}Êßæ/ÿ?iÏø(oÅ_:6øCûV,2e|�7À:ž‹ßîÿ£Ûeÿ{¯zùóGø1û`Úøª]SWý�¾+xÄÌÛž={À~)rçý©-Þ?÷Õ`tRy¾&Ü´ß"þêKòÔkGy.gæî/¿ ?j¿Û{àw–Þÿ‚yèú=Ä8ÛuÁO½ß3;Îd?‹W¥ÿÃÝটôfšŸþ_ÿòEFôWŸ:“›æ›»ó:cÅZ*Çó‘ÿuÿ‚šÑšjøi|WÿÉÃÝটôfšŸþ_ÿòEFôT9ð÷_ø)§ý¦§ÿ†—Åü‘Gü=×þ iÿFi©ÿá¥ñ_ÿ$WôoE9ð÷_ø)§ý¦§ÿ†—Åü‘Gü=×þ iÿFi©ÿá¥ñ_ÿ$WôoE9ð÷_ø)§ý¦§ÿ†—Åü‘Gü=×þ iÿFi©ÿá¥ñ_ÿ$WôoE9ð÷_ø)§ý¦§ÿ†—Åü‘Gü=×þ iÿFi©ÿá¥ñ_ÿ$WôoE9ð÷_ø)§ý¦§ÿ†—Åü‘Gü=×þ iÿFi©ÿá¥ñ_ÿ$WôoE9ð÷_ø)§ý¦§ÿ†—Åü‘Gü=×þ iÿFi©ÿá¥ñ_ÿ$WôoE9ð÷_ø)§ý¦§ÿ†—Åü‘Gü=×þ iÿFi©ÿá¥ñ_ÿ$WôoE9ð÷_ø)§ý¦§ÿ†—Åü‘Gü=×þ iÿFi©ÿá¥ñ_ÿ$WôoE9ð÷_ø)§ý¦§ÿ†—Åü‘Gü=×þ iÿFi©ÿá¥ñ_ÿ$WôoE9ð÷_ø)§ý¦§ÿ†—Åü‘Gü=×þ iÿFi©ÿá¥ñ_ÿ$WôoE9ð÷_ø)§ý¦§ÿ†—Åü‘Y_ðIO…ŸµÇø8ÏáÿÇÏŒ¾"|>‹UþÑþ׿“Àú¶“¢Xy~º°ƒ2]+÷”…~yNé$ÀûÁkúL¢€ (¢€?›?ø+_ÂÏÚƒàGügñãçÁÏ€_> Å¥gd_Çà}[VÑ/üÏ ZØO‰-UD› Ì¿$£l‘àýÒµ«ÿuÿ‚šÑšjøi|WÿÉýÑ@ÎGü=×þ iÿFi©ÿá¥ñ_ÿ$Qÿuÿ‚šÑšjøi|WÿÉýÑ@ÎGü=×þ iÿFi©ÿá¥ñ_ÿ$Qÿuÿ‚šÑšjøi|WÿÉýÑ@ÎGü=×þ iÿFi©ÿá¥ñ_ÿ$Qÿuÿ‚šÑšjøi|WÿÉýÑ@ÎGü=×þ iÿFi©ÿá¥ñ_ÿ$Qÿuÿ‚šÑšjøi|WÿÉýÑ@ÎGü=×þ iÿFi©ÿá¥ñ_ÿ$Qÿuÿ‚šÑšjøi|WÿÉýÑ@ÎGü=×þ iÿFi©ÿá¥ñ_ÿ$Qÿuÿ‚šÑšjøi|WÿÉûûñgöŸøoð6>5ñïƒü(UwlÕu‹{IýÔv ÇØ_-|^ÿƒ†¿f…~rZx¯Xñ�Ô9Ý�£Í&â;,“ùQ7Ô9õÉ_†¡üjŠ>­E”ðŽyš[û; RªïI¯¾Ö_6~SÿÃÝটôfšŸþ_ÿòEð÷_ø)§ý¦§ÿ†—Åü‘_¡#þ •ñ{ãÐ1üý”¼}â%ÿS«ë>lVkž›„qy|ÿ×Àèmþ [ûK(3ê_ ¾ØO÷âˆC<á¡ñƒcý´>¸®/íª2þ%?H»}îÈúø…ù•ùb(a|ªV‡7þOž�ùîà®ßðS@?äÍu/ü4¾*ÿäŠóÁÈ?¶‡ƒõ÷Òõ¯‚t-F3‡¶Õ<)­XÈŸQ-òâ¿T?áÀÞ5øæâ_޵ÄÏÅ1m6ÁÞxÇuŒÜI,`} Qí^Éðþû/ü#1JÞ—ÅW±t¸ñ¥=Þï¬!–øÇGÖ3ŸÃ¢¡þ)_ð�ÿ0þÅà̸ÌƦ!ö¡G•àu\ô�¼üið‡ü%û_ø·P‚Ù|ð.+‰Ûj@ºN«<Î}#¿lŸl×»ißðR¯ÛÅtd»¹ý�®u+{˜ËE6�ðŸÄÓ)Ï«-Ä þUûƒðÇàg‚¾ X_xC÷+±£Ñô¸,•‡¡ªç§zê¨ú–:§ñ«ÛÊ1KñwcÿYø[ÿ"ì§Ú5´±e?¾ý”3ù¤øÕûgÿÁB>" aƒàßí7gglZ7Ã-CJEúJ¶ÞæÕò÷‹þþÛ>9ñBjz×ìÇñïIJ)%†¿àÏ_4™õ‘R7üˆ¯ëòŠG…nõ¯7ýé7ømø/sêk“-tð±íB”)ÿäÊ<ÿù1ü§ü2ñÏíuð˜ÆÚgü¦Æêh°DÚ¯Âê�»ûßéNïÀíŠöýþ ³ÿ'ðîž–šìSsck ¿ÁÿÅ}ný Ñ^•5*Ô¢£è’>+2ÎóÆ|ù…yÕ}ç)Kólþr?áî¿ðSOú3MOÿ /Šÿù"�ø{¯üÓþŒÓSÿÃKâ¿þH¯èÞŠØóç#þëÿ4ÿ£4ÔÿðÒø¯ÿ’(ÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠþ�è ç#þëÿ4ÿ£4ÔÿðÒø¯ÿ’(ÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠþ�è ç#þëÿ4ÿ£4ÔÿðÒø¯ÿ’(ÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠþ�è ç#þëÿ4ÿ£4ÔÿðÒø¯ÿ’(ÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠþ�è ç#þëÿ4ÿ£4ÔÿðÒø¯ÿ’(ÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠþ�è ç#þëÿ4ÿ£4ÔÿðÒø¯ÿ’(ÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠþ�è çëþ …ýŸ~=i?ðXïŒß>/|ø…ðÞ?ˆÖõ îõ j:>˜oï5½6é­à{¤ëÄ¥P»>ÈØäí&¿ Z( Š( Ëø;¯ö{ñ÷í)ÿÛðN…ðëÀþ0ñþ·iñ*Âþ}?Ãz5Ϋu ºézª4Íî#$jXŒ"Œå…~wüÿ‚�ÁH¾üð�4ØïÄ:G‚´K-Ækß…({™`µ� �¥e™T¹XÁbª ’pâ¿¥ú(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ùÈÿ‡ºÿÁM?èÍ5?ü4¾+ÿäŠ?áî¿ðSOú3MOÿ /Šÿù"¿£z(ù&ÿ‚“Ý~Ý¿ðTŸøBÿá`~ÉÿtøA~ÝöøG¾x†ßÎû_ÙüÏ7Ïó³�³G·nÜe³œŒ[4Q@Q@Q@Q@Q@|iÿ÷ø`~&ÿÁ-þ ´i¾ëÃ�e­À=<«¨ÖSøBòšû.¹�ÿ m¾;| ñ—‚®ö‹oh—šC³O Ç»ðݟ¹qÔ=¶t™5÷£Þál×û39Âf/jU!'é&ÿŒÿ‚||Sÿ…×û ü&ñ;?›>§á]<Ý6s›„�c›ÿ"£×±WçÇüsñfçÄß°¦§à]StZÇÂÿÞi2Û9ùà†VûBçþÚÉp¿öοAë<²¿¶ÂS«Ý/¿¯âvñÖSý™Ä8Ü Ú%oð·x¿œZa^qûbÉ£üSÿ±CVÿÒ)«Ñë7Æ>ÓüáSBÕ­þ×¥kVsX^Á½£ó • H›”†V#*AàŠÓFU°Õ(ÇyE¯½Xù¼-UJ¼*KdÓû™øñðOÄ“þÍŸ eŸÙûXšwÒï|]à?ˆ.îgiëLºÚu?sY쯥,98·½·Äd×Öš·ü#âÂ|?¸ø×gkðóþå�ÄcàY|/6™yÿ 4Ö«®`> º�ºû2ËöÌÎ-M›)…v›€í¹~�ñwì9ð«Ç– áÕ¼!k{ÿ JòÚûÁ25ÕÂÍ¡Ko†"² È¡C$¥ÕÊ)`ÅApÁ;¾Züd—ÇQøbñ5yõÏøIä°ö£ý‚ھпÚ_ÙhþÎûf@´}ŸÍó?y»Ìù«Ò�Xʬ¤öu%/X¿gšþnH4ÝÞ¯šüÏÝó©Q”!÷PŒ~kÚ9kÑ9MI4´·-¬µøƒÅ¾-ø™wûþØ:‡Å/ø7⟆<9ñïN²Ð®´}ZÇÊ–Í1Ð-ÂêÎñÙ `RÚ0…\2²±Ž¼Söÿÿ‚<Þüký¹?i�ø[Æø{'‚¼eâ(,t/ \Xÿj ˜µ\Ã<É~U]…«‰eŽæùÀùjUŒŸ©šïü×á7‰o¾!KwáýUíþ*Ê—(ӓĺ¤Zf¥:˜Ú’\‹hnÛCºhcIf˜3g¶Ö?g¯ëÞ!ñ–«w¤y·ÿ4hðøð�™Ò|Oªé\hàF�vö—1=í¯î×÷7FTÉsŒ»îùÓö¯ÿƒjÿe¿ÚzûPÔ¬ü-¨ü8×5K?…oä³²•Ô¥¬Ik^0?ÕÆ�Àù†zõ±lE_i(¸·&ﺵݮ»Ú×Z«ßKêø¨Q­Jš…Ó\©[ÎÊöò½í³³IìG­xƒâŽ»ûJ~ÚV>"ñ‡†|H:öIñï†tï/3Ìû!º¶Žcì Ûwã8Å|SðGþ %ûøŸÄ ¨A{wðûR¸ðäŽÛQÑ®í-íô¨ZGŠÑV×̵…Ë& • 7`(RþËŸ´wì÷¥|%𿃾üFø}/†ü3¦[húF›mâX®g³µ�ba,­9!B É–<I9¨žµ(ÍU¦ÕÝÖ�EÏQïþMuÖ/Ö[}b�IEÓ’²[_û´×þ”¦ïÚKÑ{¥­Ôw¶É42$±J¡Ñу+ƒÐ‚8"¤®#p¢Š(¨o´ø58<»˜!¸�9Ù*\ý MEi5fs:§ÁokbO¶øKÃ7~iÜþ~—›Ï\œ¯5…yû#|(Ôn · ~Ï+c/'‡,ÙŽ8êc¯C¢¡ÓƒÝ#ЦW‚©üJ1~±Oô<ßþçáý¿‡øLÙñº?áŽ~Ñ+øqÿ„Í—ÿ¯H¢—±§ü«î1þÄË¿èøÈá­ÿf/†¶�,q|=ðP¿¹Óâ��Û…é[WwqX[<ÓÉ0Ä¥žI* I'€+€ñ�íqð§áæÿøH>'|=Ðü³‡þÐñ�¶Ó�9ß Ç$©­!BïÜ�ào –±§ü’=(–ÕB"ªª0Â�_4øÃþ û0ø·Û~4x2m½³ç}C×§ÙÑóÐþž¢¼‹Æÿðr/ì¹áBÿ`ñŠ|M·§öo‡§�NŸiò\t®ÈeØ©ü4å÷3WŠ¡ä¾óï:+óLÁÈz?�¿äœ~Ï¿¼lýYûE¿§üðûGû^½=øoü<ïöÙøÁÇ€¿c«�4œF|Yw2=>o;ì]ð{qùÖÿÙ•ñ¥Y%ú™ýz‹ø[~‰³ôºŠüÑÿ„cþ sñ«ýn¿ðá,s}ôŽ8. z€|«ÎF?½ü]{€ÿÁ¿jŒ<üIý²ü]mœÏcáØnc†nøùg�0¦b=±ýŸJ?Ä­éy~H>µ7ðS;/ÍŸ£>-ñÖ‰à7íšî±¥h¶ƒ?¿¿»ŽÚ>:üÎ@î+¾%ÿÁ[¿f¯„¾`Õþ4xW‹vôÒ￵�Hê Ú‰N}±šù¿Â_ðlÏÁÔ†£ã?üOñæ¤Ç3=ö­1KÔŸ¹›É$ÿ­?Ï>íðÏþ«û.ü)ƒÞ¿’=¤¾²óêÞaÊÜÉ"óܵϊr—¢KóoòlL¶Š^®ÿ‘⟿àæÙûA¿û…t߈~>¿”í·]/FG3ÛÄ‘ÉÓ=#'Û½s­ÿ�ý§>6�¿ c¿Ço& KÄ’\Go:ž„f(#ôé3WèÃï„øKcö_ ø_þ¶ °C¤é°Ù Î6ƪ1šè©ýkáÑ¿ø¤ßà¬Ƽ¾*–ô_çsó5¼3ÿ6ý¡[zׂ¶²ÞÇn ¸�)ì¥Võ²;bAþõþ;ñKãpßñŸö²ø“âˆ%æM/Mó¢¶�?{i–v�œãˆñ¯Ó*(þÖ­áF0ôŠüÝØ}Jãn^­Ÿ ü'ÿƒseï†�ßxkÄ>3ž 6É®ësŸR–ÞLmô*Gµ}?ð‡ö5øKðB|ðÛÁš ¹°Ñ Šä‘дÁ|Æ>副K¢¹kcq‰6þfÐÃÒ‡Á‚Š(®S`¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢¹ßˆ_¼'ð�MûgŠüQáß ZÝçêÚ”6Qã×tŒ£óWůø.wì½ð„¼süO°×nÓ8ƒA´ŸRó1é,haüäÏ_B�ñ¦£êÒ=¬¯†ólÉÛ.ÃT«þJ_’gÖôWæµçüWÅ+¹m> þÏŸ>&܃±Û}ž<ôÜEº\¶Ñ×�¾û{Eÿ ÿý£°|3ð—À?ô™¸ŽïZ•ò<ÿ}%™Û�úöúôöæZQæŸøbßã·â}wüB¼îŠæÌåK ¿éõjpø ÜÿòSôº¹‰_üðfÈ\xÃÅÞð¥».ñ&±ªAb…}A•”b¿>áÓŸµ§í“ñwö¹Õ´Ûy¹¸Óü# ÑÛÎ?ºDfÒ0>±0àqéÔ|5ÿƒl?gÿ_}»Å·¾=ø‰¨JÛî_VÖM¼S·ÒÙc�¬„ûÑõÌuOáP·œ¤—à®Çþ¬ð®þF·´kxУ9ýÓ¨éGðg ü\ÿ‚ôþËß hÿáb^DHû6�§\^ù˜þì»þþW‹Ïÿ k?Åð;ökø©ñ;Òîâ†Ï÷˜[Gp1ë—_r+ì?„_ðN_�^)<-ð—ÀºuÔ#ݾ•ÕÚiæ/þ=Í{DQ,*"ª"ªª0€ >¯˜ÔøêÆá�ÿ?Ð?¶8/¦/«ˆ}ëVå_ø(¯ý,üÓÿ…Ÿÿ(ý£Ûþ$þ øið[N¸â­Aážâ {º»Ü¶G¼ þíðæ�Ú/ãâîøÉû[ø²kg9›IðÚO¤„õÿ–�ÇÇ f×µ~—QGö-9ÿsŸ¬�¾ådñ±Øùápø_8Q‹—þSÚKñ> øMÿáþÍdŽ}oMñg�nÁß$šæ´è®ýIÙj!g³n÷Ï9ú—áìQð‡àDþ øgà�ÜC÷n¬ôxëñ›o˜5éôW] » CøTÒùk÷Ÿ;›q®š]fÊ•èç._üü¿€QEÚ|ÀQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEùƒðZê?ø'Ïüëǹ°x3ö�Ó—ZÓ7|±H³ÊzdÎ/‘W¯úDC¸Ïéõ|3ÿãý’u��ß³›ñÁK<> _éS[(ûA·B¯p©ßryqΣ¹ƒÕïŸðO¯ÛHý»?eO üAÓ Q]ÞÂ-u‹4?òÔ#O:íÝó&y(è{׋—¿«â*`å³|ñô{¯“üÏÓøÂÚù6‰ik(Åaë÷S¦­NOþ¾S¶½àÑíTQE{GæEPEPEPEP–üTýˆ~|oü.ð¿4£i¹»Ðížä»6Ï1~¡…|ëñþ åý–<{<²Áà}KÃsM÷ŸH×nÑAç�’¼‘¯n�ÇN¹ûrŠé¥�ÄSþÚù³áéOâŠ#óbãþ ¡ø}á ‡¸ø}ñã‚nY‹†�Q‚UFÈ#TP·^¬OÊ9¦ø"wíáPO†ÿn?бč#ŠÖú;Ù#Eã<ÿh#ŒF=3Ö¿J¨®¯íŒ_Ú•ýRš1ú…ŠÞ�¯ÔüÕ“þ Ùûwø\:hŸµÆ›¨#0\êz{nØ3ƒóC6ç�ÔœTcöBÿ‚•x|ùz_í-ðÂî'ù�¯´è‹«z ÚTœ~#é_¥ÔQý«S¬ ÿíÈÿ�}J%/ü Ÿš?ðÌ?ðSßú8¿ƒø,ƒÿ”´Ã0ÿÁOèâþ ÿà²þR×éujKþ}ÃÿAõ5üÒÿÀ™ù¢dïø)ޱþ�ûI|(·µ¿%¦�™qÈÆÝ!P3ó3×¥'ü;·öó×Ï™ª~Öº]¤©ò¢ØÙ¸F^¹;`Œgð?Zý/¢�íZ‹á„ý¹ò©C¬¥ÿ�3óJø#çíU®�š¯í¿ã›8î3 {ÂÑ·\&/"8ÏxíÚŸ'üÿã?)>+ý®¾4x†FÚÌÆi×t€cv$º—·œ�S_¥TQý±Šû-/HÇüƒê4z¦þoüÏ͘?àØ_ƒ:­ÒÏâ?ˆüC$nYÚ­š®PsjÍÉêC ï|!ÿå~Êþ öÏëþ Ú0N¡â+ÄÝÆ2~Îñ}xïíÅ}ÓEL³ll´ö�ï·äRÀáרGÍ~ÿ‚<~Ì^plþ x*b½>ßlú€êK†Aúúšõ¿þÌ? ~…ÿ„oáç�¼=³îÿfh6¶›~žZ îh®IâkOã›~­›F”#ðÅ/�QE‰ QEQEQEQEQ_ÿÁJ¿l¿Š³ÇÆ_† øk7€¬$ñ–‹¯ëZ…+½aPiÓéÇQ[ßZm/ý¤ìÌÎßê”äšðßøoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠüÍÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(ôÊŠü ñwüß㇀· câÇì¿§È�b—Àz�”ýþ-ÇðåÞ.ÿƒ‹~'øcp·ø�ðG[t­‡Â]k“è_"ŸÏõÇ[1ÂÑþ-H¯š>›+àÌÿ2³À`ªÔOª„šûíoÄý°¢¿ t�ø87öÁñ¦¡ü"Þøgâ+'<Ì~ë£þú]jTRÕÙêðR¯ÛÃãŒimcðËá¡eÛö›kvÿ|¤òÝ‘ô+øWöÝ 2Ÿøbÿ7eøŸGÿ·6£ÿ#JÔ0¿õöµ4ÿð¹ÏåËsön¹O‰_üðfÛÎñ‡Œ¼+áX±»~±«Ab¤zæWZüIñçÂÚ_ã­„ƒÆ_´×Š¢y3›]<Ü &Ï]ÑE5ºo“ò¯3ð?ü»Sð¾¯-Þ¯â�øÛÍ“Ìe×|)«�Þ µ¦¹nÇëœûÑõÌ}OáP·œ¤—à®êß á5ÇfΣ[Æ…Kî�GM~ ýoø­ÿÝý—~,ˆÿ­üAv„�o¡i÷7ûñé*§“ùÈ3^'uÿ�Š×RZüýž>,|KŸ&8äk"<ô,E²\�£¯;xë··„|)ð‡Š> ox7öC´žWØÓT#Ú£ÿ†üý©¿èkøÿ†ÓWÿæ‚�øoÏÚ›þ†¿€øm5þh(þÆ„ÿ�Rsõ•—ÝüD¼FþEX,6³�Îø_hÿ#¦ø{ÿÖ|Ò5?í/k?¾"jS×/ªk§o_Ü"KùÊ~µô¯Â_ø&ì÷ðüdø™ÿÿ|MO‡ÿ­õ_þÍ:î Ãþ(·C,þ.KÙGCÕžòDIÞ‡õ3á‡Å?|jð&�â ëZwˆ4 Z!5¥õŒÂXfSî:x*pT‚F0Ž!8IrÔ�źÿ5Ù‡ð…l¡ÃBj¶®´«Gá’ìÿ–kiAê�÷Z›ôQEzÇ—¥øßF× ô|¯ø-~i¯T×@¢Š(¢¹]㟂|I7ŠcÓ¼cák÷ð4­‰ÛV‚Sáù<ÆKÀ®~ÎÁb$ÚBŒô­ý^±ñV‡e©é—–šŽ›¨À—V—v³,Ð]Bê$�Ô•de †‚"…®«ËñÛïè;??û©nŠ À¯-ø�ûiü3øeðß@ñeω†·¡ø¯PþÉÐåð½…׉fÖ®€•š+X4èçšr¢ ËyhÁN[Méýk·ÞooêÛýÇ©QX¾ ø� |Hµ¾›@ÖtÍa4»É4ëï±Ü,­aw –Ó(9ŠdÜ»£pr2mP §°QEdøOÇÚ�F¥ý‡­i:×ö5üº^¡ö Èî~Ãw<Ûyv²TÜ»‘°Ë‘�3G[_Ö¡ækQEQEQEQEQEQEQEùõÿpÿ“Ùø ÿbG�¿ô¿ÂµäëÿðWù=Ÿ€¿ö$xÛÿKü+^A@Q@Q@Q@Q@Q@Q@Q@Q@¡©[é6¦k©á¶…zÉ+„Qøž+ƒñWícð×Á›…ÿ�ü:®¿z8/æEú¬{˜~U•Zô©«Ô’^®Ç¡�ÊqØér`¨Î£í¹~Iž‡E|çâø*/ÂýöX>½¯HNÔZy@Ç·úãý? Çÿ†øñÇ�¸ðwÁ�_FÜ¥Ý瘑cßl{ò'jó¥ž`Så�NgýÔåù&}� xªpU*á(÷«(R_ùRQ>¤¢¾Zÿ„‡ö¨ø„?q£ø?Áq? $�Ž£Ô‚óüt}(?±ÿÆóâ¯�W¶jüÉ�Š�ê>SÇüð©þÕ©?àЛõJ+ÿ&kò6ÿP0x}s,Û Mv„§Z_u(IäÇÓ߈ôÿ Úyú•ýžŸ?¼¹�bN=Ø�^qâßÛkáW‚Ë ¿hó2ÿ ‹5é'Ó÷!ÅyÞ‰ÿ³ð*Ý‹­wXñWˆ®�ß7‹?ýò»ý޽Â_±G¿”6ž Ñ¥dèרק>¿¾/G´Ìê|0„=[—ä—æQà\&•±XŒKÿ§táJ?}IMÿ䇜kðTÏý¬Úè'мGuü" E�~-¿ÿªð×_ü}ÿ"§Ák«cˆåÖd‘U½Î ÿ�cÞ¾™Ñ|=aá»A�cgaÀÛB± ÇNW(ú–6ÅÄ[ü1Kñ|Ì?ÖžÂÿ¸dêmlëV©?¾0öQùj|1ûM|Jý¦¾|/>$Õ.ôû¦½·ÓâÒ4(¡–úâYÜ",yVç'´ žÙ¬Û_��ñ[Åÿ muï‰Þ.ñ¦�ñ^ÊòúÞê+¦�lá‚Ù' åÈe [vÒ>M¤ }?û_xsQñ7…¼›a{¨Ikã�îe¶�¥0Ãìm$¬"¨%˜ðÉ5áúwÁ/ü)ÿ‚€ø?DÓt�Jïá½»k¾!ѵH­Y­4 o­˜Oa#€U\)–=ÝEÁQ÷1S –„£']Ê£»·4ŸHÅÚ×K[·µ½ÛuOóš eÂŽY_ÙQ§¬š¿3Œ§¥—Ú¾·ÙG€¿dÿÙ¿IøŠþ�VÐ ñn›ñÂÞ%Óo5-B}â;:kkï:÷SME�[Mç'œG•+K'Ú¶•æM½~“ðkX‡öƒÿ…xÚN¢¾²ñtŸ#¿n¶2+�5,<ÜÌ‹©»ÜlÎàˆœcšô0ùv‹^ÊZî’Õ.]W]œšÿÖú/™ÍøÃ=Ìy–;V§”§&—Å¥¯ÊµQZ/µµµ~þ>6x0üH> ÿ„»Ãð—…Üt?íH?´€Ùægìû¼Ìlù¾ïÝç¥3â—Ä+¯‡ßðŽý›O³¿Ö·m¤ÌnuXl>ËÛ�•<ÏõÎ…>wÏ+ä/ˆxçÇÂöüÙxóH¼¹Ð4¿g�$bò¾Ðv¢Ž¬xÍuS÷£NoíJ+äù/ÿ¥4ý©íó•}×R+¤dþk›ÿ‘Mz­Ö¯¬¾ø÷à]3ŶšÏ�<'o®ßÜ5¥®›.¯n—w3+mh’"ûÙà ��N1VuŒ^ðÿ�l|+âŸYx£SA%ž�q©ÃýÚ�Øhàf8ù�§î·¡¯™|uð_Qºýœ?h%‡Âw²kz÷Že¿²TÒØÝj)¶m ÑáwÈk”uÈ[ƒZßíu~Òw×~ Ó|_©ê>(×ôÆ×|=«øNKßjqÆ-PjVúšÇåÙË1IÛç‡ÅªO›Ù©o%ÿ�(»|®ï¾ÛZí ëSû­¯¹µš^[ïµýgá'í‡ðÿãgÄ¿øGBñ—>½áKÖ²žÓíöÍ-æÈãy&�Fw‰ ž[1Q¶Du#Šè¼ñïÀ¿4ýNïÞ4ðŸˆ-tEߨͦêö÷qØ.æf�ÈŒaXüØû§Ð×…Ù7ˆ>ø³ö’Ó¬|!©kº÷‰'“]ðõµö‹q&�­§ö=¬"¹ ös™bdhZEvã5çÞÐI°£ÊÝ8«c.¿ÞËøAÖuŸØ`ü!³ð‡‹4ߊZg…u-9ï.´I­¬íu ±Éq¥*­¼†æG8h%‘ÛÏ,À(�®µÿw)ÅkËû{}Vú++﹎^Ö0”´æ·ÊößïvÛmO§ü ñ¯Á¿.µ8|5âß øŠm‚j1隤m`İa%ïcî·¡©~ü_ðŸÆ>âïÂ^(ðïŠmlåò.&Ò5(o£‚Lgc4LÁ[pkå߇ ôˆåίÿ ïÄÙøó׺n¥á‹M ,mgHCØÛ•±³ûLß»o+Êy£_-òG˜žg¢þÆú¿‰'ñ‰tûÄÖu¿ év6vú?‰µÿ Ëá½j蜛â’(MÂ@Œ…fH"Oß0Ã6öªå´œoÓõkñI5Ó[&칎gÊ¥kk·mêÝú¤“µ›·®øÿâï…>ÇdÞ)ñ?‡¼4º”¾E™ÕulÅÜœ|‘ùŒ»Û‘ÀÉæ¼ßàOí1­üIðßùõ #Fó\{²ª‡pS‘µˆ Ö7~ÏŸ×O›_¢—£ZläçuS•m§â“ýZõOÎßMxCã„~ ø‹TÒ4øs[Õ´'1êVZ~§ ÍÆžÁŠ•š4bÑ�Êà  ŽÔžøÏàÿŠ·º�·…üWá¯\hî#¿‹KÔà¼{%€YDlÅ (àÇÝoC_"h^ñž¡ð÷Å? þZø¶÷ÿð‚ê–:=׋?Ñ|_¥iw‚üQáë�C‚ÒY5o \iI§~Å“¢yØ(Oú?™ò�.7&í ¹•ü¿Y+ú{««ø–»s9é·»áü_3í³Ó{pþ$ýµ>ßûOø«Á>ñÃÖ_è‹s¥ÞÝÇý£â —O%¤}¥×ÈX#2´‘K³ÍË*�“Üx#ãÆ©âß�ú‡¥²Òí´ícÀÉâ‰7îHnâ8ü´�Ê’ ®pÊ¿6ƒŠá|Aàmno�´=âhú£Zk~ÒìôéÖÒCüé¦([‘ÔÈ€ªä�ë‘È®Ä?¼m⯠æhºn­§j÷Nƒmq,/n°j,bÙ¤`9x?+G$ã’“‹Õ^Êþ¾ågùÆ=ú[ÍÕWŸ*vNß-hþ’—n·éo¨< ñ¯Á¿.µ8|5âß øŠm‚j1隤m`İa%ïcî·¡©~ü_ðŸÆ>âïÂ^(ðïŠmlåò.&Ò5(o£‚Lgc4LÁ[pkå߇ ôˆåίÿ ïÄÙøó׺n¥á‹M ,mgHCØÛ•±³ûLß»o+Êy£_-òG˜žg¢þÆú¿‰'ñ‰tûÄÖu¿ év6vú?‰µÿ Ëá½j蜛â’(MÂ@Œ…fH"Oß0Ã6ö­ùm'ôýZüRMtÖÉ».hæ|ªV¶»vÑ~­ßªI;Y»zÇÄ_�~ ø@ö+âßøgÂíª3%˜ÕõH,�Û.ÝÂ?5—y—;s�ÃÖ¤ñŸÆ |8‘WÄ>(ðî‚Î¨ÊºŽ¥ ©`ï±ÞÆ”zž5ó¿í­{â»ßx�Hµ´ñ¾�¤j~ 6ºmÏ„|)­qâ[ÙtkË™mnÚÃFW{@Ú%o7Œ-ÏÙ¯áf¥kñ�áN­¬ø{PŠmá¶œnï,�M…á–ÜIv$ÛCe¸Æj){í_kþ•þØ¿ð%¦×ªÞæÛÛóp_û{ÿÀ^»ÛÞ®~3ø>Ïâ4>—Å~‹Å× æE¢>§ê2®Âû–Ü·˜FÅfÈ^€ž‚¶µí~Ǻ%Þ¥©ÞÚiÚu„-quwu2ôj2ÎîÄ*¨’IÀ¾2ðÁmE¼eªxcÆš§Æoí/øXÒø’ÎÃJð多-êý¼\ÚÞj5ƒyj°ì+Þ¬¡bx•yHÛé/ڳš_�~ j6Ì>+“O76“¼ž¶ûV¥fñ\Å,wòC/–èŽPE)`§¿JWýÔjwÿ$ÿ ´öÛVµJ­ûÇ‹üßæ’k×KõÌø¡ûoü2øWðÃLñ�Ç‹tSÃÚÆ«‘iy¦ê–“Ã4²L±3,†UB‘n/) J"9Ç®£Ä_´GÃÿø?Lñ­ãŸiz·�ìíNïZ¶‚Îÿ#pòeg 'Ÿ”ž+æÅ㿉²þ½qªèš¶³&…ã�RµÕ!ð”úF±â›K«šîm4¨›ÏEŽDùc_1`R‘*íèÇ�—µB|BÕ<;ã=Cž ðm¾›£ÞYø[Q½ºÒçK©e¸‚k8àk«c2½»eâ¾Í† ªôå×vþåȤ¾ù^+mtÕèOšè¾÷Ïg÷+7åØö|_ðŸ„4 mWVñG‡t½.öÜÝÛÞ]êPÁo=é×�üYãþ$i–K Im ¾§o¨C�ä-´· x®Y¸xÁóC!flW—À^$×àŸ©á�'Ó|YuàÓmc™�¬¶÷m`# ÄàÆÁøÉÁS銎f¡R{ÙFßù=×]tIïÐisU§ì�ïÿ’Yújíóùw~ øëà�‰^#¿Ñü9ã ëú¾•¸ÞØéºµ½ÕÍžÖØÞdq¹dÃ|§p¯.§áÛ�&Ïû:KvÓã’dD¸ó%hWÍ,xƒy8[À_õöcø7o„ïlõ};âDZµôK¥´w6ª×÷BK™FÐÊ ,»¤ledàÖ²IN1NéË–ýï$”—–·ß£èÔŒ¹ß#•µår·k&ù_žŸ�•ŸÕ6ñæ‡ðÓÃsë>#Öt­HµÚ'¾Ô®ãµ¶‡s]ÒHBŒ±dòHç??jM/ÁÞ2øq-¾·ágðGŒ#Ôno5ÉoÚÅoohfIc¸‚êÇpÇLu¨ÿl ÙëþðýÍÅÏŽ´«ÝW†™¬x[F:ÍÆ‘v-ç�%–Íb�¦ˆ‰¾ ƒ˜þøó_è)ø¡ã/�Zß‹< ¬Ú^³â+ˉ!Ф²�G/Ùoå¶“{YÍ>D›$mâIHb@Í]©wM}Ú~>Zi¯{iSÝå·_ëîó×ò¿Ð²ühð|Œ_Å~O” qµ8šT¿–í¼¼ùs»ïqÖ–Ïã'„uÙøŠßÅ^ŸÃúƒµÕ#Ôák;–²`Ûü�À'î·¡¯™5=+Å |;â§è:^|\½¼¹Õ4ß SSÑ´ù-÷}ºÂÐÁ6ó$ÇË2¬2€³ÌÛO,¼·ÃÏ„:÷‹~Ko¬x{Æšµž¡ñÆÓ\ÿŠ‹D÷—šyXí“Á1Æ‘’77îÐ)ÈuV ¡Óýç-´¿'Ë™ÓÓåÎû|/Mìª>H¶õ·7Ï—žß{‡ŸÄ¼¯õ–µûJü9ðß„t½Qñÿ‚¬4p²éº•ιk¦ W†ÊÎL`çi8Åiø÷ㄾCc'Š%é×ü�msá/ CâÞ¥Äísk<-a{$K"Kˆûf%T þð,?³ÇÆÝêžñ÷ˆ<)?Ã+O i+&Œu«Û9b�Úk;Áj¯O4o20X �ƒ:„"ù’}Ûü9´õ|©/ñ-—5IZë²_;ò꼕ÝÿúÖÞçû.üVÔ>6|Ò£¨xkÆ~ñ†Ž»¯îtÍ^Þî´¶exÜ„U�ÌGžÕòõ¯�¼Auû$|Cð >ñ-Þµ¤øÒóY½ÓfÒg·‡^Òß^kÇŠÚâE÷>u®åòÒS�Û[±]/Ä [¿�u?øWÞ+Óô­'áγ¢_Üê^»Ò&Õî. Mke1Ç<Þ^ÉŸpŒ¢™B«v9Ôµ.x«û·û¡ÍFýß_’5Œ/S‘¿µo—2_“æôò»^ðÿ´7€#Ñ5�HøãÁãNðëDš­ÙÖm¼�0Ë�,NûöÄ_#nò7dc5e¾6x1~ÿÂd|]á�á »¿·©ön7ùyûFï/þ_½÷¸ë^â]#\øOû$üÒ´=TÑ­¬?³!Ö®ôŸjjþ‹ìOæOmd`˜ùæb"v0HQg”•ÎY|:ÓHñ¶�á�"K +Æ7~%Ô¾5Ýëšñ‰jš†¥ö<¯ö§±yl!*ÃqlIŒžvȦ3¼£j³¦¶‹µþp_ûsk_²Ö»¬iËš”*>ªöÿ·díÿ’Ûç{-ûkAý¤¾ø«Â§ˆ4Ïø/RÐtLhêVºÝ´Öz~zyÒ«”�?í]‚¼w¢|JðÕ¾³áÍgJ×ô{½ÞEö›wÕ´ÛX«m’2U°ÊAÁà‚;WÄ÷žÕõŸXÇe/�4ïx;âƒx�⦛¡Ù>¥§=í´²Gqib�¨A,Hg·�"5Ìœ;ÿ¬H~Èž °ð·â�NÂóâ£ÿ >¶Ú•Åç‹t¨t››É¼ˆbyc´Kkf‰–ù�#;£¿Ì¬®Ê*é¿%ø¨¿¹ó;?îõ»årºizþr_~‰Û]ýØø}ñ¢|Iø‘ãO²ð¯�nmôż�”y.…²ÜÝ»»6Ñ$Ð(àa’RXŒ¯á¯ÚÀ~4ðêêú?�¼#«i/–·¶zżöíw!Q°‘\¯šÅÐÎã½p9òïŽ|â|øóá�j3ëšoÄÔÕ¯-lR7¼»±i4ûæX£˜.øugâï…ßo¬,~/üK×|w§éÞ�þ¯ ®…—K$¢ÚQv6rÆ-L‚gºxöªª”²Xƒn•µåƒ·[¸Åþ-ò«ÚÏ©m{Íyµ%&¿¹�·]·>¨Ö>+x_ÃϨ­ÿ‰4 Ñä·†ü\jDl^à�˹†Ã! lÈÆjÿ‹5ßøEü+©ê~WŸý�i-×—»o™± mÎ3Œg¾iø áÙþ üñW†þ"øÄ>×t¯Ç.©}‰ý¥ÿ œ—5¾ª‹)ÚÞRùBE M¿ÙxáS?E|N´–ÿá·ˆ`‚)&žm2æ8ã�K4Œb`’IíQ‰n4e*nîÚà)þ½WªNèxkN¤TÖ—ýmý[äÚÔðoÁEmüiûꟄ®4ýcAšÞ KÃwàIlÓ¼S ü¿š7†â)•ü±¸6=ë·×þ:øãRøåâ?ø?Á¾Õ“Ã67—WºÇŠn4Ã!ºó¶ªG…Îvù-’XuWË_´wìûã� ~ʾÕü)áÍsQ¼ñ/…t xÇ@¶±‘ï#6þLÖצ »ÃÀë,2 gdù?êøúÙî/ˆ_¶OÄ]cY�ÆÖÙ:$vZf¿ªh¶×ŽŸkóT›Y¢IÊe2vÝÝ·sÕV1U[)5÷ŸÅ¿[_FsRœ�;Ëv¢þ÷új¾W¶¨ß»ý¤¼O¯|[ñ?†|#à�?Ä àF²�_’çÄÂèËqŸË²… e›CO-º3¡€ ÃØÁÈôö¯•lßéž"×õmC¾ø›¢üpÒíVÏÃ:ÿ‡ô‹ÈâÕ h¥†;›Ø•¬d³2 Y#¿qµRCµC+7»ü>ø½‹¼y¯xIí®ÿ¶¼#i`úµÚF¿`iîcgòbpÅ·ª¨fVPBÍä5ea~¿ÖÞ�Vñ½�÷6’³þ¼–¾·Ñìõzþ#üeð‡ÁËKYü]⯠øV ç1[I¬jpØ¥Ã�’¨ee @ì)ð˜ð¹ÖdÔõK¥´ÙroÝË#¬aHn2[=«Æ§‡8Ã1ÉyéPjtj|t¦¹©ÏüQ}{IZK£>ý˜?à¾ño‰ÿá øß¡êŸ¾"Y°‚êÛ]ŠHôד¦DΡ “ûåU‘«ï [³ñ&“o§]Û_ØÞF%‚æÚU–)ÐòYI b y×í5û|0ý±|/ý•ñÁº?‰b� [ÜMŽòÏ?óÆá Ë?Ý`pká]WþÁñ¯ö.Õî5ŸÙK㦭¥ØLçÂ>'“̲—¹¶¼1äð+ÿ-3ÍqûL~J‘ö±î´—ÍlþMzLð\%ž{ØJ¯/®þÅK΃ݨ“œøã$¿˜õoøgâÖ¿ÿ=ý¥›á·�¾xNÞ;‹äñ7‚oW‰ây!vÆÍŽ„©«²ž ÃIûü—â­ßŽÛáGÃVñ¾¡²:­Ì±I‘È÷^WšÌ’C«Èh�ŽT`¥îÕ„¥¬S»]ýè¶½R^WºÒéº©ÊœÒøœlŸm'g꜓ó³êÓ_ø·Äóø’"“[é6Ë¥þØÖV0‹ .ÚÇz+@Ûåò#O:R]·M.é�Ìp1ÞüIý¹|gá/�—ßð˜ÙYx�ÿi/‡6«-½˜–M6OÛÀÖ &ΜÒ6í¦_/2ÈÞ>Åo‚^ mÙð�†ýhx‘³¥Aój£¿?/ü} «‰¿Öp>n+Sý“>ë_o|qyðÏáýß�u/³}¯ÄxvÎMRëìÒC-¿™rcó_Ê’ÚÝÓ,v4ÁE"pKØû5=TyoçË:ø±’~Uú¦«GžU$´ræ·•å]¯»ÚÅúÁm£_ xÓöºøÛàOÙCã‡ÆøX:¶³sáï‰z·ÃïøzßDÑ¡ÓôkøISMŽþG™#y¯-áwØó]ÁjBÅ磒gú/ö ñÇñÿŒ´¯‰Z'ÅøCcµ²¼ðþ³ñ¼&ºãݱ•.í xvf¶kuT·’7x£“tÓ)g1ïv <-§ø_XÑ ðÖ�‹â®®5]>=:µÔäºfk§ž0»ei™ÜÈ\嘶rkžøû&ü+ý˜R? ~ü?øxu¯,jÃ>³Ò~ßåîòü߳ƞfÍï·vq½±ÔÓ£îÅFZµ¯V¢“ÿÉ“•ïw{=uÚœ¥(éy6¼“•ÒùGݵ­Õjî¿,¾*<ß²Tµ/Ç�:ø¦üCãÿü>ø“Q’M•Åœ ¥jl’Ö—³˜œâ BbÕ®=÷à7Äÿ|ZÒ|%ð¿Hø¿}ð7Iø{ðÂÞ0‡PÓtý&êë[’òÞxd¸ŸûNÞxÅ�ŸØ“x…cb×_4è©û²ãàσï<1â-_ xn]ÅòÏ6½`úd k­É:�î£+¶v‘W2,"¹ï‰±ïÂO�:?‡4ÿü-øsâËÇäè6Úφ¬¯áÑj.ÛT–6.Ø¢Œ(Äh?„c*4Ü0þÆúòÒŠ}¹!8½:ûórWôÒÉ¥SÞ¬ëun£ÿÀç /ü’�¢Ñ¤õOá�ØãÅ>6ý«ÿà ~'ø‡Æþ&ðž¯â߀v¾#Ô|;¦Ùi‰¦Î?´íĶȷ6rÝ-¤ÌDÇù˽B΃�ðŸÁúÿÇ?~Ä´:Ö| çÆ¾)·KO h:�•»Åm¯âxà“N‘#‘¢ŒÂÛ@R’ÈÛD¤J?GüqðÀ¿|aá¿ø“Á~ñ¿àÙšã@Ôõ="ÞîóC”•%íe‘ ÀÄ¢dÆTü‹è*Æ‹ðgÁþ³Ðíôï xjÂßÃ]èÑ[i�DšLÓ im¨<‚iƒ2`°•òNã�ÓIEE[–Søå5÷)ZÛi~¦2¦Ü§+üQKî„aÿ¶Þûëg¶¿ é_´GŒ4mW_ð‡‡µ}3Àâí+ªø&çÅ:f…§E>�j4ß¶Eh ¼××2À¶ë=ÜS’Ó�ÁÈ@;ÿ�¿¼YðöÛÀÿ ´‹Ÿ~"xïU¼Öeiü øQ|S}k`ñ‰EÅÆ¨Ñh°cqsªÛ¬Ò´‘âˆ$¤ýAâ�ÙÓá÷Ž<¯økZð'ƒux²ðê:æ•{¢Û\Yk7E‘Ì÷0ºšRÑFw¸-˜ÐçåÀñ?ìAð[Æß t/ë?~êþ ð»´º6�{á[�3Hvݹ­íž#$îl”Q�ÇÔÖ0ƒT¡M½R‚o¿,!ÿàQm¯´¥kÅݽݽ£ŸFæÿð)ÎKð’�÷ySW½—çÏ¿ˆþ5ý¹,ÐüO«ø#BÖôŸ‡‹ñ'ö“Ö|7Š´ÝM‚ãH‚=(߯–�¶žþê[qn³ÝÇ;“6JÊÊŠ>ÔñOì™ð¯Ç>Ó|1­|3ø¬xoFÔVÓô›ïÙÜXØÞ³Èís/D˜¼²±‘@bdsœ±Íÿ~Ο¼qà�ÃZ×�<«øsÅ—‡Q×4«ÝÚâËYº,Žg¹…ФҖŠ3½ÁlƇ?(Æš*Ò¨–�5m·�9oé +l¯Ù´gÈùo­ï}íîTŠõ·4eÓ™­z3â;ŸÚ?ãA×õ?…šoʼn¯.´?�¶^‡â ¾Óf¿Ô4Û­ õˆ%†8’ÄÞÚÊæ,PÆ¡¡ˆÉbT—è?ø'×Åxªÿã?ƒ¼eâ‹Ï]ü)ñü¾°×¯ìí-/õ+7ÓtýB3r–‘CoæFoš-ÑCe‰I]Ä“ëžýŸüàèмá@ð¥ÏÛtM2ÃG·¶³Ñ§ýçïm¢D þö_™?¼~~c�¯xEðŽ¡«Ýé:>—¦]x‚óûCTšÒÒ8dÔ®|¨áóçeÉ'•I½²ÛcEÎèûœÊzÝ[çjJöéðTv_Ï×V]_——K�KúüPò÷tIY´QE (¢€ (¢€ (¢€ (¢€ (¢€?>¿à®ò{?ìHñ·þ—øV¼‚¿I~9þÊ? jÓÄ¿†¾ø†4C)Ó‡‰¼=i«}ƒÍÙæù?h�ü½þ\{¶ãvÅÎp+Ï¿áÓ¿²ÏýOÀü7šGÿ#ÐÃtWÜŸðéßÙgþ�§àþÍ#ÿ‘èÿ‡NþË?ôm??ðÞiü�@ Ñ_rçeŸú6Ÿ€øo4�þG®Æÿ²gìðÏwü$Ÿ ÿd/íê5/øv×?¿þòþcÖ¦SŒäìo‡Â×ÄK’„Ÿd›�òÕè9ø—ÿµøzí¾ ý—îJöÓ>ÙjYñN/Z}f»ºrKï’Kñ:J+οáü%ø™ÇÃ/ø%o…üOœÇ{uðîÂ(•z‚ÞV›"r?é äŽOsþãŸÆ“ÃßðNÿØ÷À¶Rü¿kÖ<¢´ð“Æv¼€ñÉÿPz~íÌ4¿„¥?HÉþ–=Oø…™Í/÷ùÐïúy^”7/Âç¢Ñ^vàߎ?|E¤~Çþ±—Ÿ³h¿ 4S<=ñ¹4µ>ƒýqè‹áçü+à­?Pû_‰>%¤’HwI�á]0/NŒ’ßÜN9?´q3þ_öóŒVÿÿRò\?ûþsE>Ô¡V¯â¡ÿäß2׉>!h> V:¾·¤iAFXÞ^G3Îâ;WŸx›öçøOá@ßhñ®—9âÉd¼Éö1+Ö¾½øuÿÕ~Í ò¿´í|mâí˜Ü5Mp¯ÎqþŠ�;pzwÏ5î¾ÿ‚GþÍ àòôÿ‚¾¹]»âm§�X‘Ï{£)ïþqG>i=£z·/É/Ì>­À˜m*WÅb÷aN’9J£ÿÉOÉ oþ �ðòÚãÈÒtÿë· HAmd¨¯ÿ}¸nŸìÕ#ûs|HñŸËáO‚~"‘î]_´©ÿÈj½?ÛïùþÇ_Á,f-RM÷?³—À{‡ëº_i.3Aÿ�ý–èÚ~á¼Ò?ù�©ãçüLE¿Ã¿pÿYxSþé”sµ³­^rûãMS_+³ñßûoö¨ñÿú­+Á¾ �þã»G#(= ùæçþøRÿÃ#ülñÉÿŠ›ã]Ý€~d�GŠEVÏQòF? {v¯Øøtïì³ÿFÓðÿ æ‘ÿÈôçeŸú6Ÿ€øo4�þG£ûœ¿�Rsõ“KîVø‰˜Ê?ò.Áá°ï¼(BRÿÀªûGøŸ�Úü×ÂW÷KsâOøÃÄwC–in‘ýs•gôþ:îü-ûü%ð˜S„-/$—¾ž[­ßUv+ù_¨ßðéßÙgþ�§àþÍ#ÿ‘èÿ‡NþË?ôm??ðÞiü�[Rɰ4ÝãJ7óWüîyøïø«JØúª=£'÷C•~À^øwáÿ ]BÑô€: +(íÀÿ¾VÍ}Éÿ�ý–èÚ~á¼Ò?ù�øtïì³ÿFÓðÿ æ‘ÿÈõèF1Š´U‘ñ•«Õ­?iZNR}[»ûÙðÝ÷'ü:wöYÿ£iøÿ†óHÿäz?áÓ¿²ÏýOÀü7šGÿ#Õ Ñ_rçeŸú6Ÿ€øo4�þG£þ;û,ÿÑ´üÿÃy¤ò=|7E}Éÿ�ý–èÚ~á¼Ò?ù�øtïì³ÿFÓðÿ æ‘ÿÈôðÝ÷'ü:wöYÿ£iøÿ†óHÿäz?áÓ¿²ÏýOÀü7šGÿ#ÐÃtWÜŸðéßÙgþ�§àþÍ#ÿ‘èÿ‡NþË?ôm??ðÞiü�@ Ñ_rçeŸú6Ÿ€øo4�þG£þ;û,ÿÑ´üÿÃy¤ò=|7E}Éÿ�ý–èÚ~á¼Ò?ù�øtïì³ÿFÓðÿ æ‘ÿÈôðÝ÷'ü:wöYÿ£iøÿ†óHÿäz?áÓ¿²ÏýOÀü7šGÿ#ÐÃtWÜŸðéßÙgþ�§àþÍ#ÿ‘èÿ‡NþË?ôm??ðÞiü�@ Ñ_rçeŸú6Ÿ€øo4�þG£þ;û,ÿÑ´üÿÃy¤ò=|7E}Éÿ�ý–èÚ~á¼Ò?ù�øtïì³ÿFÓðÿ æ‘ÿÈôðÝ÷'ü:wöYÿ£iøÿ†óHÿäz?áÓ¿²ÏýOÀü7šGÿ#ÐÃtWÜŸðéßÙgþ�§àþÍ#ÿ‘èÿ‡NþË?ôm??ðÞiü�@ Ñ_rçeŸú6Ÿ€øo4�þG£þ;û,ÿÑ´üÿÃy¤ò=|7E}Éÿ�ý–èÚ~á¼Ò?ù�øtïì³ÿFÓðÿ æ‘ÿÈôðÝ÷'ü:wöYÿ£iøÿ†óHÿäz?áÓ¿²ÏýOÀü7šGÿ#ÐÃtWÜŸðéßÙgþ�§àþÍ#ÿ‘èÿ‡NþË?ôm??ðÞiü�@ Ñ_rçeŸú6Ÿ€øo4�þG£þ;û,ÿÑ´üÿÃy¤ò=|7E}Éÿ�ý–èÚ~á¼Ò?ù�øtïì³ÿFÓðÿ æ‘ÿÈôðÝ÷'ü:wöYÿ£iøÿ†óHÿäz?áÓ¿²ÏýOÀü7šGÿ#ÐÃtWÜŸðéßÙgþ�§àþÍ#ÿ‘èÿ‡NþË?ôm??ðÞiü�@ Ñ_rçeŸú6Ÿ€øo4�þG£þ;û,ÿÑ´üÿÃy¤ò=|7E}Éÿ�ý–èÚ~á¼Ò?ù�øtïì³ÿFÓðÿ æ‘ÿÈôðÝ÷'ü:wöYÿ£iøÿ†óHÿäz?áÓ¿²ÏýOÀü7šGÿ#ÐÃtWÜŸðéßÙgþ�§àþÍ#ÿ‘èÿ‡NþË?ôm??ðÞiü�@ ×)ñWে>4Ùiñx‚ÎêWÒ.~Ùaue¨\é×¶3md/Í´‘Í(̧k�À�r+ô?þ;û,ÿÑ´üÿÃy¤ò=ðéßÙgþ�§àþÍ#ÿ‘èµ÷óÓágÁß|Ò.¬ü?e5¸Ô.Zöòâêò{ëËéØdžâwy¥m¡Tv!UT`O_rçeŸú6Ÿ€øo4�þG£þ;û,ÿÑ´üÿÃy¤ò= ·¸é~Ò4_jÞ!µ³êúäö÷ó¬�‹”ƒÌò²™Ù¹D®7¸� ’@د¹?áÓ¿²ÏýOÀü7šGÿ#Ñÿ�ý–èÚ~á¼Ò?ù€>¢¾äÿ‡NþË?ôm??ðÞiü�Gü:wöYÿ£iøÿ†óHÿäzønŠû“þ;û,ÿÑ´üÿÃy¤ò=ðéßÙgþ�§àþÍ#ÿ‘èẩ¥h:ݵ••¥›_În®L¬fæb™nrAcÎzWÝÿðéßÙgþ�§àþÍ#ÿ‘èÿ‡NþË?ôm??ðÞiü�@ Ñ_rçeŸú6Ÿ€øo4�þG£þ;û,ÿÑ´üÿÃy¤ò=|¢ü>Ò<=ão_³´òu-ºê3ù®ßh+$_)%Wj³” çœÖÕ}Éÿ�ý–èÚ~á¼Ò?ù�øtïì³ÿFÓðÿ æ‘ÿÈôðµà'Ä^*ѵ»Û1q©øxÌtéYÛ­2l‘‚gib™]Ä0nlìWÜŸðéßÙgþ�§àþÍ#ÿ‘èÿ‡NþË?ôm??ðÞiü�@ Ñ_rçeŸú6Ÿ€øo4�þG£þ;û,ÿÑ´üÿÃy¤ò=|7E}Éÿ�ý–èÚ~á¼Ò?ù�øtïì³ÿFÓðÿ æ‘ÿÈôðÝ÷'ü:wöYÿ£iøÿ†óHÿäz?áÓ¿²ÏýOÀü7šGÿ#ÐÃtWÜŸðéßÙgþ�§àþÍ#ÿ‘èÿ‡NþË?ôm??ðÞiü�@ Ñ_rçeŸú6Ÿ€øo4�þG£þ;û,ÿÑ´üÿÃy¤ò=|7E}Éÿ�ý–èÚ~á¼Ò?ù�øtïì³ÿFÓðÿ æ‘ÿÈôðÝ÷'ü:wöYÿ£iøÿ†óHÿäz?áÓ¿²ÏýOÀü7šGÿ#ÐÃtWÜŸðéßÙgþ�§àþÍ#ÿ‘èÿ‡NþË?ôm??ðÞiü�@ Ñ_rçeŸú6Ÿ€øo4�þG£þ;û,ÿÑ´üÿÃy¤ò=|7E}Éÿ�ý–èÚ~á¼Ò?ù�øtïì³ÿFÓðÿ æ‘ÿÈôðÝ÷'ü:wöYÿ£iøÿ†óHÿäz?áÓ¿²ÏýOÀü7šGÿ#ÐÃtWÜŸðéßÙgþ�§àþÍ#ÿ‘èÿ‡NþË?ôm??ðÞiü�@ Ñ_rçeŸú6Ÿ€øo4�þG£þ;û,ÿÑ´üÿÃy¤ò=|7_rÁ'å_³Oý’¯ ÿé¢Ö�øtïì³ÿFÓðÿ æ‘ÿÈõî>ð¶™à_ iÚ&‰§Xhú6�k�……�º[ÚØÛÄ�#†(ÐHÑUU@ (ýQ@Q@Q@Q@Q@Q@Q@Q@Q@^ÙC©YËoqsÁ:äŠE ’) ƒÁv¯–?h¯ø"�ìÝûI›‰õ‡Z‡5[Œ“¨øiŽ•2±êÞ\¹v=IxÛ'šú²ŠÂ¾�xòÖŠ’óW=\§=̲º¾Û-¯:Rï 8ýözú3ó(Á>2þÌãÌýž¿jx~Öõˆ<Ʊã¦óhOþ÷?J•~8ÁHfØöøƒá§ÃߌšlßirEܘþêE,-Ï'þ=�áÒ¿L(¯;û”?Ýç*~’ÓîwGÚÿÄOÇbäs†¡‹ó©I)ÿàtù%ómŸš–¿ðq#|&¼ŽÓãoìïñ_áœÙÉ*ÁöˆËtÜÊ[§¯¸lÿðnƒà7øV¿~1x¢-êJ’?qö~9oûë¿9>±˜Ã⣆_æ—æ/ì~ Äë‡ÌªÑò«C›ñ§R_úIúME~l·ü/ö©ð1φ?m�êC¨]nÚíöäò2÷Sç p;ð)á™ÿà¤Þ9Ó~=ü,ñ=°êoì"II'““§gùéÜñÞŸö�xÿ?—+ü¤/õ+­þçœáßøÕjúU/Ôý%¢¿7Æ?ðS_ ùJ|)ð[Ä_9vs4I€1òŸôˆ¸>Ã=yéM?µÏü‡Ãгj³WÃ;Ð϶3a©Äq×9UÕ$8÷â�íˆ/Š•Eÿn?ÒáÿÚ¼¿…˜a%ÿs4×þ•Ê~’Ñ_›’ÁMm 4ÇWýŽ®ïRµ†�¨JĶ@ÈØ%Ü:ô×é(K^ø’ÎqÕäø¯4ñgü÷öeðcyñ£ÁSmëö ¦¿îGü°WÏOËž•å¾ÿƒv?e} óuàÍoZ‰ÅçˆïTF1û™#àuõÏ·éžÿ‚:~Ì^ ìŸ|6?çú)/ý?绿§óõ4¹³Imkç'ú öKIVÅÕ~P¥øÎoð<ÛÅðqì¯áÌý—ÆÚκGkß/¯üöŽ1Ûõç·ÿðsgÁÝVìÚxWáÿÅß^äH4»TG-÷@ÅÃ>K`}Ϧ{ý·á?Ø÷á'€˜áoÃ�¯Caá«+lrðF;€~¢½OÓ­ô›4·µ‚h#ÎØ¢@ˆ¹98ޤŸÆŸ°Ì¥ñVŒ} ßç þÖàšRË«Õÿ¯˜ˆÇð…%ùŸ›Çþ •ñ‹ÇƒýŒþ,k1H2——fê(TpFvY2òËAÛéKÿ Ÿÿø’1á¯Ù£ÁŠO”Í®_®øAÈ݇½„ä¸Ý:×é5¿³±2þ&&_%ú0ÿ]2ZîY-ÿéäëTüêE~æÈðüÓâyËøßàçô�å•-á�¢ýëžHëü={”ÿ‡YþØßÆ<_ûeêú |oÿ„vÒá6ñÛÊ’×û«éÕ¿Òz(þŤÿ‰9ËÖrý,ñ±ôÿܰ¸j/¼0ô›ûç³ó\ÿÁ¸�¿ä¤ü|øÉã}ßý28·ßñqèŸ÷ϸÇàø7;ö\ð‘S}áø˜¯ýüAr™ë×ìí¨ü‡¾~颮9.þÉ?];˜bO�æ8ß÷ÌDêŠr—æØQE±å…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÙ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.435284 masakari_monitors-22.0.0/doc/source/conf.py0000664000175000017500000000560315257164053017517 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import os import sys sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'openstackdocstheme', 'oslo_config.sphinxconfiggen', 'oslo_config.sphinxext', ] # sphinxcontrib.apidoc options config_generator_config_file = ( '../../etc/masakarimonitors/masakarimonitors-config-generator.conf') sample_config_basename = '_static/masakarimonitors' # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'masakari-monitors' copyright = '2016, OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'native' # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] # html_theme = '_theme' # html_static_path = ['static'] html_theme = 'openstackdocs' openstackdocs_repo_name ='openstack/masakari-monitors' openstackdocs_bug_project = 'masakari-monitors' # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # -- Options for LaTeX output ------------------------------------------------- # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', 'doc-%s.tex' % project, '%s Documentation' % project, 'OpenStack Foundation', 'manual'), ] # Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664 latex_use_xindy = False # Disable smartquotes, they don't work in latex smartquotes_excludes = {'builders': ['latex']} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1952207 masakari_monitors-22.0.0/doc/source/configuration/0000775000175000017500000000000015257164140021060 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.436284 masakari_monitors-22.0.0/doc/source/configuration/config.rst0000664000175000017500000000047515257164053023070 0ustar00zuulzuul===================== Configuration Options ===================== The following is an overview of all available configuration options in Masakari-monitors. For a sample configuration file, refer to :doc:`sample-config`. .. show-options:: :config-file: etc/masakarimonitors/masakarimonitors-config-generator.conf ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4372842 masakari_monitors-22.0.0/doc/source/configuration/index.rst0000664000175000017500000000110015257164053022714 0ustar00zuulzuul=================== Configuration Guide =================== The configuration for masakari-monitors lies in two files: ``masakarimonitors.conf`` and ``process_list.yaml``. These are described below: Configuration ------------- * :doc:`Config Reference `: A complete reference of all config points in masakari-monitors and what they impact. * :doc:`Sample Config File `: A sample config file with inline documentation. Process list ------------ * :doc:`Process list `: A complete reference of defining the monitoring processes. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4372842 masakari_monitors-22.0.0/doc/source/configuration/process-list.rst0000664000175000017500000000041715257164053024246 0ustar00zuulzuul========================================== Sample masakari-monitors process list File ========================================== The following is a sample masakari-monitors ``process list`` file. .. literalinclude:: /../../etc/masakarimonitors/process_list.yaml.sample ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4382844 masakari_monitors-22.0.0/doc/source/configuration/sample-config.rst0000664000175000017500000000105415257164053024341 0ustar00zuulzuul========================= Sample Configuration File ========================= The following is a sample masakari-monitors configuration for adaptation and use. The sample configuration can also be downloaded from :download:`here `. .. important:: The sample configuration file is auto-generated from masakari-monitors when this documentation is built. You must ensure your version of masakari-monitors matches the version of this documentation. .. literalinclude:: /_static/masakarimonitors.conf.sample ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4382844 masakari_monitors-22.0.0/doc/source/consul-usage.rst0000664000175000017500000001070315257164053021354 0ustar00zuulzuul============ Consul Usage ============ Consul overview =============== Consul is a service mesh solution providing a full featured control plane with service discovery, configuration, and segmentation functionality. Each of these features can be used individually as needed, or they can be used together to build a full service mesh. The Consul agent is the core process of Consul. The Consul agent maintains membership information, registers services, runs checks, responds to queries, and more. Consul clients can provide any number of health checks, either associated with a given service or with the local node. This information can be used by an operator to monitor cluster health. Please refer to `Consul Agent Overview `_. Test Environment ================ There are three controller nodes and two compute nodes in the test environment. Every node has three network interfaces. The first interface is used for management, with an ip such as '192.168.101.*'. The second interface is used to connect to storage, with an ip such as '192.168.102.*'. The third interface is used for tenant, with an ip such as '192.168.103.*'. Configure Firewalld =================== Before installing packages, you must open ports on your Consul servers. .. code-block:: console firewall-cmd --permanent --zone=public --add-port={8300,8301,8302,8400,8500,8600}/tcp firewall-cmd --reload firewall-cmd --zone=public --list-all Install Consul ============== Install Consul for RHEL-based distributions. Other OS please refer to `Download Consul `_. .. code-block:: console sudo dnf install -y dnf-plugins-core sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo sudo dnf repolist sudo dnf install consul Verify ------ .. code-block:: console which consul consul version Configure Consul agent ====================== Consul agent must runs on every node. Consul server agent runs on controller nodes, while Consul client agent runs on compute nodes, which makes up one Consul cluster. The following is an example of a config file for Consul server agent which binds to management interface of the host. management.json .. code-block:: ini { "bind_addr": "192.168.101.1", "datacenter": "management", "data_dir": "/tmp/consul_m", "log_level": "INFO", "server": true, "bootstrap_expect": 3, "node_name": "node01", "addresses": { "http": "192.168.101.1" }, "ports": { "http": 8500, "serf_lan": 8501 }, "retry_join": ["192.168.101.1:8501", "192.168.101.2:8501", "192.168.101.3:8501"] } The following is an example of a config file for Consul client agent which binds to management interface of the host. management.json .. code-block:: ini { "bind_addr": "192.168.101.4", "datacenter": "management", "data_dir": "/tmp/consul_m", "log_level": "INFO", "node_name": "node04", "addresses": { "http": "192.168.101.4" }, "ports": { "http": 8500, "serf_lan": 8501 }, "retry_join": ["192.168.101.1:8501", "192.168.101.2:8501", "192.168.101.3:8501"] } Use the tenant or storage interface ip and ports when config agent in tenant or storage datacenter. Please refer to `Consul Agent Configuration `_. Start Consul agent ================== The Consul agent is started by the following command. .. code-block:: console # Consul agent –config-file management.json .. code-block:: console # sudo systemctl start consul # sudo systemctl enable consul Test Consul installation ======================== After all Consul agents installed and started, you can see all nodes in the cluster by the following command. .. code-block:: console # Consul members -http-addr=192.168.101.1:8500 Node Address Status Type Build Protocol DC node01 192.168.101.1:8501 alive server 1.10.2 2 management node02 192.168.101.2:8501 alive server 1.10.2 2 management node03 192.168.101.3:8501 alive server 1.10.2 2 management node04 192.168.101.4:8501 alive client 1.10.2 2 management node05 192.168.101.5:8501 alive client 1.10.2 2 management ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4392848 masakari_monitors-22.0.0/doc/source/hostmonitor.rst0000664000175000017500000001331715257164053021340 0ustar00zuulzuul==================== masakari-hostmonitor ==================== Monitor Overview ------------------ The masakari-hostmonitor provides compute node High Availability for OpenStack clouds by automatically detecting compute nodes failure via monitor driver. How does it work based on pacemaker & corosync? ------------------------------------------------ - Pacemaker or pacemaker-remote is required to install into compute nodes to form a pacemaker cluster. - The compute node's status is depending on the heartbeat between the compute node and the cluster. Once the node lost the heartbeat, masakari-hostmonitor in other nodes will detect the failure and send notifications to masakari-api. How does it work based on consul? ---------------------------------- - If the nodes in the cloud have multiple interfaces to connect to management network, tenant network or storage network, monitor driver based on consul is another choice. Consul agents are required to install into all nodes, which make up multiple consul clusters. Here is an example to show how to make up one consul cluster. .. toctree:: :maxdepth: 1 consul-usage - The compute node's status is depending on assembly of multiple interfaces connectivity status, which are retrieved from multiple consul clusters. Then it sends notifition to trigger host failure recovery according to defined HA strategy - host states and the corresponding actions. How does it work based on kubernetes? ------------------------------------- If Openstack is deployed on kubernetes, just set host.monitoring_driver to `kubernetes` and deploy host-monitor pods. Then driver of host-monitor pod invokes the Kubernetes API to retrieve the status of the host(node). In case of any anomalies, it sends notifications to the masakari-api. Related configurations ------------------------ This section in masakarimonitors.conf shows an example of how to configure the hostmonitor if you choice monitor driver based on pacemaker. .. code-block:: ini [host] # Driver that hostmonitor uses for monitoring hosts. monitoring_driver = default # Monitoring interval(in seconds) of node status. monitoring_interval = 60 # Do not check whether the host is completely down. # Possible values: # * True: Do not check whether the host is completely down. # * False: Do check whether the host is completely down. # If ipmi RA is not set in pacemaker, this value should be set True. disable_ipmi_check = False # Timeout value(in seconds) of the ipmitool command. ipmi_timeout = 5 # Number of ipmitool command retries. ipmi_retry_max = 3 # Retry interval(in seconds) of the ipmitool command. ipmi_retry_interval = 10 # Only monitor pacemaker-remotes, ignore the status of full cluster # members. restrict_to_remotes = False # Standby time(in seconds) until activate STONITH. stonith_wait = 30 # Timeout value(in seconds) of the tcpdump command when monitors # the corosync communication. tcpdump_timeout = 5 # The name of interface that corosync is using for mutual communication # between hosts. # If there are multiple interfaces, specify them in comma-separated # like 'enp0s3,enp0s8'. # The number of interfaces you specify must be equal to the number of # corosync_multicast_ports values and must be in correct order with # relevant ports in corosync_multicast_ports. corosync_multicast_interfaces = enp0s3,enp0s8 # The port numbers that corosync is using for mutual communication # between hosts. # If there are multiple port numbers, specify them in comma-separated # like '5405,5406'. # The number of port numbers you specify must be equal to the number of # corosync_multicast_interfaces values and must be in correct order with # relevant interfaces in corosync_multicast_interfaces. corosync_multicast_ports = 5405,5406 If you want to use or test monitor driver based on consul, please modify following configuration. .. code-block:: ini [host] # Driver that hostmonitor uses for monitoring hosts. monitoring_driver = consul [consul] # Addr for local consul agent in management datacenter. # The addr is make up of the agent's bind_addr and http port, # such as '192.168.101.1:8500'. agent_manage = $(CONSUL_MANAGEMENT_ADDR) # Addr for local consul agent in tenant datacenter. agent_tenant = $(CONSUL_TENANT_ADDR) # Addr for local consul agent in storage datacenter. agent_storage = $(CONSUL_STORAGE_ADDR) # Config file for consul health action matrix. matrix_config_file = /etc/masakarimonitors/matrix.yaml The ``matrix_config_file`` shows the HA strategy. Matrix is combined by host health and actions. The 'health: [x, x, x]', repreasents assembly status of SEQUENCE. Action, means which actions it will trigger if host health turns into, while 'recovery' means it will trigger one host failure recovery workflow. User can define the HA strategy according to the physical environment. For example, if there is just 1 cluster to monitor management network connectivity, the user just need to configurate ``$(CONSUL_MANAGEMENT_ADDR)`` in consul section of the hostmontior' configuration file, and change the HA strategy in ``/etc/masakarimonitors/matrix.yaml`` as following: .. code-block:: yaml sequence: ['manage'] matrix: - health: ['up'] action: [] - health: ['down'] action: ['recovery'] Then the hostmonitor by consul works as same as the hostmonitor by pacemaker. If you want to use or test monitor driver based on kubernetes, please modify following configuration. .. code-block:: ini [host] # Driver that hostmonitor uses for monitoring hosts. monitoring_driver = kubernetes ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.440285 masakari_monitors-22.0.0/doc/source/index.rst0000664000175000017500000000450615257164053020062 0ustar00zuulzuul============================================= Welcome to Masakari-monitors's documentation! ============================================= Masakari is an OpenStack project designed to ensure high availability of instances and compute processes running on hosts. Masakari-monitors are the components which detects faults in the cloud. Operator Guide ============== Architecture Overview --------------------- * :doc:`Masakari Monitors architecture `: An overview of how all the components in masakari work together. Installation ------------ A detailed install guide for Masakari-monitors. .. toctree:: :maxdepth: 2 install/index hostmonitor.rst instancemonitor.rst introspectiveinstancemonitor.rst processmonitor.rst Deployment Considerations ------------------------- This documentation is based on the assumption that the basic openstack installation is done with DevStack along with Masakari and its services enabled to proceed for Masakari-monitors installation. .. note:: * OpenStack installation with DevStack services are installed using ``Systemd``. The ``systemctl`` is a tool to control the systemd and its services. This documentation refers ``systemctl`` commands for dealing with service related information. * If OpenStack installed with DevStack ``stable/train`` with Masakari service enabled in local configuration file, then it also installs Masakari-monitors along with it. Reference Material ------------------ * :doc:`Configuration Guide `: Information on configuration files. * `Masakari Docs `_: A collection of guides for Masakari. * `Nova Docs `_: A collection of guides for Nova. .. # NOTE(shilpasd): This is the section where we hide things that we don't # actually want in the table of contents but sphinx build would fail if # they aren't in the toctree somewhere. .. toctree:: :hidden: configuration/index.rst configuration/config.rst configuration/process-list.rst configuration/sample-config.rst user/architecture.rst Search ====== * :ref:`search`: Search the contents of this document. * `OpenStack wide search `_: Search the wider set of OpenStack documentation, including forums. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1962204 masakari_monitors-22.0.0/doc/source/install/0000775000175000017500000000000015257164140017657 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.440285 masakari_monitors-22.0.0/doc/source/install/index.rst0000664000175000017500000000021715257164053021523 0ustar00zuulzuul========================== Masakari-monitors services ========================== .. toctree:: overview install_and_configure verify ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4412851 masakari_monitors-22.0.0/doc/source/install/install_and_configure.rst0000664000175000017500000000072415257164053024750 0ustar00zuulzuul===================== Install and configure ===================== This section describes how to install and configure Masakari-monitors services on the compute node. This section assumes that you already have a working OpenStack environment with at least the following components installed: Nova, Glance, Cinder, Neutron and Identity. Note that installation and configuration varies by distribution. .. toctree:: :maxdepth: 1 install_and_configure_ubuntu ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4422853 masakari_monitors-22.0.0/doc/source/install/install_and_configure_ubuntu.rst0000664000175000017500000001737115257164053026360 0ustar00zuulzuul.. _install-ubuntu: Install and configure for Ubuntu ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This section describes how to install and configure Masakari-monitors for Ubuntu 18.04 (bionic). Prerequisites ------------- Before you install and configure Masakari-monitors, you must verify installations below: #. To install and confirm Masakari installation: * Masakari - is an OpenStack project designed to ensure high availability of instances and compute processes running on hosts. For detailed installation steps please refer to `masakari documentation `_ * Verify Masakari installation by verifying status of services: .. code-block:: console $ systemctl list-unit-files devstack@masakari-*.service UNIT FILE STATE devstack@masakari-api.service enabled devstack@masakari-engine.service enabled 2 unit files listed. #. IPMI support is needed for ``masakari-hostmonitor service`` which is a remote hardware health monitoring and management system that defines interfaces for use in monitoring the physical health of servers. #. Check if IPMI Hardware support is enabled: .. code-block:: console $ stonith -L | grep external/ipmi external/ipmi #. Install ipmitool and verify: * `IPMItool `_ utility is an open-source command-line interface (CLI) for controlling IPMI (Intelligent Platform Management Interface) enabled devices. .. code-block:: console $ sudo apt-get -y install ipmitool $ sudo ipmitool -V .. note:: Check for `IPMI Hardware support `_ with the command: ``sudo ipmitool lan print 1``. If IPMI configuration found, then proceed to steps below. #. Install crm and verify: * `crm `_ is a Pacemaker command line interface for configuration and management. .. code-block:: console $ sudo apt-get -y install crmsh $ sudo crm --version #. Install pacemaker and corosync and verify: Refer `Ubuntu Manpage `_ for control and configure pacemaker and corosync. * corosync - It is a Cluster Engine and a Group Communication System with additional features for implementing high availability within applications. .. code-block:: console $ sudo apt-get -y install corosync $ sudo corosync -v * pacemaker - It is an open-source high availability resource manager software used on computer clusters. .. code-block:: console $ sudo apt-get -y install pacemaker $ sudo crmadmin --version * Edit the ``/etc/corosync/corosync.conf`` file as described below: * Configure ``bindnetaddr`` value as network address of the interface to bind: .. code-block:: bash totem { ... interface { ... bindnetaddr: BINDNETADDR Replace ``BINDNETADDR`` with network address used for mutual communication between hosts. * Add the service section at end of file: .. code-block:: bash service { name: pacemaker ver: 0 use_mgmtd: yes } * Edit the ``/etc/default/corosync`` file as described below: .. code-block:: console # Start corosync at boot [yes|no] START=yes * Start corosync and pacemaker services: .. code-block:: console # systemctl start corosync.service # systemctl start pacemaker.service #. Install and confirm STONITH resource: * `STONITH `_ (Shoot The Other Node In The Head) is a Linux service for maintaining the integrity of nodes in a high-availability (HA) cluster. More description on how to create STONITH resource is here `cluster resources `_ Install and configure Masakari Monitors --------------------------------------- .. note:: * Masakari-monitors can be installed on the ComputeNodes only. #. Clone masakari-monitors using: .. code-block:: console # git clone https://opendev.org/openstack/masakari-monitors.git #. Prepare masakari-monitors configuration files: #. Generate via tox: Go to ‘opt/stack/masakari-monitors’ and execute the command below, this will generate ``masakarimonitors.conf.sample``, sample configuration file at ``/opt/stack/masakari-monitors/etc/masakarimonitors/`` .. code-block:: console # tox -egenconfig #. Or download them: # :download:`masakarimonitors.conf.sample ` #. Rename ``masakarimonitors.conf.sample`` to file to ``masakarimonitors.conf``, and edit as described below: * In the ``[api]`` section, verify keys below for sending notifications: .. code-block:: bash [api] ... auth_url = http://CONTROLLER/identity project_name = service project_domain_name = default username = masakari password = MASAKARI_PASS Replace ``CONTROLLER`` with the IP address or hostname of ControllerNode. And replace ``MASAKARI_PASS`` with the password you chose for the ``masakari`` user in the Identity service. * In the ``[host]`` section, configure about pacemaker: .. code-block:: bash [host] ... corosync_multicast_interfaces = MULTICAST_INTERFACES corosync_multicast_ports = MULTICAST_PORTS * Replace ``MULTICAST_INTERFACES`` with the interface that corosync is using for mutual communication between hosts. You can find the interface from the value of ``bindnetaddr`` in /etc/corosync/corosync.conf. If there are multiple interfaces, specify them by comma-separated like 'corosync_multicast_interfaces = enp0s3,enp0s8'. * Replace ``MULTICAST_PORTS`` with the ``mcastport`` described in /etc/corosync/corosync.conf. If there are multiple ``mcastport``, specify them by comma-separated like 'corosync_multicast_ports = 5405,5406'. #. Rename ``process_list.yaml.sample``, sample configuration file at ``/opt/stack/masakari-monitors/etc/masakarimonitors/``. Edit the file for the processes to be monitored. #. Create ``masakarimonitors`` directory in /etc/. Copy ``masakarimonitors.conf`` and ``process_list.yaml`` files to ``/etc/masakarimonitors/`` .. code-block:: console # cp -p etc/masakarimonitors/masakarimonitors.conf.sample /etc/masakarimonitors/masakarimonitors.conf # cp -p etc/masakarimonitors/process_list.yaml.sample /etc/masakarimonitors/process_list.yaml #. To install masakari-monitors run setup.py from the directory masakari-monitors: .. code-block:: console # cd masakari-monitors # sudo python3 setup.py install Finalize installation --------------------- * Restart the masakari-monitors services: .. code-block:: console # masakari-hostmonitor # masakari-introspectiveinstancemonitor # masakari-processmonitor # masakari-instancemonitor ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4422853 masakari_monitors-22.0.0/doc/source/install/overview.rst0000664000175000017500000000166615257164053022273 0ustar00zuulzuul======== Overview ======== Masakari-monitors provides Virtual Machine High Availability (VMHA) service for OpenStack clouds by automatically detecting failure events. It provides an alert for failure of either host, process or instance. If it detect such events, notifications are sent to the masakari-api. Masakari-monitors detects the four types of failures: * process failure ``masakari-processmonitor`` service detects compute process failure, and sends notifications to the masakari-api. * instance failure ``masakari-instancemonitor`` service detects VM/instance failure, and sends notifications to the masakari-api. * instance system-level failure failure ``masakari-introspectiveinstancemonitor`` service detects system-level failure events via QEMU Guest Agent. When it does, it sends notifications to the masakari-api. * compute host failure ``masakari-hostmonitor`` service detects nova-compute host failure. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4432855 masakari_monitors-22.0.0/doc/source/install/verify.rst0000664000175000017500000001306015257164053021720 0ustar00zuulzuulVerify operation ~~~~~~~~~~~~~~~~ Verify Masakari and Masakari-monitors installation. #. Source the ``admin`` credentials to gain access to admin-only CLI commands: .. code-block:: console $ . admin-openrc #. List Masakari services on ``controller node``: On controller node only Masakari services needs to be installed/listed. .. code-block:: console $ systemctl list-unit-files devstack@masakari*.service UNIT FILE STATE devstack@masakari-api.service enabled devstack@masakari-engine.service enabled #. List masakari services on ``compute node/s``: On compute nodes only Masakari-monitors services needs to be installed/listed. .. code-block:: console $ systemctl list-unit-files devstack@masakari*.service UNIT FILE STATE devstack@masakari-hostmonitor.service enabled devstack@masakari-instancemonitor.service enabled devstack@masakari-introspectiveinstancemonitor.service enabled devstack@masakari-processmonitor.service enabled #. List API endpoints in the Identity service to verify connectivity with the Identity service: .. note:: Endpoints below list may differ depending on the installation of OpenStack components. .. code-block:: console $ openstack endpoint list +-------------+----------------+--------------------------------------------------------+ | Name | Type | Endpoints | +-------------+----------------+--------------------------------------------------------+ | nova_legacy | compute_legacy | RegionOne | | | | public: http://controller/compute/v2/ | | | | | | nova | compute | RegionOne | | | | public: http://controller/compute/v2.1 | | | | | | cinder | block-storage | RegionOne | | | | public: http://controller/volume/v3/ | | | | | | glance | image | RegionOne | | | | public: http://controller/image | | | | | | cinderv3 | volumev3 | RegionOne | | | | public: http://controller/volume/v3/ | | | | | | masakari | instance-ha | RegionOne | | | | internal: http://controller/instance-ha/v1/ | | | | RegionOne | | | | admin: http://controller/instance-ha/v1/ | | | | RegionOne | | | | public: http://controller/instance-ha/v1/ | | | | | | keystone | identity | RegionOne | | | | public: http://controller/identity | | | | RegionOne | | | | admin: http://controller/identity | | | | | | cinderv2 | volumev2 | RegionOne | | | | public: http://controller/volume/v2/ | | | | | | placement | placement | RegionOne | | | | public: http://controller/placement | | | | | | neutron | network | RegionOne | | | | public: http://controller:9696/ | | | | | +-------------+----------------+--------------------------------------------------------+ #. Check CRM status .. code-block:: console $ sudo crm status Stack: corosync Current DC: controller (version 1.1.18-2b07d5c5a9) - partition with quorum Last updated: Thu Jul 11 13:33:17 2019 2 nodes configured 2 resources configured Online: [ compute-1, compute-2 ] OFFLINE: [ ] Full list of resources: st-null (stonith:null): Started compute ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4432855 masakari_monitors-22.0.0/doc/source/instancemonitor.rst0000664000175000017500000000240315257164053022161 0ustar00zuulzuul======================== masakari-instancemonitor ======================== Monitor Overview ------------------ The masakari-instancemonitor provides Virtual Machine High Availability for OpenStack clouds by automatically detecting VMs domain events via libvirt. If it detects specific libvirt events, it sends notifications to the masakari-api. How does it work? ---------------------------------------- - It runs libvirt event loop in a background thread. - Invoking libvirt.virEventRegisterDefaultImpl() will register libvirt's default event loop implementation. - Invoking libvirt.virEventRunDefaultImpl() will perform one iteration of the libvirt default event loop. - Invoking conn.domainEventRegisterAny() will register event callbacks against libvirt connection instances. The callbacks registered will be triggered from the execution context of libvirt.virEventRunDefaultImpl(), which will send notifications to the masakari-api. - It will reconnect to libvirt and reprocess if disconnected. Related configurations ------------------------ This section in masakarimonitors.conf shows an example of how to configure the monitor. .. code-block:: ini [libvirt] # Override the default libvirt URI. connection_uri = qemu:///system ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4432855 masakari_monitors-22.0.0/doc/source/introspectiveinstancemonitor.rst0000664000175000017500000000347415257164053025011 0ustar00zuulzuul===================================== masakari-introspectiveinstancemonitor ===================================== Monitor Overview ----------------- The masakari-introspectiveinstancemonitor provides Virtual Machine HA for OpenStack clouds by automatically detecting the system-level failure events via QEMU Guest Agent. If it detects VM heartbeat failure events, it sends notifications to the masakari-api. How does it work? ---------------------------------------- - libvirt and QEMU Guest Agent are used as the underlying protocol for messaging to and from VM. - The host-side qemu-agent sockets are used to detemine whether VMs are configured with QEMU Guest Agent. - qemu-guest-ping is used as the monitoring heartbeat. - For the future release, we can pass through arbitrary guest agent commands to check the health of the applications inside a VM. Related configurations ------------------------ This section in masakarimonitors.conf shows an example of how to configure the monitor. .. code-block:: ini [libvirt] # Override the default libvirt URI. connection_uri = qemu:///system [introspectiveinstancemonitor] # Guest monitoring interval of VM status (in seconds). # * The value should not be too low as there should not be false negative # * for reporting QEMU_GUEST_AGENT failures # * VM needs time to do powering-off. # * guest_monitoring_interval should be greater than # * the time to SHUTDOWN VM gracefully. guest_monitoring_interval = 10 # Guest monitoring timeout (in seconds). guest_monitoring_timeout = 2 # Failure threshold before sending notification. guest_monitoring_failure_threshold = 3 # The file path of qemu guest agent sock. qemu_guest_agent_sock_path = \ /var/lib/libvirt/qemu/org\.qemu\.guest_agent\..*\.instance-.*\.sock ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4442856 masakari_monitors-22.0.0/doc/source/processmonitor.rst0000664000175000017500000000454015257164053022037 0ustar00zuulzuul======================= masakari-processmonitor ======================= Monitor Overview ------------------ The masakari-processmonitor, provides key process High Availability for OpenStack clouds by automatically detecting the process failure. If it detects process failure, it sends notifications to masakari-api. If your OpenStack service runs in container(pod), this processmonitor will not work as expected. It is recommended not to deploy processmonitor. How does it work? ------------------- - Processes to be monitored should be pre-configured in process_list.yaml file. Define one process to be monitored as follows: .. code-block:: yaml process_name: [Name of the process as it in 'ps -ef'.] start_command: [Start command of the process.] pre_start_command: [Command which is executed before start_command.] post_start_command: [Command which is executed after start_command.] restart_command: [Restart command of the process.] pre_restart_command: [Command which is executed before restart_command.] post_restart_command: [Command which is executed after restart_command.] run_as_root: [Bool value whether to execute commands as root authority.] Sample of definitions is shown as follows: .. code-block:: yaml # nova-compute - process_name: /usr/local/bin/nova-compute start_command: systemctl start nova-compute pre_start_command: post_start_command: restart_command: systemctl restart nova-compute pre_restart_command: post_restart_command: run_as_root: True - If masakari-processmonitor detects one process failure, it will try to restart it firstly. After several retries failed, it sends notification to masakari-api. .. note:: The process_list.yaml file should contain list data. Make sure the data is formatted as a list even when only one item is defined. Related configurations ------------------------ This section in masakarimonitors.conf shows an example of how to configure the monitor. .. code-block:: ini [process] # Interval in seconds for checking a process. check_interval = 5 # Number of retries when the failure of restarting a process. restart_retries = 3 # Interval in seconds for restarting a process. restart_interval = 5 # The file path of process list. process_list_path = /etc/masakarimonitors/process_list.yaml ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1962204 masakari_monitors-22.0.0/doc/source/user/0000775000175000017500000000000015257164140017167 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4442856 masakari_monitors-22.0.0/doc/source/user/architecture.rst0000664000175000017500000000275115257164053022413 0ustar00zuulzuulMasakari Monitors System Architecture ===================================== Masakari-monitors provides Virtual Machine High Availability (VMHA) service for OpenStack clouds by detecting the failure events such as VM unavailability, VM internal faults, provisioning process unavailability and nova-compute host failure. If it detect these events, it sends notifications to the masakari-api which in turn, delegates them to masakari-engine for further processing via novaclient and nova. Masakari Components ------------------- The Masakari components described below are those which actually process the notifications received from Masakari-monitors: * DB: sql database for data storage for masakari, where all notifications received from monitors are getting stored. * masakari-api: component that receives notification from Masakari-monitors and delegates to masakari-engine. * masakari-engine: processes the notification request with the help of novaclient and nova. Masakari Monitors Components ---------------------------- Below are Masakari-monitors's key components. .. image:: /_static/images/masakari-monitors.jpg :width: 100% * masakari-instancemonitor: Detects the failure of VM/instance. * masakari-introspectiveinstancemonitor: Monitors the VMs, in order to detect, report and recover VMs from internal faults. * masakari-processmonitor: Detects the failure of process listed in process_list.yaml configuration file. * masakari-hostmonitor: Detects the nova-compute host failure. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1862216 masakari_monitors-22.0.0/etc/0000775000175000017500000000000015257164140014717 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1972203 masakari_monitors-22.0.0/etc/masakarimonitors/0000775000175000017500000000000015257164140020302 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4442856 masakari_monitors-22.0.0/etc/masakarimonitors/README-masakarimonitors.conf.txt0000664000175000017500000000022015257164053026302 0ustar00zuulzuulTo generate the sample masakarimonitors.conf file, run the following command from the top level of the masakari directory: tox -egenconfig ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4452858 masakari_monitors-22.0.0/etc/masakarimonitors/masakarimonitors-config-generator.conf0000664000175000017500000000036615257164053027773 0ustar00zuulzuul[DEFAULT] output_file = etc/masakarimonitors/masakarimonitors.conf.sample wrap_width = 80 namespace = masakarimonitors.conf namespace = oslo.concurrency namespace = oslo.log namespace = oslo.service.periodic_task namespace = oslo.service.service ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4452858 masakari_monitors-22.0.0/etc/masakarimonitors/matrix.yaml.sample0000664000175000017500000000100515257164053023751 0ustar00zuulzuul--- sequence: ['manage', 'tenant', 'storage'] matrix: - health: ['up', 'up', 'up'] action: [] - health: ['up', 'up', 'down'] action: ['recovery'] - health: ['up', 'down', 'up'] action: [] - health: ['up', 'down', 'down'] action: ['recovery'] - health: ['down', 'up', 'up'] action: [] - health: ['down', 'up', 'down'] action: ['recovery'] - health: ['down', 'down', 'up'] action: [] - health: ['down', 'down', 'down'] action: ['recovery']././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4452858 masakari_monitors-22.0.0/etc/masakarimonitors/process_list.yaml.sample0000664000175000017500000000420315257164053025161 0ustar00zuulzuul# Define the monitoring processes as follows: # process_name: [Name of the process as it in 'ps -ef'.] # start_command: [Start command of the process.] # pre_start_command: [Command which is executed before start_command.] # post_start_command: [Command which is executed after start_command.] # restart_command: [Restart command of the process.] # pre_restart_command: [Command which is executed before restart_command.] # post_restart_command: [Command which is executed after restart_command.] # run_as_root: [Bool value whether to execute commands as root authority.] # # These definitions need to be set according to the environment to use. # Sample of definitions is shown below. - # libvirtd process_name: /usr/sbin/libvirtd start_command: systemctl start libvirtd pre_start_command: post_start_command: restart_command: systemctl restart libvirtd pre_restart_command: post_restart_command: run_as_root: True - # nova-compute process_name: /usr/local/bin/nova-compute start_command: systemctl start nova-compute pre_start_command: post_start_command: restart_command: systemctl restart nova-compute pre_restart_command: post_restart_command: run_as_root: True - # instancemonitor process_name: /usr/bin/python /usr/local/bin/masakari-instancemonitor start_command: systemctl start masakari-instancemonitor pre_start_command: post_start_command: restart_command: systemctl restart masakari-instancemonitor pre_restart_command: post_restart_command: run_as_root: True - # hostmonitor process_name: /usr/bin/python /usr/local/bin/masakari-hostmonitor start_command: systemctl start masakari-hostmonitor pre_start_command: post_start_command: restart_command: systemctl restart masakari-hostmonitor pre_restart_command: post_restart_command: run_as_root: True - # sshd process_name: /usr/sbin/sshd start_command: systemctl start ssh pre_start_command: post_start_command: restart_command: systemctl restart ssh pre_restart_command: post_restart_command: run_as_root: True ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1982203 masakari_monitors-22.0.0/masakari_monitors.egg-info/0000775000175000017500000000000015257164140021360 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1392262 masakari_monitors-22.0.0/masakari_monitors.egg-info/PKG-INFO0000644000175000017500000000504215257164140022454 0ustar00zuulzuulMetadata-Version: 2.4 Name: masakari-monitors Version: 22.0.0 Summary: Monitors for Masakari Author-email: OpenStack License: Apache-2.0 Project-URL: Homepage, https://docs.openstack.org/masakari-monitors/latest/ Project-URL: Repository, https://opendev.org/openstack/masakari-monitors Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Python: >=3.11 Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: automaton>=1.9.0 Requires-Dist: keystoneauth1>=3.4.0 Requires-Dist: openstacksdk>=0.13.0 Requires-Dist: oslo.concurrency>=3.26.0 Requires-Dist: oslo.config>=5.2.0 Requires-Dist: lxml>=4.5.0 Requires-Dist: oslo.cache>=1.26.0 Requires-Dist: oslo.i18n>=3.15.3 Requires-Dist: oslo.log>=3.36.0 Requires-Dist: oslo.privsep>=1.23.0 Requires-Dist: oslo.service!=1.28.1,>=1.24.0 Requires-Dist: oslo.utils>=3.33.0 Requires-Dist: pbr!=2.1.0,>=2.0.0 Requires-Dist: py-consul>=1.4.0 Requires-Dist: kubernetes>=29.0.0 Dynamic: license-file Dynamic: requires-dist ===================== Monitors for Masakari ===================== .. image:: https://governance.openstack.org/tc/badges/masakari-monitors.svg .. Change things from this point on Monitors for Masakari provides Virtual Machine High Availability (VMHA) service for OpenStack clouds by automatically detecting the failure events such as VM process down, provisioning process down, and nova-compute host failure. If it detect the events, it sends notifications to the masakari-api. Original version of Masakari: https://github.com/ntt-sic/masakari Tokyo Summit Session: https://www.youtube.com/watch?v=BmjNKceW_9A Monitors for Masakari is distributed under the terms of the Apache License, Version 2.0. The full terms and conditions of this license are detailed in the LICENSE file. * Free software: Apache license * Documentation: https://docs.openstack.org/masakari-monitors * Source: https://opendev.org/openstack/masakari-monitors * Bugs: https://bugs.launchpad.net/masakari-monitors ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765152.180222 masakari_monitors-22.0.0/masakari_monitors.egg-info/SOURCES.txt0000664000175000017500000001751715257164140023257 0ustar00zuulzuul.coveragerc .mailmap .stestr.conf .yamllint .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst bindep.txt pyproject.toml requirements.txt setup.cfg setup.py test-requirements.txt tox.ini doc/requirements.txt doc/source/conf.py doc/source/consul-usage.rst doc/source/hostmonitor.rst doc/source/index.rst doc/source/instancemonitor.rst doc/source/introspectiveinstancemonitor.rst doc/source/processmonitor.rst doc/source/_static/.gitkeep doc/source/_static/images/masakari-monitors.jpg doc/source/configuration/config.rst doc/source/configuration/index.rst doc/source/configuration/process-list.rst doc/source/configuration/sample-config.rst doc/source/install/index.rst doc/source/install/install_and_configure.rst doc/source/install/install_and_configure_ubuntu.rst doc/source/install/overview.rst doc/source/install/verify.rst doc/source/user/architecture.rst etc/masakarimonitors/README-masakarimonitors.conf.txt etc/masakarimonitors/masakarimonitors-config-generator.conf etc/masakarimonitors/matrix.yaml.sample etc/masakarimonitors/process_list.yaml.sample masakari_monitors.egg-info/PKG-INFO masakari_monitors.egg-info/SOURCES.txt masakari_monitors.egg-info/dependency_links.txt masakari_monitors.egg-info/entry_points.txt masakari_monitors.egg-info/not-zip-safe masakari_monitors.egg-info/pbr.json masakari_monitors.egg-info/requires.txt masakari_monitors.egg-info/top_level.txt masakarimonitors/__init__.py masakarimonitors/config.py masakarimonitors/i18n.py masakarimonitors/manager.py masakarimonitors/privsep.py masakarimonitors/service.py masakarimonitors/utils.py masakarimonitors/version.py masakarimonitors/cmd/__init__.py masakarimonitors/cmd/hostmonitor.py masakarimonitors/cmd/instancemonitor.py masakarimonitors/cmd/introspectiveinstancemonitor.py masakarimonitors/cmd/processmonitor.py masakarimonitors/common/__init__.py masakarimonitors/conf/__init__.py masakarimonitors/conf/api.py masakarimonitors/conf/base.py masakarimonitors/conf/consul.py masakarimonitors/conf/host.py masakarimonitors/conf/instance.py masakarimonitors/conf/introspectiveinstancemonitor.py masakarimonitors/conf/kubernetes.py masakarimonitors/conf/opts.py masakarimonitors/conf/process.py masakarimonitors/conf/service.py masakarimonitors/ha/__init__.py masakarimonitors/ha/masakari.py masakarimonitors/hacking/__init__.py masakarimonitors/hacking/checks.py masakarimonitors/hostmonitor/__init__.py masakarimonitors/hostmonitor/driver.py masakarimonitors/hostmonitor/host.py masakarimonitors/hostmonitor/consul_check/__init__.py masakarimonitors/hostmonitor/consul_check/consul_helper.py masakarimonitors/hostmonitor/consul_check/manager.py masakarimonitors/hostmonitor/consul_check/matrix_helper.py masakarimonitors/hostmonitor/host_handler/__init__.py masakarimonitors/hostmonitor/host_handler/handle_host.py masakarimonitors/hostmonitor/host_handler/hold_host_status.py masakarimonitors/hostmonitor/host_handler/parse_cib_xml.py masakarimonitors/hostmonitor/host_handler/parse_crmmon_xml.py masakarimonitors/hostmonitor/kubernetes_check/__init__.py masakarimonitors/hostmonitor/kubernetes_check/manager.py masakarimonitors/instancemonitor/__init__.py masakarimonitors/instancemonitor/instance.py masakarimonitors/instancemonitor/libvirt_handler/__init__.py masakarimonitors/instancemonitor/libvirt_handler/callback.py masakarimonitors/instancemonitor/libvirt_handler/eventfilter.py masakarimonitors/instancemonitor/libvirt_handler/eventfilter_table.py masakarimonitors/introspectiveinstancemonitor/README.rst masakarimonitors/introspectiveinstancemonitor/__init__.py masakarimonitors/introspectiveinstancemonitor/cache.py masakarimonitors/introspectiveinstancemonitor/instance.py masakarimonitors/introspectiveinstancemonitor/qemu_utils.py masakarimonitors/introspectiveinstancemonitor/scheduler.py masakarimonitors/objects/__init__.py masakarimonitors/objects/event_constants.py masakarimonitors/processmonitor/__init__.py masakarimonitors/processmonitor/process.py masakarimonitors/processmonitor/process_handler/__init__.py masakarimonitors/processmonitor/process_handler/handle_process.py masakarimonitors/tests/__init__.py masakarimonitors/tests/base.py masakarimonitors/tests/test_masakarimonitors.py masakarimonitors/tests/unit/__init__.py masakarimonitors/tests/unit/test_hacking.py masakarimonitors/tests/unit/ha/__init__.py masakarimonitors/tests/unit/ha/test_masakari.py masakarimonitors/tests/unit/hostmonitor/__init__.py masakarimonitors/tests/unit/hostmonitor/test_host.py masakarimonitors/tests/unit/hostmonitor/consul_check/__init__.py masakarimonitors/tests/unit/hostmonitor/consul_check/test_consul_helper.py masakarimonitors/tests/unit/hostmonitor/consul_check/test_manager.py masakarimonitors/tests/unit/hostmonitor/consul_check/test_matrix_helper.py masakarimonitors/tests/unit/hostmonitor/host_handler/__init__.py masakarimonitors/tests/unit/hostmonitor/host_handler/test_handle_host.py masakarimonitors/tests/unit/hostmonitor/host_handler/test_hold_host_status.py masakarimonitors/tests/unit/hostmonitor/host_handler/test_parse_cib_xml.py masakarimonitors/tests/unit/hostmonitor/host_handler/test_parse_crmmon_xml.py masakarimonitors/tests/unit/hostmonitor/kubernetes_check/__init__.py masakarimonitors/tests/unit/hostmonitor/kubernetes_check/test_manager.py masakarimonitors/tests/unit/instancemonitor/__init__.py masakarimonitors/tests/unit/instancemonitor/test_instance.py masakarimonitors/tests/unit/instancemonitor/libvirt_handler/__init__.py masakarimonitors/tests/unit/instancemonitor/libvirt_handler/test_callback.py masakarimonitors/tests/unit/instancemonitor/libvirt_handler/test_eventfilter.py masakarimonitors/tests/unit/introspectiveinstancemonitor/__init__.py masakarimonitors/tests/unit/introspectiveinstancemonitor/test_monitor_manager.py masakarimonitors/tests/unit/introspectiveinstancemonitor/test_qemu_utils.py masakarimonitors/tests/unit/processmonitor/__init__.py masakarimonitors/tests/unit/processmonitor/test_process.py masakarimonitors/tests/unit/processmonitor/process_handler/__init__.py masakarimonitors/tests/unit/processmonitor/process_handler/test_handle_process.py releasenotes/notes/.placeholder releasenotes/notes/bp-retry-check-when-host-failure-78649c512ef79199.yaml releasenotes/notes/bug-1866660-ef8624f5283b2e5e.yaml releasenotes/notes/bug-1878548-5fab31aec6ba5407.yaml releasenotes/notes/bug-1930361-fa8ce8e9781ea967.yaml releasenotes/notes/bug-2153543-37dd1fff76f6e531.yaml releasenotes/notes/bug-cb435fc05a5e31d5.yaml releasenotes/notes/deprecate-processmonitor-95c8aefbc749c1ed.yaml releasenotes/notes/drop-cap-net-admin-8d7d7cfb274e9547.yaml releasenotes/notes/drop-py-2-7-b28de816eac45468.yaml releasenotes/notes/drop-python-38-and-39.yaml releasenotes/notes/hostmonitor-driver-based-on-consul-03f7e619d91e7e06.yaml releasenotes/notes/hostmonitor-driver-based-on-kubernetes-a898da08d2f40cac.yaml releasenotes/notes/hostmonitor-systemd-89696f96a654a918.yaml releasenotes/notes/introspectiveinstancemonitor-f4bc71f029b61d49.yaml releasenotes/notes/libvirt-sasl-support-edf1388c556a594b.yaml releasenotes/notes/libvirt-still-required-22a8d817ee8d0be8.yaml releasenotes/notes/pythonize-monitors-081e74dfaf78fe99.yaml releasenotes/notes/remove-py310-d0bfb824497ea29f.yaml releasenotes/source/2023.1.rst releasenotes/source/2023.2.rst releasenotes/source/2024.1.rst releasenotes/source/2024.2.rst releasenotes/source/2025.1.rst releasenotes/source/2025.2.rst releasenotes/source/2026.1.rst releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/queens.rst releasenotes/source/rocky.rst releasenotes/source/stein.rst releasenotes/source/train.rst releasenotes/source/unreleased.rst releasenotes/source/ussuri.rst releasenotes/source/victoria.rst releasenotes/source/wallaby.rst releasenotes/source/xena.rst releasenotes/source/yoga.rst releasenotes/source/zed.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1402261 masakari_monitors-22.0.0/masakari_monitors.egg-info/dependency_links.txt0000664000175000017500000000000115257164140025426 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1402261 masakari_monitors-22.0.0/masakari_monitors.egg-info/entry_points.txt0000664000175000017500000000131415257164140024655 0ustar00zuulzuul[console_scripts] masakari-hostmonitor = masakarimonitors.cmd.hostmonitor:main masakari-instancemonitor = masakarimonitors.cmd.instancemonitor:main masakari-introspectiveinstancemonitor = masakarimonitors.cmd.introspectiveinstancemonitor:main masakari-processmonitor = masakarimonitors.cmd.processmonitor:main [hostmonitor.driver] consul = masakarimonitors.hostmonitor.consul_check.manager:ConsulCheck default = masakarimonitors.hostmonitor.host_handler.handle_host:HandleHost kubernetes = masakarimonitors.hostmonitor.kubernetes_check.manager:KubernetesCheck simple = masakarimonitors.hostmonitor.host_handler.handle_host:HandleHost [oslo.config.opts] masakarimonitors.conf = masakarimonitors.conf.opts:list_opts ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1402261 masakari_monitors-22.0.0/masakari_monitors.egg-info/not-zip-safe0000664000175000017500000000000115257164140023606 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1392262 masakari_monitors-22.0.0/masakari_monitors.egg-info/pbr.json0000664000175000017500000000005615257164140023037 0ustar00zuulzuul{"git_version": "b0f1a1a", "is_release": true}././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765152.141226 masakari_monitors-22.0.0/masakari_monitors.egg-info/requires.txt0000664000175000017500000000044615257164140023764 0ustar00zuulzuulautomaton>=1.9.0 keystoneauth1>=3.4.0 openstacksdk>=0.13.0 oslo.concurrency>=3.26.0 oslo.config>=5.2.0 lxml>=4.5.0 oslo.cache>=1.26.0 oslo.i18n>=3.15.3 oslo.log>=3.36.0 oslo.privsep>=1.23.0 oslo.service!=1.28.1,>=1.24.0 oslo.utils>=3.33.0 pbr!=2.1.0,>=2.0.0 py-consul>=1.4.0 kubernetes>=29.0.0 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765152.141226 masakari_monitors-22.0.0/masakari_monitors.egg-info/top_level.txt0000664000175000017500000000002115257164140024103 0ustar00zuulzuulmasakarimonitors ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.1992202 masakari_monitors-22.0.0/masakarimonitors/0000775000175000017500000000000015257164140017527 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4452858 masakari_monitors-22.0.0/masakarimonitors/__init__.py0000664000175000017500000000124115257164053021641 0ustar00zuulzuul# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pbr.version __version__ = pbr.version.VersionInfo( 'masakari-monitors').version_string() ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765152.20022 masakari_monitors-22.0.0/masakarimonitors/cmd/0000775000175000017500000000000015257164140020272 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4452858 masakari_monitors-22.0.0/masakarimonitors/cmd/__init__.py0000664000175000017500000000000015257164053022374 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4452858 masakari_monitors-22.0.0/masakarimonitors/cmd/hostmonitor.py0000664000175000017500000000213515257164053023235 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Starter script for Masakari Host Monitor.""" import sys from oslo_log import log as logging import masakarimonitors.conf from masakarimonitors import config from masakarimonitors import service from masakarimonitors import utils CONF = masakarimonitors.conf.CONF def main(): config.parse_args(sys.argv) logging.setup(CONF, "masakarimonitors") utils.monkey_patch() server = service.Service.create(binary='masakarimonitors-hostmonitor') service.serve(server) service.wait() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4452858 masakari_monitors-22.0.0/masakarimonitors/cmd/instancemonitor.py0000664000175000017500000000214515257164053024065 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Starter script for Masakari Instance Monitor.""" import sys from oslo_log import log as logging import masakarimonitors.conf from masakarimonitors import config from masakarimonitors import service from masakarimonitors import utils CONF = masakarimonitors.conf.CONF def main(): config.parse_args(sys.argv) logging.setup(CONF, "masakarimonitors") utils.monkey_patch() server = service.Service.create(binary='masakarimonitors-instancemonitor') service.serve(server) service.wait() ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.446286 masakari_monitors-22.0.0/masakarimonitors/cmd/introspectiveinstancemonitor.py0000664000175000017500000000216015257164053026701 0ustar00zuulzuul# Copyright(c) 2018 WindRiver Systems # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Starter script for Masakari Introspective Instance Monitor.""" import sys from oslo_log import log as logging import masakarimonitors.conf from masakarimonitors import config from masakarimonitors import service from masakarimonitors import utils CONF = masakarimonitors.conf.CONF def main(): config.parse_args(sys.argv) logging.setup(CONF, "masakarimonitors") utils.monkey_patch() server = service.Service.create( binary='masakarimonitors-introspectiveinstancemonitor') service.serve(server) service.wait() ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.446286 masakari_monitors-22.0.0/masakarimonitors/cmd/processmonitor.py0000664000175000017500000000214315257164053023735 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Starter script for Masakari Process Monitor.""" import sys from oslo_log import log as logging import masakarimonitors.conf from masakarimonitors import config from masakarimonitors import service from masakarimonitors import utils CONF = masakarimonitors.conf.CONF def main(): config.parse_args(sys.argv) logging.setup(CONF, "masakarimonitors") utils.monkey_patch() server = service.Service.create(binary='masakarimonitors-processmonitor') service.serve(server) service.wait() ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765152.20022 masakari_monitors-22.0.0/masakarimonitors/common/0000775000175000017500000000000015257164140021017 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.446286 masakari_monitors-22.0.0/masakarimonitors/common/__init__.py0000664000175000017500000000000015257164053023121 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765152.20122 masakari_monitors-22.0.0/masakarimonitors/conf/0000775000175000017500000000000015257164140020454 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.446286 masakari_monitors-22.0.0/masakarimonitors/conf/__init__.py0000664000175000017500000000244215257164053022572 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_config import cfg from masakarimonitors.conf import api from masakarimonitors.conf import base from masakarimonitors.conf import consul from masakarimonitors.conf import host from masakarimonitors.conf import instance from masakarimonitors.conf import introspectiveinstancemonitor from masakarimonitors.conf import kubernetes from masakarimonitors.conf import process from masakarimonitors.conf import service CONF = cfg.CONF api.register_opts(CONF) base.register_opts(CONF) consul.register_opts(CONF) host.register_opts(CONF) instance.register_opts(CONF) introspectiveinstancemonitor.register_opts(CONF) kubernetes.register_opts(CONF) process.register_opts(CONF) service.register_opts(CONF) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.446286 masakari_monitors-22.0.0/masakarimonitors/conf/api.py0000664000175000017500000000323415257164053021604 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from keystoneauth1 import loading as ks_loading from oslo_config import cfg API_GROUP = 'api' api_group = cfg.OptGroup( API_GROUP, title='Api Options', help=""" Configuration options for sending notifications. """) monitor_api_opts = [ cfg.StrOpt('region', default='RegionOne', help='Region name.'), cfg.StrOpt('api_version', default='v1', help='Masakari API Version.'), cfg.StrOpt('api_interface', default='public', choices=('public', 'internal', 'admin'), help='Interface of endpoint.'), ] def register_opts(conf): conf.register_group(api_group) conf.register_opts(monitor_api_opts, group=api_group) ks_loading.register_session_conf_options(conf, api_group) ks_loading.register_auth_conf_options(conf, api_group) conf.set_default('auth_type', 'password', group=api_group) def list_opts(): return { api_group: ( monitor_api_opts + ks_loading.get_auth_plugin_conf_options('password')) } ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.446286 masakari_monitors-22.0.0/masakarimonitors/conf/base.py0000664000175000017500000000266515257164053021754 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_config import cfg base_options = [ cfg.StrOpt( 'tempdir', help='Explicitly specify the temporary working directory.'), cfg.BoolOpt( 'monkey_patch', default=False, help=""" Determine if monkey patching should be applied. Related options: * ``monkey_patch_modules``: This must have values set for this option to have any effect """), cfg.ListOpt( 'monkey_patch_modules', default=[], help=""" List of modules/decorators to monkey patch. This option allows you to patch a decorator for all functions in specified modules. Related options: * ``monkey_patch``: This must be set to ``True`` for this option to have any effect """), ] def register_opts(conf): conf.register_opts(base_options) def list_opts(): return {'DEFAULT': base_options} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4472861 masakari_monitors-22.0.0/masakarimonitors/conf/consul.py0000664000175000017500000000225415257164053022337 0ustar00zuulzuul# Copyright(c) 2019 Inspur # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_config import cfg consul_opts = [ cfg.StrOpt('agent_manage', help='Addr for local consul agent in management datacenter.'), cfg.StrOpt('agent_tenant', help='Addr for local consul agent in tenant datacenter.'), cfg.StrOpt('agent_storage', help='Addr for local consul agent in storage datacenter.'), cfg.StrOpt('matrix_config_file', help='Config file for consul health action matrix.'), ] def register_opts(conf): conf.register_opts(consul_opts, group='consul') def list_opts(): return { 'consul': consul_opts } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4472861 masakari_monitors-22.0.0/masakarimonitors/conf/host.py0000664000175000017500000001054115257164053022007 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_config import cfg from oslo_config import types monitor_host_opts = [ cfg.StrOpt('monitoring_driver', default='default', help='Driver that hostmonitor uses for monitoring hosts.'), cfg.IntOpt('monitoring_interval', default=60, help='Monitoring interval(in seconds) of node status.'), cfg.IntOpt('monitoring_samples', default=1, help=''' Monitoring probes to collect before making the decision to send Masakari notification about the node status. If and only if ``monitoring_samples`` consecutive reports have the same status, will the Masakari notification be sent. '''), cfg.IntOpt('api_retry_max', default=12, help='Number of retries for send a notification in' ' hostmonitor.'), cfg.IntOpt('api_retry_interval', default=10, help='Trial interval of time of the notification processing' ' is error(in seconds).'), cfg.BoolOpt('disable_ipmi_check', default=False, help=''' Do not check whether the host is completely down. Possible values: * True: Do not check whether the host is completely down. * False: Do check whether the host is completely down. If ipmi RA is not set in pacemaker, this value should be set True. '''), cfg.BoolOpt('restrict_to_remotes', default=False, help='Only monitor pacemaker-remotes, ignore the status of' ' full cluster members.'), cfg.IntOpt('ipmi_timeout', default=5, help='Timeout value(in seconds) of the ipmitool command.'), cfg.IntOpt('ipmi_retry_max', default=3, help='Number of ipmitool command retries.'), cfg.IntOpt('ipmi_retry_interval', default=10, help='Retry interval(in seconds) of the ipmitool command.'), cfg.IntOpt('stonith_wait', default=30, help='Standby time(in seconds) until activate STONITH.'), cfg.IntOpt('tcpdump_timeout', default=5, help='Timeout value(in seconds) of the tcpdump command when' ' monitors the corosync communication.'), cfg.ListOpt('corosync_multicast_interfaces', help=''' The name of interface that corosync is using for mutual communication between hosts. If there are multiple interfaces, specify them in comma-separated like 'enp0s3,enp0s8'. The number of interfaces you specify must be equal to the number of corosync_multicast_ports values and must be in correct order with relevant ports in corosync_multicast_ports. '''), cfg.ListOpt('corosync_multicast_ports', item_type=types.Port(), help=''' The port numbers that corosync is using for mutual communication between hosts. If there are multiple port numbers, specify them in comma-separated like '5405,5406'. The number of port numbers you specify must be equal to the number of corosync_multicast_interfaces values and must be in correct order with relevant interfaces in corosync_multicast_interfaces. '''), cfg.StrOpt('pacemaker_node_type', default='autodetect', choices=('autodetect', 'cluster', 'remote'), help=''' Using this option, one can avoid systemd checks that would establish whether this hostmonitor is running alongside Corosync and Pacemaker (the ``cluster`` stack) or Pacemaker Remote (the ``remote`` stack). The default (``autodetect``) ensures backward compatibility and means systemd is used to check the stack. '''), ] def register_opts(conf): conf.register_opts(monitor_host_opts, group='host') def list_opts(): return { 'host': monitor_host_opts } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4472861 masakari_monitors-22.0.0/masakarimonitors/conf/instance.py0000664000175000017500000000261615257164053022642 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_config import cfg monitor_callback_opts = [ cfg.IntOpt('retry_max', default=12, help='Number of retries when the notification processing' ' is error.'), cfg.IntOpt('retry_interval', default=10, help='Trial interval of time of the notification processing' ' is error(in seconds).'), ] libvirt_opts = [ cfg.StrOpt('connection_uri', default='qemu:///system', help='Override the default libvirt URI.') ] def register_opts(conf): conf.register_opts(monitor_callback_opts, group='callback') conf.register_opts(libvirt_opts, group='libvirt') def list_opts(): return { 'callback': monitor_callback_opts, 'libvirt': libvirt_opts } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4472861 masakari_monitors-22.0.0/masakarimonitors/conf/introspectiveinstancemonitor.py0000664000175000017500000000427115257164053027070 0ustar00zuulzuul# Copyright(c) 2018 WindRiver Systems # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_config import cfg # Note: this string is being used for regex parsing later with re module. # # Use Python's raw string notation for regular expressions and # uses the backslash character ('\') to indicate special # forms or to allow special characters to be used without invoking # their special meaning. SOCK = r'/var/lib/libvirt/qemu/org\.qemu\.guest_agent\..*\.instance-.*\.sock' monitor_opts = [ cfg.IntOpt('guest_monitoring_interval', default=10, help=''' Guest monitoring interval of VM status (in seconds). * The value should not be too low as there should not be false negative * for reporting QEMU_GUEST_AGENT failures * VM needs time to do powering-off. * guest_monitoring_interval should be greater than * the time to SHUTDOWN VM gracefully. * e.g. | 565da9ba-3c0c-4087-83ca | iim1 | ACTIVE | powering-off | Running '''), cfg.IntOpt('guest_monitoring_timeout', default=2, help='Guest monitoring timeout (in seconds).'), cfg.IntOpt('guest_monitoring_failure_threshold', default=3, help='Failure threshold before sending notification.'), cfg.StrOpt('qemu_guest_agent_sock_path', default=SOCK, help=r''' * The file path of qemu guest agent sock. * Please use Python raw string notation as regular expressions. e.g. r'/var/lib/libvirt/qemu/org\.qemu\.guest_agent\..*\.instance-.*\.sock' '''), ] def register_opts(conf): conf.register_opts(monitor_opts, group='introspectiveinstancemonitor') def list_opts(): return { 'introspectiveinstancemonitor': monitor_opts } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4482863 masakari_monitors-22.0.0/masakarimonitors/conf/kubernetes.py0000664000175000017500000000211515257164053023177 0ustar00zuulzuul# Copyright(c) 2024 Samsung SDS # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_config import cfg kubernetes_opts = [ cfg.StrOpt('monitoring_node_labels', default='', help=''' Options for identifying the nodes to monitor. Only nodes with node labels defined in this option are included in the monitor. Example value: monitoring_node_labels = "monitoring=true,compute-node=enabled,..." '''), ] def register_opts(conf): conf.register_opts(kubernetes_opts, group='kubernetes') def list_opts(): return { 'kubernetes': kubernetes_opts } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4482863 masakari_monitors-22.0.0/masakarimonitors/conf/opts.py0000664000175000017500000000570515257164053022025 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ This is the single point of entry to generate the sample configuration file for Masakari Monitors. It collects all the necessary info from the other modules in this package. It is assumed that: * every other module in this package has a 'list_opts' function which return a dict where * the keys are strings which are the group names * the value of each key is a list of config options for that group * the masakari.conf package doesn't have further packages with config options * this module is only used in the context of sample file generation """ import collections import importlib import os import pkgutil LIST_OPTS_FUNC_NAME = "list_opts" def _tupleize(dct): """Take the dict of options and convert to the 2-tuple format.""" return [(key, val) for key, val in dct.items()] def list_opts(): opts = collections.defaultdict(list) module_names = _list_module_names() imported_modules = _import_modules(module_names) _append_config_options(imported_modules, opts) return _tupleize(opts) def _list_module_names(): module_names = [] package_path = os.path.dirname(os.path.abspath(__file__)) for _, modname, ispkg in pkgutil.iter_modules(path=[package_path]): if modname == "opts" or ispkg: continue else: module_names.append(modname) return module_names def _import_modules(module_names): imported_modules = [] for modname in module_names: mod = importlib.import_module("masakarimonitors.conf." + modname) if not hasattr(mod, LIST_OPTS_FUNC_NAME): msg = "The module 'masakarimonitors.conf.%s' should have a" \ " '%s' function which returns the config options." % \ (modname, LIST_OPTS_FUNC_NAME) raise Exception(msg) else: imported_modules.append(mod) return imported_modules def _process_old_opts(configs): """Convert old-style 2-tuple configs to dicts.""" if isinstance(configs, tuple): configs = [configs] return {label: options for label, options in configs} def _append_config_options(imported_modules, config_options): for mod in imported_modules: configs = mod.list_opts() if not isinstance(configs, dict): configs = _process_old_opts(configs) for key, val in configs.items(): config_options[key].extend(val) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4482863 masakari_monitors-22.0.0/masakarimonitors/conf/process.py0000664000175000017500000000334715257164053022516 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_config import cfg monitor_process_opts = [ cfg.IntOpt('check_interval', default=5, help='Interval in seconds for checking a process.'), cfg.IntOpt('restart_retries', default=3, help='Number of retries when the failure of restarting a' ' process.'), cfg.IntOpt('restart_interval', default=5, help='Interval in seconds for restarting a process.'), cfg.IntOpt('api_retry_max', default=12, help='Number of retries for send a notification in' ' processmonitor.'), cfg.IntOpt('api_retry_interval', default=10, help='Interval between re-sending a notification in' ' processmonitor(in seconds).'), cfg.StrOpt('process_list_path', default='/etc/masakarimonitors/process_list.yaml', help='The file path of process list.'), ] def register_opts(conf): conf.register_opts(monitor_process_opts, group='process') def list_opts(): return { 'process': monitor_process_opts } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4482863 masakari_monitors-22.0.0/masakarimonitors/conf/service.py0000664000175000017500000000407415257164053022476 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import socket from oslo_config import cfg service_opts = [ cfg.StrOpt('hostname', default=socket.gethostname(), deprecated_name="host", sample_default='', help=''' Hostname, FQDN or IP address of this host. Must be valid within AMQP key. Possible values: * String with hostname, FQDN or IP address. Default is hostname of this host. '''), cfg.StrOpt('instancemonitor_manager', default='masakarimonitors.instancemonitor.instance' '.InstancemonitorManager', help='Full class name for the Manager for instancemonitor.'), cfg.StrOpt('introspectiveinstancemonitor_manager', default='masakarimonitors.introspectiveinstancemonitor.instance' '.IntrospectiveInstanceMonitorManager', help='Full class name for introspectiveinstancemonitor.'), cfg.StrOpt('processmonitor_manager', default='masakarimonitors.processmonitor.process' '.ProcessmonitorManager', help='Full class name for the Manager for processmonitor.'), cfg.StrOpt('hostmonitor_manager', default='masakarimonitors.hostmonitor.host' '.HostmonitorManager', help='Full class name for the Manager for hostmonitor.'), ] def register_opts(conf): conf.register_opts(service_opts) def list_opts(): return {'DEFAULT': service_opts} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4492865 masakari_monitors-22.0.0/masakarimonitors/config.py0000664000175000017500000000221715257164053021353 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_log import log import masakarimonitors.conf from masakarimonitors import version CONF = masakarimonitors.conf.CONF def parse_args(argv, default_config_files=None): log.register_options(CONF) # We use the oslo.log default log levels which includes suds=INFO # and add only the extra levels that Masakari needs log.set_defaults(default_log_levels=log.get_default_log_levels()) CONF(argv[1:], project='masakarimonitors', version=version.version_string(), default_config_files=default_config_files) ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765152.20222 masakari_monitors-22.0.0/masakarimonitors/ha/0000775000175000017500000000000015257164140020117 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4492865 masakari_monitors-22.0.0/masakarimonitors/ha/__init__.py0000664000175000017500000000000015257164053022221 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4492865 masakari_monitors-22.0.0/masakarimonitors/ha/masakari.py0000664000175000017500000000626215257164053022272 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import eventlet from keystoneauth1 import loading as ks_loading from openstack import connection from openstack import exceptions from oslo_log import log as oslo_logging import masakarimonitors.conf LOG = oslo_logging.getLogger(__name__) CONF = masakarimonitors.conf.CONF class SendNotification(object): def __init__(self): self._masakari_client = None @property def masakari_client(self): if not self._masakari_client: self._masakari_client = self._make_client() return self._masakari_client def _make_client(self): auth = ks_loading.load_auth_from_conf_options(CONF, 'api') session = ks_loading.load_session_from_conf_options(CONF, 'api', auth=auth) conn = connection.Connection(session=session, interface=CONF.api.api_interface, region_name=CONF.api.region) return conn.instance_ha def send_notification(self, api_retry_max, api_retry_interval, event): """Send a notification. This method sends a notification to the masakari-api. :param api_retry_max: Number of retries when the notification processing is error. :param api_retry_interval: Trial interval of time of the notification processing is error. :param event: dictionary of event that included in notification. """ LOG.info("Send a notification. %s", event) # Send a notification. retry_count = 0 while True: try: response = self.masakari_client.create_notification( type=event['notification']['type'], hostname=event['notification']['hostname'], generated_time=event['notification']['generated_time'], payload=event['notification']['payload']) LOG.info("Response: %s", response) break except Exception as e: if isinstance(e, exceptions.HttpException): if e.status_code in [400, 409]: LOG.info(e) break if retry_count < api_retry_max: LOG.warning("Retry sending a notification. (%s)", e) retry_count = retry_count + 1 eventlet.greenthread.sleep(api_retry_interval) else: LOG.exception("Exception caught: %s", e) self._masakari_client = None break ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765152.20222 masakari_monitors-22.0.0/masakarimonitors/hacking/0000775000175000017500000000000015257164140021133 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4492865 masakari_monitors-22.0.0/masakarimonitors/hacking/__init__.py0000664000175000017500000000000015257164053023235 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4492865 masakari_monitors-22.0.0/masakarimonitors/hacking/checks.py0000664000175000017500000000642515257164053022757 0ustar00zuulzuul# Copyright (c) 2017, NTT Data # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import re from hacking import core """ Guidelines for writing new hacking checks - Use only for masakarimonitors specific tests. OpenStack general tests should be submitted to the common 'hacking' module. - Pick numbers in the range M3xx. Find the current test with the highest allocated number and then pick the next value. - Keep the test method code in the source file ordered based on the M3xx value. - List the new rule in the top level HACKING.rst file - Add test cases for each new rule to masakarimonitors/tests/unit/ test_hacking.py """ UNDERSCORE_IMPORT_FILES = [] translated_log = re.compile( r"(.)*LOG\.(audit|error|info|critical|exception)" r"\(\s*_\(\s*('|\")") string_translation = re.compile(r"[^_]*_\(\s*('|\")") underscore_import_check = re.compile(r"(.)*import _(.)*") underscore_import_check_multi = re.compile(r"(.)*i18n\s+import(.)* _, (.)*") # We need this for cases where they have created their own _ function. custom_underscore_check = re.compile(r"(.)*_\s*=\s*(.)*") log_translation = re.compile( r"(.)*LOG\." r"(audit|debug|error|info|warn|warning|critical|exception)" r"\(" r"(_|_LC|_LE|_LI|_LW)" r"\(") @core.flake8ext def check_explicit_underscore_import(logical_line, filename): """Check for explicit import of the _ function We need to ensure that any files that are using the _() function to translate logs are explicitly importing the _ function. We can't trust unit test to catch whether the import has been added so we need to check for it here. """ # Build a list of the files that have _ imported. No further # checking needed once it is found. for file in UNDERSCORE_IMPORT_FILES: if file in filename: return if (underscore_import_check.match(logical_line) or underscore_import_check_multi.match(logical_line) or custom_underscore_check.match(logical_line)): UNDERSCORE_IMPORT_FILES.append(filename) elif (translated_log.match(logical_line) or string_translation.match(logical_line)): yield (0, "M301: Found use of _() without explicit import of _ !") @core.flake8ext def no_translate_logs(logical_line): """Check that logging doesn't translate messages M302 """ if log_translation.match(logical_line): yield (0, "M302 Don't translate log messages!") @core.flake8ext def no_log_warn(logical_line): """Disallow 'LOG.warn(' LOG.warn() is deprecated and LOG.warning should be used instead. https://docs.python.org/3/library/logging.html#logging.warning N352 """ msg = ("M305: LOG.warn is deprecated, please use LOG.warning!") if "LOG.warn(" in logical_line: yield (0, msg) ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765152.20222 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/0000775000175000017500000000000015257164140022114 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4502866 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/__init__.py0000664000175000017500000000000015257164053024216 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765152.20322 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/consul_check/0000775000175000017500000000000015257164140024554 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4502866 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/consul_check/__init__.py0000664000175000017500000000000015257164053026656 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4502866 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/consul_check/consul_helper.py0000664000175000017500000001002515257164053027771 0ustar00zuulzuul# Copyright(c) 2021 Inspur # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Main abstraction layer for retrieving node status from consul """ import consul from oslo_utils import netutils from masakarimonitors.i18n import _ DEFAULT_CONSUL_PORT = 8500 class ConsulException(Exception): """Base Consul Exception""" msg_fmt = _("An unknown exception occurred.") def __init__(self, message=None, **kwargs): if not message: message = self.msg_fmt % kwargs super(ConsulException, self).__init__(message) class ConsulAgentNotExist(ConsulException): msg_fmt = _("Consul agent of %(cluster)s not exist.") class ConsulGetMembersException(ConsulException): msg_fmt = _("Failed to get members of %(cluster)s: %(err)s.") class ConsulManager(object): """Consul manager class This class helps to pull health data from all consul clusters, and return health data in sequence. """ def __init__(self, CONF): self.agents = {} self.init_agents(CONF) def init_agents(self, CONF): if CONF.consul.agent_manage: addr, port = netutils.parse_host_port( CONF.consul.agent_manage, DEFAULT_CONSUL_PORT) self.agents['manage'] = ConsulAgent('manage', addr, port) if CONF.consul.agent_tenant: addr, port = netutils.parse_host_port( CONF.consul.agent_tenant, DEFAULT_CONSUL_PORT) self.agents['tenant'] = ConsulAgent('tenant', addr, port) if CONF.consul.agent_storage: addr, port = netutils.parse_host_port( CONF.consul.agent_storage, DEFAULT_CONSUL_PORT) self.agents['storage'] = ConsulAgent('storage', addr, port) def valid_agents(self, sequence): for name in sequence: if self.agents.get(name) is None: raise ConsulAgentNotExist(cluster=name) def get_health(self, sequence): hosts_health = {} all_agents = [] for name in sequence: if name in self.agents: consul_agent = self.agents.get(name) agent_health = consul_agent.get_health() hosts_health[name] = agent_health if not all_agents: all_agents = agent_health.keys() sequence_hosts_health = {} for host in all_agents: sequence_hosts_health[host] = [] for name in sequence: if name in self.agents: state = hosts_health[name].get(host) if state: sequence_hosts_health[host].append(state) else: continue return sequence_hosts_health class ConsulAgent(object): """Agent to consul cluster""" def __init__(self, name, addr=None, port=None): self.name = name self.addr = addr self.port = port # connection to consul cluster self.cluster = consul.Consul(host=addr, port=self.port) def get_agents(self): try: members = self.cluster.agent.members() except Exception as e: raise ConsulGetMembersException(cluster=self.name, err=str(e)) return members def get_health(self): agents_health = {} agents = self.get_agents() for agent in agents: host = agent.get('Name') status = agent.get('Status') if status == 1: agents_health[host] = 'up' else: agents_health[host] = 'down' return agents_health ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4512868 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/consul_check/manager.py0000664000175000017500000001615615257164053026554 0ustar00zuulzuul# Copyright(c) 2019 Inspur # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import eventlet import socket from collections import deque from oslo_log import log from oslo_utils import timeutils from masakarimonitors import conf from masakarimonitors.ha import masakari from masakarimonitors.hostmonitor.consul_check import consul_helper from masakarimonitors.hostmonitor.consul_check import matrix_helper from masakarimonitors.hostmonitor import driver from masakarimonitors.objects import event_constants as ec LOG = log.getLogger(__name__) CONF = conf.CONF class ConsulCheck(driver.DriverBase): """Check host status by consul""" def __init__(self): super(ConsulCheck, self).__init__() self.hostname = socket.gethostname() self.monitoring_interval = CONF.host.monitoring_interval self.monitoring_samples = CONF.host.monitoring_samples self.matrix_manager = matrix_helper.MatrixManager(CONF) self.consul_manager = consul_helper.ConsulManager(CONF) self.notifier = masakari.SendNotification() self._matrix = None self._sequence = None self.monitoring_data = {} self.last_host_health = {} @property def matrix(self): if not self._matrix: self._matrix = self.matrix_manager.get_matrix() return self._matrix @property def sequence(self): if not self._sequence: self._sequence = self.matrix_manager.get_sequence() return self._sequence def _formate_health(self, host_health): formate_health = {} for i in range(len(host_health)): layer = "%s-interface" % self.sequence[i] state = host_health[i] formate_health[layer] = state return formate_health def _event(self, host, host_health): host_status = ec.EventConstants.HOST_STATUS_NORMAL # Skip notification for unstable health state (mixed up/down samples) if None in host_health: LOG.debug("Host %s has unstable health state %s, skipping " "notification.", host, str(self._formate_health(host_health))) return None if 'down' not in host_health: event_type = ec.EventConstants.EVENT_STARTED cluster_status = ec.EventConstants.CLUSTER_STATUS_ONLINE else: actions = self.get_action_from_matrix(host_health) if 'recovery' in actions: LOG.info("Host %s needs recovery, health status: %s.", host, str(self._formate_health(host_health))) event_type = ec.EventConstants.EVENT_STOPPED cluster_status = ec.EventConstants.CLUSTER_STATUS_OFFLINE else: return None # TODO(suzhengwei): Add host status detail in the payload to show # host status in all Consul clusters. event = { 'notification': { 'type': ec.EventConstants.TYPE_COMPUTE_HOST, 'hostname': host, 'generated_time': timeutils.utcnow(), 'payload': { 'event': event_type, 'cluster_status': cluster_status, 'host_status': host_status } } } return event def update_monitoring_data(self): '''update monitoring data from consul clusters''' LOG.debug("update monitoring data from consul.") # Get current host health in sequence [x, y, z]. The example of # the return value is {'node01':[x, y, z], 'node02':[x, y, z]...} cluster_health = self.consul_manager.get_health(self.sequence) LOG.debug("Current cluster state: %s.", cluster_health) # Reassemble host health history with the latest host health. # Example of 'host_health_history' is [[x, y, x], [x, y, z]...] for host, health in cluster_health.items(): host_health_history = self.monitoring_data.get( host, deque([], maxlen=self.monitoring_samples)) host_health_history.append(health) self.monitoring_data[host] = host_health_history def get_host_health(self, host): health_history = self.monitoring_data.get(host, []) if len(health_history) < self.monitoring_samples: LOG.debug("Not enough monitoring data for host %s", host) return None # Caculate host health from host health history. # Only continous 'down' represents the interface 'down', # while continous 'up' represents the interface 'up'. host_sequence_health = [] for host_health in zip(*health_history): if 'up' in host_health and 'down' in host_health: host_sequence_health.append(None) else: host_sequence_health.append(host_health[0]) return host_sequence_health def _host_health_changed(self, host, health): last_health = self.last_host_health.get(host) if last_health is None: self.last_host_health[host] = health return False if health != last_health: self.last_host_health[host] = health return True else: return False def get_action_from_matrix(self, host_health): for health_action in self.matrix: matrix_health = health_action["health"] matrix_action = health_action["action"] if host_health == matrix_health: return matrix_action return [] def poll_hosts(self): '''poll and check hosts health''' for host in self.monitoring_data.keys(): if host == self.hostname: continue host_health = self.get_host_health(host) if host_health is None: continue if not self._host_health_changed(host, host_health): continue # it will send notifition to trigger host failure recovery # according to defined HA strategy event = self._event(host, host_health) if event: self.notifier.send_notification( CONF.host.api_retry_max, CONF.host.api_retry_interval, event) def stop(self): self.running = False def monitor_hosts(self): self.running = True while self.running: try: self.update_monitoring_data() self.poll_hosts() except Exception as e: LOG.exception("Exception when host-monitor by consul: %s", e) eventlet.greenthread.sleep(self.monitoring_interval) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4512868 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/consul_check/matrix_helper.py0000664000175000017500000000445115257164053030000 0ustar00zuulzuul# Copyright(c) 2021 Inspur # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import yaml DEFAULT_SEQUENCE = ['manage', 'tenant', 'storage'] # matrix is combined by health and actions. # health: [x, x, x], repreasents status of DEFAULT_SEQUENCE. # action, means which actions it will trigge if host health turns into. # action choice: 'recovery'. # 'recovery' means it will trigge one host recovery event. DEFAULT_MATRIX = [ {"health": ["up", "up", "up"], "action": []}, {"health": ["up", "up", "down"], "action": ["recovery"]}, {"health": ["up", "down", "up"], "action": []}, {"health": ["up", "down", "down"], "action": ["recovery"]}, {"health": ["down", "up", "up"], "action": []}, {"health": ["down", "up", "down"], "action": ["recovery"]}, {"health": ["down", "down", "up"], "action": []}, {"health": ["down", "down", "down"], "action": ["recovery"]}, ] class MatrixManager(object): """Matrix Manager""" def __init__(self, CONF): cfg_file = CONF.consul.matrix_config_file matrix_conf = self.load_config(cfg_file) if not matrix_conf: self.sequence = DEFAULT_SEQUENCE self.matrix = DEFAULT_MATRIX else: self.sequence = matrix_conf.get("sequence") self.matrix = matrix_conf.get("matrix") self.valid_matrix(self.matrix, self.sequence) def load_config(self, cfg_file): if not cfg_file or not os.path.exists(cfg_file): return None with open(cfg_file) as f: data = f.read() matrix_conf = yaml.safe_load(data) return matrix_conf def get_sequence(self): return self.sequence def get_matrix(self): return self.matrix def valid_matrix(self, matrix, sequence): pass ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4512868 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/driver.py0000664000175000017500000000161215257164053023764 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import abc class DriverBase(object, metaclass=abc.ABCMeta): """Driver Base class. This class is base of monitoring hosts. """ def __init__(self): pass @abc.abstractmethod def monitor_hosts(self): """Must override monitor_hosts method.""" pass ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4512868 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/host.py0000664000175000017500000000375015257164053023453 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os from stevedore import driver from oslo_log import log as oslo_logging import masakarimonitors.conf from masakarimonitors import manager LOG = oslo_logging.getLogger(__name__) CONF = masakarimonitors.conf.CONF class HostmonitorManager(manager.Manager): """Manages the masakari-hostmonitor.""" def __init__(self, *args, **kwargs): super(HostmonitorManager, self).__init__( service_name="hostmonitor", *args, **kwargs) self.driver = None def init_host(self): """Initialization for hostmonitor.""" try: # Determine dynamic load driver from configuration. driver_name = CONF.host.monitoring_driver # Load the driver to global. self.driver = driver.DriverManager( namespace='hostmonitor.driver', name=driver_name, invoke_on_load=True, invoke_args=(), ) except Exception as e: LOG.exception( "Exception caught during initializing hostmonitor: %s", e) os._exit(1) def stop(self): self.driver.driver.stop() def main(self): """Main method.""" try: # Call the host monitoring driver. self.driver.driver.monitor_hosts() except Exception as e: LOG.exception("Exception caught: %s", e) return ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2042196 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/host_handler/0000775000175000017500000000000015257164140024566 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4512868 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/host_handler/__init__.py0000664000175000017500000000000015257164053026670 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4522872 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/host_handler/handle_host.py0000664000175000017500000004422715257164053027444 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import socket from collections import deque import eventlet from oslo_log import log as oslo_logging from oslo_utils import timeutils import masakarimonitors.conf from masakarimonitors.ha import masakari import masakarimonitors.hostmonitor.driver as driver from masakarimonitors.hostmonitor.host_handler import hold_host_status from masakarimonitors.hostmonitor.host_handler import parse_cib_xml from masakarimonitors.hostmonitor.host_handler import parse_crmmon_xml from masakarimonitors.objects import event_constants as ec from masakarimonitors import utils LOG = oslo_logging.getLogger(__name__) CONF = masakarimonitors.conf.CONF class CibSchemaCompliantTag(dict): """Create a dict which has the same attributes as a cib node tag. Given a crm node tag convert it to a dict with corresponding cib tag attributes. """ def __init__(self, crmon_entry): self['uname'] = crmon_entry.get('name') online = crmon_entry.get('online') self['crmd'] = 'online' if online == 'true' else 'offline' class HandleHost(driver.DriverBase): """Handle hosts. This class handles the host status. """ def __init__(self): super(HandleHost, self).__init__() self.my_hostname = socket.gethostname() self.xml_parser = parse_cib_xml.ParseCibXml() self.crmmon_xml_parser = parse_crmmon_xml.ParseCrmMonXml() self.status_holder = hold_host_status.HostHoldStatus() self.notifier = masakari.SendNotification() self.monitoring_data = {} def _update_monitoring_data(self, hostname, status): health_history = self.monitoring_data.setdefault( hostname, deque([], maxlen=CONF.host.monitoring_samples)) health_history.append(status) def get_stabilised_host_status(self, hostname): health_history = self.monitoring_data.get(hostname) if len(health_history) < CONF.host.monitoring_samples: LOG.debug("Not enough monitoring data for host %s.", hostname) return '_being_collected' stabilised_status = health_history[0] # If and only if the sequence of host status is consistently the same, # will it return that status. if len(health_history) == health_history.count(stabilised_status): return stabilised_status else: return '_uncertain' def _check_pacemaker_services(self, target_service): try: cmd_str = 'systemctl status ' + target_service command = cmd_str.split() # Execute command. out, err = utils.execute(*command, run_as_root=True) if err: raise Exception("Service check for %s returned errors: %s" % (target_service, err)) return True except Exception: LOG.warning("Failed to check pacemaker service '%s'.", target_service, exc_info=True) return False def _check_hb_line(self): """Check whether the corosync communication is normal. :returns: 0 if normal, 1 if abnormal, 2 if configuration file is wrong or neither pacemaker nor pacemaker-remote is running. """ if CONF.host.pacemaker_node_type == 'autodetect': # Check whether the Corosync/Pacemaker services are running. corosync_status = self._check_pacemaker_services('corosync') pacemaker_status = self._check_pacemaker_services('pacemaker') pacemaker_remote_status = self._check_pacemaker_services( 'pacemaker_remote') else: corosync_status = ( CONF.host.pacemaker_node_type == 'cluster') pacemaker_status = ( CONF.host.pacemaker_node_type == 'cluster') pacemaker_remote_status = ( CONF.host.pacemaker_node_type == 'remote') if corosync_status is False or pacemaker_status is False: if pacemaker_remote_status is False: LOG.error( "Neither pacemaker nor pacemaker-remote is running.") return 2 else: LOG.info("Works on pacemaker-remote.") return 0 # Check whether the necessary parameters are set. if not CONF.host.corosync_multicast_interfaces or \ not CONF.host.corosync_multicast_ports: msg = ("corosync_multicast_interfaces or " "corosync_multicast_ports is not set.") LOG.error("%s", msg) return 2 # Check whether the corosync communication is normal. corosync_multicast_interfaces = CONF.host.corosync_multicast_interfaces corosync_multicast_ports = CONF.host.corosync_multicast_ports if len(corosync_multicast_interfaces) != len(corosync_multicast_ports): msg = ("Incorrect parameters corosync_multicast_interfaces or " "corosync_multicast_ports.") LOG.error("%s", msg) return 2 is_nic_normal = False for num in range(0, len(corosync_multicast_interfaces)): cmd_str = ("timeout %s tcpdump -n -c 1 -p -i %s port %d") \ % (CONF.host.tcpdump_timeout, corosync_multicast_interfaces[num], corosync_multicast_ports[num]) command = cmd_str.split() try: # Execute tcpdump command. out, err = utils.execute(*command, run_as_root=True) # If command doesn't raise exception, nic is normal. msg = ("Corosync communication using '%s' is normal.") \ % corosync_multicast_interfaces[num] LOG.info("%s", msg) is_nic_normal = True break except Exception: msg = ("Corosync communication using '%s' is failed.") \ % corosync_multicast_interfaces[num] LOG.warning("%s", msg) if is_nic_normal is False: LOG.error("Corosync communication is failed.") return 1 return 0 def _check_host_status_by_crmadmin(self): try: # Execute crmadmin command. out, err = utils.execute('crmadmin', '-S', self.my_hostname, run_as_root=True) if err: msg = ("crmadmin command output stderr: %s") % err raise Exception(msg) # If own host is stable status, crmadmin outputs # 'S_IDLE' or 'S_NOT_DC' if 'S_IDLE' in out or 'S_NOT_DC' in out: return 0 else: raise Exception( "crmadmin command output unexpected host status.") except Exception as e: LOG.warning("Exception caught: %s", e) LOG.warning("'%s' is unstable state on cluster.", self.my_hostname) return 1 def _get_cib_xml(self): try: # Execute cibadmin command. out, err = utils.execute('cibadmin', '--query', run_as_root=True) if err: msg = ("cibadmin command output stderr: %s") % err raise Exception(msg) except Exception as e: LOG.warning("Exception caught: %s", e) return return out def _get_crmmon_xml(self): """Get summary of cluster's current state in XML format.""" try: # Execute crm_mon command. out, err = utils.execute('crm_mon', '-X', run_as_root=True) if err: msg = ("crmmon command output stderr: %s") % err raise Exception(msg) except Exception as e: LOG.warning("Exception caught: %s", e) return return out def _is_poweroff(self, hostname): ipmi_values = self.xml_parser.get_stonith_ipmi_params(hostname) if ipmi_values is None: LOG.error("Failed to get params of ipmi RA.") return False cmd_str = ("timeout %s ipmitool -U %s -P %s -I %s -H %s " "power status") \ % (str(CONF.host.ipmi_timeout), ipmi_values['userid'], ipmi_values['passwd'], ipmi_values['interface'], ipmi_values['ipaddr']) command = cmd_str.split() retry_count = 0 while True: try: # Execute ipmitool command. out, err = utils.execute(*command, run_as_root=False) if err: msg = ("ipmitool command output stderr: %s") % err raise Exception(msg) msg = ("ipmitool command output stdout: %s") % out if 'Power is off' in out: LOG.info("%s", msg) return True else: raise Exception(msg) except Exception as e: if retry_count < CONF.host.ipmi_retry_max: LOG.warning("Retry executing ipmitool command. (%s)", e) retry_count = retry_count + 1 eventlet.greenthread.sleep(CONF.host.ipmi_retry_interval) else: LOG.error("Exception caught: %s", e) return False def _make_event(self, hostname, current_status): if current_status == 'online': # Set values that host has started. event_type = ec.EventConstants.EVENT_STARTED cluster_status = current_status.upper() host_status = ec.EventConstants.HOST_STATUS_NORMAL else: # Set values that host has stopped. event_type = ec.EventConstants.EVENT_STOPPED cluster_status = current_status.upper() if not CONF.host.disable_ipmi_check: if self._is_poweroff(hostname): # Set value that host status is normal. host_status = ec.EventConstants.HOST_STATUS_NORMAL else: # Set value that host status is unknown. host_status = ec.EventConstants.HOST_STATUS_UNKNOWN else: # Set value that host status is normal. host_status = ec.EventConstants.HOST_STATUS_NORMAL current_time = timeutils.utcnow() event = { 'notification': { 'type': ec.EventConstants.TYPE_COMPUTE_HOST, 'hostname': hostname, 'generated_time': current_time, 'payload': { 'event': event_type, 'cluster_status': cluster_status, 'host_status': host_status } } } return event def _normalize_host_status(self, host_status): if host_status is None: return None try: return 'online' if int(host_status) > 0 else 'offline' except ValueError: return host_status def _check_if_status_changed(self, node_state_tag_list): # Check if host status changed. for node_state_tag in node_state_tag_list: hostname = node_state_tag.get('uname') # hostmonitor doesn't monitor itself. if hostname == self.my_hostname: continue current_status = self._normalize_host_status( node_state_tag.get('crmd') ) self._update_monitoring_data(hostname, current_status) old_status = self._normalize_host_status( self.status_holder.get_host_status(hostname) ) # If old_status is None, This is first get of host status. if old_status is None: msg = ("Recognized '%s' as a new member of cluster." " Host status is '%s'.") \ % (hostname, current_status) LOG.info("%s", msg) self.status_holder.set_host_status(node_state_tag) continue stabilised_status = self.get_stabilised_host_status(hostname) # Output host status. msg = ("'%s' is '%s' (current: '%s').") % (hostname, stabilised_status, current_status) LOG.info("%s", msg) if stabilised_status == '_being_collected': continue # If host stabilised status changed, send a notification. if stabilised_status != old_status: if stabilised_status not in ['online', 'offline']: # If stabilised_status is not 'online' or 'offline', # hostmonitor doesn't send a notification. msg = ("Since host status is '%s'," " hostmonitor doesn't send a notification.") \ % stabilised_status LOG.info("%s", msg) else: event = self._make_event(hostname, stabilised_status) # Send a notification. self.notifier.send_notification( CONF.host.api_retry_max, CONF.host.api_retry_interval, event) if stabilised_status != '_uncertain': # Update host status. self.status_holder.set_host_status(node_state_tag) def _check_host_status_by_crm_mon(self): crmmon_xml = self._get_crmmon_xml() if crmmon_xml is None: # crm_mon command failure. return 1 # Set to the ParseCrmMonXml object. self.crmmon_xml_parser.set_crmmon_xml(crmmon_xml) # Check if the cluster has quorum. if not self.crmmon_xml_parser.has_quorum(): msg = "Pacemaker cluster doesn't have quorum." LOG.warning("%s", msg) return 2 # Get node_state tag list. node_state_tag_list = self.crmmon_xml_parser.get_node_state_tag_list() if len(node_state_tag_list) == 0: # If crmmon xml doesn't have node_state tag, # it is an unexpected result. raise Exception( "Failed to get nodes tag from crm_mon xml.") node_state_tag_list = [CibSchemaCompliantTag(n) for n in node_state_tag_list if n.get('type') == 'remote'] # Check if status changed. self._check_if_status_changed(node_state_tag_list) return 0 def _check_host_status_by_cibadmin(self): # Get xml of cib info. cib_xml = self._get_cib_xml() if cib_xml is None: # cibadmin command failure. return 1 # Set to the ParseCibXml object. self.xml_parser.set_cib_xml(cib_xml) # Check if pacemaker cluster have quorum. if self.xml_parser.have_quorum() == 0: msg = "Pacemaker cluster doesn't have quorum." LOG.warning("%s", msg) return 2 # Get node_state tag list. node_state_tag_list = self.xml_parser.get_node_state_tag_list() if len(node_state_tag_list) == 0: # If cib xml doesn't have node_state tag, # it is an unexpected result. raise Exception( "Failed to get node_state tag from cib xml.") # Check if status changed. self._check_if_status_changed(node_state_tag_list) return 0 def stop(self): self.running = False def monitor_hosts(self): """Host monitoring main method. This method monitors hosts. """ self.running = True while self.running: try: # Check whether corosync communication between hosts # is normal. ret = self._check_hb_line() if ret == 1: # Because my host may be fenced by stonith due to split # brain condition, sleep for a certain time. eventlet.greenthread.sleep(CONF.host.stonith_wait) elif ret == 2: LOG.warning("hostmonitor skips monitoring hosts.") eventlet.greenthread.sleep(CONF.host.monitoring_interval) continue if CONF.host.pacemaker_node_type == 'autodetect': pacemaker_remote_status = self._check_pacemaker_services( 'pacemaker_remote') else: pacemaker_remote_status = ( CONF.host.pacemaker_node_type == 'remote') # Check the host status is stable or unstable by crmadmin. # It only checks when this process runs on the full cluster # stack of corosync. if pacemaker_remote_status is False: if self._check_host_status_by_crmadmin() != 0: LOG.warning("hostmonitor skips monitoring hosts.") eventlet.greenthread.sleep( CONF.host.monitoring_interval) continue # Check the host status is online or offline. if CONF.host.restrict_to_remotes: status_func = self._check_host_status_by_crm_mon else: status_func = self._check_host_status_by_cibadmin if status_func() != 0: LOG.warning("hostmonitor skips monitoring hosts.") except Exception as e: LOG.exception("Exception caught: %s", e) eventlet.greenthread.sleep(CONF.host.monitoring_interval) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4522872 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/host_handler/hold_host_status.py0000664000175000017500000000265615257164053030542 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. class HostHoldStatus(object): """Hold host status. This class holds the host status. """ def __init__(self): # host_status is dictionary like {'hostname': 'online'}. self.host_status = {} def set_host_status(self, node_state_tag): """Setter method. This method set host status by node_state tag of cib xml. :params node_state_tag: node_state tag of cib xml. """ self.host_status[node_state_tag.get('uname')] = \ node_state_tag.get('crmd') def get_host_status(self, hostname): """Getter method. This method returns the requested host status. host status is 'online' or 'offline'. :params hostname: Hostname to get status. :returns: Host status. """ return self.host_status.get(hostname) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4522872 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/host_handler/parse_cib_xml.py0000664000175000017500000001424315257164053027756 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from xml.etree import ElementTree from oslo_log import log as oslo_logging LOG = oslo_logging.getLogger(__name__) class ParseCibXml(object): """ParseCibXml class This class parses the cib xml. """ def __init__(self): self.cib_tag = None def set_cib_xml(self, cib_xml): """Set xml.etree.ElementTree.Element object. This method recieves string of cib xml, and convert it to xml.etree.ElementTree.Element object. :params cib_xml: String of cib xml """ # Convert xml.etree.ElementTree.Element object. self.cib_tag = ElementTree.fromstring(cib_xml) def have_quorum(self): """Returns if cluster has quorum or not. :returns: 0 on no-quorum, 1 if cluster has quorum. """ return int(self.cib_tag.get('have-quorum')) def _get_status_tag(self): # status tag exists in the cib tag. child_list = list(self.cib_tag) for child in child_list: if child.tag == 'status': return child return None def _get_node_states(self, status_tag): node_state_tag_list = [] # node_state tag exists in the status tag. child_list = list(status_tag) for child in child_list: if child.tag == 'node_state': node_state_tag_list.append(child) return node_state_tag_list def get_node_state_tag_list(self): """Get node_state tag list. This method gets node_state tag list from cib xml. :returns: node_state tag list """ # Get status tag. status_tag = self._get_status_tag() if status_tag is None: LOG.error("Cib xml doesn't have status tag.") return [] # Get node_state tag list. node_state_tag_list = self._get_node_states(status_tag) if len(node_state_tag_list) == 0: LOG.error("Cib xml doesn't have node_state tag.") return node_state_tag_list def _parse_instance_attributes_tag(self, instance_attributes_tag, hostname): # Parse nvpair tag under the instance_attributes tag. is_target_ipmi = False ipmi_values = {} nvpair_tag_list = list(instance_attributes_tag) for nvpair_tag in nvpair_tag_list: if nvpair_tag.get('name') == 'hostname' and \ nvpair_tag.get('value') == hostname: is_target_ipmi = True elif nvpair_tag.get('name') == 'ipaddr': ipmi_values['ipaddr'] = nvpair_tag.get('value') elif nvpair_tag.get('name') == 'userid': ipmi_values['userid'] = nvpair_tag.get('value') elif nvpair_tag.get('name') == 'passwd': ipmi_values['passwd'] = nvpair_tag.get('value') elif nvpair_tag.get('name') == 'interface': ipmi_values['interface'] = nvpair_tag.get('value') if is_target_ipmi is True: return ipmi_values else: return None def _parse_primitive_tag(self, primitive_tag, hostname): if primitive_tag.get('type') != 'external/ipmi': return None # Parse instance_attributes tag under the primitive tag. child_list = list(primitive_tag) for child in child_list: if child.tag == 'instance_attributes': ipmi_values = self._parse_instance_attributes_tag( child, hostname) if ipmi_values is not None: return ipmi_values return None def _parse_group_tag(self, group_tag, hostname): # Parse primitive tag under the group tag. child_list = list(group_tag) for child in child_list: if child.tag == 'primitive': ipmi_values = self._parse_primitive_tag(child, hostname) if ipmi_values is not None: return ipmi_values return None def get_stonith_ipmi_params(self, hostname): """Get stonith ipmi params from cib xml. This method gets params of ipmi resource agent(RA) which is set on resources tag. The resources tag exists under the configuration tag. And it is assumed that ipmi RA belongs to some resource group. :params hostname: hostname :returns: Dictionary of ipmi RA's params. They are ipaddr, userid, passwd and interface. """ # Get configuration tag from cib tag. configuration_tag = None child_list = list(self.cib_tag) for child in child_list: if child.tag == 'configuration': configuration_tag = child break if configuration_tag is None: LOG.error("Cib xml doesn't have configuration tag.") return None # Get resources tag from configuration tag. resources_tag = None child_list = list(configuration_tag) for child in child_list: if child.tag == 'resources': resources_tag = child break if resources_tag is None: LOG.error("Cib xml doesn't have resources tag.") return None # They are set at nvpair tag which exists under the # instance_attributes of primitive of group tag. ipmi_values = None child_list = list(resources_tag) for child in child_list: if child.tag == 'group': ipmi_values = self._parse_group_tag(child, hostname) if ipmi_values is not None: break return ipmi_values ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4522872 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/host_handler/parse_crmmon_xml.py0000664000175000017500000000606115257164053030513 0ustar00zuulzuul# Copyright(c) 2019 Canonical Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from xml.etree import ElementTree from oslo_log import log as oslo_logging LOG = oslo_logging.getLogger(__name__) class ParseCrmMonXml(object): """ParseCrmMonXml class This class parses the crmmon xml. """ def __init__(self): self.crmmon_tag = None def set_crmmon_xml(self, crmmon_xml): """Set xml.etree.ElementTree.Element object. This method receives string of crmmon xml, and convert it to xml.etree.ElementTree.Element object. :params crmmon_xml: String of crmmon xml """ # Convert xml.etree.ElementTree.Element object. self.crmmon_tag = ElementTree.fromstring(crmmon_xml) def has_quorum(self): """Answers if cluster has quorum or not. :returns: True if cluster has quorum, False otherwise. """ current_dc = self._get_current_dc() return current_dc.get('with_quorum') == 'true' def _get_summary(self): child_list = list(self.crmmon_tag) for child in child_list: if child.tag == 'summary': return child return None def _get_current_dc(self): child_list = list(self._get_summary()) for child in child_list: if child.tag == 'current_dc': return child return None def _get_nodes(self): # status tag exists in the crmmon tag. if self.crmmon_tag is None: return None child_list = list(self.crmmon_tag) for child in child_list: if child.tag == 'nodes': return child return None def _get_node_states(self, nodes_tag): node_state_tag_list = [] # node_state tag exists in the status tag. child_list = list(nodes_tag) for child in child_list: if child.tag == 'node': node_state_tag_list.append(child) return node_state_tag_list def get_node_state_tag_list(self): """Get node_state tag list. This method gets node_state tag list from crmmon xml. :returns: node_state tag list """ # Get status tag. nodes_tag = self._get_nodes() if nodes_tag is None: LOG.error("crm_mon xml doesn't have nodes tag.") return [] # Get node_state tag list. node_state_tag_list = self._get_node_states(nodes_tag) if len(node_state_tag_list) == 0: LOG.error("crm_mon xml doesn't have online tag.") return node_state_tag_list ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2042196 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/kubernetes_check/0000775000175000017500000000000015257164140025420 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4522872 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/kubernetes_check/__init__.py0000664000175000017500000000000015257164053027522 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4522872 masakari_monitors-22.0.0/masakarimonitors/hostmonitor/kubernetes_check/manager.py0000664000175000017500000001107115257164053027407 0ustar00zuulzuul# Copyright(c) 2024 Samsung SDS # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from collections import deque import eventlet from kubernetes import client from kubernetes import config from oslo_log import log from oslo_utils import timeutils import socket from masakarimonitors import conf from masakarimonitors.ha import masakari from masakarimonitors.hostmonitor import driver from masakarimonitors.objects import event_constants as ec LOG = log.getLogger(__name__) CONF = conf.CONF class KubernetesCheck(driver.DriverBase): """Check host status by k8s""" def __init__(self): super(KubernetesCheck, self).__init__() self.hostname = socket.gethostname() self.monitoring_interval = CONF.host.monitoring_interval self.monitoring_samples = CONF.host.monitoring_samples self.notifier = masakari.SendNotification() self.monitoring_data = {} self.last_status = {} self.running = False config.load_incluster_config() self.kube_client = client.CoreV1Api() def update_monitoring_data(self): """update monitoring data from k8s""" LOG.debug("update monitoring data from k8s.") node_list = self.kube_client.list_node( label_selector=CONF.kubernetes.monitoring_node_labels ).items # Remove non-exist node from monitoring_data self.monitoring_data = { node: status for node, status in self.monitoring_data.items() if node in [i.metadata.name for i in node_list] } for host in node_list: hostname = host.metadata.name for condition in host.status.conditions: if condition.type != "Ready": continue host_health_history = self.monitoring_data.get( hostname, deque([], maxlen=self.monitoring_samples)) host_health_history.appendleft(condition.status) LOG.debug("hostname: %s, condition: %s", hostname, host_health_history) # Status is 'True' if node is healthy else 'False' or 'unknown' # Most up-to-date state is stored in index 0 self.monitoring_data[hostname] = host_health_history @staticmethod def _event(host, host_health): host_status = ec.EventConstants.HOST_STATUS_NORMAL LOG.info("Host %s needs recovery, health status: %s.", host, host_health) event = { 'notification': { 'type': ec.EventConstants.TYPE_COMPUTE_HOST, 'hostname': host, 'generated_time': timeutils.utcnow(), 'payload': { 'event': ec.EventConstants.EVENT_STOPPED, 'host_status': host_status } } } return event def poll_hosts(self): """poll and check hosts health""" for host, health_history in self.monitoring_data.items(): if not self.last_status: self.last_status[host] = self.monitoring_data[host][0] if host == self.hostname: continue if len(health_history) < self.monitoring_samples: continue is_host_alive = "True" in health_history if not is_host_alive and self.last_status.get(host): event = self._event(host, is_host_alive) self.notifier.send_notification( CONF.host.api_retry_max, CONF.host.api_retry_interval, event) self.last_status[host] = is_host_alive def stop(self): self.running = False def monitor_hosts(self): self.running = True while self.running: try: self.update_monitoring_data() LOG.info("Monitoring data: %s", self.monitoring_data) self.poll_hosts() except Exception as e: LOG.exception("Exception when host-monitor by k8s: %s", e) eventlet.greenthread.sleep(self.monitoring_interval) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4532874 masakari_monitors-22.0.0/masakarimonitors/i18n.py0000664000175000017500000000165715257164053020674 0ustar00zuulzuul# Copyright 2016 NTT DATA # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import oslo_i18n DOMAIN = 'masakarimonitors' _translators = oslo_i18n.TranslatorFactory(domain=DOMAIN) # The primary translation function using the well-known name "_" _ = _translators.primary def translate(value, user_locale): return oslo_i18n.translate(value, user_locale) def get_available_languages(): return oslo_i18n.get_available_languages(DOMAIN) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2042196 masakari_monitors-22.0.0/masakarimonitors/instancemonitor/0000775000175000017500000000000015257164140022743 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4532874 masakari_monitors-22.0.0/masakarimonitors/instancemonitor/__init__.py0000664000175000017500000000000015257164053025045 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4532874 masakari_monitors-22.0.0/masakarimonitors/instancemonitor/instance.py0000664000175000017500000001673415257164053025137 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import threading import time import eventlet import libvirt from oslo_config import cfg from oslo_log import log as oslo_logging from masakarimonitors.instancemonitor.libvirt_handler import eventfilter from masakarimonitors import manager LOG = oslo_logging.getLogger(__name__) CONF = cfg.CONF class InstancemonitorManager(manager.Manager): """Manages the masakari-instancemonitor.""" def __init__(self, *args, **kwargs): super(InstancemonitorManager, self).__init__( service_name="instancemonitor", *args, **kwargs) self.evf = eventfilter.EventFilter() # This keeps track of what thread is running the event loop, # (if it is run in a background thread) self.event_loop_thread = None def _vir_event_loop_native_run(self): # Directly run the event loop in the current thread while True: libvirt.virEventRunDefaultImpl() def _vir_event_loop_native_start(self): libvirt.virEventRegisterDefaultImpl() self.event_loop_thread = threading.Thread( target=self._vir_event_loop_native_run, name="lib_virt_eventLoop") self.event_loop_thread.daemon = True self.event_loop_thread.start() def _my_domain_event_callback(self, conn, dom, event, detail, opaque): self.evf.vir_event_filter(libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, event, detail, dom.UUIDString()) def _my_domain_event_reboot_callback(self, conn, dom, opaque): self.evf.vir_event_filter(libvirt.VIR_DOMAIN_EVENT_ID_REBOOT, -1, -1, dom.UUIDString()) def _my_domain_event_rtc_change_callback(self, conn, dom, utcoffset, opaque): self.evf.vir_event_filter(libvirt.VIR_DOMAIN_EVENT_ID_RTC_CHANGE, -1, -1, dom.UUIDString()) def _my_domain_event_watchdog_callback(self, conn, dom, action, opaque): self.evf.vir_event_filter(libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG, action, -1, dom.UUIDString()) def _my_domain_event_io_error_callback(self, conn, dom, srcpath, devalias, action, opaque): self.evf.vir_event_filter(libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR, action, -1, dom.UUIDString()) def _my_domain_event_graphics_callback(self, conn, dom, phase, localAddr, remoteAddr, authScheme, subject, opaque): self.evf.vir_event_filter(libvirt.VIR_DOMAIN_EVENT_ID_GRAPHICS, -1, phase, dom.UUIDString()) def _my_domain_event_disk_change_callback(self, conn, dom, oldSrcPath, newSrcPath, devAlias, reason, opaque): self.evf.vir_event_filter(libvirt.VIR_DOMAIN_EVENT_ID_DISK_CHANGE, -1, -1, dom.UUIDString()) def _my_domain_event_io_error_reason_callback(self, conn, dom, srcPath, devAlias, action, reason, opaque): self.evf.vir_event_filter(libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON, -1, -1, dom.UUIDString()) def _my_domain_event_generic_callback(self, conn, dom, opaque): self.evf.vir_event_filter(libvirt.VIR_DOMAIN_EVENT_ID_CONTROL_ERROR, -1, -1, dom.UUIDString()) def _err_handler(self, ctxt, err): LOG.warning("Error from libvirt : %s", err[2]) @staticmethod def _connect_auth_cb(creds, user_data): if len(creds) == 0: return 0 raise Exception("Can not handle authentication request for %d " "credentials" % len(creds)) def _virt_event(self, uri): auth = [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_ECHOPROMPT, libvirt.VIR_CRED_REALM, libvirt.VIR_CRED_PASSPHRASE, libvirt.VIR_CRED_NOECHOPROMPT, libvirt.VIR_CRED_EXTERNAL], InstancemonitorManager._connect_auth_cb, None] flags = libvirt.VIR_CONNECT_RO # Run a background thread with the event loop self._vir_event_loop_native_start() event_callback_handlers = { libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE: self._my_domain_event_callback, libvirt.VIR_DOMAIN_EVENT_ID_REBOOT: self._my_domain_event_reboot_callback, libvirt.VIR_DOMAIN_EVENT_ID_RTC_CHANGE: self._my_domain_event_rtc_change_callback, libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR: self._my_domain_event_io_error_callback, libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG: self._my_domain_event_watchdog_callback, libvirt.VIR_DOMAIN_EVENT_ID_GRAPHICS: self._my_domain_event_graphics_callback, libvirt.VIR_DOMAIN_EVENT_ID_DISK_CHANGE: self._my_domain_event_disk_change_callback, libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON: self._my_domain_event_io_error_reason_callback, libvirt.VIR_DOMAIN_EVENT_ID_CONTROL_ERROR: self._my_domain_event_generic_callback } # Connect to libvirt - If be disconnected, reprocess. self.running = True while self.running: vc = libvirt.openAuth(uri, auth, flags) # Event callback settings callback_ids = [] for event, callback in event_callback_handlers.items(): cid = vc.domainEventRegisterAny(None, event, callback, None) callback_ids.append(cid) # Connection monitoring. vc.setKeepAlive(5, 3) while vc.isAlive() == 1 and self.running: eventlet.greenthread.sleep(1) # If connection between libvirtd was lost, # clear callback connection. LOG.warning("Libvirt Connection Closed Unexpectedly.") for cid in callback_ids: try: vc.domainEventDeregisterAny(cid) except Exception: LOG.debug("Failed to deregister libvirt event " "callback %s.", cid, exc_info=True) vc.close() del vc time.sleep(3) def stop(self): self.running = False def main(self): """Main method. Set the URI, error handler, and executes event loop processing. """ uri = CONF.libvirt.connection_uri LOG.debug("Using uri:" + uri) # set error handler & do event loop libvirt.registerErrorHandler(self._err_handler, '_virt_event') self._virt_event(uri) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2052195 masakari_monitors-22.0.0/masakarimonitors/instancemonitor/libvirt_handler/0000775000175000017500000000000015257164140026113 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4542875 masakari_monitors-22.0.0/masakarimonitors/instancemonitor/libvirt_handler/__init__.py0000664000175000017500000000000015257164053030215 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4542875 masakari_monitors-22.0.0/masakarimonitors/instancemonitor/libvirt_handler/callback.py0000664000175000017500000000557715257164053030242 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_log import log as oslo_logging import masakarimonitors.conf from masakarimonitors.ha import masakari LOG = oslo_logging.getLogger(__name__) CONF = masakarimonitors.conf.CONF class Callback(object): """Class of callback processing.""" def __init__(self): self.notifier = masakari.SendNotification() def libvirt_event_callback(self, event_id, details, domain_uuid, notice_type, hostname, current_time): """Callback method. Callback processing be executed as result of the libvirt event filter. :param event_id: Event ID notify to the callback function :param details: Event code notify to the callback function :param domain_uuid: Uuid notify to the callback function :param notice_type: Notice type notify to the callback function :param hostname: Server host name of the source an event occur notify to the callback function :param current_time: Event occurred time notify to the callback function """ # Output to the syslog. LOG.info("Libvirt Event: type=%(notice_type)s," " hostname=%(hostname)s," " uuid=%(uuid)s, time=%(current_time)s," " event_id=%(event_id)s," " detail=%(detail)s)", {'notice_type': notice_type, 'hostname': hostname, 'uuid': domain_uuid, 'current_time': current_time, 'event_id': event_id, 'detail': details}) # Set the event to the dictionary. event = { 'notification': { 'type': notice_type, 'hostname': hostname, 'generated_time': current_time, 'payload': { 'event': event_id, 'instance_uuid': domain_uuid, 'vir_domain_event': details } } } self.notifier.send_notification(CONF.callback.retry_max, CONF.callback.retry_interval, event) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4542875 masakari_monitors-22.0.0/masakarimonitors/instancemonitor/libvirt_handler/eventfilter.py0000664000175000017500000000567215257164053031031 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import socket import sys import threading from oslo_log import log as oslo_logging from oslo_utils import excutils from oslo_utils import timeutils import masakarimonitors.conf from masakarimonitors.instancemonitor.libvirt_handler import callback from masakarimonitors.instancemonitor.libvirt_handler \ import eventfilter_table as evft from masakarimonitors.objects import event_constants as ec LOG = oslo_logging.getLogger(__name__) CONF = masakarimonitors.conf.CONF class EventFilter(object): """Class of filtering events.""" def __init__(self): self.callback = callback.Callback() def vir_event_filter(self, eventID, eventType, detail, uuID): """Filter events from libvirt. :param eventID: EventID :param eventType: Event type :param detail: Event name :pram uuID: UUID """ noticeType = ec.EventConstants.TYPE_VM hostname = CONF.hostname or socket.gethostname() currentTime = timeutils.utcnow() # All Event Output if debug mode is on. msg = "libvirt Event Received.type = %s \ hostname = %s uuid = %s time = %s eventID = %d eventType = %d \ detail = %d" % ( noticeType, hostname, uuID, currentTime, eventID, eventType, detail) LOG.debug(msg) try: if detail in evft.event_filter_dic[eventID][eventType]: LOG.debug("Event Filter Matched.") eventID_val = evft.eventID_dic[eventID] detail_val = evft.detail_dic[eventID][eventType][detail] # callback Thread Start thread = threading.Thread( target=self.callback.libvirt_event_callback, args=(eventID_val, detail_val, uuID, noticeType, hostname, currentTime) ) thread.start() else: LOG.debug("Event Filter Unmatched.") except KeyError: LOG.debug("virEventFilter KeyError") except IndexError: LOG.debug("virEventFilter IndexError") except TypeError: LOG.debug("virEventFilter TypeError") except Exception: with excutils.save_and_reraise_exception(): LOG.debug("Unexpected error: %s", sys.exc_info()[0]) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4552877 masakari_monitors-22.0.0/masakarimonitors/instancemonitor/libvirt_handler/eventfilter_table.py0000664000175000017500000001105715257164053032172 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import libvirt # If is not defined internal , -1 is stored. DUMMY = -1 # Enumerate all event that can get. # Comment out events that is not targeted in the callback. event_filter_dic = { libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE: { libvirt.VIR_DOMAIN_EVENT_SUSPENDED: ( libvirt.VIR_DOMAIN_EVENT_SUSPENDED_IOERROR, libvirt.VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG, libvirt.VIR_DOMAIN_EVENT_SUSPENDED_API_ERROR ), libvirt.VIR_DOMAIN_EVENT_STOPPED: ( libvirt.VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN, libvirt.VIR_DOMAIN_EVENT_STOPPED_DESTROYED, libvirt.VIR_DOMAIN_EVENT_STOPPED_FAILED, ), libvirt.VIR_DOMAIN_EVENT_SHUTDOWN: ( libvirt.VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED, ) }, libvirt.VIR_DOMAIN_EVENT_ID_REBOOT: {DUMMY: (DUMMY,)}, libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG: { libvirt.VIR_DOMAIN_EVENT_WATCHDOG_NONE: (DUMMY,), libvirt.VIR_DOMAIN_EVENT_WATCHDOG_PAUSE: (DUMMY,), libvirt.VIR_DOMAIN_EVENT_WATCHDOG_RESET: (DUMMY,), libvirt.VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF: (DUMMY,), libvirt.VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN: (DUMMY,), libvirt.VIR_DOMAIN_EVENT_WATCHDOG_DEBUG: (DUMMY,) }, libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR: { libvirt.VIR_DOMAIN_EVENT_IO_ERROR_NONE: (DUMMY,), libvirt.VIR_DOMAIN_EVENT_IO_ERROR_PAUSE: (DUMMY,), libvirt.VIR_DOMAIN_EVENT_IO_ERROR_REPORT: (DUMMY,) }, libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON: {DUMMY: (DUMMY,)}, libvirt.VIR_DOMAIN_EVENT_ID_CONTROL_ERROR: {DUMMY: (DUMMY,)} } eventID_dic = { libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE: 'LIFECYCLE', libvirt.VIR_DOMAIN_EVENT_ID_REBOOT: 'REBOOT', libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG: 'WATCHDOG', libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR: 'IO_ERROR', libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON: 'IO_ERROR_REASON', libvirt.VIR_DOMAIN_EVENT_ID_CONTROL_ERROR: 'CONTROL_ERROR'} detail_dic = { libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE: { libvirt.VIR_DOMAIN_EVENT_SUSPENDED: { libvirt.VIR_DOMAIN_EVENT_SUSPENDED_IOERROR: 'SUSPENDED_IOERROR', libvirt.VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG: 'SUSPENDED_WATCHDOG', libvirt.VIR_DOMAIN_EVENT_SUSPENDED_API_ERROR: 'SUSPENDED_API_ERROR'}, libvirt.VIR_DOMAIN_EVENT_STOPPED: { libvirt.VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN: 'STOPPED_SHUTDOWN', libvirt.VIR_DOMAIN_EVENT_STOPPED_DESTROYED: 'STOPPED_DESTROYED', libvirt.VIR_DOMAIN_EVENT_STOPPED_FAILED: 'STOPPED_FAILED'}, libvirt.VIR_DOMAIN_EVENT_SHUTDOWN: { libvirt.VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED: 'SHUTDOWN_FINISHED'} }, libvirt.VIR_DOMAIN_EVENT_ID_REBOOT: { DUMMY: { DUMMY: 'UNKNOWN'}}, libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG: { libvirt.VIR_DOMAIN_EVENT_WATCHDOG_NONE: { DUMMY: 'WATCHDOG_NONE'}, libvirt.VIR_DOMAIN_EVENT_WATCHDOG_PAUSE: { DUMMY: 'WATCHDOG_PAUSE'}, libvirt.VIR_DOMAIN_EVENT_WATCHDOG_RESET: { DUMMY: 'WATCHDOG_RESET'}, libvirt.VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF: { DUMMY: 'WATCHDOG_POWEROFF'}, libvirt.VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN: { DUMMY: 'WATCHDOG_SHUTDOWN'}, libvirt.VIR_DOMAIN_EVENT_WATCHDOG_DEBUG: { DUMMY: 'WATCHDOG_DEBUG'}}, libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR: { libvirt.VIR_DOMAIN_EVENT_IO_ERROR_NONE: { DUMMY: 'IO_ERROR_NONE'}, libvirt.VIR_DOMAIN_EVENT_IO_ERROR_PAUSE: { DUMMY: 'IO_ERROR_PAUSE'}, libvirt.VIR_DOMAIN_EVENT_IO_ERROR_REPORT: { DUMMY: 'IO_ERROR_REPORT'}}, libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON: { DUMMY: { DUMMY: 'UNKNOWN'}}, libvirt.VIR_DOMAIN_EVENT_ID_CONTROL_ERROR: { DUMMY: { DUMMY: 'UNKNOWN'}} } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2062194 masakari_monitors-22.0.0/masakarimonitors/introspectiveinstancemonitor/0000775000175000017500000000000015257164140025562 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4552877 masakari_monitors-22.0.0/masakarimonitors/introspectiveinstancemonitor/README.rst0000664000175000017500000000522115257164053027254 0ustar00zuulzuul========================================= masakarimonitors-introspectiveinstancemonitor ========================================= Introspective instance monitor for Masakari ---------------------------------------- - masakarimonitors-introspectiveinstancemonitor, provides Virtual Machine High Availability (VMHA) service for OpenStack clouds by automatically detecting the system-level failure events via QEMU Guest Agent. If it detects VM heartbeat failure events, it sends notifications to the masakari-api. - Based on the QEMU Guest Agent, masakarimonitors-introspectiveinstancemonitor aims to provide access to a system-level agent via standard qemu-ga protocol How does it work? ---------------------------------------- - libvirt and QEMU Guest Agent are used as the underlying protocol for messaging to and from VM. - The host-side qemu-agent sockets are used to determine whether VMs are configured with QEMU Guest Agent. - qemu-guest-ping is used as the monitoring heartbeat. - For the future release, we can pass through arbitrary guest agent commands to check the health of the applications inside a VM. QEMU Guest Agent Installation notes ---------------------------------------- - Set image property: hw_qemu_guest_agent=yes. - This tells NOVA to setup the virtual serial interface thru QEMU to VM - e.g. $ openstack image create --public --disk-format qcow2 --container-format bare --file ~ubuntu/xenial-server-cloudimg-amd64-disk1.img --public --property hw_qemu_guest_agent=yes xenial-server-cloudimg * Inside VM:: $ sudo apt-get install qemu-guest-agent $ sudo systemctl start qemu-guest-agent $ ubuntu@test:~$ ps -ef | fgrep qemu $ ... /usr/sbin/qemu-ga --daemonize -m virtio-serial -p /dev/virtio-ports/org.qemu.guest_agent.0 $ ubuntu@test:~$ ls /dev/virtio-ports/ $ org.qemu.guest_agent.0 Configure masakarimonitors-introspectiveinstancemonitor ---------------------------------------------- #. Clone masakari-monitors using:: $ git clone https://github.com/openstack/masakari-monitors.git #. Create masakarimonitors directory in /etc/. #. Run setup.py from masakari-monitors:: $ sudo python setup.py install #. Copy masakarimonitors.conf and process_list.yaml files from masakari-monitors/etc/ to /etc/masakarimonitors folder and make necessary changes to the masakarimonitors.conf and process_list.yaml files. To generate the sample masakarimonitors.conf file, run the following command from the top level of the masakari-monitors directory:: $ tox -egenconfig #. To run masakari-introspectiveinstancemonitor simply use following binary:: $ masakari-introspectiveinstancemonitor ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4552877 masakari_monitors-22.0.0/masakarimonitors/introspectiveinstancemonitor/__init__.py0000664000175000017500000000000015257164053027664 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4552877 masakari_monitors-22.0.0/masakarimonitors/introspectiveinstancemonitor/cache.py0000664000175000017500000000404715257164053027207 0ustar00zuulzuul# Copyright (c) 2018 WindRiver Systems # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. """ The code related to integration between oslo.cache module and masakarimonitors. Use 'oslo_cache.dict' i.e. dogpile.cache backend that uses dictionary for storage Dogpile consists of two subsystems, one building on top of the other. dogpile provides the concept of a dogpile lock, a control structure which allows a single thread of execution to be selected as the creator of some resource, while allowing other threads of execution to refer to the previous version of this resource as the creation proceeds; if there is no previous version, then those threads block until the object is available. """ from oslo_cache import core from oslo_config import cfg WEEK = 604800 def register_cache_configurations(conf): """Register all configurations required for oslo.cache. The procedure registers all configurations required for oslo.cache. It should be called before configuring of cache region :param conf: instance of configuration :returns: updated configuration """ # register global configurations for caching core.configure(conf) return conf # variable that stores an initialized cache region _REGION = None def get_cache_region(): global _REGION if not _REGION: _REGION = core.create_region() _REGION.configure('oslo_cache.dict', arguments={'expiration_time': WEEK}) core.configure_cache_region( conf=register_cache_configurations(cfg.CONF), region=_REGION) return _REGION ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4552877 masakari_monitors-22.0.0/masakarimonitors/introspectiveinstancemonitor/instance.py0000664000175000017500000001376615257164053027760 0ustar00zuulzuul# Copyright (c) 2018 WindRiver Systems # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import eventlet import libvirt import socket import sys import threading import time from oslo_config import cfg from oslo_log import log as oslo_logging from oslo_utils import excutils from oslo_utils import timeutils from masakarimonitors.introspectiveinstancemonitor import qemu_utils from masakarimonitors.introspectiveinstancemonitor import scheduler from masakarimonitors import manager from masakarimonitors.objects import event_constants as ec LOG = oslo_logging.getLogger(__name__) CONF = cfg.CONF class IntrospectiveInstanceMonitorManager(manager.Manager): def __init__(self, *args, **kwargs): self.init_tgm() super(IntrospectiveInstanceMonitorManager, self).__init__( service_name="introspectiveinstancemonitor", *args, **kwargs) # This keeps track of what thread is running the event loop, # (if it is run in a background thread) self.event_loop_thread = None def _reset_journal(self, event_id, event_type, detail, domain_uuid): """To reset the monitoring to discovery stage :param event_id: Event ID :param event_type: Event type :param detail: Event code :param domain_uuid: Domain uuid """ def _reset_handler(event_id, event_type, detail, domain_uuid): """Reset monitoring To reset monitoring to discovery stage for the following event: libvirt.VIR_DOMAIN_EVENT_STARTED """ LOG.debug( "libvirt Event Received.type = %s hostname = %s uuid = %s " "time = %s eventID = %d eventType = %d detail = %d", ec.EventConstants.TYPE_VM, socket.gethostname(), domain_uuid, timeutils.utcnow(), event_id, event_type, detail) if (event_id == libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE) and \ (event_type == libvirt.VIR_DOMAIN_EVENT_STARTED): qemu_utils.resetJournal(domain_uuid) try: thread = threading.Thread( _reset_handler(event_id, event_type, detail, domain_uuid) ) thread.start() except KeyError as e: LOG.error("virEventFilter KeyError: {0}", e) except IndexError as e: LOG.error("virEventFilter IndexError: {0}", e) except TypeError as e: LOG.error("virEventFilter TypeError: {0}", e) except Exception: with excutils.save_and_reraise_exception(): LOG.error("Unexpected error: %s", sys.exc_info()[0]) def init_tgm(self): """Manages the masakari-introspectiveinstancemonitor.""" self.TG = scheduler.ThreadGroupManager() def _vir_event_loop_native_run(self): # Directly run the event loop in the current thread while True: libvirt.virEventRunDefaultImpl() def _vir_event_loop_native_start(self): libvirt.virEventRegisterDefaultImpl() self.event_loop_thread = threading.Thread( target=self._vir_event_loop_native_run, name="lib_virt_eventLoop") self.event_loop_thread.daemon = True self.event_loop_thread.start() def _my_domain_event_callback(self, conn, dom, event, detail, opaque): self._reset_journal(libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, event, detail, dom.UUIDString()) def _my_domain_event_reboot_callback(self, conn, dom, opaque): self._reset_journal(libvirt.VIR_DOMAIN_EVENT_ID_REBOOT, -1, -1, dom.UUIDString()) def _err_handler(self, ctxt, err): LOG.warning("Error from libvirt : %s", err[2]) def _virt_event(self, uri): # Run a background thread with the event loop self._vir_event_loop_native_start() event_callback_handlers = { libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE: self._my_domain_event_callback, libvirt.VIR_DOMAIN_EVENT_ID_REBOOT: self._my_domain_event_reboot_callback } # Connect to libvirt - If be disconnected, reprocess. self.running = True while self.running: vc = libvirt.openReadOnly(uri) # Event callback settings callback_ids = [] for event, callback in event_callback_handlers.items(): cid = vc.domainEventRegisterAny(None, event, callback, None) callback_ids.append(cid) # Connection monitoring. vc.setKeepAlive(5, 3) while vc.isAlive() == 1 and self.running: eventlet.greenthread.sleep(1) # If connection between libvirtd was lost, # clear callback connection. LOG.warning("Libvirt Connection Closed Unexpectedly.") for cid in callback_ids: try: vc.domainEventDeregisterAny(cid) except Exception: LOG.debug("Failed to deregister libvirt event " "callback %s.", cid, exc_info=True) vc.close() del vc time.sleep(3) def stop(self): self.running = False def main(self): """Main method. Set the URI, error handler, and executes event loop processing. """ uri = CONF.libvirt.connection_uri LOG.debug("Using uri:" + uri) # set error handler & do event loop libvirt.registerErrorHandler(self._err_handler, '_virt_event') self._virt_event(uri) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4552877 masakari_monitors-22.0.0/masakarimonitors/introspectiveinstancemonitor/qemu_utils.py0000664000175000017500000003321115257164053030326 0ustar00zuulzuul# Copyright (c) 2018 WindRiver Systems # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # Introspective instance monitoring depends on the qemu guest agent to # monitoring what inside of a VM. # # A few items to get in the way of the design: # # - After VM is active, it needs time to start qemu-guest-agent. # Before error/failure is reported, we need a discovery phase to wait # until VM is guest_pingable. # # - Debouncing is needed to enforce that masakari_notifier function # not calling twice or more for the same failure. # # After reported a masakari notification, sent_notification flag will # need to be reset when there is a QEMU LIFECYCLE event like: # STARTED_BOOTED # SUSPENDED_PAUSED import eventlet import libvirt import libvirtmod_qemu import logging import re import socket import time import traceback # Machine module contains the state machine logic, state and transition events from automaton import machines from dogpile.cache.api import NoValue from lxml import etree from oslo_config import cfg from oslo_utils import timeutils from masakarimonitors.ha import masakari from masakarimonitors.introspectiveinstancemonitor import cache from masakarimonitors.objects import event_constants as ec from masakarimonitors import utils CONF = cfg.CONF ICONF = cfg.CONF.introspectiveinstancemonitor # The VM QEMU Quest Agent states # # discovery = initial state of VM, # remains in this state until it is determined that # the VM has a qemu-agent interface enabling intrusive-instance-monitoring # # healthy = no failure event is detected # # error = An error is recorded on every audit cycle where # the VM is in the error state # # reported = a transient state # to keep track of reporting of notification # # Transitions # # Representation of a transition managed by a ``Machine`` instance. # source (str): Source state of the transition. # dest (str): Destination state of the transition. # trigger (str): The type of triggering event # that advances to the next state in the sequence. def action_on_enter(new_state, triggered_event): pass def action_on_exit(old_state, triggered_event): pass STATE_SPACE = [ { 'name': 'discovery', 'next_states': { 'guest_pingable': 'healthy', 'guest_not_pingable': 'discovery', }, 'on_enter': action_on_enter, 'on_exit': action_on_exit, }, { 'name': 'healthy', 'next_states': { 'guest_pingable': 'healthy', 'guest_not_pingable': 'error', }, 'on_enter': action_on_enter, 'on_exit': action_on_exit, }, { 'name': 'error', 'next_states': { 'report': 'reported', 'guest_pingable': 'healthy', 'guest_not_pingable': 'error', }, 'on_enter': action_on_enter, 'on_exit': action_on_exit, }, { 'name': 'reported', 'next_states': { 'guest_pingable': 'discovery', 'guest_not_pingable': 'reported', }, 'on_enter': action_on_enter, 'on_exit': action_on_exit, }, ] # Journal representation of a managed ``Machine`` instance. class Journal(machines.FiniteMachine): # Factory pattern to create a Journal object @classmethod def factory(cls, domain_uuid): jo = cls.build(STATE_SPACE) jo.default_start_state = 'discovery' jo.initialize() jo.failedCount = 0 # Conditions to reset sent_notification jo.sent_notification = False jo.domain_uuid = domain_uuid jo.lastUsed = time.time() LOG.debug(str(domain_uuid) + ':Journal:__init__:') return jo def resetState(self): self.default_start_state = 'discovery' self.initialize() self.failedCount = 0 self.sent_notification = False LOG.debug(str(self.domain_uuid) + '__resetState__:') def processEvent(self, event): self.process_event(event) self.lastUsed = time.time() def getFailedCount(self): return self.failedCount def incrementFailedCount(self): if (self.current_state == 'error'): self.failedCount += 1 def resetFailedCount(self): self.failedCount = 0 def setSentNotification(self, boolean): self.sent_notification = boolean def getSentNotification(self): return self.sent_notification # libvirt state verbose dictionary STATES = { libvirt.VIR_DOMAIN_NOSTATE: 'no state', libvirt.VIR_DOMAIN_RUNNING: 'running', libvirt.VIR_DOMAIN_BLOCKED: 'blocked on resource', libvirt.VIR_DOMAIN_PAUSED: 'paused by user', libvirt.VIR_DOMAIN_SHUTDOWN: 'being shut down', libvirt.VIR_DOMAIN_SHUTOFF: 'shut off', libvirt.VIR_DOMAIN_CRASHED: 'crashed', } LOG = logging.getLogger(__name__) def get_function_name(): return traceback.extract_stack(None, 2)[0][2] # To reset journal object by domain uuid def resetJournal(domain_uuid): # To get the VM Journal object from the dictionary # :param domain: QEMU domain UUID dict = cache.get_cache_region() jo = None if type(dict.get(domain_uuid)) is NoValue: jo = Journal.factory(domain_uuid) dict.set(domain_uuid, jo) else: jo = dict.get(domain_uuid) jo.resetState() # Qemu guest agent is used to check VM status # The checking pre-conditions are as follows: # - VM is running # - VM has Qemu guest agent installed # # then status is determined by # - VM is guest-agent-pingable or not # # Note: checkGuests function is called by the scheduler class QemuGuestAgent(object): def __init__(self): super(QemuGuestAgent, self).__init__() self.notifier = masakari.SendNotification() # _thresholdsCrossing # # We only issue a notification # to masakari-engine if the VM is 'already' in the error state # when there are consecutive guest_ping failures. # Suggested value for guest_monitoring_failure_threshold >= 3 # # Note: When operators are trying to gracefully shutdown VM, # QEMU may take time to powering-off. # E.g. When you do or # you may see that QEMU is active but monitoring may fail # due to VM is still "powering-off" # Status | Task State | Power State # ACTIVE | powering-off | Running def _thresholdsCrossing(self, domain): if (((self.getVmFsm(domain.UUIDString()).current_state) == 'error') and (self._getJournalObject(domain.UUIDString()).getFailedCount() > ICONF.guest_monitoring_failure_threshold)): LOG.debug('_thresholdsCrossing:' + domain.UUIDString()) LOG.debug(self._getJournalObject( domain.UUIDString()).getFailedCount()) return True else: return False def _masakari_notifier(self, domain_uuid): if self._getJournalObject(domain_uuid).getSentNotification(): LOG.debug('notifier.send_notification Skipped:' + domain_uuid) else: hostname = socket.gethostname() noticeType = ec.EventConstants.TYPE_VM current_time = timeutils.utcnow() event = { 'notification': { 'type': noticeType, 'hostname': hostname, 'generated_time': current_time, 'payload': { 'event': 'QEMU_GUEST_AGENT_ERROR', 'instance_uuid': domain_uuid, 'vir_domain_event': 'STOPPED_FAILED' } } } try: self.notifier.send_notification(CONF.callback.retry_max, CONF.callback.retry_interval, event) self._getJournalObject(domain_uuid).processEvent('report') self._getJournalObject(domain_uuid).setSentNotification(True) except Exception: LOG.warning('Failed to send notification for instance ' '%s in %s.', domain_uuid, get_function_name(), exc_info=True) def _qemuAgentGuestPing(self, domain, timeout, flags=0): def _no_heartbeat(domain_uuid): # Also advance the FSM self.getVmFsm(domain_uuid).processEvent('guest_not_pingable') self._getJournalObject(domain_uuid).incrementFailedCount() def _with_heartbeat(domain_uuid): #The order matters as we want to decrease the counter first self._getJournalObject(domain_uuid).resetFailedCount() self.getVmFsm(domain_uuid).processEvent('guest_pingable') def _record(result): if result is None: LOG.debug(domain.UUIDString() + '\tqemu-ga_guest-ping is not responding.') if self._thresholdsCrossing(domain): self._masakari_notifier(domain.UUIDString()) _no_heartbeat(domain.UUIDString()) else: _with_heartbeat(domain.UUIDString()) """Send a Guest Agent ping to domain """ # must pass domain._o to the c library as virDomainPtr ret = libvirtmod_qemu.virDomainQemuAgentCommand(domain._o, '{"execute": "guest-ping"}', timeout, flags) _record(ret) def _getJournalObject(self, domain_uuid): """Function: To get the dictionary :param domain: QEMU domain :return: the journal object referred by domain_uuid """ dict = cache.get_cache_region() if type(dict.get(domain_uuid)) is NoValue: jo = Journal.factory(domain_uuid) dict.set(domain_uuid, jo) return jo else: return dict.get(domain_uuid) def getVmFsm(self, domain_uuid): """Function: To get the VM Finite State Machine from the dictionary :param domain: QEMU domain :return: FSM object """ dict = cache.get_cache_region() if type(dict.get(domain_uuid)) is NoValue: jo = Journal.factory(domain_uuid) dict.set(domain_uuid, jo) return jo else: return dict.get(domain_uuid) def _hasQemuGuestAgent(self, domain): """Function: To check whether the VM has an QEMU Guest Agent by examining the qemu.guest_agent sock First check if libvirt is running or not, then sock :param domain: QEMU domain :return: true or false """ def qemuGuestAgentPathMatch(path): SOCK = ICONF.qemu_guest_agent_sock_path return re.match('%s' % SOCK, path) state, reason = domain.state() # First check if libvirt is running or not if state != libvirt.VIR_DOMAIN_RUNNING: return False xmlDesc = domain.XMLDesc() tree = etree.fromstring(xmlDesc) ''' Example
''' try: source = tree.find("devices/channel/source") if (source is not None): mode = source.get('mode') path = source.get('path') # There should be a bind for a sock file for qemu guest_agent if (qemuGuestAgentPathMatch(path) and mode == 'bind'): return True except Exception: LOG.warning("Failed to check QEMU guest agent presence.", exc_info=True) return False def checkGuests(self): """Function: Check QEMU Guests Condition: VM under intrusive monitoring must have QEMU agent client configured, installed and qemu "guest-agent-pingable". """ try: conn = libvirt.open(CONF.libvirt.connection_uri) for domain_id in conn.listDomainsID(): try: domain = conn.lookupByID(domain_id) if self._hasQemuGuestAgent(domain): @utils.synchronized(domain.UUIDString()) def do_qemuAgentGuestPing(domain, timeout): self._qemuAgentGuestPing(domain, timeout) do_qemuAgentGuestPing(domain, ICONF.guest_monitoring_timeout) except libvirt.libvirtError as le: LOG.warning(le) continue except Exception: LOG.warning("Exception during guest checks.", exc_info=True) def reschedule(action, sleep_time=1): """Eventlet Sleep for the specified number of seconds. :param sleep_time: seconds to sleep; if None, no sleep; """ LOG.debug('At reschedule') if sleep_time is not None: LOG.debug('Action %s sleep for %s seconds', action.id, sleep_time) eventlet.sleep(sleep_time) def sleep(sleep_time): """Interface for sleeping.""" eventlet.sleep(sleep_time) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4562879 masakari_monitors-22.0.0/masakarimonitors/introspectiveinstancemonitor/scheduler.py0000664000175000017500000000614315257164053030121 0ustar00zuulzuul# Copyright (c) 2018 WindRiver Systems # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import eventlet import logging from oslo_config import cfg from oslo_service import threadgroup from masakarimonitors.introspectiveinstancemonitor import qemu_utils LOG = logging.getLogger(__name__) CONF = cfg.CONF class ThreadGroupManager(object): """Thread group manager.""" def init_qemu_ga(self): self.qemuGA = qemu_utils.QemuGuestAgent() LOG.debug('Started QemuGuestAgent') def __init__(self): self.init_qemu_ga() super(ThreadGroupManager, self).__init__() self.threads = {} self.group = threadgroup.ThreadGroup() # Create dummy service task, because when there is nothing queued # on self.tg the process exits self.add_timer( CONF.introspectiveinstancemonitor.guest_monitoring_interval, self._service_task) def _service_task(self): # This is used to trigger periodic monitoring tasks self.qemuGA.checkGuests() def start(self, func, *args, **kwargs): """Run the given method in a sub-thread.""" LOG.debug('add_thread') return self.group.add_thread(func, *args, **kwargs) def add_timer(self, interval, func, *args, **kwargs): """Define a periodic task to be run in the thread group. The task will be executed in a separate green thread. Interval is from cfg.CONF.periodic_interval """ LOG.debug('group.add_timer') self.group.add_timer(interval, func, *args, **kwargs) def stop_timers(self): self.group.stop_timers() def stop(self, graceful=False): """Stop any active threads belong to this threadgroup.""" # Try to stop all threads gracefully self.group.stop(graceful) self.group.wait() # Wait for link()ed functions (i.e. lock release) threads = self.group.threads[:] links_done = dict((th, False) for th in threads) def mark_done(gt, th): links_done[th] = True for th in threads: th.link(mark_done, th) while not all(links_done.values()): eventlet.sleep() def reschedule(action, sleep_time=1): """Eventlet Sleep for the specified number of seconds. :param sleep_time: seconds to sleep; if None, no sleep; """ LOG.debug('At reschedule') if sleep_time is not None: LOG.debug('Action %s sleep for %s seconds', action.id, sleep_time) eventlet.sleep(sleep_time) def sleep(sleep_time): """Interface for sleeping.""" LOG.debug('sleep') eventlet.sleep(sleep_time) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4562879 masakari_monitors-22.0.0/masakarimonitors/manager.py0000664000175000017500000000674415257164053021531 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Base Manager class. Managers are responsible for a certain aspect of the system. It is a logical grouping of code relating to a portion of the system. In general other components should be using the manager to make changes to the components that it is responsible for. We have adopted a basic strategy of Smart managers and dumb data, which means rather than attaching methods to data objects, components should call manager methods that act on the data. Methods on managers that can be executed locally should be called directly. If a particular method must execute on a remote host, this should be done via rpc to the service that wraps the manager. Managers should be responsible for most of the db access, and non-implementation specific data. Anything implementation specific that can't be generalized should be done by the Driver. Managers will often provide methods for initial setup of a host or periodic tasks to a wrapping service. This module provides Manager, a base class for managers. """ from oslo_service import periodic_task import masakarimonitors.conf CONF = masakarimonitors.conf.CONF class PeriodicTasks(periodic_task.PeriodicTasks): def __init__(self): super(PeriodicTasks, self).__init__(CONF) class Manager(PeriodicTasks): def __init__(self, host=None, service_name='undefined'): if not host: host = CONF.hostname self.host = host self.service_name = service_name super(Manager, self).__init__() def periodic_tasks(self, context, raise_on_error=False): """Tasks to be run at a periodic interval.""" return self.run_periodic_tasks(context, raise_on_error=raise_on_error) def init_host(self): """Hook to do additional manager initialization when one requests the service be started. This is called before any service record is created. Child classes should override this method. """ pass def cleanup_host(self): """Hook to do cleanup work when the service shuts down. Child classes should override this method. """ pass def pre_start_hook(self): """Hook to provide the manager the ability to do additional start-up work before any RPC queues/consumers are created. This is called after other initialization has succeeded and a service record is created. Child classes should override this method. """ pass def post_start_hook(self): """Hook to provide the manager the ability to do additional start-up work immediately after a service creates RPC consumers and starts 'running'. Child classes should override this method. """ pass def reset(self): """Hook called on SIGHUP to signal the manager to re-read any dynamic configuration or do any reconfiguration tasks. """ pass ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2062194 masakari_monitors-22.0.0/masakarimonitors/objects/0000775000175000017500000000000015257164140021160 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4562879 masakari_monitors-22.0.0/masakarimonitors/objects/__init__.py0000664000175000017500000000000015257164053023262 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4562879 masakari_monitors-22.0.0/masakarimonitors/objects/event_constants.py0000664000175000017500000000202615257164053024752 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. class EventConstants(object): # Define event types. TYPE_PROCESS = "PROCESS" TYPE_COMPUTE_HOST = "COMPUTE_HOST" TYPE_VM = "VM" # Define event details. EVENT_STARTED = "STARTED" EVENT_STOPPED = "STOPPED" # Define host status. HOST_STATUS_NORMAL = "NORMAL" HOST_STATUS_UNKNOWN = "UNKNOWN" # Define cluster status. CLUSTER_STATUS_ONLINE = "ONLINE" CLUSTER_STATUS_OFFLINE = "OFFLINE" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4562879 masakari_monitors-22.0.0/masakarimonitors/privsep.py0000664000175000017500000000147215257164053021600 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_privsep import priv_context monitors_priv = priv_context.PrivContext( "masakarimonitors", cfg_section="masakarimonitors_privileged", pypath=__name__ + ".monitors_priv", capabilities=[], ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2062194 masakari_monitors-22.0.0/masakarimonitors/processmonitor/0000775000175000017500000000000015257164140022615 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4562879 masakari_monitors-22.0.0/masakarimonitors/processmonitor/__init__.py0000664000175000017500000000000015257164053024717 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4562879 masakari_monitors-22.0.0/masakarimonitors/processmonitor/process.py0000664000175000017500000000623615257164053024657 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import yaml import eventlet from oslo_log import log as oslo_logging import masakarimonitors.conf from masakarimonitors import manager from masakarimonitors.processmonitor.process_handler import handle_process LOG = oslo_logging.getLogger(__name__) CONF = masakarimonitors.conf.CONF class ProcessmonitorManager(manager.Manager): """Manages the masakari-processmonitor.""" def __init__(self, *args, **kwargs): super(ProcessmonitorManager, self).__init__( service_name="processmonitor", *args, **kwargs) self.process_handler = handle_process.HandleProcess() def _load_process_list(self): try: process_list = yaml.safe_load(open(CONF.process.process_list_path)) LOG.debug("Loaded process list. %s", process_list) return process_list except yaml.YAMLError as e: LOG.exception("YAMLError caught: %s", e) return except Exception as e: LOG.exception("Exception caught: %s", e) return def stop(self): self.running = False def main(self): """Main method.""" try: # Load process list. process_list = self._load_process_list() if process_list is None: LOG.error("Failed to load process list file.") return # Set process_list object to the process handler. self.process_handler.set_process_list(process_list) # Initial start of processes. self.process_handler.start_processes() self.running = True while self.running: # Monitor processes. down_process_list = self.process_handler.monitor_processes() if len(down_process_list) != 0: # Restart down processes. self.process_handler.restart_processes(down_process_list) else: # Since no down process, clear the restart_failure_list self.process_handler.restart_failure_list[:] = [] # Reload process list and set to the process handler. process_list = self._load_process_list() if process_list is None: LOG.error("Failed to reload process list file.") break self.process_handler.set_process_list(process_list) eventlet.greenthread.sleep(CONF.process.check_interval) except Exception as e: LOG.exception("Exception caught: %s", e) return return ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2072194 masakari_monitors-22.0.0/masakarimonitors/processmonitor/process_handler/0000775000175000017500000000000015257164140025770 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4562879 masakari_monitors-22.0.0/masakarimonitors/processmonitor/process_handler/__init__.py0000664000175000017500000000000015257164053030072 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4562879 masakari_monitors-22.0.0/masakarimonitors/processmonitor/process_handler/handle_process.py0000664000175000017500000001662415257164053031347 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import socket import eventlet from oslo_log import log as oslo_logging from oslo_utils import timeutils import masakarimonitors.conf from masakarimonitors.ha import masakari from masakarimonitors.objects import event_constants as ec from masakarimonitors import utils LOG = oslo_logging.getLogger(__name__) CONF = masakarimonitors.conf.CONF class HandleProcess(object): """Handle process.""" def __init__(self): self.process_list = None self.restart_failure_list = [] self.notifier = masakari.SendNotification() def set_process_list(self, process_list): """Set process list object. :param process_list: process list object """ self.process_list = process_list def _execute_cmd(self, cmd_str, run_as_root): # Split command string and delete empty elements. command = cmd_str.split() command = filter(lambda x: x != '', command) try: # Execute start command. out, err = utils.execute(*command, run_as_root=run_as_root) if out: msg = ("CMD '%s' output stdout: %s") % (cmd_str, out) LOG.info("%s", msg) if err: msg = ("CMD '%s' output stderr: %s") % (cmd_str, err) LOG.warning("%s", msg) return 1 except Exception as e: msg = ("CMD '%s' raised exception: %s") % (cmd_str, e) LOG.error("%s", e) return 1 return 0 def start_processes(self): """Initial start of processes. This method starts the processes using start command written in the process list. """ for process in self.process_list: cmd_str = process['start_command'] pre_cmd_str = process['pre_start_command'] post_cmd_str = process['post_start_command'] # Execute pre start command. if pre_cmd_str: ret = self._execute_cmd(pre_cmd_str, process['run_as_root']) if ret != 0: continue # Execute start command. LOG.info("Start of process with executing command: %s", cmd_str) self._execute_cmd(cmd_str, process['run_as_root']) # Execute post start command. if post_cmd_str: ret = self._execute_cmd(post_cmd_str, process['run_as_root']) def monitor_processes(self): """Monitor processes. This method monitors the processes using process name written in the process list. :returns: List of down process """ down_process_list = [] for process in self.process_list: process_name = process['process_name'] try: # Execute monitoring command. out, err = utils.execute('ps', '-ef', run_as_root=False) if process_name in out: LOG.debug("Process '%s' is found.", process_name) else: # Append down_process_list. down_process_list.append(process) LOG.warning("Process '%s' is not found.", process_name) except Exception as e: LOG.error("Monitoring command raised exception: %s", e) return down_process_list def _make_event(self, process_name): hostname = socket.gethostname() current_time = timeutils.utcnow() event = { 'notification': { 'type': ec.EventConstants.TYPE_PROCESS, 'hostname': hostname, 'generated_time': current_time, 'payload': { 'event': ec.EventConstants.EVENT_STOPPED, 'process_name': process_name.split('/')[-1] } } } return event def restart_processes(self, down_process_list): """Restart processes. This method restarts the processes using restart command written in the process list. :param down_process_list: down process list object """ tmp_restart_failure_list = [] for down_process in down_process_list: # The process which failed to restart previously doesn't restart. if down_process['process_name'] in self.restart_failure_list: msg = "Process '%s' doesn't be restarted because it failed" \ " to restart previously." % down_process['process_name'] LOG.warning("%s", msg) tmp_restart_failure_list.append(down_process['process_name']) continue cmd_str = down_process['restart_command'] pre_cmd_str = down_process['pre_restart_command'] post_cmd_str = down_process['post_restart_command'] LOG.info("Restart of process with executing command: %s", cmd_str) for retries in range(0, CONF.process.restart_retries + 1): # Execute pre restart command. if pre_cmd_str: ret = self._execute_cmd(pre_cmd_str, down_process['run_as_root']) if ret != 0: # Failed to restart process. eventlet.greenthread.sleep( CONF.process.restart_interval) continue # Execute restart command. ret = self._execute_cmd(cmd_str, down_process['run_as_root']) if ret != 0: # Failed to restart process. eventlet.greenthread.sleep(CONF.process.restart_interval) continue # Execute post restart command. if post_cmd_str: ret = self._execute_cmd(post_cmd_str, down_process['run_as_root']) if ret != 0: # Failed to restart process. eventlet.greenthread.sleep( CONF.process.restart_interval) continue # Succeeded in restarting process. break if retries == CONF.process.restart_retries: # Send a notification. event = self._make_event(down_process['process_name']) self.notifier.send_notification( CONF.process.api_retry_max, CONF.process.api_retry_interval, event) # Add the process name which failed to restart to the # failure list. tmp_restart_failure_list.append(down_process['process_name']) # Replace the old restart_failure_list with new one self.restart_failure_list = tmp_restart_failure_list[:] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4562879 masakari_monitors-22.0.0/masakarimonitors/service.py0000664000175000017500000000766115257164053021556 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Generic Node base class for all workers that run on hosts.""" import os import sys from oslo_log import log as logging from oslo_service import service from oslo_utils import importutils import masakarimonitors.conf from masakarimonitors.i18n import _ from masakarimonitors import utils LOG = logging.getLogger(__name__) CONF = masakarimonitors.conf.CONF class Service(service.Service): """Service object for binaries running on hosts. A service takes a manager. """ def __init__(self, host, binary, manager): super(Service, self).__init__() self.host = host self.binary = binary self.manager_class_name = manager manager_class = importutils.import_class(self.manager_class_name) self.manager = manager_class(host=self.host) def __repr__(self): return "<%(cls_name)s: host=%(host)s, binary=%(binary)s, " \ "manager_class_name=%(manager)s>" %\ { 'cls_name': self.__class__.__name__, 'host': self.host, 'binary': self.binary, 'manager': self.manager_class_name } def start(self): LOG.info('Starting %s', self.binary) self.basic_config_check() self.manager.init_host() self.manager.main() def __getattr__(self, key): manager = self.__dict__.get('manager', None) return getattr(manager, key) @classmethod def create(cls, host=None, binary=None, manager=None): """Instantiates class and passes back application object. :param host: defaults to CONF.hostname :param binary: defaults to basename of executable :param manager: defaults to CONF._manager """ if not host: host = CONF.hostname if not binary: binary = os.path.basename(sys.argv[0]) if not manager: manager_cls = ('%s_manager' % binary.rpartition('masakarimonitors-')[2]) manager = CONF.get(manager_cls, None) service_obj = cls(host, binary, manager) return service_obj def kill(self): """Destroy the service object in the datastore. NOTE: Although this method is not used anywhere else than tests, it is convenient to have it here, so the tests might easily and in clean way stop and remove the service_ref. """ self.stop() def stop(self): LOG.info('Stopping %s', self.binary) self.manager.stop() super(Service, self).stop() def basic_config_check(self): """Perform basic config checks before starting processing.""" # Make sure the tempdir exists and is writable try: with utils.tempdir(): pass except Exception as e: LOG.error('Temporary directory is invalid: %s', e) sys.exit(1) def reset(self): self.manager.reset() # NOTE: the global launcher is to maintain the existing # functionality of calling service.serve + # service.wait _launcher = None def serve(server, workers=None): global _launcher if _launcher: raise RuntimeError(_('serve() can only be called once')) _launcher = service.launch(CONF, server, workers=workers) def wait(): _launcher.wait() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2072194 masakari_monitors-22.0.0/masakarimonitors/tests/0000775000175000017500000000000015257164140020671 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4562879 masakari_monitors-22.0.0/masakarimonitors/tests/__init__.py0000664000175000017500000000000015257164053022773 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.457288 masakari_monitors-22.0.0/masakarimonitors/tests/base.py0000664000175000017500000000140115257164053022154 0ustar00zuulzuul# Copyright 2010-2011 OpenStack Foundation # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslotest import base class TestCase(base.BaseTestCase): """Test case base class for all unit tests.""" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.457288 masakari_monitors-22.0.0/masakarimonitors/tests/test_masakarimonitors.py0000664000175000017500000000141315257164053025667 0ustar00zuulzuul# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ test_masakarimonitors ---------------------------------- Tests for `masakarimonitors` module. """ from masakarimonitors.tests import base class TestMasakarimonitors(base.TestCase): def test_something(self): pass ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2072194 masakari_monitors-22.0.0/masakarimonitors/tests/unit/0000775000175000017500000000000015257164140021650 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.457288 masakari_monitors-22.0.0/masakarimonitors/tests/unit/__init__.py0000664000175000017500000000000015257164053023752 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2082193 masakari_monitors-22.0.0/masakarimonitors/tests/unit/ha/0000775000175000017500000000000015257164140022240 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.457288 masakari_monitors-22.0.0/masakarimonitors/tests/unit/ha/__init__.py0000664000175000017500000000000015257164053024342 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.457288 masakari_monitors-22.0.0/masakarimonitors/tests/unit/ha/test_masakari.py0000664000175000017500000001304515257164053025447 0ustar00zuulzuul# Copyright(c) 2017 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import testtools from unittest import mock import uuid import eventlet from keystoneauth1 import loading as ks_loading from openstack import connection from openstack import exceptions from oslo_utils import timeutils import masakarimonitors.conf from masakarimonitors.ha import masakari from masakarimonitors.objects import event_constants as ec CONF = masakarimonitors.conf.CONF PROFILE_TYPE = "ha" PROFILE_NAME = "masakari" class FakeResponse(object): def __init__(self, status_code=200, headers=None): self.status_code = status_code self.headers = { 'content-type': 'application/json', 'x-openstack-request-id': uuid.uuid4().hex, } class TestSendNotification(testtools.TestCase): def setUp(self): super(TestSendNotification, self).setUp() self.api_retry_max = 3 self.api_retry_interval = 1 self.event = { 'notification': { 'type': ec.EventConstants.TYPE_COMPUTE_HOST, 'hostname': 'compute-node1', 'generated_time': timeutils.utcnow(), 'payload': { 'event': ec.EventConstants.EVENT_STOPPED, 'cluster_status': 'OFFLINE', 'host_status': ec.EventConstants.HOST_STATUS_NORMAL } } } @mock.patch.object(connection, 'Connection') @mock.patch.object(ks_loading, 'load_session_from_conf_options') @mock.patch.object(ks_loading, 'load_auth_from_conf_options') def test_send_notification( self, mock_auth, mock_session, mock_connection): mock_conn = mock.Mock() mock_conn.instance_ha.return_value = mock.Mock() mock_conn.instance_ha.create_notification.return_value = mock.Mock() mock_connection.return_value = mock_conn notifier = masakari.SendNotification() notifier.send_notification( self.api_retry_max, self.api_retry_interval, self.event) mock_conn.instance_ha.create_notification.assert_called_once_with( type=self.event['notification']['type'], hostname=self.event['notification']['hostname'], generated_time=self.event['notification']['generated_time'], payload=self.event['notification']['payload']) mock_auth.assert_called_once_with(CONF, 'api') mock_session.assert_called_once_with(CONF, 'api', auth=mock_auth.return_value) @mock.patch.object(connection, 'Connection') @mock.patch.object(ks_loading, 'load_session_from_conf_options') @mock.patch.object(ks_loading, 'load_auth_from_conf_options') def test_send_notification_409_error( self, mock_auth, mock_session, mock_connection): mock_conn = mock.Mock() mock_conn.instance_ha.return_value = mock.Mock() mock_conn.instance_ha.create_notification.return_value = mock.Mock() mock_connection.return_value = mock_conn response = FakeResponse(status_code=409) status_ex = exceptions.HttpException(response=response) mock_conn.instance_ha.create_notification.side_effect = status_ex notifier = masakari.SendNotification() notifier.send_notification( self.api_retry_max, self.api_retry_interval, self.event) mock_conn.instance_ha.create_notification.assert_called_once_with( type=self.event['notification']['type'], hostname=self.event['notification']['hostname'], generated_time=self.event['notification']['generated_time'], payload=self.event['notification']['payload']) @mock.patch.object(eventlet.greenthread, 'sleep') @mock.patch.object(connection, 'Connection') @mock.patch.object(ks_loading, 'load_session_from_conf_options') @mock.patch.object(ks_loading, 'load_auth_from_conf_options') def test_send_notification_500_error( self, mock_auth, mock_session, mock_connection, mock_sleep): mock_conn = mock.Mock() mock_conn.instance_ha.return_value = mock.Mock() mock_conn.instance_ha.create_notification.return_value = mock.Mock() mock_connection.return_value = mock_conn response = FakeResponse(status_code=500) status_ex = exceptions.HttpException(response=response) mock_conn.instance_ha.create_notification.side_effect = status_ex mock_sleep.return_value = None notifier = masakari.SendNotification() notifier.send_notification( self.api_retry_max, self.api_retry_interval, self.event) mock_conn.instance_ha.create_notification.assert_called_with( type=self.event['notification']['type'], hostname=self.event['notification']['hostname'], generated_time=self.event['notification']['generated_time'], payload=self.event['notification']['payload']) self.assertEqual(self.api_retry_max + 1, mock_conn.instance_ha.create_notification.call_count) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2082193 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/0000775000175000017500000000000015257164140024235 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.457288 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/__init__.py0000664000175000017500000000000015257164053026337 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2082193 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/consul_check/0000775000175000017500000000000015257164140026675 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.457288 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/consul_check/__init__.py0000664000175000017500000000000015257164053030777 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4582882 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/consul_check/test_consul_helper.py0000664000175000017500000001716215257164053033162 0ustar00zuulzuul# Copyright(c) 2021 Inspur # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import testtools from unittest import mock from oslo_config import fixture as fixture_config from masakarimonitors.hostmonitor.consul_check import consul_helper class FakerAgentMembers(object): def __init__(self): self.agent_members = [] def create_agent(self, name, status=1): agent = { 'Name': name, 'Status': status, 'Port': 'agent_lan_port', 'Addr': 'agent_ip', 'Tags': { 'dc': 'storage', 'role': 'consul', 'port': 'agent_server_port', 'wan_join_port': 'agent_wan_port', 'expect': '3', 'id': 'agent_id', 'vsn_max': '3', 'vsn_min': '2', 'vsn': '2', 'raft_vsn': '2', }, 'ProtocolMax': 5, 'ProtocolMin': 1, 'ProtocolCur': 2, 'DelegateMax': 5, 'DelegateMin': 2, 'DelegateCur': 4, } self.agent_members.append(agent) def generate_agent_members(self): return self.agent_members class TestConsulManager(testtools.TestCase): def setUp(self): super(TestConsulManager, self).setUp() self.CONF = self.useFixture(fixture_config.Config()).conf self.consul_manager = consul_helper.ConsulManager(self.CONF) self.consul_manager.agents = { 'manage': consul_helper.ConsulAgent('manage'), 'tenant': consul_helper.ConsulAgent('tenant'), 'storage': consul_helper.ConsulAgent('storage'), } @mock.patch('consul.Consul') def test_init_agents_no_agent(self, mock_consul): mgr = consul_helper.ConsulManager(self.CONF) self.assertEqual({}, mgr.agents) mock_consul.assert_not_called() @mock.patch('consul.Consul') def test_init_agents_ipv4(self, mock_consul): self.CONF.set_override('agent_manage', '192.0.2.10:1000', 'consul') self.CONF.set_override('agent_tenant', '192.0.2.11:1001', 'consul') self.CONF.set_override('agent_storage', '192.0.2.12', 'consul') mgr = consul_helper.ConsulManager(self.CONF) self.assertEqual('manage', mgr.agents['manage'].name) self.assertEqual('192.0.2.10', mgr.agents['manage'].addr) self.assertEqual(1000, mgr.agents['manage'].port) self.assertEqual(mock.call(host='192.0.2.10', port=1000), mock_consul.mock_calls[0]) self.assertEqual('tenant', mgr.agents['tenant'].name) self.assertEqual('192.0.2.11', mgr.agents['tenant'].addr) self.assertEqual(1001, mgr.agents['tenant'].port) self.assertEqual(mock.call(host='192.0.2.11', port=1001), mock_consul.mock_calls[1]) self.assertEqual('storage', mgr.agents['storage'].name) self.assertEqual('192.0.2.12', mgr.agents['storage'].addr) self.assertEqual(8500, mgr.agents['storage'].port) self.assertEqual(mock.call(host='192.0.2.12', port=8500), mock_consul.mock_calls[2]) self.assertEqual(3, mock_consul.call_count) @mock.patch('consul.Consul') def test_init_agents_ipv6(self, mock_consul): self.CONF.set_override( 'agent_manage', '[2001:db8::10]:1000', 'consul') self.CONF.set_override( 'agent_tenant', '[2001:db8::11]:1001', 'consul') self.CONF.set_override( 'agent_storage', '2001:db8::12', 'consul') mgr = consul_helper.ConsulManager(self.CONF) self.assertEqual('manage', mgr.agents['manage'].name) self.assertEqual('2001:db8::10', mgr.agents['manage'].addr) self.assertEqual(1000, mgr.agents['manage'].port) self.assertEqual(mock.call(host='2001:db8::10', port=1000), mock_consul.mock_calls[0]) self.assertEqual('tenant', mgr.agents['tenant'].name) self.assertEqual('2001:db8::11', mgr.agents['tenant'].addr) self.assertEqual(1001, mgr.agents['tenant'].port) self.assertEqual(mock.call(host='2001:db8::11', port=1001), mock_consul.mock_calls[1]) self.assertEqual('storage', mgr.agents['storage'].name) self.assertEqual('2001:db8::12', mgr.agents['storage'].addr) self.assertEqual(8500, mgr.agents['storage'].port) self.assertEqual(mock.call(host='2001:db8::12', port=8500), mock_consul.mock_calls[2]) self.assertEqual(3, mock_consul.call_count) def test_get_health(self): fake_manage_agents = FakerAgentMembers() fake_manage_agents.create_agent('node01', status=1) fake_manage_agents.create_agent('node02', status=1) fake_manage_agents.create_agent('node03', status=1) agent_manage_members = fake_manage_agents.generate_agent_members() fake_tenant_agents = FakerAgentMembers() fake_tenant_agents.create_agent('node01', status=1) fake_tenant_agents.create_agent('node02', status=1) fake_tenant_agents.create_agent('node03', status=1) agent_tenant_members = fake_tenant_agents.generate_agent_members() fake_storage_agents = FakerAgentMembers() fake_storage_agents.create_agent('node01', status=1) fake_storage_agents.create_agent('node02', status=1) fake_storage_agents.create_agent('node03', status=3) agent_storage_members = fake_storage_agents.generate_agent_members() with mock.patch.object(self.consul_manager.agents['manage'], 'get_agents', return_value=agent_manage_members): with mock.patch.object(self.consul_manager.agents['tenant'], 'get_agents', return_value=agent_tenant_members): with mock.patch.object(self.consul_manager.agents['storage'], 'get_agents', return_value=agent_storage_members): excepted_health = { "node01": ['up', 'up', 'up'], "node02": ['up', 'up', 'up'], "node03": ['up', 'up', 'down'], } sequence = ['manage', 'tenant', 'storage'] agents_health = self.consul_manager.get_health(sequence) self.assertEqual(excepted_health, agents_health) class TestConsulAgent(testtools.TestCase): def setUp(self): super(TestConsulAgent, self).setUp() self.consul_agent = consul_helper.ConsulAgent('test') def test_get_health(self): fake_agents = FakerAgentMembers() fake_agents.create_agent('node01', status=1) fake_agents.create_agent('node02', status=1) fake_agents.create_agent('node03', status=3) agent_members = fake_agents.generate_agent_members() with mock.patch.object(self.consul_agent, 'get_agents', return_value=agent_members): excepted_health = { "node01": 'up', "node02": 'up', "node03": 'down', } agents_health = self.consul_agent.get_health() self.assertEqual(excepted_health, agents_health) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4592884 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/consul_check/test_manager.py0000664000175000017500000001646515257164053031737 0ustar00zuulzuul# Copyright(c) 2021 Inspur # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import testtools from unittest import mock from collections import deque import eventlet from oslo_config import fixture as fixture_config import masakarimonitors.conf from masakarimonitors.ha import masakari from masakarimonitors.hostmonitor.consul_check import consul_helper from masakarimonitors.hostmonitor.consul_check import manager from masakarimonitors.hostmonitor.consul_check import matrix_helper eventlet.monkey_patch(os=False) CONF = masakarimonitors.conf.CONF class TestConsulCheck(testtools.TestCase): def setUp(self): super(TestConsulCheck, self).setUp() self.CONF = self.useFixture(fixture_config.Config()).conf self.host_monitor = manager.ConsulCheck() self.host_monitor.matrix_manager = \ matrix_helper.MatrixManager(self.CONF) self.host_monitor._matrix = matrix_helper.DEFAULT_MATRIX self.host_monitor.consul_manager = \ consul_helper.ConsulManager(self.CONF) self.host_monitor._sequence = ['manage', 'tenant', 'storage'] self.host_monitor.monitoring_data = { "node01": deque([['up', 'up', 'up'], ['up', 'up', 'up'], ['up', 'up', 'up']], maxlen=3), "node02": deque([['up', 'up', 'up'], ['up', 'up', 'up'], ['up', 'up', 'down']], maxlen=3), "node03": deque([['up', 'up', 'up'], ['down', 'up', 'up'], ['down', 'up', 'up']], maxlen=3), } def test_update_monitoring_data(self): mock_health = { 'node01': ['up', 'up', 'up'], 'node02': ['up', 'up', 'up'], 'node03': ['up', 'up', 'up'] } with mock.patch.object(self.host_monitor.consul_manager, 'get_health', return_value=mock_health): self.host_monitor.update_monitoring_data() excepted_monitoring_data = { "node01": deque([['up', 'up', 'up'], ['up', 'up', 'up'], ['up', 'up', 'up']], maxlen=3), "node02": deque([['up', 'up', 'up'], ['up', 'up', 'down'], ['up', 'up', 'up']], maxlen=3), "node03": deque([['down', 'up', 'up'], ['down', 'up', 'up'], ['up', 'up', 'up']], maxlen=3), } self.assertEqual(excepted_monitoring_data, self.host_monitor.monitoring_data) def test_get_host_statistical_health(self): self.assertEqual(['up', 'up', 'up'], self.host_monitor.get_host_health('node01')) self.assertEqual(['up', 'up', None], self.host_monitor.get_host_health('node02')) self.assertEqual([None, 'up', 'up'], self.host_monitor.get_host_health('node03')) def test_host_statistical_health_changed(self): self.host_monitor.last_host_health = { 'node02': ['up', 'up', None], 'node03': ['up', 'up', 'down'] } self.assertFalse(self.host_monitor._host_health_changed( 'node01', ['up', 'up', 'up'])) self.assertTrue(self.host_monitor._host_health_changed( 'node02', ['up', 'up', 'up'])) self.assertTrue(self.host_monitor._host_health_changed( 'node03', ['up', 'up', 'up'])) last_host_health = { 'node01': ['up', 'up', 'up'], 'node02': ['up', 'up', 'up'], 'node03': ['up', 'up', 'up'] } self.assertEqual(self.host_monitor.last_host_health, last_host_health) def test_get_action_from_matrix_by_host_health(self): self.assertEqual( [], self.host_monitor.get_action_from_matrix(['up', 'up', 'up'])) self.assertEqual( ["recovery"], self.host_monitor.get_action_from_matrix(['up', 'up', 'down'])) self.assertEqual( [], self.host_monitor.get_action_from_matrix(['down', 'up', 'up'])) self.assertEqual( ["recovery"], self.host_monitor.get_action_from_matrix(['down', 'down', 'down'])) def test_event_unstable_health_returns_none(self): self.assertIsNone( self.host_monitor._event('node01', [None])) self.assertIsNone( self.host_monitor._event('node01', ['up', 'up', None])) self.assertIsNone( self.host_monitor._event('node01', [None, 'up', 'up'])) self.assertIsNone( self.host_monitor._event('node01', ['up', None, 'down'])) def test_event_all_up_returns_started(self): event = self.host_monitor._event('node01', ['up', 'up', 'up']) self.assertIsNotNone(event) self.assertEqual('STARTED', event['notification']['payload']['event']) def test_event_down_with_recovery_returns_stopped(self): event = self.host_monitor._event('node01', ['up', 'up', 'down']) self.assertIsNotNone(event) self.assertEqual('STOPPED', event['notification']['payload']['event']) def test_event_down_without_recovery_returns_none(self): self.assertIsNone( self.host_monitor._event('node01', ['down', 'up', 'up'])) @mock.patch.object(masakari.SendNotification, 'send_notification') @mock.patch.object(manager.ConsulCheck, '_event') def test_poll_hosts(self, mock_event, mock_send_notification): self.host_monitor.monitoring_data = { "node01": deque([['up', 'up', 'up'], ['up', 'up', 'up'], ['up', 'up', 'up']], maxlen=3), "node02": deque([['up', 'up', 'down'], ['up', 'up', 'down'], ['up', 'up', 'down']], maxlen=3), "node03": deque([['up', 'up', 'up'], ['up', 'up', 'up'], ['up', 'up', 'up']], maxlen=3), } self.host_monitor.last_host_health = { 'node02': ['up', 'up', None], 'node03': ['up', 'up', 'up'] } test_event = {'notification': 'test'} mock_event.return_value = test_event self.host_monitor.poll_hosts() mock_send_notification.assert_called_once_with( CONF.host.api_retry_max, CONF.host.api_retry_interval, test_event) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4592884 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/consul_check/test_matrix_helper.py0000664000175000017500000000410515257164053033154 0ustar00zuulzuul# Copyright(c) 2021 Inspur # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import tempfile from oslo_config import fixture as fixture_config import testtools import yaml from masakarimonitors.hostmonitor.consul_check import matrix_helper class TestMatrixManager(testtools.TestCase): def setUp(self): super(TestMatrixManager, self).setUp() self.CONF = self.useFixture(fixture_config.Config()).conf def test_get_matrix_and_sequence_from_file(self): matrix_cfg = { 'sequence': ['manage', 'tenant', 'storage'], 'matrix': [{"health": ["up", "up", "up"], "action": ['test']}] } tmp_cfg = tempfile.NamedTemporaryFile(mode='w', delete=False) tmp_cfg.write(yaml.safe_dump(matrix_cfg)) tmp_cfg.close() self.CONF.set_override('matrix_config_file', tmp_cfg.name, group='consul') matrix_manager = matrix_helper.MatrixManager(self.CONF) self.assertEqual(matrix_cfg.get('sequence'), matrix_manager.get_sequence()) self.assertEqual(matrix_cfg.get('matrix'), matrix_manager.get_matrix()) def test_get_default_matrix_and_sequence(self): self.CONF.set_override('matrix_config_file', None, group='consul') matrix_manager = matrix_helper.MatrixManager(self.CONF) self.assertEqual(matrix_helper.DEFAULT_SEQUENCE, matrix_manager.get_sequence()) self.assertEqual(matrix_helper.DEFAULT_MATRIX, matrix_manager.get_matrix()) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2092192 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/host_handler/0000775000175000017500000000000015257164140026707 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4592884 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/host_handler/__init__.py0000664000175000017500000000000015257164053031011 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4602885 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/host_handler/test_handle_host.py0000664000175000017500000011756715257164053032634 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import socket import testtools from unittest import mock from xml.etree import ElementTree from collections import deque import eventlet from oslo_utils import timeutils import masakarimonitors.conf from masakarimonitors.ha import masakari from masakarimonitors.hostmonitor.host_handler import handle_host from masakarimonitors.hostmonitor.host_handler import hold_host_status from masakarimonitors.hostmonitor.host_handler import parse_cib_xml from masakarimonitors.hostmonitor.host_handler import parse_crmmon_xml from masakarimonitors.objects import event_constants as ec from masakarimonitors import utils eventlet.monkey_patch(os=False) CONF = masakarimonitors.conf.CONF STATUS_TAG_XML = ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' CRMMON_NODES_TAG_XML = """ """ class TestCibSchemaCompliantTag(testtools.TestCase): def setUp(self): super(TestCibSchemaCompliantTag, self).setUp() def test_init_offline(self): tag = handle_host.CibSchemaCompliantTag( {'name': 'test1', 'online': 'false'}) self.assertEqual(tag['uname'], 'test1') self.assertEqual(tag['crmd'], 'offline') def test_init_online(self): tag = handle_host.CibSchemaCompliantTag( {'name': 'test1', 'online': 'true'}) self.assertEqual(tag['uname'], 'test1') self.assertEqual(tag['crmd'], 'online') class TestHandleHost(testtools.TestCase): def setUp(self): super(TestHandleHost, self).setUp() @mock.patch.object(utils, 'execute') def test_check_pacemaker_services(self, mock_execute): mock_execute.return_value = ('test_stdout', '') obj = handle_host.HandleHost() ret = obj._check_pacemaker_services('corosync') self.assertTrue(ret) mock_execute.assert_called_once_with( 'systemctl', 'status', 'corosync', run_as_root=True) @mock.patch.object(utils, 'execute') def test_check_pacemaker_services_stderr(self, mock_execute): mock_execute.return_value = ('test_stdout', 'test_stderr') obj = handle_host.HandleHost() ret = obj._check_pacemaker_services('corosync') self.assertFalse(ret) mock_execute.assert_called_once_with( 'systemctl', 'status', 'corosync', run_as_root=True) @mock.patch.object(utils, 'execute') @mock.patch.object(handle_host.HandleHost, '_check_pacemaker_services') def test_check_hb_line( self, mock_check_pacemaker_services, mock_execute): mock_check_pacemaker_services.side_effect = [True, True, False] interfaces = "enp0s8" ports = "5405" CONF.set_override("corosync_multicast_interfaces", interfaces, "host") CONF.set_override("corosync_multicast_ports", ports, "host") mock_execute.return_value = ('', '') obj = handle_host.HandleHost() ret = obj._check_hb_line() self.assertEqual(0, ret) self.assertEqual(3, mock_check_pacemaker_services.call_count) mock_check_pacemaker_services.assert_any_call('corosync') mock_check_pacemaker_services.assert_any_call('pacemaker') mock_check_pacemaker_services.assert_any_call('pacemaker_remote') cmd_str = ("timeout %s tcpdump -n -c 1 -p -i %s port %s") \ % (CONF.host.tcpdump_timeout, interfaces, ports) command = cmd_str.split() mock_execute.assert_called_once_with(*command, run_as_root=True) @mock.patch.object(handle_host.HandleHost, '_check_pacemaker_services') def test_check_hb_line_pacemaker_not_running( self, mock_check_pacemaker_services): mock_check_pacemaker_services.side_effect = [False, False, False] obj = handle_host.HandleHost() ret = obj._check_hb_line() self.assertEqual(2, ret) self.assertEqual(3, mock_check_pacemaker_services.call_count) mock_check_pacemaker_services.assert_any_call('corosync') mock_check_pacemaker_services.assert_any_call('pacemaker') mock_check_pacemaker_services.assert_any_call('pacemaker_remote') @mock.patch.object(handle_host.HandleHost, '_check_pacemaker_services') def test_check_hb_line_work_on_pacemaker_remote( self, mock_check_pacemaker_services): mock_check_pacemaker_services.side_effect = [False, False, True] obj = handle_host.HandleHost() ret = obj._check_hb_line() self.assertEqual(0, ret) self.assertEqual(3, mock_check_pacemaker_services.call_count) mock_check_pacemaker_services.assert_any_call('corosync') mock_check_pacemaker_services.assert_any_call('pacemaker') mock_check_pacemaker_services.assert_any_call('pacemaker_remote') @mock.patch.object(handle_host.HandleHost, '_check_pacemaker_services') def test_check_hb_line_conf_interfaces_is_none( self, mock_check_pacemaker_services): mock_check_pacemaker_services.side_effect = [True, True, False] interfaces = None ports = "5405" CONF.set_override("corosync_multicast_interfaces", interfaces, "host") CONF.set_override("corosync_multicast_ports", ports, "host") obj = handle_host.HandleHost() ret = obj._check_hb_line() self.assertEqual(2, ret) self.assertEqual(3, mock_check_pacemaker_services.call_count) mock_check_pacemaker_services.assert_any_call('corosync') mock_check_pacemaker_services.assert_any_call('pacemaker') mock_check_pacemaker_services.assert_any_call('pacemaker_remote') @mock.patch.object(handle_host.HandleHost, '_check_pacemaker_services') def test_check_hb_line_conf_ports_is_none( self, mock_check_pacemaker_services): mock_check_pacemaker_services.side_effect = [True, True, False] interfaces = "enp0s8" ports = None CONF.set_override("corosync_multicast_interfaces", interfaces, "host") CONF.set_override("corosync_multicast_ports", ports, "host") obj = handle_host.HandleHost() ret = obj._check_hb_line() self.assertEqual(2, ret) self.assertEqual(3, mock_check_pacemaker_services.call_count) mock_check_pacemaker_services.assert_any_call('corosync') mock_check_pacemaker_services.assert_any_call('pacemaker') mock_check_pacemaker_services.assert_any_call('pacemaker_remote') @mock.patch.object(handle_host.HandleHost, '_check_pacemaker_services') def test_check_hb_line_incorrect_interfaces( self, mock_check_pacemaker_services): mock_check_pacemaker_services.side_effect = [True, True, False] interfaces = "enp0s3,enp0s8" ports = "5405" CONF.set_override("corosync_multicast_interfaces", interfaces, "host") CONF.set_override("corosync_multicast_ports", ports, "host") obj = handle_host.HandleHost() ret = obj._check_hb_line() self.assertEqual(2, ret) self.assertEqual(3, mock_check_pacemaker_services.call_count) mock_check_pacemaker_services.assert_any_call('corosync') mock_check_pacemaker_services.assert_any_call('pacemaker') mock_check_pacemaker_services.assert_any_call('pacemaker_remote') @mock.patch.object(handle_host.HandleHost, '_check_pacemaker_services') def test_check_hb_line_incorrect_ports( self, mock_check_pacemaker_services): mock_check_pacemaker_services.side_effect = [True, True, False] interfaces = "enp0s8" ports = "5405,5406" CONF.set_override("corosync_multicast_interfaces", interfaces, "host") CONF.set_override("corosync_multicast_ports", ports, "host") obj = handle_host.HandleHost() ret = obj._check_hb_line() self.assertEqual(2, ret) self.assertEqual(3, mock_check_pacemaker_services.call_count) mock_check_pacemaker_services.assert_any_call('corosync') mock_check_pacemaker_services.assert_any_call('pacemaker') mock_check_pacemaker_services.assert_any_call('pacemaker_remote') @mock.patch.object(utils, 'execute') @mock.patch.object(handle_host.HandleHost, '_check_pacemaker_services') def test_check_hb_line_tcpdump_fail( self, mock_check_pacemaker_services, mock_execute): mock_check_pacemaker_services.side_effect = [True, True, False] interfaces = "enp0s8" ports = "5405" CONF.set_override("corosync_multicast_interfaces", interfaces, "host") CONF.set_override("corosync_multicast_ports", ports, "host") mock_execute.side_effect = Exception("Test exception.") obj = handle_host.HandleHost() ret = obj._check_hb_line() self.assertEqual(1, ret) self.assertEqual(3, mock_check_pacemaker_services.call_count) mock_check_pacemaker_services.assert_any_call('corosync') mock_check_pacemaker_services.assert_any_call('pacemaker') mock_check_pacemaker_services.assert_any_call('pacemaker_remote') cmd_str = ("timeout %s tcpdump -n -c 1 -p -i %s port %s") \ % (CONF.host.tcpdump_timeout, interfaces, ports) command = cmd_str.split() mock_execute.assert_called_once_with(*command, run_as_root=True) @mock.patch.object(utils, 'execute') def test_check_host_status_by_crmadmin_idle(self, mock_execute): my_hostname = socket.gethostname() crmadmin_stdout = "Status of crmd@%s: S_IDLE (ok)" % my_hostname mock_execute.return_value = (crmadmin_stdout, '') obj = handle_host.HandleHost() ret = obj._check_host_status_by_crmadmin() self.assertEqual(0, ret) mock_execute.assert_called_once_with( 'crmadmin', '-S', my_hostname, run_as_root=True) @mock.patch.object(utils, 'execute') def test_check_host_status_by_crmadmin_not_dc(self, mock_execute): my_hostname = socket.gethostname() crmadmin_stdout = "Status of crmd@%s: S_NOT_DC (ok)" % my_hostname mock_execute.return_value = (crmadmin_stdout, '') obj = handle_host.HandleHost() ret = obj._check_host_status_by_crmadmin() self.assertEqual(0, ret) mock_execute.assert_called_once_with( 'crmadmin', '-S', my_hostname, run_as_root=True) @mock.patch.object(utils, 'execute') def test_check_host_status_by_crmadmin_output_stderr(self, mock_execute): my_hostname = socket.gethostname() mock_execute.return_value = ('', 'crmadmin: command not found') obj = handle_host.HandleHost() ret = obj._check_host_status_by_crmadmin() self.assertEqual(1, ret) mock_execute.assert_called_once_with( 'crmadmin', '-S', my_hostname, run_as_root=True) @mock.patch.object(utils, 'execute') def test_check_host_status_by_crmadmin_output_unexpected_message( self, mock_execute): my_hostname = socket.gethostname() mock_execute.return_value = ('Unexpected message.', '') obj = handle_host.HandleHost() ret = obj._check_host_status_by_crmadmin() self.assertEqual(1, ret) mock_execute.assert_called_once_with( 'crmadmin', '-S', my_hostname, run_as_root=True) @mock.patch.object(utils, 'execute') def test_get_cib_xml(self, mock_execute): mock_execute.return_value = ('test_stdout', '') obj = handle_host.HandleHost() ret = obj._get_cib_xml() self.assertEqual('test_stdout', ret) mock_execute.assert_called_once_with( 'cibadmin', '--query', run_as_root=True) @mock.patch.object(utils, 'execute') def test_get_cib_xml_output_stderr(self, mock_execute): mock_execute.return_value = ('test_stdout', 'test_stderr') obj = handle_host.HandleHost() ret = obj._get_cib_xml() self.assertIsNone(ret) mock_execute.assert_called_once_with( 'cibadmin', '--query', run_as_root=True) @mock.patch.object(utils, 'execute') def test_get_crmmon_xml(self, mock_execute): mock_execute.return_value = ('test_stdout', '') obj = handle_host.HandleHost() ret = obj._get_crmmon_xml() self.assertEqual('test_stdout', ret) mock_execute.assert_called_once_with( 'crm_mon', '-X', run_as_root=True) @mock.patch.object(utils, 'execute') def test_get_crmmon_xml_stderr(self, mock_execute): mock_execute.return_value = ('test_stdout', 'test_stderr') obj = handle_host.HandleHost() ret = obj._get_crmmon_xml() self.assertIsNone(ret) mock_execute.assert_called_once_with( 'crm_mon', '-X', run_as_root=True) @mock.patch.object(utils, 'execute') @mock.patch.object(parse_cib_xml.ParseCibXml, 'get_stonith_ipmi_params') def test_is_poweroff(self, mock_get_stonith_ipmi_params, mock_execute): ipmi_values = { 'userid': 'admin', 'passwd': 'password', 'interface': 'lanplus', 'ipaddr': '0.0.0.0' } mock_get_stonith_ipmi_params.return_value = ipmi_values mock_execute.return_value = ('Chassis Power is off', '') obj = handle_host.HandleHost() ret = obj._is_poweroff('test_hostname') self.assertTrue(ret) cmd_str = ("timeout %s ipmitool -U %s -P %s -I %s -H %s " "power status") \ % (str(CONF.host.ipmi_timeout), ipmi_values['userid'], ipmi_values['passwd'], ipmi_values['interface'], ipmi_values['ipaddr']) command = cmd_str.split() mock_execute.assert_called_once_with(*command, run_as_root=False) @mock.patch.object(utils, 'execute') @mock.patch.object(parse_cib_xml.ParseCibXml, 'get_stonith_ipmi_params') def test_is_poweroff_ipmi_values_is_none( self, mock_get_stonith_ipmi_params, mock_execute): mock_get_stonith_ipmi_params.return_value = None mock_execute.return_value = ('Chassis Power is off', '') obj = handle_host.HandleHost() ret = obj._is_poweroff('test_hostname') self.assertFalse(ret) mock_execute.assert_not_called() @mock.patch.object(eventlet.greenthread, 'sleep') @mock.patch.object(utils, 'execute') @mock.patch.object(parse_cib_xml.ParseCibXml, 'get_stonith_ipmi_params') def test_is_poweroff_output_stderr( self, mock_get_stonith_ipmi_params, mock_execute, mock_sleep): ipmi_values = { 'userid': 'admin', 'passwd': 'password', 'interface': 'lanplus', 'ipaddr': '0.0.0.0' } mock_get_stonith_ipmi_params.return_value = ipmi_values mock_execute.return_value = ('Chassis Power is off', 'test_stderr') mock_sleep.return_value = None obj = handle_host.HandleHost() ret = obj._is_poweroff('test_hostname') self.assertFalse(ret) cmd_str = ("timeout %s ipmitool -U %s -P %s -I %s -H %s " "power status") \ % (str(CONF.host.ipmi_timeout), ipmi_values['userid'], ipmi_values['passwd'], ipmi_values['interface'], ipmi_values['ipaddr']) command = cmd_str.split() calls = [mock.call(*command, run_as_root=False), mock.call(*command, run_as_root=False), mock.call(*command, run_as_root=False), mock.call(*command, run_as_root=False)] mock_execute.assert_has_calls(calls) @mock.patch.object(eventlet.greenthread, 'sleep') @mock.patch.object(utils, 'execute') @mock.patch.object(parse_cib_xml.ParseCibXml, 'get_stonith_ipmi_params') def test_is_poweroff_output_unexpected_message( self, mock_get_stonith_ipmi_params, mock_execute, mock_sleep): ipmi_values = { 'userid': 'admin', 'passwd': 'password', 'interface': 'lanplus', 'ipaddr': '0.0.0.0' } mock_get_stonith_ipmi_params.return_value = ipmi_values mock_execute.return_value = ('Unexpected message.', '') mock_sleep.return_value = None obj = handle_host.HandleHost() ret = obj._is_poweroff('test_hostname') self.assertFalse(ret) cmd_str = ("timeout %s ipmitool -U %s -P %s -I %s -H %s " "power status") \ % (str(CONF.host.ipmi_timeout), ipmi_values['userid'], ipmi_values['passwd'], ipmi_values['interface'], ipmi_values['ipaddr']) command = cmd_str.split() calls = [mock.call(*command, run_as_root=False), mock.call(*command, run_as_root=False), mock.call(*command, run_as_root=False), mock.call(*command, run_as_root=False)] mock_execute.assert_has_calls(calls) @mock.patch.object(timeutils, 'utcnow') def test_make_event_online(self, mock_utcnow): current_time = timeutils.utcnow() mock_utcnow.return_value = current_time obj = handle_host.HandleHost() hostname = 'test_hostname' current_status = 'online' ret = obj._make_event(hostname, current_status) event_type = ec.EventConstants.EVENT_STARTED cluster_status = current_status.upper() host_status = ec.EventConstants.HOST_STATUS_NORMAL event = { 'notification': { 'type': ec.EventConstants.TYPE_COMPUTE_HOST, 'hostname': hostname, 'generated_time': current_time, 'payload': { 'event': event_type, 'cluster_status': cluster_status, 'host_status': host_status } } } self.assertEqual(event, ret) @mock.patch.object(timeutils, 'utcnow') @mock.patch.object(handle_host.HandleHost, '_is_poweroff') def test_make_event_offline_disable_ipmi_check_false_poweroff( self, mock_is_poweroff, mock_utcnow): mock_is_poweroff.return_value = True current_time = timeutils.utcnow() mock_utcnow.return_value = current_time CONF.host.disable_ipmi_check = False obj = handle_host.HandleHost() hostname = 'test_hostname' current_status = 'offline' ret = obj._make_event(hostname, current_status) event_type = ec.EventConstants.EVENT_STOPPED cluster_status = current_status.upper() host_status = ec.EventConstants.HOST_STATUS_NORMAL event = { 'notification': { 'type': ec.EventConstants.TYPE_COMPUTE_HOST, 'hostname': hostname, 'generated_time': current_time, 'payload': { 'event': event_type, 'cluster_status': cluster_status, 'host_status': host_status } } } self.assertEqual(event, ret) mock_is_poweroff.assert_called_once_with(hostname) @mock.patch.object(timeutils, 'utcnow') @mock.patch.object(handle_host.HandleHost, '_is_poweroff') def test_make_event_offline_disable_ipmi_check_false_poweron( self, mock_is_poweroff, mock_utcnow): mock_is_poweroff.return_value = False current_time = timeutils.utcnow() mock_utcnow.return_value = current_time CONF.host.disable_ipmi_check = False obj = handle_host.HandleHost() hostname = 'test_hostname' current_status = 'offline' ret = obj._make_event(hostname, current_status) event_type = ec.EventConstants.EVENT_STOPPED cluster_status = current_status.upper() host_status = ec.EventConstants.HOST_STATUS_UNKNOWN event = { 'notification': { 'type': ec.EventConstants.TYPE_COMPUTE_HOST, 'hostname': hostname, 'generated_time': current_time, 'payload': { 'event': event_type, 'cluster_status': cluster_status, 'host_status': host_status } } } self.assertEqual(event, ret) mock_is_poweroff.assert_called_once_with(hostname) @mock.patch.object(timeutils, 'utcnow') def test_make_event_offline_disable_ipmi_check_true(self, mock_utcnow): current_time = timeutils.utcnow() mock_utcnow.return_value = current_time CONF.host.disable_ipmi_check = True obj = handle_host.HandleHost() hostname = 'test_hostname' current_status = 'offline' ret = obj._make_event(hostname, current_status) event_type = ec.EventConstants.EVENT_STOPPED cluster_status = current_status.upper() host_status = ec.EventConstants.HOST_STATUS_NORMAL event = { 'notification': { 'type': ec.EventConstants.TYPE_COMPUTE_HOST, 'hostname': hostname, 'generated_time': current_time, 'payload': { 'event': event_type, 'cluster_status': cluster_status, 'host_status': host_status } } } self.assertEqual(event, ret) def test_normalize_host_status_pre_pacemaker_pre_2_1_7(self): status_xml = ElementTree.fromstring(""" """) obj = handle_host.HandleHost() response = [obj._normalize_host_status(status.get('crmd')) for status in list(status_xml)] self.assertEqual(["online", "offline", "other"], response) def test_normalize_host_status_pacemaker_post_2_1_7(self): status_tag = ElementTree.fromstring(""" """) obj = handle_host.HandleHost() response = [obj._normalize_host_status(status.get('crmd')) for status in list(status_tag)] self.assertEqual(["online", "offline"], response) @mock.patch.object(masakari.SendNotification, 'send_notification') @mock.patch.object(handle_host.HandleHost, '_make_event') @mock.patch.object(hold_host_status.HostHoldStatus, 'set_host_status') @mock.patch.object(hold_host_status.HostHoldStatus, 'get_host_status') @mock.patch.object(socket, 'gethostname') def test_check_if_status_changed( self, mock_gethostname, mock_get_host_status, mock_set_host_status, mock_make_event, mock_send_notification): mock_gethostname.return_value = 'node1' mock_get_host_status.side_effect = \ [None, 'online', 'online', 'online'] mock_set_host_status.return_value = None test_event = {'notification': 'test'} mock_make_event.return_value = test_event status_tag = ElementTree.fromstring(STATUS_TAG_XML) node_state_tag_list = list(status_tag) obj = handle_host.HandleHost() obj._check_if_status_changed(node_state_tag_list) node_state_node2 = node_state_tag_list[1] node_state_node3 = node_state_tag_list[2] node_state_node4 = node_state_tag_list[3] node_state_node5 = node_state_tag_list[4] node2 = node_state_node2.get('uname') node3 = node_state_node3.get('uname') node4 = node_state_node4.get('uname') node5 = node_state_node5.get('uname') calls_get_host_status = [mock.call(node2), mock.call(node3), mock.call(node4), mock.call(node5)] calls_set_host_status = [mock.call(node_state_node4), mock.call(node_state_node5)] mock_get_host_status.assert_has_calls(calls_get_host_status) mock_set_host_status.assert_has_calls(calls_set_host_status) mock_make_event.assert_called_once_with(node4, 'offline') mock_send_notification.assert_called_once_with( CONF.host.api_retry_max, CONF.host.api_retry_interval, test_event) @mock.patch.object(masakari.SendNotification, 'send_notification') @mock.patch.object(handle_host.HandleHost, '_make_event') @mock.patch.object(hold_host_status.HostHoldStatus, 'set_host_status') @mock.patch.object(hold_host_status.HostHoldStatus, 'get_host_status') @mock.patch.object(socket, 'gethostname') def test_check_if_status_changed_with_3_samples( self, mock_gethostname, mock_get_host_status, mock_set_host_status, mock_make_event, mock_send_notification): mock_gethostname.return_value = 'node1' mock_get_host_status.side_effect = \ [None, 'online', 'online', 'online'] mock_set_host_status.return_value = None test_event = {'notification': 'test'} mock_make_event.return_value = test_event status_tag = ElementTree.fromstring(STATUS_TAG_XML) node_state_tag_list = list(status_tag) CONF.host.monitoring_samples = 3 obj = handle_host.HandleHost() obj.monitoring_data = { "node1": deque(['online', 'online', 'online'], maxlen=3), "node2": deque(['offline', 'online', 'online'], maxlen=3), "node3": deque(['offline'], maxlen=3), "node4": deque(['online', 'offline', 'offline'], maxlen=3), "node5": deque(['online', 'online', 'online'], maxlen=3), } obj._check_if_status_changed(node_state_tag_list) self.assertEqual(deque(['online', 'online', 'online'], maxlen=3), obj.monitoring_data.get('node2')) self.assertEqual('online', obj.get_stabilised_host_status('node2')) self.assertIn(mock.call(node_state_tag_list[1]), mock_set_host_status.mock_calls) self.assertEqual(deque(['offline', 'online'], maxlen=3), obj.monitoring_data.get('node3')) self.assertEqual('_being_collected', obj.get_stabilised_host_status('node3')) self.assertNotIn(mock.call(node_state_tag_list[2]), mock_set_host_status.mock_calls) self.assertEqual(deque(['offline', 'offline', 'offline'], maxlen=3), obj.monitoring_data.get('node4')) self.assertEqual('offline', obj.get_stabilised_host_status('node4')) self.assertIn(mock.call(node_state_tag_list[3]), mock_set_host_status.mock_calls) self.assertEqual(deque(['online', 'online', 'other'], maxlen=3), obj.monitoring_data.get('node5')) self.assertEqual('_uncertain', obj.get_stabilised_host_status('node5')) self.assertNotIn(mock.call(node_state_tag_list[4]), mock_set_host_status.mock_calls) mock_make_event.assert_called_once_with("node4", 'offline') mock_send_notification.assert_called_once_with( CONF.host.api_retry_max, CONF.host.api_retry_interval, test_event) @mock.patch.object(handle_host.HandleHost, '_check_if_status_changed') @mock.patch.object(parse_crmmon_xml.ParseCrmMonXml, 'get_node_state_tag_list') @mock.patch.object(parse_crmmon_xml.ParseCrmMonXml, 'has_quorum') @mock.patch.object(parse_crmmon_xml.ParseCrmMonXml, 'set_crmmon_xml') @mock.patch.object(handle_host.HandleHost, '_get_crmmon_xml') def test_check_host_status_by_crm_mon( self, mock_get_crmmon_xml, mock_set_crmmon_xml, mock_has_quorum, mock_get_node_state_tag_list, mock_check_if_status_changed): mock_get_crmmon_xml.return_value = CRMMON_NODES_TAG_XML mock_set_crmmon_xml.return_value = None mock_has_quorum.return_value = True status_tag = ElementTree.fromstring(CRMMON_NODES_TAG_XML) node_state_tag_list = list(status_tag) mock_get_node_state_tag_list.return_value = node_state_tag_list mock_check_if_status_changed.return_value = None obj = handle_host.HandleHost() ret = obj._check_host_status_by_crm_mon() self.assertEqual(0, ret) mock_set_crmmon_xml.assert_called_once_with(CRMMON_NODES_TAG_XML) mock_get_node_state_tag_list.assert_called_once_with() mock_check_if_status_changed.assert_called_once_with( [ {'uname': 'remote1', 'crmd': 'online'}, {'uname': 'remote2', 'crmd': 'online'}, {'uname': 'remote3', 'crmd': 'online'}]) @mock.patch.object(handle_host.HandleHost, '_check_if_status_changed') @mock.patch.object(parse_crmmon_xml.ParseCrmMonXml, 'has_quorum') @mock.patch.object(parse_crmmon_xml.ParseCrmMonXml, 'set_crmmon_xml') @mock.patch.object(handle_host.HandleHost, '_get_crmmon_xml') def test_check_host_status_by_crm_mon_no_quorum( self, mock_get_crmmon_xml, mock_set_crmmon_xml, mock_has_quorum, mock_check_if_status_changed): mock_get_crmmon_xml.return_value = CRMMON_NODES_TAG_XML mock_set_crmmon_xml.return_value = None mock_has_quorum.return_value = False obj = handle_host.HandleHost() ret = obj._check_host_status_by_crm_mon() self.assertEqual(2, ret) mock_set_crmmon_xml.assert_called_once_with(CRMMON_NODES_TAG_XML) mock_check_if_status_changed.assert_not_called() @mock.patch.object(parse_crmmon_xml.ParseCrmMonXml, 'has_quorum') @mock.patch.object(parse_crmmon_xml.ParseCrmMonXml, 'get_node_state_tag_list') @mock.patch.object(parse_crmmon_xml.ParseCrmMonXml, 'set_crmmon_xml') @mock.patch.object(handle_host.HandleHost, '_get_crmmon_xml') def test_check_host_status_by_crm_mon_not_have_node_state_tag( self, mock_get_crmmon_xml, mock_set_crmmon_xml, mock_get_node_state_tag_list, mock_has_quorum): mock_get_crmmon_xml.return_value = CRMMON_NODES_TAG_XML mock_set_crmmon_xml.return_value = None mock_get_node_state_tag_list.return_value = [] mock_has_quorum.return_value = True obj = handle_host.HandleHost() self.assertRaisesRegex( Exception, "Failed to get nodes tag from crm_mon xml.", obj._check_host_status_by_crm_mon) mock_get_crmmon_xml.assert_called_once_with() mock_set_crmmon_xml.assert_called_once_with(CRMMON_NODES_TAG_XML) mock_get_node_state_tag_list.assert_called_once_with() @mock.patch.object(handle_host.HandleHost, '_get_crmmon_xml') def test_check_host_status_by_crm_mon_xml_is_None( self, mock_get_crmmon_xml): mock_get_crmmon_xml.return_value = None obj = handle_host.HandleHost() ret = obj._check_host_status_by_crm_mon() self.assertEqual(1, ret) mock_get_crmmon_xml.assert_called_once_with() @mock.patch.object(handle_host.HandleHost, '_check_if_status_changed') @mock.patch.object(parse_cib_xml.ParseCibXml, 'get_node_state_tag_list') @mock.patch.object(parse_cib_xml.ParseCibXml, 'have_quorum') @mock.patch.object(parse_cib_xml.ParseCibXml, 'set_cib_xml') @mock.patch.object(handle_host.HandleHost, '_get_cib_xml') def test_check_host_status_by_cibadmin( self, mock_get_cib_xml, mock_set_cib_xml, mock_have_quorum, mock_get_node_state_tag_list, mock_check_if_status_changed): mock_get_cib_xml.return_value = STATUS_TAG_XML mock_set_cib_xml.return_value = None mock_have_quorum.return_value = 1 status_tag = ElementTree.fromstring(STATUS_TAG_XML) node_state_tag_list = list(status_tag) mock_get_node_state_tag_list.return_value = node_state_tag_list mock_check_if_status_changed.return_value = None obj = handle_host.HandleHost() ret = obj._check_host_status_by_cibadmin() self.assertEqual(0, ret) mock_get_cib_xml.assert_called_once_with() mock_set_cib_xml.assert_called_once_with(STATUS_TAG_XML) mock_have_quorum.assert_called_once_with() mock_get_node_state_tag_list.assert_called_once_with() mock_check_if_status_changed.assert_called_once_with( node_state_tag_list) @mock.patch.object(handle_host.HandleHost, '_check_if_status_changed') @mock.patch.object(parse_cib_xml.ParseCibXml, 'have_quorum') @mock.patch.object(parse_cib_xml.ParseCibXml, 'set_cib_xml') @mock.patch.object(handle_host.HandleHost, '_get_cib_xml') def test_check_host_status_by_cibadmin_no_quorum( self, mock_get_cib_xml, mock_set_cib_xml, mock_have_quorum, mock_check_if_status_changed): mock_get_cib_xml.return_value = STATUS_TAG_XML mock_set_cib_xml.return_value = None mock_have_quorum.return_value = 0 obj = handle_host.HandleHost() ret = obj._check_host_status_by_cibadmin() self.assertEqual(2, ret) mock_get_cib_xml.assert_called_once_with() mock_set_cib_xml.assert_called_once_with(STATUS_TAG_XML) mock_have_quorum.assert_called_once_with() mock_check_if_status_changed.assert_not_called() @mock.patch.object(handle_host.HandleHost, '_get_cib_xml') def test_check_host_status_by_cibadmin_cib_xml_is_None( self, mock_get_cib_xml): mock_get_cib_xml.return_value = None obj = handle_host.HandleHost() ret = obj._check_host_status_by_cibadmin() self.assertEqual(1, ret) mock_get_cib_xml.assert_called_once_with() @mock.patch.object(parse_cib_xml.ParseCibXml, 'get_node_state_tag_list') @mock.patch.object(parse_cib_xml.ParseCibXml, 'have_quorum') @mock.patch.object(parse_cib_xml.ParseCibXml, 'set_cib_xml') @mock.patch.object(handle_host.HandleHost, '_get_cib_xml') def test_check_host_status_by_cibadmin_not_have_node_state_tag( self, mock_get_cib_xml, mock_set_cib_xml, mock_have_quorum, mock_get_node_state_tag_list): mock_get_cib_xml.return_value = STATUS_TAG_XML mock_set_cib_xml.return_value = None mock_have_quorum.return_value = 1 mock_get_node_state_tag_list.return_value = [] obj = handle_host.HandleHost() self.assertRaisesRegex( Exception, "Failed to get node_state tag from cib xml.", obj._check_host_status_by_cibadmin) mock_get_cib_xml.assert_called_once_with() mock_set_cib_xml.assert_called_once_with(STATUS_TAG_XML) mock_have_quorum.assert_called_once_with() mock_get_node_state_tag_list.assert_called_once_with() def test_stop(self): obj = handle_host.HandleHost() obj.stop() self.assertFalse(obj.running) @mock.patch.object(eventlet.greenthread, 'sleep') @mock.patch.object(handle_host.HandleHost, '_check_host_status_by_cibadmin') @mock.patch.object(handle_host.HandleHost, '_check_host_status_by_crmadmin') @mock.patch.object(handle_host.HandleHost, '_check_pacemaker_services') @mock.patch.object(handle_host.HandleHost, '_check_hb_line') def test_monitor_hosts(self, mock_check_hb_line, mock_check_pacemaker_services, mock_check_host_status_by_crmadmin, mock_check_host_status_by_cibadmin, mock_sleep): mock_check_hb_line.side_effect = \ [0, 1, 2, 0, Exception("Test exception."), 0, KeyboardInterrupt()] mock_check_pacemaker_services.side_effect = [True, False, False, True] mock_check_host_status_by_crmadmin.side_effect = [0, 1] mock_check_host_status_by_cibadmin.side_effect = [0, 1, 0] mock_sleep.return_value = None obj = handle_host.HandleHost() self.assertRaises(KeyboardInterrupt, obj.monitor_hosts) self.assertEqual(7, mock_check_hb_line.call_count) self.assertEqual(4, mock_check_pacemaker_services.call_count) mock_check_pacemaker_services.assert_called_with('pacemaker_remote') self.assertEqual(2, mock_check_host_status_by_crmadmin.call_count) self.assertEqual(3, mock_check_host_status_by_cibadmin.call_count) @mock.patch.object(eventlet.greenthread, 'sleep') @mock.patch.object(handle_host.HandleHost, '_check_host_status_by_crm_mon') @mock.patch.object(handle_host.HandleHost, '_check_pacemaker_services') @mock.patch.object(handle_host.HandleHost, '_check_hb_line') def test_monitor_hosts_remotes_only(self, mock_check_hb_line, mock_check_pacemaker_services, mock_check_host_status_by_crm_mon, mock_sleep): CONF.host.restrict_to_remotes = True mock_check_hb_line.side_effect = \ [0, Exception("Test exception."), 0, KeyboardInterrupt()] mock_check_pacemaker_services.return_value = True mock_check_host_status_by_crm_mon.return_value = 0 mock_sleep.return_value = None obj = handle_host.HandleHost() self.assertRaises(KeyboardInterrupt, obj.monitor_hosts) self.assertEqual(4, mock_check_hb_line.call_count) self.assertEqual(2, mock_check_pacemaker_services.call_count) mock_check_pacemaker_services.assert_called_with('pacemaker_remote') self.assertEqual(2, mock_check_host_status_by_crm_mon.call_count) ././@PaxHeader0000000000000000000000000000021400000000000010212 xustar00112 path=masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/host_handler/test_hold_host_status.py 28 mtime=1790765099.4602885 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/host_handler/test_hold_host_status.0000664000175000017500000000274215257164053033345 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import testtools from xml.etree import ElementTree import eventlet from masakarimonitors.hostmonitor.host_handler import hold_host_status eventlet.monkey_patch(os=False) NODE_STATE_XML = '' \ ' ' \ '' NODE_STATE_TAG = ElementTree.fromstring(NODE_STATE_XML) class TestHostHoldStatus(testtools.TestCase): def setUp(self): super(TestHostHoldStatus, self).setUp() def test_set_host_status(self): obj = hold_host_status.HostHoldStatus() obj.set_host_status(NODE_STATE_TAG) self.assertEqual('online', obj.get_host_status('masakari-node')) def test_get_host_status(self): obj = hold_host_status.HostHoldStatus() obj.set_host_status(NODE_STATE_TAG) self.assertEqual('online', obj.get_host_status('masakari-node')) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4602885 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/host_handler/test_parse_cib_xml.py0000664000175000017500000001351015257164053033132 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import testtools from unittest import mock from xml.etree import ElementTree import eventlet from masakarimonitors.hostmonitor.host_handler import parse_cib_xml eventlet.monkey_patch(os=False) CIB_XML = '' \ ' ' \ ' test' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ '' CIB_TAG = ElementTree.fromstring(CIB_XML) class TestParseCibXml(testtools.TestCase): def setUp(self): super(TestParseCibXml, self).setUp() @mock.patch.object(ElementTree, 'fromstring') def test_set_cib_xml(self, mock_fromstring): obj = parse_cib_xml.ParseCibXml() mock_fromstring.return_value = CIB_TAG obj.set_cib_xml(CIB_XML) def test_have_quorum(self): obj = parse_cib_xml.ParseCibXml() obj.set_cib_xml(CIB_XML) self.assertEqual(1, obj.have_quorum()) def test_get_node_state_tag_list(self): obj = parse_cib_xml.ParseCibXml() obj.set_cib_xml(CIB_XML) node_state_tag_list = obj.get_node_state_tag_list() for node_state_tag in node_state_tag_list: self.assertEqual('online', node_state_tag.get('crmd')) def test_get_stonith_ipmi_params(self): obj = parse_cib_xml.ParseCibXml() obj.set_cib_xml(CIB_XML) ipmi_values = obj.get_stonith_ipmi_params('compute-node') self.assertEqual('192.168.10.21', ipmi_values['ipaddr']) self.assertEqual('admin', ipmi_values['userid']) self.assertEqual('password', ipmi_values['passwd']) self.assertEqual('lanplus', ipmi_values['interface']) ././@PaxHeader0000000000000000000000000000021400000000000010212 xustar00112 path=masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/host_handler/test_parse_crmmon_xml.py 28 mtime=1790765099.4602885 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/host_handler/test_parse_crmmon_xml.0000664000175000017500000000671315257164053033326 0ustar00zuulzuul# Copyright(c) 2019 Canonical Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import testtools from masakarimonitors.hostmonitor.host_handler import parse_crmmon_xml CRMMON_XML = '' \ '' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ ' ' \ '' CRMMON_XML_NO_QUORUM = \ '' \ '' \ ' ' \ ' ' \ ' ' \ ' ' \ '' CRMMON_NONODES_XML = '' \ '' \ ' ' \ ' ' \ '' CRMMON_NONODES_TAG_XML = '' \ '' \ '' class TestParseCrmMonXml(testtools.TestCase): def setUp(self): super(TestParseCrmMonXml, self).setUp() def test_set_crmmon_xml(self): obj = parse_crmmon_xml.ParseCrmMonXml() obj.set_crmmon_xml(CRMMON_XML) def test_has_quorum(self): obj = parse_crmmon_xml.ParseCrmMonXml() obj.set_crmmon_xml(CRMMON_XML) self.assertEqual(True, obj.has_quorum()) def test_has_quorum_no_quorum(self): obj = parse_crmmon_xml.ParseCrmMonXml() obj.set_crmmon_xml(CRMMON_XML_NO_QUORUM) self.assertEqual(False, obj.has_quorum()) def test_get_node_state_tag_list(self): obj = parse_crmmon_xml.ParseCrmMonXml() obj.set_crmmon_xml(CRMMON_XML) node_state_tag_list = obj.get_node_state_tag_list() expected = { 'node-1': 'true', 'node-2': 'false', 'node-3': 'true'} for node_state_tag in node_state_tag_list: self.assertEqual( expected[node_state_tag.get('name')], node_state_tag.get('online')) def test_get_node_state_tag_list_unset(self): obj = parse_crmmon_xml.ParseCrmMonXml() self.assertEqual(obj.get_node_state_tag_list(), []) def test_get_node_state_tag_list_nonodes(self): obj = parse_crmmon_xml.ParseCrmMonXml() obj.set_crmmon_xml(CRMMON_NONODES_XML) self.assertEqual(obj.get_node_state_tag_list(), []) def test_get_node_state_tag_list_nonodes_tag(self): obj = parse_crmmon_xml.ParseCrmMonXml() obj.set_crmmon_xml(CRMMON_NONODES_TAG_XML) self.assertEqual(obj.get_node_state_tag_list(), []) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2102191 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/kubernetes_check/0000775000175000017500000000000015257164140027541 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4602885 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/kubernetes_check/__init__.py0000664000175000017500000000000015257164053031643 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4612887 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/kubernetes_check/test_manager.py0000664000175000017500000002012115257164053032563 0ustar00zuulzuul# Copyright(c) 2024 Samsung SDS # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from collections import deque from unittest import mock from kubernetes import client from kubernetes.client import V1Node from kubernetes.client import V1NodeCondition from kubernetes.client import V1NodeList from kubernetes.client import V1NodeStatus from kubernetes.client import V1ObjectMeta from kubernetes import config from oslo_config import fixture as fixture_config import testtools import masakarimonitors.conf from masakarimonitors.ha import masakari from masakarimonitors.hostmonitor.kubernetes_check import manager CONF = masakarimonitors.conf.CONF class TestKubernetesCheck(testtools.TestCase): @mock.patch.object(config, 'load_incluster_config') @mock.patch.object(client, 'CoreV1Api') def setUp(self, mock_core_v1_api, mock_load_incluster_config): super(TestKubernetesCheck, self).setUp() self.CONF = self.useFixture(fixture_config.Config()).conf self.CONF.kubernetes.monitoring_node_labels = \ 'openstack-compute-node=enabled' self.host_monitor = manager.KubernetesCheck() self.host_monitor.monitoring_samples = 1 self.host_monitor.monitoring_data = {} self.last_status = {} def test_update_monitoring_data(self): mock_list_node = V1NodeList( items=[ V1Node( metadata=V1ObjectMeta( name="node0" + str(num), labels={ "openstack-compute-node": "enabled" } ), status=V1NodeStatus(conditions=[ V1NodeCondition(status="False", type="MemoryPressure"), V1NodeCondition(status="False", type="DiskPressure"), V1NodeCondition(status="False", type="PIDPressure"), V1NodeCondition(status="True", type="Ready") ]) ) for num in range(1, 4) ] ) with mock.patch.object(self.host_monitor.kube_client, 'list_node', return_value=mock_list_node): self.host_monitor.update_monitoring_data() excepted_monitoring_data = { "node01": deque(['True'], maxlen=self.host_monitor.monitoring_samples), "node02": deque(['True'], maxlen=self.host_monitor.monitoring_samples), "node03": deque(['True'], maxlen=self.host_monitor.monitoring_samples) } self.assertEqual(excepted_monitoring_data, self.host_monitor.monitoring_data) def test_update_monitoring_data_exclude_node(self): mock_list_node = V1NodeList( items=[ V1Node( metadata=V1ObjectMeta( name="node0" + str(num), labels={ "openstack-compute-node": "enabled", "monitoring": "enabled" } ), status=V1NodeStatus(conditions=[ V1NodeCondition(status="False", type="MemoryPressure"), V1NodeCondition(status="False", type="DiskPressure"), V1NodeCondition(status="False", type="PIDPressure"), V1NodeCondition(status="True", type="Ready") ]) ) for num in range(1, 4) ] ) with mock.patch.object(self.host_monitor.kube_client, 'list_node', return_value=mock_list_node): self.host_monitor.update_monitoring_data() mock_list_node.items.pop() self.host_monitor.update_monitoring_data() excepted_monitoring_data = { "node01": deque(['True', 'True'], maxlen=self.host_monitor.monitoring_samples), "node02": deque(['True', 'True'], maxlen=self.host_monitor.monitoring_samples) } self.assertEqual(2, len(self.host_monitor.monitoring_data)) self.assertEqual(excepted_monitoring_data, self.host_monitor.monitoring_data) @mock.patch.object(masakari.SendNotification, 'send_notification') def test_poll_hosts_with_healthy_nodes(self, mock_send_notification): self.host_monitor.monitoring_data = { "node01": deque(['True'], maxlen=self.host_monitor.monitoring_samples), "node02": deque(['True'], maxlen=self.host_monitor.monitoring_samples), "node03": deque(['True'], maxlen=self.host_monitor.monitoring_samples), } self.host_monitor.poll_hosts() mock_send_notification.assert_not_called() @mock.patch.object(masakari.SendNotification, 'send_notification') def test_poll_hosts_with_healthy_nodes_3samples( self, mock_send_notification): self.host_monitor.monitoring_samples = 3 self.host_monitor.monitoring_data = { "node01": deque(['True', 'True', 'True'], maxlen=self.host_monitor.monitoring_samples), "node02": deque(['Unknown', 'True', 'True'], maxlen=self.host_monitor.monitoring_samples), "node03": deque(['True', 'Unknown', 'Unknown'], maxlen=self.host_monitor.monitoring_samples), } self.host_monitor.poll_hosts() mock_send_notification.assert_not_called() @mock.patch.object(masakari.SendNotification, 'send_notification') @mock.patch.object(manager.KubernetesCheck, '_event') def test_poll_hosts_with_unhealthy_node(self, mock_event, mock_send_notification): self.host_monitor.last_status = { "node01": True, "node02": True, "node03": True } self.host_monitor.monitoring_samples = 3 self.host_monitor.monitoring_data = { "node01": deque(['True', 'True', 'True'], maxlen=self.host_monitor.monitoring_samples), "node02": deque(['Unknown', 'Unknown', 'Unknown'], maxlen=self.host_monitor.monitoring_samples), "node03": deque(['Unknown', 'True', 'Unknown'], maxlen=self.host_monitor.monitoring_samples), } test_event = {'notification': 'test'} mock_event.return_value = test_event self.host_monitor.poll_hosts() mock_send_notification.assert_called_once_with( CONF.host.api_retry_max, CONF.host.api_retry_interval, test_event) @mock.patch.object(masakari.SendNotification, 'send_notification') def test_poll_hosts_with_having_less_healthy_history_than_samples( self, mock_send_notification): self.host_monitor.monitoring_samples = 3 self.host_monitor.monitoring_data = { "node01": deque(['True', 'True'], maxlen=self.host_monitor.monitoring_samples), "node02": deque(['True', 'Unknown'], maxlen=self.host_monitor.monitoring_samples), "node03": deque(['Unknown', 'Unknown'], maxlen=self.host_monitor.monitoring_samples), } self.host_monitor.poll_hosts() mock_send_notification.assert_not_called() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4612887 masakari_monitors-22.0.0/masakarimonitors/tests/unit/hostmonitor/test_host.py0000664000175000017500000000726615257164053026641 0ustar00zuulzuul# Copyright(c) 2017 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import testtools from unittest import mock from stevedore import driver from masakarimonitors.hostmonitor import host class TestHostmonitorManager(testtools.TestCase): def setUp(self): super(TestHostmonitorManager, self).setUp() @mock.patch.object(driver, 'DriverManager') def test_init_host(self, mock_DriverManager): mock_driver = mock.Mock() mock_DriverManager.return_value = mock_driver host_manager = host.HostmonitorManager() host_manager.init_host() mock_DriverManager.assert_called_once_with( namespace='hostmonitor.driver', name='default', invoke_on_load=True, invoke_args=(), ) @mock.patch.object(os, '_exit') @mock.patch.object(driver, 'DriverManager') def test_init_host_exception(self, mock_DriverManager, mock_exit): mock_DriverManager.side_effect = Exception("Test exception.") mock_exit.return_value = None host_manager = host.HostmonitorManager() host_manager.init_host() mock_DriverManager.assert_called_once_with( namespace='hostmonitor.driver', name='default', invoke_on_load=True, invoke_args=(), ) mock_exit.assert_called_once_with(1) @mock.patch.object(driver, 'DriverManager') def test_stop(self, mock_DriverManager): mock_driver = mock.Mock() mock_DriverManager.return_value = mock_driver host_manager = host.HostmonitorManager() host_manager.init_host() host_manager.stop() mock_DriverManager.assert_called_once_with( namespace='hostmonitor.driver', name='default', invoke_on_load=True, invoke_args=(), ) mock_driver.driver.stop.assert_called_once_with() @mock.patch.object(driver, 'DriverManager') def test_main(self, mock_DriverManager): mock_driver = mock.Mock() mock_DriverManager.return_value = mock_driver host_manager = host.HostmonitorManager() host_manager.init_host() ret = host_manager.main() mock_DriverManager.assert_called_once_with( namespace='hostmonitor.driver', name='default', invoke_on_load=True, invoke_args=(), ) mock_driver.driver.monitor_hosts.assert_called_once_with() self.assertIsNone(ret) @mock.patch.object(driver, 'DriverManager') def test_main_exception(self, mock_DriverManager): mock_driver = mock.Mock() mock_DriverManager.return_value = mock_driver mock_driver.driver.monitor_hosts.side_effect = \ Exception("Test exception.") host_manager = host.HostmonitorManager() host_manager.init_host() ret = host_manager.main() mock_DriverManager.assert_called_once_with( namespace='hostmonitor.driver', name='default', invoke_on_load=True, invoke_args=(), ) mock_driver.driver.monitor_hosts.assert_called_once_with() self.assertIsNone(ret) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2102191 masakari_monitors-22.0.0/masakarimonitors/tests/unit/instancemonitor/0000775000175000017500000000000015257164140025064 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4612887 masakari_monitors-22.0.0/masakarimonitors/tests/unit/instancemonitor/__init__.py0000664000175000017500000000000015257164053027166 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2102191 masakari_monitors-22.0.0/masakarimonitors/tests/unit/instancemonitor/libvirt_handler/0000775000175000017500000000000015257164140030234 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4612887 masakari_monitors-22.0.0/masakarimonitors/tests/unit/instancemonitor/libvirt_handler/__init__.py0000664000175000017500000000000015257164053032336 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000021300000000000010211 xustar00111 path=masakari_monitors-22.0.0/masakarimonitors/tests/unit/instancemonitor/libvirt_handler/test_callback.py 28 mtime=1790765099.4612887 masakari_monitors-22.0.0/masakarimonitors/tests/unit/instancemonitor/libvirt_handler/test_callback.p0000664000175000017500000000411215257164053033211 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import socket import testtools from unittest import mock import uuid import eventlet from oslo_utils import timeutils from masakarimonitors.ha import masakari from masakarimonitors.instancemonitor.libvirt_handler import callback from masakarimonitors.objects import event_constants as ec eventlet.monkey_patch(os=False) class TestCallback(testtools.TestCase): def setUp(self): super(TestCallback, self).setUp() @mock.patch.object(masakari.SendNotification, 'send_notification') def test_libvirt_event_callback(self, mock_send_notification): mock_send_notification.return_value = None obj = callback.Callback() event_id = 0 details = 5 domain_uuid = uuid.uuid4() notice_type = ec.EventConstants.TYPE_VM hostname = socket.gethostname() current_time = timeutils.utcnow() obj.libvirt_event_callback(event_id, details, domain_uuid, notice_type, hostname, current_time) retry_max = 12 retry_interval = 10 event = { 'notification': { 'type': notice_type, 'hostname': hostname, 'generated_time': current_time, 'payload': { 'event': event_id, 'instance_uuid': domain_uuid, 'vir_domain_event': details } } } mock_send_notification.assert_called_once_with( retry_max, retry_interval, event) ././@PaxHeader0000000000000000000000000000021600000000000010214 xustar00114 path=masakari_monitors-22.0.0/masakarimonitors/tests/unit/instancemonitor/libvirt_handler/test_eventfilter.py 28 mtime=1790765099.4612887 masakari_monitors-22.0.0/masakarimonitors/tests/unit/instancemonitor/libvirt_handler/test_eventfilte0000664000175000017500000001332115257164053033366 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import socket import testtools import threading from unittest import mock import uuid import eventlet from oslo_utils import excutils from oslo_utils import timeutils from masakarimonitors.instancemonitor.libvirt_handler import callback from masakarimonitors.instancemonitor.libvirt_handler import eventfilter from masakarimonitors.instancemonitor.libvirt_handler \ import eventfilter_table as evft from masakarimonitors.objects import event_constants as ec eventlet.monkey_patch(os=False) class TestEventFilter(testtools.TestCase): def setUp(self): super(TestEventFilter, self).setUp() @mock.patch.object(excutils, 'save_and_reraise_exception') @mock.patch.object(callback.Callback, 'libvirt_event_callback') @mock.patch.object(timeutils, 'utcnow') def test_vir_event_filter(self, mock_utcnow, mock_libvirt_event_callback, mock_save_and_reraise_exception): current_time = timeutils.utcnow() mock_utcnow.return_value = current_time mock_libvirt_event_callback.return_value = None mock_save_and_reraise_exception.return_value = None obj = eventfilter.EventFilter() eventID = 0 eventType = 5 detail = 5 uuID = uuid.uuid4() obj.vir_event_filter(eventID, eventType, detail, uuID) mock_libvirt_event_callback.assert_called_once_with( evft.eventID_dic[eventID], evft.detail_dic[eventID][eventType][detail], uuID, ec.EventConstants.TYPE_VM, socket.gethostname(), current_time) mock_save_and_reraise_exception.assert_not_called() @mock.patch.object(excutils, 'save_and_reraise_exception') @mock.patch.object(callback.Callback, 'libvirt_event_callback') def test_vir_event_filter_unmatched(self, mock_libvirt_event_callback, mock_save_and_reraise_exception): mock_libvirt_event_callback.return_value = None mock_save_and_reraise_exception.return_value = None obj = eventfilter.EventFilter() eventID = 0 eventType = 5 detail = 2 uuID = uuid.uuid4() obj.vir_event_filter(eventID, eventType, detail, uuID) mock_libvirt_event_callback.assert_not_called() mock_save_and_reraise_exception.assert_not_called() @mock.patch.object(excutils, 'save_and_reraise_exception') @mock.patch.object(callback.Callback, 'libvirt_event_callback') def test_vir_event_filter_key_error(self, mock_libvirt_event_callback, mock_save_and_reraise_exception): mock_libvirt_event_callback.return_value = None mock_save_and_reraise_exception.return_value = None obj = eventfilter.EventFilter() eventID = 0 eventType = 0 detail = 0 uuID = uuid.uuid4() obj.vir_event_filter(eventID, eventType, detail, uuID) mock_libvirt_event_callback.assert_not_called() mock_save_and_reraise_exception.assert_not_called() @mock.patch.object(excutils, 'save_and_reraise_exception') @mock.patch.object(callback.Callback, 'libvirt_event_callback') @mock.patch.object(threading, 'Thread') def test_vir_event_filter_type_error(self, mock_Thread, mock_libvirt_event_callback, mock_save_and_reraise_exception): mock_Thread.side_effect = TypeError("Threading exception.") mock_libvirt_event_callback.return_value = None mock_save_and_reraise_exception.return_value = None obj = eventfilter.EventFilter() eventID = 0 eventType = 5 detail = 5 uuID = uuid.uuid4() obj.vir_event_filter(eventID, eventType, detail, uuID) mock_libvirt_event_callback.assert_not_called() mock_save_and_reraise_exception.assert_not_called() @mock.patch.object(excutils, 'save_and_reraise_exception') @mock.patch.object(callback.Callback, 'libvirt_event_callback') @mock.patch.object(threading, 'Thread') def test_vir_event_filter_index_error(self, mock_Thread, mock_libvirt_event_callback, mock_save_and_reraise_exception): mock_Thread.side_effect = IndexError("Threading exception.") mock_libvirt_event_callback.return_value = None mock_save_and_reraise_exception.return_value = None obj = eventfilter.EventFilter() eventID = 0 eventType = 5 detail = 5 uuID = uuid.uuid4() obj.vir_event_filter(eventID, eventType, detail, uuID) mock_libvirt_event_callback.assert_not_called() mock_save_and_reraise_exception.assert_not_called() @mock.patch.object(callback.Callback, 'libvirt_event_callback') @mock.patch.object(threading, 'Thread') def test_vir_event_filter_other_exception(self, mock_Thread, mock_libvirt_event_callback): mock_Thread.side_effect = NameError("Threading exception.") mock_libvirt_event_callback.return_value = None obj = eventfilter.EventFilter() eventID = 0 eventType = 5 detail = 5 uuID = uuid.uuid4() self.assertRaises(NameError, obj.vir_event_filter, eventID, eventType, detail, uuID) mock_libvirt_event_callback.assert_not_called() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4612887 masakari_monitors-22.0.0/masakarimonitors/tests/unit/instancemonitor/test_instance.py0000664000175000017500000002336615257164053030316 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import libvirt import testtools import threading import time from unittest import mock import uuid import eventlet from masakarimonitors.instancemonitor import instance from masakarimonitors.instancemonitor.libvirt_handler import eventfilter eventlet.monkey_patch(os=False) class TestInstancemonitorManager(testtools.TestCase): def setUp(self): super(TestInstancemonitorManager, self).setUp() def _make_callback_params(self): mock_conn = mock.Mock() mock_dom = mock.Mock() test_uuid = uuid.uuid4() mock_dom.UUIDString.return_value = test_uuid mock_opaque = mock.Mock() return mock_conn, mock_dom, mock_opaque, test_uuid @mock.patch.object(libvirt, 'virEventRunDefaultImpl') def test_vir_event_loop_native_run(self, mock_virEventRunDefaultImpl): mock_virEventRunDefaultImpl.side_effect = Exception("Test exception.") obj = instance.InstancemonitorManager() exception_flag = False try: obj._vir_event_loop_native_run() except Exception: exception_flag = True self.assertTrue(exception_flag) mock_virEventRunDefaultImpl.assert_called_once() @mock.patch.object(eventfilter.EventFilter, 'vir_event_filter') def test_my_domain_event_callback(self, mock_vir_event_filter): mock_vir_event_filter.return_value = None mock_conn, mock_dom, mock_opaque, test_uuid = \ self._make_callback_params() obj = instance.InstancemonitorManager() obj._my_domain_event_callback(mock_conn, mock_dom, 0, 1, mock_opaque) mock_vir_event_filter.assert_called_once_with( libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, 0, 1, test_uuid) @mock.patch.object(eventfilter.EventFilter, 'vir_event_filter') def test_my_domain_event_reboot_callback(self, mock_vir_event_filter): mock_vir_event_filter.return_value = None mock_conn, mock_dom, mock_opaque, test_uuid = \ self._make_callback_params() obj = instance.InstancemonitorManager() obj._my_domain_event_reboot_callback(mock_conn, mock_dom, mock_opaque) mock_vir_event_filter.assert_called_once_with( libvirt.VIR_DOMAIN_EVENT_ID_REBOOT, -1, -1, test_uuid) @mock.patch.object(eventfilter.EventFilter, 'vir_event_filter') def test_my_domain_event_rtc_change_callback(self, mock_vir_event_filter): mock_vir_event_filter.return_value = None mock_conn, mock_dom, mock_opaque, test_uuid = \ self._make_callback_params() utcoffset = "" obj = instance.InstancemonitorManager() obj._my_domain_event_rtc_change_callback( mock_conn, mock_dom, utcoffset, mock_opaque) mock_vir_event_filter.assert_called_once_with( libvirt.VIR_DOMAIN_EVENT_ID_RTC_CHANGE, -1, -1, test_uuid) @mock.patch.object(eventfilter.EventFilter, 'vir_event_filter') def test_my_domain_event_watchdog_callback(self, mock_vir_event_filter): mock_vir_event_filter.return_value = None mock_conn, mock_dom, mock_opaque, test_uuid = \ self._make_callback_params() action = 0 obj = instance.InstancemonitorManager() obj._my_domain_event_watchdog_callback( mock_conn, mock_dom, action, mock_opaque) mock_vir_event_filter.assert_called_once_with( libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG, action, -1, test_uuid) @mock.patch.object(eventfilter.EventFilter, 'vir_event_filter') def test_my_domain_event_io_error_callback(self, mock_vir_event_filter): mock_vir_event_filter.return_value = None mock_conn, mock_dom, mock_opaque, test_uuid = \ self._make_callback_params() srcpath = "" devalias = "" action = 0 obj = instance.InstancemonitorManager() obj._my_domain_event_io_error_callback( mock_conn, mock_dom, srcpath, devalias, action, mock_opaque) mock_vir_event_filter.assert_called_once_with( libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR, action, -1, test_uuid) @mock.patch.object(eventfilter.EventFilter, 'vir_event_filter') def test_my_domain_event_graphics_callback(self, mock_vir_event_filter): mock_vir_event_filter.return_value = None mock_conn, mock_dom, mock_opaque, test_uuid = \ self._make_callback_params() phase = 0 localAddr = "" remoteAddr = "" authScheme = "" subject = "" obj = instance.InstancemonitorManager() obj._my_domain_event_graphics_callback( mock_conn, mock_dom, phase, localAddr, remoteAddr, authScheme, subject, mock_opaque) mock_vir_event_filter.assert_called_once_with( libvirt.VIR_DOMAIN_EVENT_ID_GRAPHICS, -1, phase, test_uuid) @mock.patch.object(eventfilter.EventFilter, 'vir_event_filter') def test_my_domain_event_disk_change_callback( self, mock_vir_event_filter): mock_vir_event_filter.return_value = None mock_conn, mock_dom, mock_opaque, test_uuid = \ self._make_callback_params() oldSrcPath = "" newSrcPath = "" devAlias = "" reason = "" obj = instance.InstancemonitorManager() obj._my_domain_event_disk_change_callback( mock_conn, mock_dom, oldSrcPath, newSrcPath, devAlias, reason, mock_opaque) mock_vir_event_filter.assert_called_once_with( libvirt.VIR_DOMAIN_EVENT_ID_DISK_CHANGE, -1, -1, test_uuid) @mock.patch.object(eventfilter.EventFilter, 'vir_event_filter') def test_my_domain_event_io_error_reason_callback( self, mock_vir_event_filter): mock_vir_event_filter.return_value = None mock_conn, mock_dom, mock_opaque, test_uuid = \ self._make_callback_params() srcPath = "" devAlias = "" action = "" reason = "" obj = instance.InstancemonitorManager() obj._my_domain_event_io_error_reason_callback( mock_conn, mock_dom, srcPath, devAlias, action, reason, mock_opaque) mock_vir_event_filter.assert_called_once_with( libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON, -1, -1, test_uuid) @mock.patch.object(eventfilter.EventFilter, 'vir_event_filter') def test_my_domain_event_generic_callback(self, mock_vir_event_filter): mock_vir_event_filter.return_value = None mock_conn, mock_dom, mock_opaque, test_uuid = \ self._make_callback_params() obj = instance.InstancemonitorManager() obj._my_domain_event_generic_callback( mock_conn, mock_dom, mock_opaque) mock_vir_event_filter.assert_called_once_with( libvirt.VIR_DOMAIN_EVENT_ID_CONTROL_ERROR, -1, -1, test_uuid) def test_err_handler(self): obj = instance.InstancemonitorManager() obj._err_handler("Test context.", ('err0.', 'err1', 'err2')) def test_stop(self): obj = instance.InstancemonitorManager() obj.stop() self.assertFalse(obj.running) @mock.patch.object(time, 'sleep') @mock.patch.object(eventlet.greenthread, 'sleep') @mock.patch.object(libvirt, 'openAuth') @mock.patch.object(threading, 'Thread') @mock.patch.object(libvirt, 'virEventRegisterDefaultImpl') def test_main(self, mock_virEventRegisterDefaultImpl, mock_Thread, mock_openAuth, mock_greenthread_sleep, mock_time_sleep): mock_virEventRegisterDefaultImpl.return_value = None mock_event_loop_thread = mock.Mock(return_value=None) mock_Thread.return_value = mock_event_loop_thread mock_vc = mock.Mock() mock_openAuth.return_value = mock_vc mock_vc.domainEventRegisterAny.side_effect = \ [0, 0, 0, 0, 0, 0, 0, 0, 0] mock_vc.setKeepAlive.return_value = None mock_vc.isAlive.side_effect = [1, 0] mock_vc.domainEventDeregisterAny.side_effect = \ [None, None, None, None, None, None, None, None, Exception("Test exception.")] mock_vc.close.return_value = None mock_greenthread_sleep.return_value = None mock_time_sleep.side_effect = Exception("Test exception.") obj = instance.InstancemonitorManager() exception_flag = False try: obj.main() except Exception: exception_flag = True handlers_count = 9 self.assertTrue(exception_flag) mock_virEventRegisterDefaultImpl.assert_called_once() self.assertEqual(True, mock_event_loop_thread.daemon) mock_event_loop_thread.start.assert_called_once() mock_openAuth.assert_called_once_with( "qemu:///system", [[2, 6, 8, 5, 7, 9], instance.InstancemonitorManager._connect_auth_cb, None], 1) self.assertEqual( handlers_count, mock_vc.domainEventRegisterAny.call_count) mock_vc.setKeepAlive.assert_called_once_with(5, 3) self.assertEqual(2, mock_vc.isAlive.call_count) self.assertEqual( handlers_count, mock_vc.domainEventDeregisterAny.call_count) mock_vc.close.assert_called_once() ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765152.211219 masakari_monitors-22.0.0/masakarimonitors/tests/unit/introspectiveinstancemonitor/0000775000175000017500000000000015257164140027703 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4622889 masakari_monitors-22.0.0/masakarimonitors/tests/unit/introspectiveinstancemonitor/__init__.py0000664000175000017500000000000015257164053032005 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000021700000000000010215 xustar00115 path=masakari_monitors-22.0.0/masakarimonitors/tests/unit/introspectiveinstancemonitor/test_monitor_manager.py 28 mtime=1790765099.4622889 masakari_monitors-22.0.0/masakarimonitors/tests/unit/introspectiveinstancemonitor/test_monitor_manag0000664000175000017500000000257015257164053033526 0ustar00zuulzuul# Copyright(c) 2018 WindRiver Systems # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import eventlet import libvirt import testtools from unittest import mock from masakarimonitors.introspectiveinstancemonitor import instance eventlet.monkey_patch(os=False) class TestMonitorManager(testtools.TestCase): def setUp(self): super(TestMonitorManager, self).setUp() @mock.patch.object(libvirt, 'virEventRunDefaultImpl') def test_vir_event_loop_native_run(self, mock_virEventRunDefaultImpl): mock_virEventRunDefaultImpl.side_effect = Exception("Test exception.") obj = instance.IntrospectiveInstanceMonitorManager() exception_flag = False try: obj._vir_event_loop_native_run() except Exception: exception_flag = True self.assertTrue(exception_flag) mock_virEventRunDefaultImpl.assert_called_once() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4622889 masakari_monitors-22.0.0/masakarimonitors/tests/unit/introspectiveinstancemonitor/test_qemu_utils.py0000664000175000017500000001013215257164053033503 0ustar00zuulzuul# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import libvirt import testtools from unittest import mock import uuid from masakarimonitors.introspectiveinstancemonitor import instance as object from masakarimonitors.introspectiveinstancemonitor import qemu_utils from masakarimonitors.introspectiveinstancemonitor import scheduler class TestQemuUtils(testtools.TestCase): def setup(self): super(TestQemuUtils, self).setUp() @mock.patch.object(qemu_utils.libvirt, 'virDomain') def test_getVmFsm(self, mock_domain): """To test the state machines Initial stage should be dicovery and will advance to a healthy stage after enough pingable events. Also, it should reach an error stage from healthy after a pingable event failure. """ reference = qemu_utils.QemuGuestAgent() mock_domain.UUID.return_value = uuid.uuid4() reference.getVmFsm(mock_domain) self.assertEqual(reference.getVmFsm(mock_domain).current_state, 'discovery') reference.getVmFsm(mock_domain).process_event('guest_not_pingable') self.assertEqual(reference.getVmFsm(mock_domain).current_state, 'discovery') reference.getVmFsm(mock_domain).process_event('guest_pingable') self.assertEqual(reference.getVmFsm(mock_domain).current_state, 'healthy') reference.getVmFsm(mock_domain).process_event('guest_pingable') self.assertEqual(reference.getVmFsm(mock_domain).current_state, 'healthy') reference.getVmFsm(mock_domain).process_event('guest_not_pingable') self.assertEqual(reference.getVmFsm(mock_domain).current_state, 'error') @mock.patch.object(qemu_utils.libvirt, 'virDomain') def test_hasQemuGuestAgent(self, mock_domain): mock_domain.UUID.return_value = 'testuuid' mock_domain.state.return_value = libvirt.VIR_DOMAIN_RUNNING, 'reason' mock_domain.XMLDesc.return_value = """ """ obj = qemu_utils.QemuGuestAgent() self.assertFalse(obj._hasQemuGuestAgent(mock_domain)) mock_domain.XMLDesc.return_value = """
""" obj = qemu_utils.QemuGuestAgent() self.assertTrue(obj._hasQemuGuestAgent(mock_domain)) @mock.patch.object(scheduler.ThreadGroupManager, '_service_task') @mock.patch.object(qemu_utils, 'resetJournal') def test_resetJournal(self, mock_resetJournal, mock_service_task): mock_resetJournal.return_value = None obj = object.IntrospectiveInstanceMonitorManager() event_id = 0 domain_uuid = uuid.uuid4() event_type = libvirt.VIR_DOMAIN_EVENT_STARTED detail = libvirt.VIR_DOMAIN_EVENT_STARTED_BOOTED obj._reset_journal(event_id, event_type, detail, domain_uuid) mock_resetJournal.assert_called_once_with(domain_uuid) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765152.211219 masakari_monitors-22.0.0/masakarimonitors/tests/unit/processmonitor/0000775000175000017500000000000015257164140024736 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.463289 masakari_monitors-22.0.0/masakarimonitors/tests/unit/processmonitor/__init__.py0000664000175000017500000000000015257164053027040 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765152.211219 masakari_monitors-22.0.0/masakarimonitors/tests/unit/processmonitor/process_handler/0000775000175000017500000000000015257164140030111 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.463289 masakari_monitors-22.0.0/masakarimonitors/tests/unit/processmonitor/process_handler/__init__.py0000664000175000017500000000000015257164053032213 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000021700000000000010215 xustar00116 path=masakari_monitors-22.0.0/masakarimonitors/tests/unit/processmonitor/process_handler/test_handle_process.py 27 mtime=1790765099.463289 masakari_monitors-22.0.0/masakarimonitors/tests/unit/processmonitor/process_handler/test_handle_proc0000664000175000017500000003612415257164053033362 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import socket import testtools from unittest import mock import ddt import eventlet from oslo_utils import timeutils import masakarimonitors.conf from masakarimonitors.ha import masakari from masakarimonitors.objects import event_constants as ec from masakarimonitors.processmonitor.process_handler import handle_process from masakarimonitors import utils CONF = masakarimonitors.conf.CONF eventlet.monkey_patch(os=False) NOVA_COMPUTE = 'nova-compute' MOCK_PROCESS_LIST = [ { 'id': 1, 'process_name': '/usr/local/bin/nova-compute', 'start_command': 'mock_start_command', 'pre_start_command': 'mock_pre_start_command', 'post_start_command': 'mock_post_start_command', 'restart_command': 'mock_restart_command', 'pre_restart_command': 'mock_pre_restart_command', 'post_restart_command': 'mock_post_restart_command', 'run_as_root': True }, ] MOCK_DOWN_PROCESS_LIST = [ { 'id': 1, 'process_name': '/usr/local/bin/nova-compute', 'start_command': 'mock_start_command', 'pre_start_command': 'mock_pre_start_command', 'post_start_command': 'mock_post_start_command', 'restart_command': 'mock_restart_command', 'pre_restart_command': 'mock_pre_restart_command', 'post_restart_command': 'mock_post_restart_command', 'run_as_root': True }, ] PS_RESULT = \ "\n" \ "UID PID PPID C STIME TTY TIME CMD\n" \ "root 11187 1 0 18:52 ? 00:00:00 /usr/local/bin/nova-compute\n" @ddt.ddt class TestHandleProcess(testtools.TestCase): def setUp(self): super(TestHandleProcess, self).setUp() def test_set_process_list(self): process_list = MOCK_PROCESS_LIST obj = handle_process.HandleProcess() obj.set_process_list(process_list) self.assertEqual(process_list, obj.process_list) @ddt.data("/usr/local/bin/nova-compute", "nova-compute") def test_make_event(self, process_name): obj = handle_process.HandleProcess() event = obj._make_event(process_name) self.assertEqual( NOVA_COMPUTE, event['notification']['payload']['process_name']) @mock.patch.object(utils, 'execute') def test_start_processes(self, mock_execute): process_list = MOCK_PROCESS_LIST obj = handle_process.HandleProcess() obj.set_process_list(process_list) mock_execute.side_effect = [('test_stdout', ''), ('test_stdout', ''), ('test_stdout', 'test_stderr')] obj.start_processes() mock_execute.assert_any_call( MOCK_PROCESS_LIST[0].get('pre_start_command'), run_as_root=MOCK_PROCESS_LIST[0].get('run_as_root')) mock_execute.assert_any_call( MOCK_PROCESS_LIST[0].get('start_command'), run_as_root=MOCK_PROCESS_LIST[0].get('run_as_root')) mock_execute.assert_any_call( MOCK_PROCESS_LIST[0].get('post_start_command'), run_as_root=MOCK_PROCESS_LIST[0].get('run_as_root')) @mock.patch.object(utils, 'execute') def test_start_processes_pre_cmd_fail(self, mock_execute): process_list = MOCK_PROCESS_LIST obj = handle_process.HandleProcess() obj.set_process_list(process_list) mock_execute.return_value = ('test_stdout', 'test_stderr') obj.start_processes() mock_execute.assert_called_once_with( MOCK_PROCESS_LIST[0].get('pre_start_command'), run_as_root=MOCK_PROCESS_LIST[0].get('run_as_root')) @mock.patch.object(utils, 'execute') def test_monitor_processes(self, mock_execute): process_list = MOCK_PROCESS_LIST obj = handle_process.HandleProcess() obj.set_process_list(process_list) mock_execute.return_value = (PS_RESULT, '') down_process_list = obj.monitor_processes() self.assertEqual([], down_process_list) mock_execute.assert_called_once_with( 'ps', '-ef', run_as_root=False) @mock.patch.object(utils, 'execute') def test_monitor_processes_not_found(self, mock_execute): process_list = MOCK_PROCESS_LIST obj = handle_process.HandleProcess() obj.set_process_list(process_list) mock_execute.return_value = ('', '') down_process_list = obj.monitor_processes() self.assertEqual(MOCK_PROCESS_LIST, down_process_list) mock_execute.assert_called_once_with( 'ps', '-ef', run_as_root=False) @mock.patch.object(utils, 'execute') def test_monitor_processes_exception(self, mock_execute): process_list = MOCK_PROCESS_LIST obj = handle_process.HandleProcess() obj.set_process_list(process_list) mock_execute.side_effect = Exception("Test exception.") down_process_list = obj.monitor_processes() self.assertEqual([], down_process_list) @mock.patch.object(utils, 'execute') def test_restart_processes(self, mock_execute): process_list = MOCK_PROCESS_LIST obj = handle_process.HandleProcess() obj.set_process_list(process_list) down_process_list = MOCK_DOWN_PROCESS_LIST mock_execute.side_effect = [('test_stdout', ''), ('test_stdout', ''), ('test_stdout', '')] obj.restart_processes(down_process_list) mock_execute.assert_any_call( down_process_list[0].get('pre_restart_command'), run_as_root=down_process_list[0].get('run_as_root')) mock_execute.assert_any_call( down_process_list[0].get('restart_command'), run_as_root=down_process_list[0].get('run_as_root')) mock_execute.assert_any_call( down_process_list[0].get('post_restart_command'), run_as_root=down_process_list[0].get('run_as_root')) self.assertEqual([], obj.restart_failure_list) @mock.patch.object(utils, 'execute') def test_restart_processes_failed_to_restart_previously( self, mock_execute): process_list = MOCK_PROCESS_LIST obj = handle_process.HandleProcess() obj.set_process_list(process_list) restart_failure_list = [MOCK_DOWN_PROCESS_LIST[0].get('process_name')] obj.restart_failure_list = restart_failure_list down_process_list = MOCK_DOWN_PROCESS_LIST obj.restart_processes(down_process_list) self.assertEqual(restart_failure_list, obj.restart_failure_list) mock_execute.assert_not_called() @mock.patch.object(masakari.SendNotification, 'send_notification') @mock.patch.object(timeutils, 'utcnow') @mock.patch.object(eventlet.greenthread, 'sleep') @mock.patch.object(utils, 'execute') def test_restart_processes_pre_restart_command_retry_over( self, mock_execute, mock_sleep, mock_utcnow, mock_send_notification): process_list = MOCK_PROCESS_LIST obj = handle_process.HandleProcess() obj.set_process_list(process_list) down_process_list = MOCK_DOWN_PROCESS_LIST mock_execute.side_effect = [('test_stdout', 'test_stderr'), ('test_stdout', 'test_stderr'), ('test_stdout', 'test_stderr'), ('test_stdout', 'test_stderr')] mock_sleep.return_value = None current_time = timeutils.utcnow() mock_utcnow.return_value = current_time mock_send_notification.return_value = None obj.restart_processes(down_process_list) pre_execute_count = CONF.process.restart_retries + 1 self.assertEqual(pre_execute_count, mock_execute.call_count) for var in range(0, mock_execute.call_count): args, kwargs = mock_execute.call_args_list[var] self.assertEqual( (down_process_list[0].get('pre_restart_command'),), args) self.assertEqual({'run_as_root': True}, kwargs) event = { 'notification': { 'type': ec.EventConstants.TYPE_PROCESS, 'hostname': socket.gethostname(), 'generated_time': current_time, 'payload': { 'event': ec.EventConstants.EVENT_STOPPED, 'process_name': NOVA_COMPUTE } } } mock_send_notification.assert_called_once_with( CONF.process.api_retry_max, CONF.process.api_retry_interval, event) self.assertEqual( [down_process_list[0].get('process_name')], obj.restart_failure_list) @mock.patch.object(masakari.SendNotification, 'send_notification') @mock.patch.object(timeutils, 'utcnow') @mock.patch.object(eventlet.greenthread, 'sleep') @mock.patch.object(utils, 'execute') def test_restart_processes_restart_command_retry_over( self, mock_execute, mock_sleep, mock_utcnow, mock_send_notification): process_list = MOCK_PROCESS_LIST obj = handle_process.HandleProcess() obj.set_process_list(process_list) down_process_list = MOCK_DOWN_PROCESS_LIST mock_execute.side_effect = [('test_stdout', ''), ('test_stdout', 'test_stderr'), ('test_stdout', ''), ('test_stdout', 'test_stderr'), ('test_stdout', ''), ('test_stdout', 'test_stderr'), ('test_stdout', ''), ('test_stdout', 'test_stderr')] mock_sleep.return_value = None current_time = timeutils.utcnow() mock_utcnow.return_value = current_time mock_send_notification.return_value = None obj.restart_processes(down_process_list) pre_execute_count = CONF.process.restart_retries + 1 execute_count = CONF.process.restart_retries + 1 total_execute_count = pre_execute_count + execute_count self.assertEqual(total_execute_count, mock_execute.call_count) for var in range(0, mock_execute.call_count): # Execute order of restart_command is the second. execute_order = 2 if (var + 1) % execute_order == 0: args, kwargs = mock_execute.call_args_list[var] self.assertEqual( (down_process_list[0].get('restart_command'),), args) self.assertEqual({'run_as_root': True}, kwargs) event = { 'notification': { 'type': ec.EventConstants.TYPE_PROCESS, 'hostname': socket.gethostname(), 'generated_time': current_time, 'payload': { 'event': ec.EventConstants.EVENT_STOPPED, 'process_name': NOVA_COMPUTE } } } mock_send_notification.assert_called_once_with( CONF.process.api_retry_max, CONF.process.api_retry_interval, event) self.assertEqual( [down_process_list[0].get('process_name')], obj.restart_failure_list) @mock.patch.object(masakari.SendNotification, 'send_notification') @mock.patch.object(timeutils, 'utcnow') @mock.patch.object(eventlet.greenthread, 'sleep') @mock.patch.object(utils, 'execute') def test_restart_processes_post_restart_command_retry_over( self, mock_execute, mock_sleep, mock_utcnow, mock_send_notification): process_list = MOCK_PROCESS_LIST obj = handle_process.HandleProcess() obj.set_process_list(process_list) down_process_list = MOCK_DOWN_PROCESS_LIST mock_execute.side_effect = [('test_stdout', ''), ('test_stdout', ''), ('test_stdout', 'test_stderr'), ('test_stdout', ''), ('test_stdout', ''), ('test_stdout', 'test_stderr'), ('test_stdout', ''), ('test_stdout', ''), ('test_stdout', 'test_stderr'), ('test_stdout', ''), ('test_stdout', ''), ('test_stdout', 'test_stderr')] mock_sleep.return_value = None current_time = timeutils.utcnow() mock_utcnow.return_value = current_time mock_send_notification.return_value = None obj.restart_processes(down_process_list) pre_execute_count = CONF.process.restart_retries + 1 execute_count = CONF.process.restart_retries + 1 post_execute_count = CONF.process.restart_retries + 1 total_execute_count = \ pre_execute_count + execute_count + post_execute_count self.assertEqual(total_execute_count, mock_execute.call_count) for var in range(0, mock_execute.call_count): # Execute order of restart_command is the third. execute_order = 3 if (var + 1) % execute_order == 0: args, kwargs = mock_execute.call_args_list[var] self.assertEqual( (down_process_list[0].get('post_restart_command'),), args) self.assertEqual({'run_as_root': True}, kwargs) event = { 'notification': { 'type': ec.EventConstants.TYPE_PROCESS, 'hostname': socket.gethostname(), 'generated_time': current_time, 'payload': { 'event': ec.EventConstants.EVENT_STOPPED, 'process_name': NOVA_COMPUTE } } } mock_send_notification.assert_called_once_with( CONF.process.api_retry_max, CONF.process.api_retry_interval, event) self.assertEqual( [down_process_list[0].get('process_name')], obj.restart_failure_list) @mock.patch.object(utils, 'execute') def test_execute_cmd_exception(self, mock_execute): mock_execute.side_effect = Exception("Test exception.") obj = handle_process.HandleProcess() ret = obj._execute_cmd(MOCK_PROCESS_LIST[0].get('start_command'), MOCK_PROCESS_LIST[0].get('run_as_root')) self.assertEqual(ret, 1) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.463289 masakari_monitors-22.0.0/masakarimonitors/tests/unit/processmonitor/test_process.py0000664000175000017500000001442415257164053030035 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import testtools from unittest import mock import yaml import eventlet from masakarimonitors.processmonitor import process as processmonitor_manager from masakarimonitors.processmonitor.process_handler import handle_process eventlet.monkey_patch(os=False) MOCK_PROCESS_LIST = [ { 'process_name': 'mock_process_name_A', 'start_command': 'mock_start_command', 'pre_start_command': 'mock_pre_start_command', 'post_start_command': 'mock_post_start_command', 'restart_command': 'mock_restart_command', 'pre_restart_command': 'mock_pre_restart_command', 'post_restart_command': 'mock_post_restart_command', 'run_as_root': True }, { 'id': 2, 'process_name': 'mock_process_name_B', 'start_command': 'mock_start_command', 'pre_start_command': 'mock_pre_start_command', 'post_start_command': 'mock_post_start_command', 'restart_command': 'mock_restart_command', 'pre_restart_command': 'mock_pre_restart_command', 'post_restart_command': 'mock_post_restart_command', 'run_as_root': True }, ] MOCK_DOWN_PROCESS_LIST = [ { 'process_name': 'mock_process_name_A', 'start_command': 'mock_start_command', 'pre_start_command': 'mock_pre_start_command', 'post_start_command': 'mock_post_start_command', 'restart_command': 'mock_restart_command', 'pre_restart_command': 'mock_pre_restart_command', 'post_restart_command': 'mock_post_restart_command', 'run_as_root': True }, ] class TestProcessmonitorManager(testtools.TestCase): def setUp(self): super(TestProcessmonitorManager, self).setUp() def _get_mock_process_list(self, call_count): if call_count == 0: return MOCK_PROCESS_LIST else: return @mock.patch.object(eventlet.greenthread, 'sleep') @mock.patch.object(handle_process.HandleProcess, 'restart_processes') @mock.patch.object(handle_process.HandleProcess, 'monitor_processes') @mock.patch.object(handle_process.HandleProcess, 'start_processes') @mock.patch.object(handle_process.HandleProcess, 'set_process_list') @mock.patch.object(yaml, 'safe_load') @mock.patch('builtins.open') def test_main(self, mock_file, mock_load, mock_set_process_list, mock_start_processes, mock_monitor_processes, mock_restart_processes, mock_sleep): mock_load.side_effect = [self._get_mock_process_list(0), self._get_mock_process_list(0), self._get_mock_process_list(1)] mock_set_process_list.return_value = None mock_start_processes.return_value = None mock_monitor_processes.side_effect = [MOCK_DOWN_PROCESS_LIST, []] mock_restart_processes.return_value = None mock_sleep.return_value = None obj = processmonitor_manager.ProcessmonitorManager() obj.main() mock_set_process_list.assert_called_with(MOCK_PROCESS_LIST) mock_start_processes.assert_called_once_with() self.assertEqual(2, mock_monitor_processes.call_count) mock_restart_processes.assert_called_once_with(MOCK_DOWN_PROCESS_LIST) @mock.patch.object(handle_process.HandleProcess, 'restart_processes') @mock.patch.object(handle_process.HandleProcess, 'monitor_processes') @mock.patch.object(handle_process.HandleProcess, 'start_processes') @mock.patch.object(handle_process.HandleProcess, 'set_process_list') @mock.patch.object(yaml, 'safe_load') @mock.patch('builtins.open') def test_main_exception(self, mock_file, mock_load, mock_set_process_list, mock_start_processes, mock_monitor_processes, mock_restart_processes): mock_load.return_value = self._get_mock_process_list(0) mock_set_process_list.return_value = None mock_start_processes.side_effect = Exception("Test exception.") obj = processmonitor_manager.ProcessmonitorManager() obj.main() mock_set_process_list.assert_called_once_with(MOCK_PROCESS_LIST) mock_start_processes.assert_called_once_with() mock_monitor_processes.assert_not_called() mock_restart_processes.assert_not_called() @mock.patch.object(handle_process.HandleProcess, 'set_process_list') @mock.patch.object(yaml, 'safe_load') @mock.patch('builtins.open') def test_load_process_list_yaml_error(self, mock_file, mock_load, mock_set_process_list): mock_load.side_effect = yaml.YAMLError obj = processmonitor_manager.ProcessmonitorManager() obj.main() mock_set_process_list.assert_not_called() @mock.patch.object(handle_process.HandleProcess, 'set_process_list') @mock.patch.object(yaml, 'safe_load') @mock.patch('builtins.open') def test_load_process_list_exception(self, mock_file, mock_load, mock_set_process_list): mock_load.side_effect = Exception("Test exception.") obj = processmonitor_manager.ProcessmonitorManager() obj.main() mock_set_process_list.assert_not_called() def test_stop(self): obj = processmonitor_manager.ProcessmonitorManager() obj.stop() self.assertFalse(obj.running) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4642892 masakari_monitors-22.0.0/masakarimonitors/tests/unit/test_hacking.py0000664000175000017500000001365615257164053024703 0ustar00zuulzuul# Copyright 2017 NTT Data. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import textwrap from unittest import mock import pycodestyle import testtools from masakarimonitors.hacking import checks class HackingTestCase(testtools.TestCase): """This class tests the hacking checks in masakarimonitors.hacking.checks by passing strings to the check methods like the pycodestyle/flake8 parser would. The parser loops over each line in the file and then passes the parameters to the check method. The parameter names in the check method dictate what type of object is passed to the check method. The parameter types are:: logical_line: A processed line with the following modifications: - Multi-line statements converted to a single line. - Stripped left and right. - Contents of strings replaced with "xxx" of same length. - Comments removed. physical_line: Raw line of text from the input file. lines: a list of the raw lines from the input file tokens: the tokens that contribute to this logical line line_number: line number in the input file total_lines: number of lines in the input file blank_lines: blank lines before this one indent_char: indentation character in this file (" " or "\t") indent_level: indentation (with tabs expanded to multiples of 8) previous_indent_level: indentation on previous line previous_logical: previous logical line filename: Path of the file being run through pycodestyle When running a test on a check method the return will be False/None if there is no violation in the sample input. If there is an error a tuple is returned with a position in the line, and a message. So to check the result just assertTrue if the check is expected to fail and assertFalse if it should pass. """ # We are patching pycodestyle so that only the check under test is actually # installed. @mock.patch('pycodestyle._checks', {'physical_line': {}, 'logical_line': {}, 'tree': {}}) def _run_check(self, code, checker, filename=None): pycodestyle.register_check(checker) lines = textwrap.dedent(code).strip().splitlines(True) checker = pycodestyle.Checker(filename=filename, lines=lines) checker.check_all() checker.report._deferred_print.sort() return checker.report._deferred_print def _assert_has_errors(self, code, checker, expected_errors=None, filename=None): actual_errors = [e[:3] for e in self._run_check(code, checker, filename)] self.assertEqual(expected_errors or [], actual_errors) def _assert_has_no_errors(self, code, checker, filename=None): self._assert_has_errors(code, checker, filename=filename) def test_check_explicit_underscore_import(self): self.assertEqual(len(list(checks.check_explicit_underscore_import( "LOG.info(_('My info message'))", "masakarimonitors/tests/other_files.py"))), 1) self.assertEqual(len(list(checks.check_explicit_underscore_import( "msg = _('My message')", "masakarimonitors/tests/other_files.py"))), 1) self.assertEqual(len(list(checks.check_explicit_underscore_import( "from masakarimonitors.i18n import _", "masakarimonitors/tests/other_files.py"))), 0) self.assertEqual(len(list(checks.check_explicit_underscore_import( "LOG.info(_('My info message'))", "masakarimonitors/tests/other_files.py"))), 0) self.assertEqual(len(list(checks.check_explicit_underscore_import( "msg = _('My message')", "masakarimonitors/tests/other_files.py"))), 0) self.assertEqual(len(list(checks.check_explicit_underscore_import( "from masakarimonitors.i18n import _", "masakarimonitors/tests/other_files2.py"))), 0) self.assertEqual(len(list(checks.check_explicit_underscore_import( "msg = _('My message')", "masakarimonitors/tests/other_files2.py"))), 0) self.assertEqual(len(list(checks.check_explicit_underscore_import( "_ = translations.ugettext", "masakarimonitors/tests/other_files3.py"))), 0) self.assertEqual(len(list(checks.check_explicit_underscore_import( "msg = _('My message')", "masakarimonitors/tests/other_files3.py"))), 0) def test_no_translate_logs(self): self.assertEqual(1, len(list(checks.no_translate_logs( "LOG.error(_LE('foo'))")))) self.assertEqual(0, len(list(checks.no_translate_logs( "LOG.debug('foo')")))) self.assertEqual(1, len(list(checks.no_translate_logs( "LOG.info(_LI('foo'))")))) self.assertEqual(1, len(list(checks.no_translate_logs( "LOG.warning(_LW('foo'))")))) self.assertEqual(1, len(list(checks.no_translate_logs( "LOG.critical(_LC('foo'))")))) def test_no_log_warn(self): code = """ LOG.warn("LOG.warn is deprecated") """ errors = [(1, 0, 'M305')] self._assert_has_errors(code, checks.no_log_warn, expected_errors=errors) code = """ LOG.warning("LOG.warn is deprecated") """ self._assert_has_no_errors(code, checks.no_log_warn) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4642892 masakari_monitors-22.0.0/masakarimonitors/utils.py0000664000175000017500000001066615257164053021255 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Utilities and helper functions.""" import contextlib import functools import inspect import pyclbr import shutil import sys import tempfile from oslo_concurrency import lockutils from oslo_concurrency import processutils from oslo_log import log as logging from oslo_utils import importutils import masakarimonitors.conf from masakarimonitors.i18n import _ from masakarimonitors import privsep CONF = masakarimonitors.conf.CONF LOG = logging.getLogger(__name__) def monkey_patch(): """monkey_patch function. If the CONF.monkey_patch set as True, this function patches a decorator for all functions in specified modules. You can set decorators for each modules using CONF.monkey_patch_modules. The format is "Module path:Decorator function". name - name of the function function - object of the function """ # If CONF.monkey_patch is not True, this function do nothing. if not CONF.monkey_patch: return def is_method(obj): return inspect.ismethod(obj) or inspect.isfunction(obj) # Get list of modules and decorators for module_and_decorator in CONF.monkey_patch_modules: md_value = module_and_decorator.split(':') if len(md_value) != 2: msg = _("'monkey_patch_modules' config option is not configured " "correctly") raise Exception(msg) module, decorator_name = md_value # import decorator function decorator = importutils.import_class(decorator_name) __import__(module) # Retrieve module information using pyclbr module_data = pyclbr.readmodule_ex(module) for key, value in module_data.items(): # set the decorator for the class methods if isinstance(value, pyclbr.Class): clz = importutils.import_class("%s.%s" % (module, key)) for method, func in inspect.getmembers(clz, is_method): setattr(clz, method, decorator("%s.%s.%s" % (module, key, method), func)) # set the decorator for the function if isinstance(value, pyclbr.Function): func = importutils.import_class("%s.%s" % (module, key)) setattr(sys.modules[module], key, decorator("%s.%s" % (module, key), func)) @contextlib.contextmanager def tempdir(**kwargs): argdict = kwargs.copy() if 'dir' not in argdict: argdict['dir'] = CONF.tempdir tmpdir = tempfile.mkdtemp(**argdict) try: yield tmpdir finally: try: shutil.rmtree(tmpdir) except OSError as e: LOG.error('Could not remove tmpdir: %s', e) @privsep.monitors_priv.entrypoint def privsep_execute(*cmd, **kwargs): return processutils.execute(*cmd, **kwargs) def execute(*cmd, **kwargs): """Convenience wrapper around oslo's execute() method.""" if 'run_as_root' in kwargs and kwargs.get('run_as_root'): return privsep_execute(*cmd, **kwargs) else: return processutils.execute(*cmd, **kwargs) def synchronized(name, semaphores=None, blocking=False): def wrap(f): @functools.wraps(f) def inner(*args, **kwargs): lock_str = 'masakarimonitors-%s' % name int_lock = lockutils.internal_lock(lock_str, semaphores=semaphores) msg = "Lock blocking: %s on resource %s " % (lock_str, f.__name__) """Acquiring lock: %(lock_str)s on resource """ if not int_lock.acquire(blocking=blocking): raise Exception(msg) try: return f(*args, **kwargs) finally: """Releasing lock: %(lock_str)s on resource """ int_lock.release() return inner return wrap ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4642892 masakari_monitors-22.0.0/masakarimonitors/version.py0000664000175000017500000000460015257164053021571 0ustar00zuulzuul# Copyright(c) 2016 Nippon Telegraph and Telephone Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from pbr import version as pbr_version MONITORS_VENDOR = "OpenStack Foundation" MONITORS_PRODUCT = "OpenStack Masakari Monitors" MONITORS_PACKAGE = None # OS distro package version suffix loaded = False version_info = pbr_version.VersionInfo('masakari-monitors') version_string = version_info.version_string def _load_config(): # Don't load in global context, since we can't assume # these modules are accessible when distutils uses # this module import configparser from oslo_config import cfg from oslo_log import log as logging global loaded, MONITORS_VENDOR, MONITORS_PRODUCT, MONITORS_PACKAGE if loaded: return loaded = True cfgfile = cfg.CONF.find_file("release") if cfgfile is None: return try: cfg = configparser.RawConfigParser() cfg.read(cfgfile) if cfg.has_option("Masakarimonitors", "vendor"): MONITORS_VENDOR = cfg.get("Masakarimonitors", "vendor") if cfg.has_option("Masakarimonitors", "product"): MONITORS_PRODUCT = cfg.get("Masakarimonitors", "product") if cfg.has_option("Masakarimonitors", "package"): MONITORS_PACKAGE = cfg.get("Masakarimonitors", "package") except Exception as ex: LOG = logging.getLogger(__name__) LOG.error("Failed to load %(cfgfile)s: %(ex)s", {'cfgfile': cfgfile, 'ex': ex}) def vendor_string(): _load_config() return MONITORS_VENDOR def product_string(): _load_config() return MONITORS_PRODUCT def package_string(): _load_config() return MONITORS_PACKAGE def version_string_with_package(): if package_string() is None: return version_info.version_string() else: return "%s-%s" % (version_info.version_string(), package_string()) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.495295 masakari_monitors-22.0.0/pyproject.toml0000664000175000017500000000366315257164053017073 0ustar00zuulzuul[build-system] requires = ["pbr>=6.1.1"] build-backend = "pbr.build" [project] name = "masakari-monitors" description = "Monitors for Masakari" authors = [ {name = "OpenStack", email = "openstack-discuss@lists.openstack.org"}, ] readme = {file = "README.rst", content-type = "text/x-rst"} license = {text = "Apache-2.0"} dynamic = ["version", "dependencies"] requires-python = ">=3.11" classifiers = [ "Environment :: OpenStack", "Intended Audience :: Information Technology", "Intended Audience :: System Administrators", "License :: OSI Approved :: Apache Software License", "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", ] [project.urls] Homepage = "https://docs.openstack.org/masakari-monitors/latest/" Repository = "https://opendev.org/openstack/masakari-monitors" [tool.setuptools] packages = [ "masakarimonitors" ] [project.entry-points."oslo.config.opts"] "masakarimonitors.conf" = "masakarimonitors.conf.opts:list_opts" [project.entry-points."console_scripts"] masakari-introspectiveinstancemonitor = "masakarimonitors.cmd.introspectiveinstancemonitor:main" masakari-instancemonitor = "masakarimonitors.cmd.instancemonitor:main" masakari-processmonitor = "masakarimonitors.cmd.processmonitor:main" masakari-hostmonitor = "masakarimonitors.cmd.hostmonitor:main" [project.entry-points."hostmonitor.driver"] "simple" = "masakarimonitors.hostmonitor.host_handler.handle_host:HandleHost" "default" = "masakarimonitors.hostmonitor.host_handler.handle_host:HandleHost" "consul" = "masakarimonitors.hostmonitor.consul_check.manager:ConsulCheck" "kubernetes" = "masakarimonitors.hostmonitor.kubernetes_check.manager:KubernetesCheck" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765152.190221 masakari_monitors-22.0.0/releasenotes/0000775000175000017500000000000015257164140016635 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2142186 masakari_monitors-22.0.0/releasenotes/notes/0000775000175000017500000000000015257164140017765 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4642892 masakari_monitors-22.0.0/releasenotes/notes/.placeholder0000664000175000017500000000000015257164053022241 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4652896 masakari_monitors-22.0.0/releasenotes/notes/bp-retry-check-when-host-failure-78649c512ef79199.yaml0000664000175000017500000000115215257164053031061 0ustar00zuulzuul--- features: - | Support for repeated check of node status in hostmonitor. Repeated check is more reliable than single check to determine host status, especially when there is network instability in play. With this feature, the following config option can be set. .. code-block:: ini [host] monitoring_samples = 3 The above means 3 checks will be done before the node status is decided. The default value is 1 which is backwards compatible. `Blueprint retry-check-when-host-failure `__ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4652896 masakari_monitors-22.0.0/releasenotes/notes/bug-1866660-ef8624f5283b2e5e.yaml0000664000175000017500000000065515257164053024524 0ustar00zuulzuul--- features: - | A 'hostname' option has been added to the Masakari monitors configuration file which replaces the 'host' option. The option should be used to specify the Hostname, FQDN or IP address of the host. deprecations: - | The 'host' option in the 'DEFAULT' section has been deprecated as it clashes with the name of a section in the same file. It has been replaced by the 'hostname' option. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4652896 masakari_monitors-22.0.0/releasenotes/notes/bug-1878548-5fab31aec6ba5407.yaml0000664000175000017500000000036115257164053024640 0ustar00zuulzuul--- fixes: - | Fixes hostmonitor reporting hosts down because of Pacemaker cluster partitioning. Now hostmonitor properly respects the status of Pacemaker cluster quorum. `LP#1878548 `__ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4652896 masakari_monitors-22.0.0/releasenotes/notes/bug-1930361-fa8ce8e9781ea967.yaml0000664000175000017500000000021115257164053024575 0ustar00zuulzuul--- fixes: - | Fixes hostmonitor hanging forever after certain exceptions. `LP#1930361 `__ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4652896 masakari_monitors-22.0.0/releasenotes/notes/bug-2153543-37dd1fff76f6e531.yaml0000664000175000017500000000115315257164053024566 0ustar00zuulzuul--- fixes: - | Fixed an issue with the Consul hostmonitor driver where false STARTED notifications were sent when ``monitoring_samples`` was set to a value greater than 1. During host state transitions (e.g., from UP to DOWN), mixed health samples (e.g., ``['up', 'up', 'down']``) were incorrectly treated as stable UP states, causing premature notifications. The fix now skips notifications when the health state is unstable (contains None values) and only sends notifications when the state is stable. See `Bug #2153543 `_. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4662898 masakari_monitors-22.0.0/releasenotes/notes/bug-cb435fc05a5e31d5.yaml0000664000175000017500000000041215257164053023712 0ustar00zuulzuul--- fixes: - | kubernetes_check manager.py script missing host status from the payload data to allow the masakari-engine to initiate the recovery workflow message to the API server. Previous payload would be ignored by masakari-engine workflow driver. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4662898 masakari_monitors-22.0.0/releasenotes/notes/deprecate-processmonitor-95c8aefbc749c1ed.yaml0000664000175000017500000000043615257164053030347 0ustar00zuulzuul--- deprecations: - | Masakari Process Monitor (``masakari-processmonitor``) is deprecated and may be removed in any later release. Please use modern tools, such as systemd, Docker or Kubernetes (K8s), to control, monitor and ensure the desired processes are running. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4662898 masakari_monitors-22.0.0/releasenotes/notes/drop-cap-net-admin-8d7d7cfb274e9547.yaml0000664000175000017500000000016615257164053026477 0ustar00zuulzuul--- other: - | Masakari hostmonitor and processmonitor will no longer require ``CAP_NET_ADMIN`` capability. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4662898 masakari_monitors-22.0.0/releasenotes/notes/drop-py-2-7-b28de816eac45468.yaml0000664000175000017500000000034015257164053024773 0ustar00zuulzuul--- upgrade: - | Python 2.7 support has been dropped. Last release of masakari-monitors to support python 2.7 is OpenStack Train. The minimum version of Python now supported by masakari-monitors is Python 3.6. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4662898 masakari_monitors-22.0.0/releasenotes/notes/drop-python-38-and-39.yaml0000664000175000017500000000011015257164053024350 0ustar00zuulzuul--- upgrade: - | Support for Python 3.8 and 3.9 has been dropped. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4662898 masakari_monitors-22.0.0/releasenotes/notes/hostmonitor-driver-based-on-consul-03f7e619d91e7e06.yaml0000664000175000017500000000035615257164053031674 0ustar00zuulzuul--- features: - | Added hostmonitor driver based on consul. It can detects interfaces connectivity status via multiple consul clusters, and sends notifition to trigger host failure recovery according to defined HA strategy. ././@PaxHeader0000000000000000000000000000021400000000000010212 xustar00114 path=masakari_monitors-22.0.0/releasenotes/notes/hostmonitor-driver-based-on-kubernetes-a898da08d2f40cac.yaml 26 mtime=1790765099.46729 masakari_monitors-22.0.0/releasenotes/notes/hostmonitor-driver-based-on-kubernetes-a898da08d2f40cac.0000664000175000017500000000047215257164053032060 0ustar00zuulzuul--- features: - | In OpenStack on Kubernetes, this is a new host monitor by Kubernetes. This driver enable seamless integration with the Kubernetes API, allowing for efficient and straightforward host monitoring without the need for additional configuration using tools like Consul or Pacemaker. ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765099.46729 masakari_monitors-22.0.0/releasenotes/notes/hostmonitor-systemd-89696f96a654a918.yaml0000664000175000017500000000030015257164053026753 0ustar00zuulzuul--- features: - | Adds ``pacemaker_node_type`` option to ``hostmonitor`` to allow skipping systemd service status checks which are often impossible in containerised deployments. ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765099.46729 masakari_monitors-22.0.0/releasenotes/notes/introspectiveinstancemonitor-f4bc71f029b61d49.yaml0000664000175000017500000000026115257164053031125 0ustar00zuulzuul--- features: - | Added Introspective Instance Monitoring through QEMU Guest Agent, in order to detect, report and optionally recover VMs from internal VM faults. ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765099.46729 masakari_monitors-22.0.0/releasenotes/notes/libvirt-sasl-support-edf1388c556a594b.yaml0000664000175000017500000000051515257164053027232 0ustar00zuulzuul--- features: - | Add support for libvirt auth in instancemonitor. Use the standard methods to provide the actual authentication credentials. The SASL library and pluggable authentication modules should be installed on the instancemonitor host, use the packages provided in the distro alongside libvirt-python. ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765099.46729 masakari_monitors-22.0.0/releasenotes/notes/libvirt-still-required-22a8d817ee8d0be8.yaml0000664000175000017500000000073515257164053027602 0ustar00zuulzuul--- other: - | *Note to packagers*: ``libvirt-python`` package is still required by instance-oriented monitors. It is available in most distros as ``python3-libvirt``. It is no longer listed in ``requirements.txt`` so it will not get installed when installing ``masakari-monitors`` package from PyPI. The change was done to follow how Nova handles this requirement. This is to satisfy both libvirtd compatibility and recent pip sanity checks. ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765099.46729 masakari_monitors-22.0.0/releasenotes/notes/pythonize-monitors-081e74dfaf78fe99.yaml0000664000175000017500000000050015257164053027073 0ustar00zuulzuul--- features: - Added the masakari-hostmonitor and masakari-processmonitor which were implemented in python. deprecations: - The masakari-hostmonitor and masakari-processmonitor which were implemented in bash shell script are deprecated as of the Ocata release and will be removed in the Queens release. ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765099.46729 masakari_monitors-22.0.0/releasenotes/notes/remove-py310-d0bfb824497ea29f.yaml0000664000175000017500000000016715257164053025333 0ustar00zuulzuul--- upgrade: - | Support for Python 3.10 has been removed. Now Python 3.11 is the minimum version supported. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2172184 masakari_monitors-22.0.0/releasenotes/source/0000775000175000017500000000000015257164140020135 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765099.46829 masakari_monitors-22.0.0/releasenotes/source/2023.1.rst0000664000175000017500000000021015257164053021410 0ustar00zuulzuul=========================== 2023.1 Series Release Notes =========================== .. release-notes:: :branch: unmaintained/2023.1 ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765099.46829 masakari_monitors-22.0.0/releasenotes/source/2023.2.rst0000664000175000017500000000020215257164053021412 0ustar00zuulzuul=========================== 2023.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2023.2 ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765099.46829 masakari_monitors-22.0.0/releasenotes/source/2024.1.rst0000664000175000017500000000021015257164053021411 0ustar00zuulzuul=========================== 2024.1 Series Release Notes =========================== .. release-notes:: :branch: unmaintained/2024.1 ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765099.46829 masakari_monitors-22.0.0/releasenotes/source/2024.2.rst0000664000175000017500000000020215257164053021413 0ustar00zuulzuul=========================== 2024.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2024.2 ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1790765099.46829 masakari_monitors-22.0.0/releasenotes/source/2025.1.rst0000664000175000017500000000020215257164053021413 0ustar00zuulzuul=========================== 2025.1 Series Release Notes =========================== .. release-notes:: :branch: stable/2025.1 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4692903 masakari_monitors-22.0.0/releasenotes/source/2025.2.rst0000664000175000017500000000020215257164053021414 0ustar00zuulzuul=========================== 2025.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2025.2 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4692903 masakari_monitors-22.0.0/releasenotes/source/2026.1.rst0000664000175000017500000000020215257164053021414 0ustar00zuulzuul=========================== 2026.1 Series Release Notes =========================== .. release-notes:: :branch: stable/2026.1 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2172184 masakari_monitors-22.0.0/releasenotes/source/_static/0000775000175000017500000000000015257164140021563 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4702904 masakari_monitors-22.0.0/releasenotes/source/_static/.placeholder0000664000175000017500000000000015257164053024037 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2172184 masakari_monitors-22.0.0/releasenotes/source/_templates/0000775000175000017500000000000015257164140022272 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4702904 masakari_monitors-22.0.0/releasenotes/source/_templates/.placeholder0000664000175000017500000000000015257164053024546 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4702904 masakari_monitors-22.0.0/releasenotes/source/conf.py0000664000175000017500000002215215257164053021441 0ustar00zuulzuul# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # Masakarimonitors Release Notes documentation build configuration file, # created by sphinx-quickstart on Tue Oct 25 17:40:50 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'openstackdocstheme', 'reno.sphinxext', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'masakarimonitors Release Notes' copyright = '2016, OpenStack Foundation' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. # The full version, including alpha/beta/rc tags. release = '' # The short X.Y version. version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'native' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'openstackdocs' openstackdocs_repo_name = 'openstack/masakari-monitors' openstackdocs_bug_project = 'masakari-monitors' openstackdocs_auto_name = False # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'MasakarimonitorsReleaseNotesdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'MasakarimonitorsReleaseNotes.tex', 'Masakarimonitors Release' 'Notes Documentation', 'Masakarimonitors Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'Masakarimonitorsreleasenotes', 'Masakarimonitors Release Notes' 'Documentation', ['Masakarimonitors Developers'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'MasakarimonitorsReleaseNotes', 'Masakarimonitors Release Notes' 'Documentation', 'Masakarimonitors Developers', 'MasakarimonitorsReleaseNotes', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.495295 masakari_monitors-22.0.0/releasenotes/source/index.rst0000664000175000017500000000054615257164053022006 0ustar00zuulzuulWelcome to Masakarimonitor Release Notes documentation! ======================================================== Contents ======== .. toctree:: :maxdepth: 1 unreleased 2026.1 2025.2 2025.1 2024.2 2024.1 2023.2 2023.1 zed yoga xena wallaby victoria ussuri train stein rocky queens pike ocata ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4702904 masakari_monitors-22.0.0/releasenotes/source/ocata.rst0000664000175000017500000000021115257164053021753 0ustar00zuulzuul=========================== Ocata Series Release Notes =========================== .. release-notes:: :branch: origin/stable/ocata ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4702904 masakari_monitors-22.0.0/releasenotes/source/pike.rst0000664000175000017500000000020515257164053021617 0ustar00zuulzuul========================== Pike Series Release Notes ========================== .. release-notes:: :branch: origin/stable/pike ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4702904 masakari_monitors-22.0.0/releasenotes/source/queens.rst0000664000175000017500000000021515257164053022170 0ustar00zuulzuul============================ Queens Series Release Notes ============================ .. release-notes:: :branch: origin/stable/queens ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4702904 masakari_monitors-22.0.0/releasenotes/source/rocky.rst0000664000175000017500000000022115257164053022014 0ustar00zuulzuul=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4702904 masakari_monitors-22.0.0/releasenotes/source/stein.rst0000664000175000017500000000022115257164053022007 0ustar00zuulzuul=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4712906 masakari_monitors-22.0.0/releasenotes/source/train.rst0000664000175000017500000000017615257164053022013 0ustar00zuulzuul========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4712906 masakari_monitors-22.0.0/releasenotes/source/unreleased.rst0000664000175000017500000000016015257164053023016 0ustar00zuulzuul============================== Current Series Release Notes ============================== .. release-notes:: ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4712906 masakari_monitors-22.0.0/releasenotes/source/ussuri.rst0000664000175000017500000000020215257164053022216 0ustar00zuulzuul=========================== Ussuri Series Release Notes =========================== .. release-notes:: :branch: stable/ussuri ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4712906 masakari_monitors-22.0.0/releasenotes/source/victoria.rst0000664000175000017500000000022015257164053022504 0ustar00zuulzuul============================= Victoria Series Release Notes ============================= .. release-notes:: :branch: unmaintained/victoria ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4712906 masakari_monitors-22.0.0/releasenotes/source/wallaby.rst0000664000175000017500000000021415257164053022322 0ustar00zuulzuul============================ Wallaby Series Release Notes ============================ .. release-notes:: :branch: unmaintained/wallaby ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4712906 masakari_monitors-22.0.0/releasenotes/source/xena.rst0000664000175000017500000000020015257164053021615 0ustar00zuulzuul========================= Xena Series Release Notes ========================= .. release-notes:: :branch: unmaintained/xena ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4712906 masakari_monitors-22.0.0/releasenotes/source/yoga.rst0000664000175000017500000000020015257164053021621 0ustar00zuulzuul========================= Yoga Series Release Notes ========================= .. release-notes:: :branch: unmaintained/yoga ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4722908 masakari_monitors-22.0.0/releasenotes/source/zed.rst0000664000175000017500000000017415257164053021456 0ustar00zuulzuul======================== Zed Series Release Notes ======================== .. release-notes:: :branch: unmaintained/zed ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4722908 masakari_monitors-22.0.0/requirements.txt0000664000175000017500000000230715257164053017435 0ustar00zuulzuul# Requirements lower bounds listed here are our best effort to keep them up to # date but we do not test them so no guarantee of having them all correct. If # you find any incorrect lower bounds, let us know or propose a fix. automaton>=1.9.0 # Apache-2.0 keystoneauth1>=3.4.0 # Apache-2.0 openstacksdk>=0.13.0 # Apache-2.0 oslo.concurrency>=3.26.0 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0 lxml>=4.5.0 # BSD oslo.cache>=1.26.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.privsep>=1.23.0 # Apache-2.0 oslo.service!=1.28.1,>=1.24.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 pbr!=2.1.0,>=2.0.0 # Apache-2.0 py-consul>=1.4.0 # MIT kubernetes>=29.0.0 # Apache-2.0 # Due to the nature of libvirt-python package, in DevStack we use the one # provided in the distro alongside libvirtd - to ensure the two are compatible, # and also to avoid the pip error when it tries to uninstall the distro version # (installed in such a way for Nova in DevStack). # Do note libvirt-python is used only for instance-oriented monitors, so, e.g., # it is not used by any host monitor. # # Note for package maintainers: just use the libvirt-python package version # as seen in the Nova dependencies. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765152.2182183 masakari_monitors-22.0.0/setup.cfg0000664000175000017500000000011315257164140015760 0ustar00zuulzuul[metadata] name = masakari-monitors [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4722908 masakari_monitors-22.0.0/setup.py0000664000175000017500000000127115257164053015662 0ustar00zuulzuul# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import setuptools setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765099.4722908 masakari_monitors-22.0.0/test-requirements.txt0000664000175000017500000000152315257164053020411 0ustar00zuulzuulhacking>=8.1.0,<8.2.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 oslotest>=3.2.0 # Apache-2.0 stestr>=1.0.0 # Apache-2.0 os-testr>=1.0.0 # Apache-2.0 testscenarios>=0.4 # Apache-2.0/BSD testtools>=2.2.0 # MIT ddt>=1.0.1 # MIT pycodestyle>=2.0.0 # MIT # Due to the nature of libvirt-python package, in DevStack we use the one # provided in the distro alongside libvirtd - to ensure the two are compatible, # and also to avoid the pip error when it tries to uninstall the distro version # (installed in such a way for Nova in DevStack). # Do note libvirt-python is used only for instance-oriented monitors, so, e.g., # it is not used by any host monitor. # TODO(yoctozepto): Refactor code to not require libvirt-python for unit tests, # basically following how it is handled in nova-compute and ceilometer-compute. libvirt-python>=6.0.0 # LGPLv2+ ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765099.473291 masakari_monitors-22.0.0/tox.ini0000664000175000017500000000735115257164053015470 0ustar00zuulzuul[tox] minversion = 4.0.0 envlist = pep8,py3 [testenv] usedevelop = True setenv = LANGUAGE=en_US LC_ALL=en_US.utf-8 deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = stestr run {posargs} passenv = OS_DEBUG GENERATE_HASHES [testenv:genconfig] commands = oslo-config-generator --config-file=etc/masakarimonitors/masakarimonitors-config-generator.conf [testenv:linters] skip_install = True deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} {[testenv:pep8]deps} {[testenv:doc8]deps} {[testenv:yamllint]deps} commands = {[testenv:pep8]commands} {[testenv:doc8]commands} {[testenv:yamllint]commands} [testenv:pep8] skip_install = True deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} # NOTE(yoctozepto): this is to pin hacking to the tested version -c{toxinidir}/test-requirements.txt hacking commands = flake8 {posargs} [testenv:doc8] skip_install = True deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} doc8 commands = doc8 README.rst CONTRIBUTING.rst HACKING.rst doc/source doc8 releasenotes/source doc8 -e '.yaml' releasenotes/notes [testenv:yamllint] skip_install = True deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} yamllint commands = yamllint -s . [testenv:venv] commands = {posargs} [testenv:cover] setenv = PYTHON=coverage run --source masakarimonitors --parallel-mode commands = stestr run {posargs} coverage combine coverage html -d cover coverage xml -o cover/coverage.xml [testenv:docs] deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W -b html doc/source doc/build/html [testenv:pdf-docs] deps = {[testenv:docs]deps} allowlist_externals = make commands = sphinx-build -W -b latex doc/source doc/build/pdf make -C doc/build/pdf [testenv:releasenotes] skip_install = True deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt allowlist_externals = rm commands = rm -fr releasenotes/build sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [testenv:debug] commands = oslo_debug_helper {posargs} [flake8] show-source = True # E123 - closing bracket does not match indentation of opening bracket's line # E125 - continuation line with same indent as next logical line # E128 - continuation line under-indented for visual indent # E265 - block comment should start with '# ' # H405 - multi line docstring summary not separated with an empty line # W503 - line break before binary operator # W504 - line break after binary operator ignore = E123,E125,E128,E265,H405,W503,W504 # [H106] Don't put vim configuration in source files. # [H203] Use assertIs(Not)None to check for None. # [H904] Delay string interpolations at logging calls. enable-extensions = H106,H203,H904 builtins = _ exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build [hacking] import_exceptions = masakarimonitors.i18n [flake8:local-plugins] extension = M301 = checks:check_explicit_underscore_import M302 = checks:no_translate_logs M305 = checks:no_log_warn paths = ./masakarimonitors/hacking [testenv:bindep] skip_install = True deps = bindep commands = bindep test [doc8] # NOTE(yoctozepto): this is due to multiple violations - it is better to keep # it limited sanely rather than disable the D001 "Line too long" rule altogether max-line-length = 105