pax_global_header00006660000000000000000000000064133547007140014516gustar00rootroot0000000000000052 comment=508c3d5d801dfd28fb565f948efd50a747943ad0 bdii-5.2.25/000077500000000000000000000000001335470071400125205ustar00rootroot00000000000000bdii-5.2.25/.gitignore000066400000000000000000000000221335470071400145020ustar00rootroot00000000000000build docs/_build bdii-5.2.25/.rpmlint.ini000066400000000000000000000002771335470071400147720ustar00rootroot00000000000000from Config import * # BDII config files are protected as they include a password addFilter(".*non-readable.*") # Check broken for this specific init script addFilter(".*subsys-not-used.*") bdii-5.2.25/.travis.yml000066400000000000000000000053131335470071400146330ustar00rootroot00000000000000--- sudo: required services: - docker language: python python: - "2.6" - "2.7" env: global: - OS_NAME=centos matrix: - OS_MAJOR_VERSION=6 - OS_MAJOR_VERSION=7 before_install: - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" - sudo apt update - sudo apt install -y docker-ce - docker --version install: - wget http://archive.ubuntu.com/ubuntu/pool/universe/r/rpmlint/rpmlint_1.7-1_all.deb - sudo dpkg -i rpmlint_1.7-1_all.deb || sudo apt install -f -y - gem install mdl - if [[ $TRAVIS_PYTHON_VERSION == 2.6 ]]; then pip install 'flake8<3.0.0'; fi - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then pip install flake8 flake8-import-order hacking; fi script: # First syntax, linting and pre-test stages - mdl -s relaxed README.md - rpmlint bdii.spec - flake8 bin/bdii-update # Then, start the build container - docker run --name build_container -d -ti -v $PWD:/src -w /src $OS_NAME:$OS_MAJOR_VERSION sh -c 'while true ; do sleep 1000 ; done' # Now, install prerequisites for the build - docker exec -i -w /root build_container yum install -y rpm-build rpmlint make rsync # Eventually build the rpm - docker exec -i -w /src build_container make rpm # lint the RPM - docker exec -i -w /src/build build_container rpmlint --file ../.rpmlint.ini RPMS/ # Now, start the test container - docker run --name install_container -d -ti -v $PWD:/src -w /src $OS_NAME:$OS_MAJOR_VERSION sh -c 'while true ; do sleep 1000 ; done' # and install it - docker exec -i -w /src/build/RPMS/noarch install_container yum install -y epel-release - docker exec -i -w /src/build/RPMS/noarch install_container sh -c "yum localinstall -y bdii*.el${OS_MAJOR_VERSION}.noarch.rpm" deploy: provider: releases api_key: secure: tw560GrnanaDUSQ27wmyn+tryv7NYwvKjafc23VWpJRw78BYrIZopAL3r/YN9k7iQmk0WyJ7pjV3ayL/XSW0vYhCme1hpKaKTrRl8r+C4pJcmz9q50bVE1G2invgiqiw2Tpne7BG/LS7aERQxPs2tgmzvoy0XLFgoWOec+cBts5D+KtAnOZ5jxob6V9K1na79pK9OK4mVt/rYeuqEb8O44tjHywa7PWzg9mHAtzAGLtrgki3ICEosFkqRyBaQmwbLc/fUhA1De9TtGtbxaQ3GYRCIgnQTJ5DNOcuj3X21WlXGhQXEovR1oC3lBwMPxx2Ap1Ke3JNIU9y8YiW7CGqQm8XV9wm5PFN6O2ZccjPRVp+xkGkl0w1+RL/Ms2zfevFaxztqzKCt+AVXDw4aQrxyawuNCRLku3uzzQa0cQPfJscWHAqLCTXSP13hbKyyu+nvVFBpMjY6xO8TICHeb5dXVOU1JIw6Qu+AqfEIYZQsNuJI6SUwkkonHrRZ0iEfLi+0JQ8adelGqCfDiK7bDSFQ4prjLECjjp3HHSrvvcRNl8gEc+0P/qdWsIdC8j1a332f3VsO3JF/8+NWjU2Uy8fs04DyzWM4l3Ccg+7DEfx/zdPWCwlzNq6giByMiDm0RvPgrNVigdN8o6QnVOxkpeL4lNnOVfIb/RtJq4ND3zKvOE= file_glob: true file: - build/RPMS/noarch/bdii*.el${OS_MAJOR_VERSION}.noarch.rpm - build/SRPMS/bdii*.el${OS_MAJOR_VERSION}.src.rpm on: tags: true bdii-5.2.25/AUTHORS000066400000000000000000000007001335470071400135650ustar00rootroot00000000000000Maintainers ----------- Paolo Andreetto Baptiste Grenier Original Authors ---------------- David Groep Contributors ------------ Maria Alandes Pradillo Laurence Field Maarten Litmaath Felix Ehm Andrew Elwell Daniel Johansson bdii-5.2.25/COPYRIGHT000066400000000000000000000002561335470071400140160ustar00rootroot00000000000000This project is licensed under Apache 2.0. Copyrights in this project are retained by their contributors. No copyright assignment is required to contribute to this project. bdii-5.2.25/LICENSE.txt000066400000000000000000000261161335470071400143510ustar00rootroot00000000000000 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. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2018 The authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. bdii-5.2.25/Makefile000066400000000000000000000054421335470071400141650ustar00rootroot00000000000000NAME= $(shell grep Name: *.spec | sed 's/^[^:]*:[^a-zA-Z]*//' ) VERSION= $(shell grep Version: *.spec | sed 's/^[^:]*:[^0-9]*//' ) RELEASE= $(shell grep Release: *.spec |cut -d"%" -f1 |sed 's/^[^:]*:[^0-9]*//') build=$(shell pwd)/build DATE=$(shell date "+%a, %d %b %Y %T %z") dist=$(shell rpm --eval '%dist' | sed 's/%dist/.el5/') init_dir=$(shell rpm --eval '%{_initrddir}' || echo '/etc/init.d/') default: @echo "Nothing to do" install: @echo installing ... @mkdir -p $(prefix)/usr/sbin/ @mkdir -p $(prefix)/var/run/bdii/ @mkdir -p $(prefix)/var/lib/bdii/gip/ldif/ @mkdir -p $(prefix)/var/lib/bdii/gip/provider/ @mkdir -p $(prefix)/var/lib/bdii/gip/plugin/ @mkdir -p $(prefix)/etc/bdii/ @mkdir -p $(prefix)/etc/sysconfig/ @mkdir -p $(prefix)$(init_dir)/ @mkdir -p $(prefix)/etc/logrotate.d/ @mkdir -p $(prefix)/var/log/bdii/ @mkdir -p $(prefix)/usr/share/doc/bdii/ @mkdir -p $(prefix)/usr/share/man/man1 @install -m 0755 etc/init.d/bdii $(prefix)/${init_dir}/ @install -m 0644 etc/sysconfig/bdii $(prefix)/etc/sysconfig/ @install -m 0755 bin/bdii-update $(prefix)/usr/sbin/ @install -m 0644 etc/bdii.conf $(prefix)/etc/bdii/ @install -m 0644 etc/BDII.schema $(prefix)/etc/bdii/ @install -m 0640 etc/bdii-slapd.conf $(prefix)/etc/bdii/ @install -m 0640 etc/bdii-top-slapd.conf $(prefix)/etc/bdii/ @install -m 0644 etc/DB_CONFIG $(prefix)/etc/bdii/ @install -m 0644 etc/DB_CONFIG_top $(prefix)/etc/bdii/ @install -m 0644 etc/default.ldif $(prefix)/var/lib/bdii/gip/ldif/ @install -m 0644 etc/logrotate.d/bdii $(prefix)/etc/logrotate.d @install -m 0644 man/bdii-update.1 $(prefix)/usr/share/man/man1/ @install -m 0644 README.md $(prefix)/usr/share/doc/bdii/ @install -m 0644 AUTHORS $(prefix)/usr/share/doc/bdii/ @install -m 0644 COPYRIGHT $(prefix)/usr/share/doc/bdii/ @install -m 0644 LICENSE.txt $(prefix)/usr/share/doc/bdii/ dist: @mkdir -p $(build)/$(NAME)-$(VERSION)/ rsync -HaS --exclude ".git" --exclude "$(build)" * $(build)/$(NAME)-$(VERSION)/ cd $(build); tar --gzip -cf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)/; cd - sources: dist cp $(build)/$(NAME)-$(VERSION).tar.gz . deb: dist cd $(build)/$(NAME)-$(VERSION); dpkg-buildpackage -us -uc; cd - prepare: dist @mkdir -p $(build)/RPMS/noarch @mkdir -p $(build)/SRPMS/ @mkdir -p $(build)/SPECS/ @mkdir -p $(build)/SOURCES/ @mkdir -p $(build)/BUILD/ cp $(build)/$(NAME)-$(VERSION).tar.gz $(build)/SOURCES cp $(NAME).spec $(build)/SPECS srpm: prepare rpmbuild -bs --define="dist ${dist}" --define='_topdir ${build}' $(build)/SPECS/$(NAME).spec rpm: srpm rpmbuild --rebuild --define='_topdir ${build}' --define="dist ${dist}" $(build)/SRPMS/$(NAME)-$(VERSION)-$(RELEASE)${dist}.src.rpm clean: rm -f *~ $(NAME)-$(VERSION).tar.gz rm -rf $(build) .PHONY: dist srpm rpm sources clean bdii-5.2.25/README.md000066400000000000000000000067141335470071400140070ustar00rootroot00000000000000# README for bdii package Documentation: [bdii.readthedocs.io](http://bdii.readthedocs.io) ## Function The Berkeley Database Information Index (BDII) consists of two or more standard LDAP databases that are populated by an update process. Port forwarding is used to enable one or more databases to serve data while one database is being refreshed. The databases are refreshed cyclically. Any incoming connection is forwarded to the most recently updated database, while old connections are allowed to linger until it is the turn of their database to be refreshed and restarted. The update process obtains LDIF from either doing an ldapsearch on LDAP URLs or by running a local script (given by a URL with "file" protocol) that generates LDIF. The LDIF is then inserted into the LDAP database. Options exist to update the list of LDAP URLs from a web page and to use an LDIF file from a web page to modify the data before it is inserted into the database. ## Cache use Whenever a remote server is contacted and the ldapsearch command times out the update process tries to find an (old) cached entry in the `/var/cache/` directory. If no entry is found a message is printed to the logfile. *Attention!* If the remote host cannot be contacted due to a connection problem no cached entry is taken. No message is printed to the logfile. ## Compressed Content Exchange Mechanism (CCEM) The Compressed Content Exchange Mechanism is intended to speed up the gathering of information in case of a ldapsearch to another BDII instance. The update process first tries to find the entry containing the compressed content of the queried instance and subsequently adds the information to its upcoming database. If the CCEM fails the normal procedure as described in the previous paragraph is executed. The CCEM function is enabled by default in version `>= 3.9.1`. To disable, add the following to your bdii.conf: ``` BDII_CCEM=no ``` ## BDII Status Information Mechanism (BSIM) The BDII Status Information Mechanism is intended to allow better monitoring possibilities, spotting of upraising problems and resulting failure prevention. It adds status information about the BDII instance into the 'o=infosys' root containing metrics like the number of entries added in the last cycle, the time to do so, etc. The description of thoese metrics can be found in the etc/BDII.schema file. ## Installing from source ```sh make install ``` * Build dependencies: None * Runtime dependencies: openldap ## Building packages ### Building a RPM The required build dependencies are: * rpm-build * make * rsync ```sh # Checkout tag to be packaged git clone https://github.com/EGI-Foundation/bdii.git cd bdii git checkout X.X.X # Building in a container docker run --rm -v $(pwd):/source -it centos:7 yum install -y rpm-build make rsync cd /source && make rpm ``` The RPM will be available into the `build/RPMS` directory. ### Building a deb ```sh # Checkout tag to be packaged git clone https://github.com/EGI-Foundation/bdii.git cd bdii git checkout X.X.X # Building in a container using the source files docker run --rm -v $(pwd):/source -it ubuntu:xenial apt update apt install -y devscripts debhelper make rsync python-all-dev cd /source && make deb ``` The DEB will be available into the `build/` directory. ## History This work started under the EGEE project, and was hosted and maintained for a long time by CERN. This is now hosted here on GitHub, maintained by the BDII community with support of members of the EGI Federation. bdii-5.2.25/bdii.spec000066400000000000000000000232401335470071400143040ustar00rootroot00000000000000Name: bdii Version: 5.2.25 Release: 1%{?dist} Summary: The Berkeley Database Information Index (BDII) Group: System Environment/Daemons License: ASL 2.0 URL: https://github.com/EGI-Foundation/bdii Source: %{name}-%{version}.tar.gz BuildArch: noarch BuildRoot: %{_tmppath}/%{name}-%{version}-build Requires: openldap-clients Requires: openldap-servers Requires: glue-schema >= 2.0.0 Requires: python Requires(post): chkconfig Requires(post): expect Requires(preun): chkconfig Requires(preun): initscripts Requires(postun): initscripts %if %{?fedora}%{!?fedora:0} >= 5 || %{?rhel}%{!?rhel:0} >= 5 Requires(post): policycoreutils Requires(postun): policycoreutils %if %{?fedora}%{!?fedora:0} >= 11 || %{?rhel}%{!?rhel:0} >= 6 Requires(post): policycoreutils-python Requires(postun): policycoreutils-python %endif %endif %description The Berkeley Database Information Index (BDII) consists of a standard LDAP database which is updated by an external process. The update process obtains LDIF from a number of sources and merges them. It then compares this to the contents of the database and creates an LDIF file of the differences. This is then used to update the database. %prep %setup -q %build %install rm -rf %{buildroot} make install prefix=%{buildroot} chmod 644 %{buildroot}%{_sysconfdir}/sysconfig/%{name} %clean rm -rf %{buildroot} %pre # Temp fix for upgrade from 5.2.5 to 5.2.7 service %{name} status > /dev/null 2>&1 if [ $? -eq 0 ]; then touch %{_localstatedir}/run/%{name}/bdii.upgrade service %{name} stop > /dev/null 2>&1 fi %post sed "s/\(rootpw *\)secret/\1$(mkpasswd -s 0 | tr '/' 'x')/" \ -i %{_sysconfdir}/%{name}/bdii-slapd.conf \ %{_sysconfdir}/%{name}/bdii-top-slapd.conf # Temp fix for upgrade from 5.2.5 to 5.2.7 if [ -f %{_localstatedir}/run/%{name}/bdii.upgrade ]; then rm -f %{_localstatedir}/run/%{name}/bdii.upgrade service %{name} start > /dev/null 2>&1 fi /sbin/chkconfig --add %{name} %if %{?fedora}%{!?fedora:0} >= 5 || %{?rhel}%{!?rhel:0} >= 5 semanage port -a -t ldap_port_t -p tcp 2170 2>/dev/null || : semanage fcontext -a -t slapd_db_t "%{_localstatedir}/lib/%{name}/db(/.*)?" 2>/dev/null || : semanage fcontext -a -t slapd_var_run_t "%{_localstatedir}/run/%{name}/db(/.*)?" 2>/dev/null || : # Remove selinux labels for old bdii var dir semanage fcontext -d -t slapd_db_t "%{_localstatedir}/run/%{name}(/.*)?" 2>/dev/null || : %endif %preun if [ $1 -eq 0 ]; then service %{name} stop > /dev/null 2>&1 /sbin/chkconfig --del %{name} fi %postun if [ $1 -ge 1 ]; then service %{name} condrestart > /dev/null 2>&1 fi %if %{?fedora}%{!?fedora:0} >= 5 || %{?rhel}%{!?rhel:0} >= 5 if [ $1 -eq 0 ]; then semanage port -d -t ldap_port_t -p tcp 2170 2>/dev/null || : semanage fcontext -d -t slapd_db_t "%{_localstatedir}/lib/%{name}/db(/.*)?" 2>/dev/null || : semanage fcontext -d -t slapd_var_run_t "%{_localstatedir}/run/%{name}/db(/.*)?" 2>/dev/null || : fi %endif %files %defattr(-,root,root,-) %attr(-,ldap,ldap) %{_localstatedir}/lib/%{name} %attr(-,ldap,ldap) %{_localstatedir}/log/%{name} %dir %{_sysconfdir}/%{name} %config(noreplace) %{_sysconfdir}/%{name}/DB_CONFIG %config(noreplace) %{_sysconfdir}/%{name}/DB_CONFIG_top %config(noreplace) %{_sysconfdir}/%{name}/bdii.conf %config(noreplace) %{_sysconfdir}/%{name}/BDII.schema %attr(-,ldap,ldap) %config %{_sysconfdir}/%{name}/bdii-slapd.conf %attr(-,ldap,ldap) %config %{_sysconfdir}/%{name}/bdii-top-slapd.conf %config(noreplace) %{_sysconfdir}/sysconfig/%{name} %config(noreplace) %{_sysconfdir}/logrotate.d/%{name} %{_initrddir}/%{name} %{_sbindir}/bdii-update %{_mandir}/man1/bdii-update.1* %doc /usr/share/doc/bdii/README.md %doc /usr/share/doc/bdii/AUTHORS %doc /usr/share/doc/bdii/COPYRIGHT %doc /usr/share/doc/bdii/LICENSE.txt %changelog * Tue Oct 2 2018 Baptiste Grenier - 5.2.25-1 - Import product card JSON in codemeta.json format (Bruce Becker) - Lint, build, test install and attach packages to GitHub tags using Travis. (Baptiste Grenier) * Mon Aug 27 2018 Baptiste Grenier - 5.2.24-1 - Fix #3: init script failing on stale PID (Paolo Andreetto) - Update build, documetation and link to new GitHub repository (Baptiste Grenier) * Wed Aug 27 2014 Maria Alandes - 5.2.23-1 - #GRIDINFO-55: Increase the number of simultaneous threads * Mon Sep 9 2013 Maria Alandes - 5.2.22-1 - BUG #102503: Make /var/run/bdii configurable * Fri Aug 2 2013 Maria Alandes - 5.2.21-1 - Add plugin modifications to LDIF modify instead of LDIF new for cached objects - Do not clean glite-update-endpoints cache files - Fixed wrong 'if' check in init.d script - BUG #99298: Set status attributes of delayed delete entries to 'Unknown' - BUG #102014: Clean caches after a BDII restart - BUG #101709: Start bdii-update daemon with -l option - BUG #102140: Start daemons from "/" - BUG #101389: RAM size can be now configured - BUG #101398: Defined the max log file size for the LDAP DB backend in top level BDIIs * Fri May 31 2013 Maria Alandes - 5.2.20-1 - Changed URL in spec file to point to new Information System web pages - Added missing dist in the rpm target of the Makefile * Fri May 31 2013 Maria Alandes - 5.2.19-1 - BUG #101090: added missing symlink to DB_CONFIG_top for GLUE2 DB backend * Fri May 03 2013 Maria Alandes - 5.2.18-1 - BUG #101237: bdii-update: GLUE2 entries marked for deletion keep the correct case and can be deleted * Tue Jan 15 2013 Maria Alandes - 5.2.17-1 - BUG #99622: Add dependency on openldap2.4-clients in SL5 * Thu Jan 10 2013 Maria Alandes - 5.2.16-1 - BUG #99622: Add dependency on openldap2.4-servers in SL5 * Wed Nov 28 2012 Maria Alandes - 5.2.15-1 - Fixes after testing: Load rwm and back_relay modules in the slapd configuration for site and resource BDII * Tue Nov 20 2012 Maria Alandes - 5.2.14-1 - BUG #98931: /sbin/runuser instead of runuser - BUG #98711: Optimise LDAP queries in GLUE 2.0 - BUG #98682: Delete delayed_delete.pkl when BDII is restarted - BUG #97717: Relay database created to be able to define the GLUE2GroupName and services alias * Wed Aug 15 2012 Laurence Field - 5.2.13-1 - Included Fedora patches upstream: - BUG #97223: Changes needed for EPEL - BUG #97217: Issues with lsb dependencies * Fri Jul 20 2012 Maria Alandes - 5.2.12-1 - Fixed BDII_IPV6_SUPPORT after testing * Wed Jul 18 2012 Maria Alandes - 5.2.11-1 - BUG 95122: Created SLAPD_DB_DIR directoy with correct ownership if it doesn't exist - BUG 95839: Added BDII_IPV6_SUPPORT * Thu Mar 8 2012 Laurence Field - 5.2.10-1 - New upsteam version that includes a new DB_CONFIG * Wed Feb 8 2012 Laurence Field - 5.2.9-1 - Fixed /var/run packaging issue * Wed Feb 8 2012 Laurence Field - 5.2.8-1 - Fixed a base64 encoding issue and added /var/run/bdii to the package * Tue Feb 7 2012 Laurence Field - 5.2.7-1 - Performance improvements to reduce memory and disk usage * Wed Jan 25 2012 Laurence Field - 5.2.6-1 - New upstream version that includes fedora patches and fix for EGI RT 3235 * Thu Jan 12 2012 Fedora Release Engineering - 5.2.5-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild * Sun Sep 4 2011 Mattias Ellert - 5.2.5-1 - New upstream version 5.2.5 * Tue Jul 26 2011 Mattias Ellert - 5.2.4-1 - New upstream version 5.2.4 - Drop patch accepted upstream: bdii-mdsvo.patch - Move large files away from /var/run in order not to fill up /run partition * Mon Jun 27 2011 Mattias Ellert - 5.2.3-2 - Revert upstream hack that breaks ARC infosys * Mon Jun 13 2011 Mattias Ellert - 5.2.3-1 - New upstream version 5.2.3 - Drop patches accepted upstream: bdii-runuser.patch, bdii-context.patch, bdii-default.patch, bdii-shadowerr.patch, bdii-sysconfig.patch * Mon Feb 07 2011 Fedora Release Engineering - 5.1.13-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild * Sat Jan 01 2011 Mattias Ellert - 5.1.13-1 - New upstream version 5.1.13 - Move restorecon from post sctiptlet to startup script in order to support /var/run on tmpfs * Thu Sep 23 2010 Mattias Ellert - 5.1.9-1 - New upstream version 5.1.9 * Thu Sep 02 2010 Mattias Ellert - 5.1.8-1 - New upstream version 5.1.8 * Fri Jun 18 2010 Mattias Ellert - 5.1.7-1 - New upstream version 5.1.7 * Sun May 23 2010 Mattias Ellert - 5.1.5-1 - New upstream release 5.1.5 - Get rid of lsb initscript dependency * Mon Apr 05 2010 Mattias Ellert - 5.1.0-1 - New upstream verison 5.1.0 - Add SELinux context management to scriptlets * Thu Mar 25 2010 Mattias Ellert - 5.0.8-4.460 - Update (svn revision 460) - Use proper anonymous svn checkout instead of svnweb generated tarball * Fri Feb 26 2010 Mattias Ellert - 5.0.8-3.443 - Update (svn revision 443) * Wed Feb 24 2010 Mattias Ellert - 5.0.8-2.436 - Update (svn revision 436) * Mon Feb 08 2010 Mattias Ellert - 5.0.8-1.375 - Initial package (svn revision 375) bdii-5.2.25/bin/000077500000000000000000000000001335470071400132705ustar00rootroot00000000000000bdii-5.2.25/bin/bdii-update000077500000000000000000001105741335470071400154150ustar00rootroot00000000000000#!/usr/bin/python ############################################################################## # Copyright (c) Members of the EGEE Collaboration. 2004. # See http://www.eu-egee.org/partners/ for details on the copyright # holders. # # 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 getopt import logging import os import pickle import re import signal import sys import tempfile import time def parse_options(): config = {} try: opts, args = getopt.getopt(sys.argv[1:], "dc:", ["config"]) except getopt.GetoptError: sys.stderr.write("Error: Invalid option specified.\n") print_usage() sys.exit(2) for o, a in opts: if o in ("-c", "--config"): config['BDII_CONFIG_FILE'] = a if o in ("-d", "--daemon"): config['BDII_DAEMON'] = True if 'BDII_CONFIG_FILE' not in config: sys.stderr.write("Error: Configuration file not specified.\n") print_usage() sys.exit(1) if not os.path.exists(config['BDII_CONFIG_FILE']): sys.stderr.write("Error: Configuration file %s does not exist.\n" % config['BDII_CONFIG_FILE']) sys.exit(1) return config def get_config(config): for line in open(config['BDII_CONFIG_FILE']).readlines(): index = line.find("#") if index > -1: line = line[:index] index = line.find("=") if index > -1: key = line[:index].strip() value = line[index+1:].strip() config[key] = value if 'SLAPD_CONF' in os.environ: config['SLAPD_CONF'] = os.environ['SLAPD_CONF'] if 'BDII_DAEMON' not in config: config['BDII_DAEMON'] = False if 'BDII_RUN_DIR' not in config: config['BDII_RUN_DIR'] = '/var/run/bdii' if 'BDII_PID_FILE' not in config: config['BDII_PID_FILE'] = "%s/bdii-update.pid" % config['BDII_RUN_DIR'] for parameter in ['BDII_LOG_FILE', 'BDII_LOG_LEVEL', 'BDII_LDIF_DIR', 'BDII_PROVIDER_DIR', 'BDII_PLUGIN_DIR', 'BDII_READ_TIMEOUT']: if parameter not in config: sys.stderr.write(("Error: Configuration parameter %s is not" " specified in the configuration file %s.\n") % ( parameter, config['BDII_CONFIG_FILE'])) sys.exit(1) for parameter in ['BDII_LDIF_DIR', 'BDII_PROVIDER_DIR', 'BDII_PLUGIN_DIR']: if not os.path.exists(config[parameter]): sys.stderr.write("Error: %s %s does not exist.\n" % ( parameter, config[parameter])) sys.exit(1) if 'BDII_LOG_LEVEL' not in config: config['BDII_LOG_LEVEL'] = 'ERROR' else: log_levels = ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'] try: log_levels.index(config['BDII_LOG_LEVEL']) except ValueError: sys.stderr.write(("Error: Log level %s is not an" " allowed level. %s\n") % ( config['BDII_LOG_LEVEL'], log_levels)) sys.exit(1) config['BDII_READ_TIMEOUT'] = int(config['BDII_READ_TIMEOUT']) if config['BDII_DAEMON'] is True: for parameter in ['BDII_PORT', 'BDII_BREATHE_TIME', 'BDII_VAR_DIR', 'BDII_ARCHIVE_SIZE', 'BDII_DELETE_DELAY', 'SLAPD_CONF']: if parameter not in config: sys.stderr.write(("Error: Configuration parameter %s is not" " specified in the configuration file %s.\n") % (parameter, config['BDII_CONFIG_FILE'])) sys.exit(1) if os.path.exists(config['SLAPD_CONF']): config['BDII_PASSWD'] = {} config['BDII_PASSWD_FILE'] = {} if not os.path.exists(config['BDII_RUN_DIR']): os.makedirs(config['BDII_RUN_DIR']) rootdn = False rootpw = False filename = "" for line in open(config['SLAPD_CONF']): if line.find("rootdn") > -1: rootdn = line.replace("rootdn", "").strip() rootdn = rootdn.replace('"', '').replace(" ", "") filename = rootdn.replace('o=', '') if rootpw: config['BDII_PASSWD'][rootdn] = rootpw config['BDII_PASSWD_FILE'][rootdn] = "%s/%s" % ( config['BDII_RUN_DIR'], filename) pf = os.open(config['BDII_PASSWD_FILE'][rootdn], os.O_WRONLY | os.O_CREAT, 0o600) os.write(pf, rootpw) os.close(pf) rootdn = False rootpw = False if line.find("rootpw") > -1: rootpw = line.replace("rootpw", "").strip() if rootdn: config['BDII_PASSWD'][rootdn] = rootpw config['BDII_PASSWD_FILE'][rootdn] = "%s/%s" % ( config['BDII_RUN_DIR'], filename) pf = os.open(config['BDII_PASSWD_FILE'][rootdn], os.O_WRONLY | os.O_CREAT, 0o600) os.write(pf, rootpw) os.close(pf) rootdn = False rootpw = False config['BDII_BREATHE_TIME'] = float(config['BDII_BREATHE_TIME']) config['BDII_ARCHIVE_SIZE'] = int(config['BDII_ARCHIVE_SIZE']) config['BDII_DELETE_DELAY'] = int(config['BDII_DELETE_DELAY']) config['BDII_HOSTNAME'] = 'localhost' return config def print_usage(): sys.stderr.write('''Usage: %s [ OPTIONS ] -c --config BDII configuration file -d --daemon Run BDII in daemon mode ''' % str(sys.argv[0])) def create_daemon(log_file): try: pid = os.fork() except OSError as e: return((e.errno, e.strerror)) if pid == 0: os.setsid() signal.signal(signal.SIGHUP, signal.SIG_IGN) try: pid = os.fork() except OSError as e: return((e.errno, e.strerror)) if pid == 0: os.umask(0o022) else: os._exit(0) else: os._exit(0) try: maxfd = os.sysconf("SC_OPEN_MAX") except (AttributeError, ValueError): maxfd = 256 for fd in range(3, maxfd): try: os.close(fd) except OSError: pass os.close(0) os.open("/dev/null", os.O_RDONLY) os.close(1) os.open("/dev/null", os.O_WRONLY) # connect stderr to log file e = os.open(log_file, os.O_WRONLY | os.O_APPEND | os.O_CREAT, 0o644) os.dup2(e, 2) os.close(e) sys.stderr = os.fdopen(2, 'a', 0) # Write PID pid_file = open(config['BDII_PID_FILE'], 'w') pid_file.write("%s\n" % str(os.getpid())) pid_file.close() def get_logger(log_file, log_level): log = logging.getLogger('bdii-update') hdlr = logging.StreamHandler(sys.stderr) formatter = logging.Formatter('%(asctime)s: [%(levelname)s] %(message)s') hdlr.setFormatter(formatter) log.addHandler(hdlr) log.setLevel(logging.__dict__.get(log_level)) return log def handler(signum, frame): if signum == 14: # Commit suicide process_group = os.getpgrp() os.killpg(process_group, signal.SIGTERM) sys.exit(1) def read_ldif(source): # Get pipe file descriptors read_fd, write_fd = os.pipe() # Fork pid = os.fork() if pid: # Close write file descriptor as we don't need it. os.close(write_fd) read_fh = os.fdopen(read_fd) raw_ldif = read_fh.read() result = os.waitpid(pid, 0) if result[1] > 0: log.error("Timed out while reading %s", source) return "" raw_ldif = raw_ldif.replace("\n ", "") return raw_ldif else: # Close read file d os.close(read_fd) # Set process group os.setpgrp() # Setup signal handler signal.signal(signal.SIGALRM, handler) signal.alarm(config['BDII_READ_TIMEOUT']) # Open pipe to LDIF if source[:7] == 'ldap://': url = source.split('/') command = "ldapsearch -LLL -x -h %s -b %s 2>/dev/null" % ( url[2], url[3]) pipe = os.popen(command) elif source[:7] == 'file://': pipe = open(source[7:]) else: pipe = os.popen(source) raw_ldif = pipe.read() # Close LDIF pipe pipe.close() try: write_fh = os.fdopen(write_fd, 'w') write_fh.write(raw_ldif) write_fh.close() except IOError: log.error("Information provider %s terminated unexpectedly." % source) # Disable the alarm signal.alarm(0) sys.exit(0) def get_dns(ldif): dns = {} last_dn_index = len(ldif) while True: dn_index = ldif.rfind("dn:", 0, last_dn_index) if dn_index == -1: break end_dn_index = ldif.find("\n", dn_index, last_dn_index) dn = ldif[dn_index + 4:end_dn_index].lower() dn = re.sub("\s*,\s*", ",", dn) dn = re.sub("\s*=\s*", "=", dn) # Replace encoded slash dn = dn.replace("\\5c", "\\\\") # Replace encoded comma dn = dn.replace("\\2c", "\\,") # Replace encoded equals dn = dn.replace("\\3d", "\\=") # Replace encoded plus dn = dn.replace("\\2b", "\\+") # Replace encoded semi colon dn = dn.replace("\\3b", "\\;") # Replace encoded quote dn = dn.replace("\\22", "\\\"") # Replace encoded greater than dn = dn.replace("\\3e", "\\>") # Replace encoded less than dn = dn.replace("\\3c", "\\<") dns[dn] = (dn_index, last_dn_index, end_dn_index) last_dn_index = dn_index return dns def group_dns(dns): grouped = {} for dn in dns: index = dn.rfind(",") root = dn[index + 1:].strip() if root in grouped: grouped[root].append(dn) else: if root in config['BDII_PASSWD']: grouped[root] = [dn] else: if "o=shadow" in config['BDII_PASSWD'] and root == "o=grid": grouped[root] = [dn] elif root != "o=shadow": log.error(("dn suffix %s in not specified in the slapd" " configuration file.") % root) return grouped def convert_entry(entry_string): entry = {} for line in entry_string.split("\n"): index = line.find(":") if index > -1: attribute = line[:index].lower() value = line[index + 1:].strip() if attribute in entry: if value not in entry[attribute]: entry[attribute].append(value) else: entry[attribute] = [value] return entry def convert_back(entry): entry_string = "dn: %s\n" % entry["dn"][0] entry.pop("dn") for attribute in entry.keys(): attribute = attribute.lower() for value in entry[attribute]: entry_string += "%s: %s\n" % (attribute, value) return entry_string def ldif_diff(dn, old_entry, new_entry): add_attribute = {} delete_attribute = {} replace_attribute = {} old_entry = convert_entry(old_entry) new_entry = convert_entry(new_entry) dn_perserved_case = None for attribute in new_entry.keys(): attribute = attribute.lower() if attribute == "dn": dn_perserved_case = new_entry['dn'][0] continue # If the old entry has the attribue we need to compare values if attribute in old_entry: # If the old entries are different find the modify. if not new_entry[attribute] == old_entry[attribute]: replace_attribute[attribute] = new_entry[attribute] # The old entry does not have the attribute so add it. else: add_attribute[attribute] = new_entry[attribute] # Checking for removed attributes for attribute in old_entry.keys(): if (attribute.lower() == "dn"): continue if attribute not in new_entry: delete_attribute[attribute] = old_entry[attribute] # Create LDIF modify statement ldif = ['dn: %s' % dn_perserved_case] ldif.append('changetype: modify') for attribute in add_attribute.keys(): attribute = attribute.lower() ldif.append('add: %s' % attribute) for value in add_attribute[attribute]: ldif.append('%s: %s' % (attribute, value)) ldif.append('-') for attribute in replace_attribute.keys(): attribute = attribute.lower() ldif.append('replace: %s' % attribute) for value in replace_attribute[attribute]: ldif.append('%s: %s' % (attribute, value)) ldif.append('-') for attribute in delete_attribute.keys(): attribute = attribute.lower() ldif.append('delete: %s' % attribute) ldif.append('-') if len(ldif) > 3: ldif = "\n".join(ldif) + "\n\n" else: ldif = "" return ldif def modify_entry(entry, mods): mods = convert_entry(mods) entry = convert_entry(entry) if 'changetype' in mods: # Handle LDIF delete attribute if 'delete' in mods: for attribute in mods['delete']: attribute = attribute.lower() if attribute in entry: if attribute in mods: for value in mods[attribute]: try: entry[attribute].remove(value) if len(entry[attribute]) == 0: entry.pop(attribute) except ValueError: pass except KeyError: pass else: entry.pop(attribute) # Handle LDIF replace attribute if 'replace' in mods: for attribute in mods['replace']: attribute = attribute.lower() if attribute in entry: if attribute in mods: entry[attribute] = mods[attribute] # Handle LDIF add attribute if 'add' in mods: for attribute in mods['add']: attribute = attribute.lower() if attribute not in entry: log.debug("attribute: %s" % attribute) entry[attribute] = mods[attribute] else: entry[attribute].extend(mods[attribute]) # Just old style just change else: for attribute in mods.keys(): if attribute in entry: entry[attribute] = mods[attribute] entry_string = convert_back(entry) return entry_string def fix(dns, ldif): response = [] append = response.append for dn in dns.keys(): entry = convert_entry(ldif[dns[dn][0]:dns[dn][1]]) if dn[:11].lower() == "mds-vo-name": if 'objectclass' in entry: if 'mds' in map(lambda x: x.lower(), entry['objectclass']): if 'gluetop' in map(lambda x: x.lower(), entry['objectclass']): value = dn[12:dn.index(",")] entry = {'dn': [dn], 'objectclass': ['MDS'], 'mds-vo-name': [value]} entry = convert_back(entry) append(entry) response = "".join(response) return response def log_errors(error_file, dns): log.debug("Logging Errors") request = 0 dn = None error_counter = 0 for line in open(error_file).readlines(): if line[:7] == 'request': request += 1 else: if request > 1: try: if not dn == dns[request - 2]: error_counter += 1 dn = dns[request - 2] log.warn("dn: %s" % dn) except IndexError: log.error("Problem with error reporting ...") log.error("Request Num: %i, Line: %s, dns: %i" % (request, line, len(dns))) if len(line) > 5: log.warn(line.strip()) return error_counter def main(config, log): log.info("Starting Update Process") while True: log.info("Starting Update") stats = {} stats['update_start'] = time.time() new_ldif = "" log.info("Reading static LDIF files ...") stats['read_start'] = time.time() ldif_files = os.listdir(config['BDII_LDIF_DIR']) for file_name in ldif_files: if file_name[-5:] == '.ldif': if file_name[0] not in ('#', '.'): file_url = "file://%s/%s" % (config['BDII_LDIF_DIR'], file_name) log.debug("Reading %s" % file_url[7:]) response = read_ldif(file_url) new_ldif = new_ldif + response stats['read_stop'] = time.time() log.info("Running Providers") stats['providers_start'] = time.time() providers = os.listdir(config['BDII_PROVIDER_DIR']) for provider in providers: if provider[-1:] != '~' or (provider[0] in ('#', '.')): log.debug("Running %s/%s" % (config['BDII_PROVIDER_DIR'], provider)) response = read_ldif("%s/%s" % (config['BDII_PROVIDER_DIR'], provider)) new_ldif = new_ldif + response stats['providers_stop'] = time.time() new_dns = get_dns(new_ldif) ldif_modify = "" log.info("Running Plugins") stats['plugins_start'] = time.time() plugins = os.listdir(config['BDII_PLUGIN_DIR']) for plugin in plugins: if plugin[-1:] != '~' or (plugin[0] in ('#', '.')): log.debug("Running %s/%s" % (config['BDII_PLUGIN_DIR'], plugin)) response = read_ldif("%s/%s" % (config['BDII_PLUGIN_DIR'], plugin)) modify_dns = get_dns(response) for dn in modify_dns.keys(): if dn in new_dns: mod_entry = modify_entry( new_ldif[new_dns[dn][0]:new_dns[dn][1]], response[modify_dns[dn][0]:modify_dns[dn][1]]) start = len(new_ldif) end = start + len(mod_entry) new_dns[dn] = (start, end) new_ldif = new_ldif + mod_entry else: ldif_modify += response[ modify_dns[dn][0]:modify_dns[dn][1] ] stats['plugins_stop'] = time.time() log.debug("Doing Fix") new_ldif = fix(new_dns, new_ldif) log.debug("Writing new_ldif to disk") if config['BDII_LOG_LEVEL'] == 'DEBUG': dump_fh = open("%s/new.ldif" % (config['BDII_VAR_DIR']), 'w') dump_fh.write(new_ldif) dump_fh.close() if not config['BDII_DAEMON']: print(new_ldif) sys.exit(0) log.info("Reading old LDIF file ...") stats['read_old_start'] = time.time() old_ldif_file = "%s/old.ldif" % (config['BDII_VAR_DIR']) if os.path.exists(old_ldif_file): old_ldif = read_ldif("file://%s" % (old_ldif_file)) else: old_ldif = "" stats['read_old_stop'] = time.time() log.debug("Starting Diff") ldif_add = [] ldif_delete = [] new_dns = get_dns(new_ldif) old_dns = get_dns(old_ldif) for dn in new_dns.keys(): if dn in old_dns: old = old_ldif[old_dns[dn][0]:old_dns[dn][1]].strip() new = new_ldif[new_dns[dn][0]:new_dns[dn][1]].strip() # If the entries are different we need to compare them if not new == old: entry = ldif_diff(dn, old, new) ldif_modify += entry else: ldif_add.append(dn) # Checking for removed entries for dn in old_dns.keys(): if dn not in new_dns: ldif_delete.append(old_ldif[old_dns[dn][0] + 4:old_dns[dn][2]].strip()) log.debug("Finished Diff") log.debug("Sorting Add Keys") ldif_add.sort(lambda x, y: cmp(len(x), len(y))) log.debug("Writing ldif_add to disk") if config['BDII_LOG_LEVEL'] == 'DEBUG': dump_fh = open("%s/add.ldif" % (config['BDII_VAR_DIR']), 'w') for dn in ldif_add: dump_fh.write(new_ldif[new_dns[dn][0]:new_dns[dn][1]]) dump_fh.write("\n") dump_fh.close() log.debug("Adding New Entries") stats['db_update_start'] = time.time() if config['BDII_LOG_LEVEL'] == 'DEBUG': error_file = "%s/add.err" % config['BDII_VAR_DIR'] else: error_file = tempfile.mktemp() roots = group_dns(ldif_add) suffixes = roots.keys() if "o=shadow" in suffixes: index = suffixes.index("o=shadow") if index > 0: suffixes[index] = suffixes[0] suffixes[0] = "o=shadow" add_error_counter = 0 for root in suffixes: try: bind = root if "o=shadow" in config['BDII_PASSWD']: if root == "o=grid": bind = "o=shadow" input_fh = os.popen(("ldapadd -d 256 -x -c -h %s -p %s" " -D %s -y %s >/dev/null 2>%s") % ( config['BDII_HOSTNAME'], config['BDII_PORT'], bind, config['BDII_PASSWD_FILE'][bind], error_file), 'w') for dn in roots[root]: input_fh.write(new_ldif[new_dns[dn][0]:new_dns[dn][1]]) input_fh.write("\n") input_fh.close() except (IOError, KeyError): log.error("Could not add new entries to the database.") add_error_counter += log_errors(error_file, ldif_add) if not config['BDII_LOG_LEVEL'] == 'DEBUG': os.remove(error_file) log.debug("Writing ldif_modify to disk") if config['BDII_LOG_LEVEL'] == 'DEBUG': dump_fh = open("%s/modify.ldif" % (config['BDII_VAR_DIR']), 'w') dump_fh.write(ldif_modify) dump_fh.close() log.debug("Modify New Entries") if config['BDII_LOG_LEVEL'] == 'DEBUG': error_file = "%s/modify.err" % config['BDII_VAR_DIR'] else: error_file = tempfile.mktemp() ldif_modify_dns = get_dns(ldif_modify) roots = group_dns(ldif_modify_dns) modify_error_counter = 0 for root in roots.keys(): try: bind = root if "o=shadow" in config['BDII_PASSWD']: if root == "o=grid": bind = "o=shadow" input_fh = os.popen(("ldapmodify -d 256 -x -c -h %s -p %s -D" " %s -y %s >/dev/null 2>%s") % ( config['BDII_HOSTNAME'], config['BDII_PORT'], bind, config['BDII_PASSWD_FILE'][bind], error_file), 'w') for dn in roots[root]: input_fh.write(ldif_modify[ ldif_modify_dns[dn][0]:ldif_modify_dns[dn][1] ]) input_fh.write("\n") input_fh.close() except (IOError, KeyError): log.error("Could not modify entries in the database.") modify_error_counter += log_errors(error_file, ldif_modify_dns.keys()) if config['BDII_LOG_LEVEL'] != 'DEBUG': os.remove(error_file) log.debug("Sorting Delete Keys") ldif_delete.sort(lambda x, y: cmp(len(y), len(x))) log.debug("Writing ldif_delete to disk") if config['BDII_LOG_LEVEL'] == 'DEBUG': dump_fh = open("%s/delete.ldif" % config['BDII_VAR_DIR'], 'w') for dn in ldif_delete: dump_fh.write("%s\n" % (dn)) dump_fh.close() # Delayed delete Function if config['BDII_DELETE_DELAY'] > 0: log.debug("Doing Delayed Delete") delete_timestamp = time.time() # Get DNs of entries to be deleted not yet in delayed delete so # their status can be updated new_delayed_delete_file = '%s/new_delayed_delete.pkl' % config[ 'BDII_VAR_DIR' ] try: nfh = open(new_delayed_delete_file, 'w') nfh.write("") except IOError: log.error("Unable to open new_delayed_delete file %s" % new_delayed_delete_file) delayed_delete_file = '%s/delayed_delete.pkl' % config[ 'BDII_VAR_DIR' ] if os.path.exists(delayed_delete_file): file_handle = open(delayed_delete_file, 'rb') delay_delete = pickle.load(file_handle) file_handle.close() else: delay_delete = {} # Add remove cache timestamps that have been readded for dn in delay_delete.keys(): if dn not in ldif_delete: log.debug("Removing %s from cache (readded)" % dn) delay_delete.pop(dn) # Add current timestamp for new deletes for dn in ldif_delete: if dn not in delay_delete: delay_delete[dn] = delete_timestamp nfh.write("%s\n" % (dn)) nfh.close() # Remove delayed deletes from LDIF or remove from cache for dn in delay_delete.keys(): if delay_delete[dn] + config[ 'BDII_DELETE_DELAY'] >= delete_timestamp: ldif_delete.remove(dn) else: delay_delete.pop(dn) # Store Delayed Deletes log.debug("Storing delayed deletes") file_handle = open(delayed_delete_file, 'wb') pickle.dump(delay_delete, file_handle) file_handle.close() log.debug("Deleting Old Entries") if config['BDII_LOG_LEVEL'] == 'DEBUG': error_file = "%s/delete.err" % config['BDII_VAR_DIR'] else: error_file = tempfile.mktemp() roots = group_dns(ldif_delete) delete_error_counter = 0 for root in roots.keys(): try: bind = root if "o=shadow" in config['BDII_PASSWD']: if root == "o=grid": bind = "o=shadow" input_fh = os.popen(("ldapdelete -d 256 -x -c -h %s -p %s" " -D %s -y %s >/dev/null 2>%s") % ( config['BDII_HOSTNAME'], config['BDII_PORT'], bind, config['BDII_PASSWD_FILE'][bind], error_file), 'w') for dn in roots[root]: input_fh.write("%s\n" % dn) log.debug("Deleting %s" % dn) input_fh.close() except (IOError, KeyError): log.error("Could not delete old entries in the database.") delete_error_counter += log_errors(error_file, ldif_delete) if config['BDII_LOG_LEVEL'] != 'DEBUG': os.remove(error_file) roots = group_dns(new_dns) stats['query_start'] = time.time() if os.path.exists("%s/old.ldif" % config['BDII_VAR_DIR']): os.remove("%s/old.ldif" % config['BDII_VAR_DIR']) if os.path.exists("%s/old.err" % config['BDII_VAR_DIR']): os.remove("%s/old.err" % config['BDII_VAR_DIR']) for root in roots.keys(): # Stop flapping due to o=shadow if root == "o=shadow": command = ("ldapsearch -LLL -x -h %s -p %s -b %s -s base" " >> %s/old.ldif 2>> %s/old.err") % ( config['BDII_HOSTNAME'], config['BDII_PORT'], root, config['BDII_VAR_DIR'], config['BDII_VAR_DIR']) else: command = ("ldapsearch -LLL -x -h %s -p %s -b %s" " >> %s/old.ldif 2>> %s/old.err") % ( config['BDII_HOSTNAME'], config['BDII_PORT'], root, config['BDII_VAR_DIR'], config['BDII_VAR_DIR']) result = os.system(command) if result > 0: log.error("Query to self failed.") stats['query_stop'] = time.time() out_file = "%s/archive/%s-snapshot.gz" % ( config['BDII_VAR_DIR'], time.strftime('%y-%m-%d-%H-%M-%S')) log.debug("Creating GZIP file") os.system("gzip -c %s/old.ldif > %s" % (config['BDII_VAR_DIR'], out_file)) infosys_output = "" if len(old_ldif) == 0: log.debug("ldapadd o=infosys compression") command = "ldapadd" infosys_output += "dn: o=infosys\n" infosys_output += "objectClass: organization\n" infosys_output += "o: infosys\n\n" infosys_output += "dn: CompressionType=zip,o=infosys\n" infosys_output += "objectClass: CompressedContent\n" infosys_output += "Hostname: %s\n" % config['BDII_HOSTNAME'] infosys_output += "CompressionType: zip\n" infosys_output += "Data: file://%s\n\n" % out_file else: log.debug("ldapmodify o=infosys compression") command = "ldapmodify" infosys_output += "dn: CompressionType=zip,o=infosys\n" infosys_output += "changetype: Modify\n" infosys_output += "replace: Data\n" infosys_output += "Data: file://%s\n\n" % out_file try: output_fh = os.popen(("%s -x -c -h %s -p %s -D o=infosys -y %s" " >/dev/null") % ( command, config['BDII_HOSTNAME'], config['BDII_PORT'], config['BDII_PASSWD_FILE']['o=infosys']), 'w') output_fh.write(infosys_output) output_fh.close() except (IOError, KeyError): log.error("Could not add compressed data to the database.") old_files = os.popen("ls -t %s/archive" % config[ 'BDII_VAR_DIR']).readlines() log.debug("Deleting old GZIP files") for file in old_files[config['BDII_ARCHIVE_SIZE']:]: os.remove("%s/archive/%s" % (config['BDII_VAR_DIR'], file.strip())) stats['db_update_stop'] = time.time() stats['update_stop'] = time.time() stats['UpdateTime'] = int(stats['update_stop'] - stats['update_start']) stats['ReadTime'] = int(stats['read_old_stop'] - stats['read_old_start']) stats['ProvidersTime'] = int(stats['providers_stop'] - stats['providers_start']) stats['PluginsTime'] = int(stats['plugins_stop'] - stats['plugins_start']) stats['QueryTime'] = int(stats['query_stop'] - stats['query_start']) stats['DBUpdateTime'] = int(stats['db_update_stop'] - stats['db_update_start']) stats['TotalEntries'] = len(old_dns) stats['NewEntries'] = len(ldif_add) stats['ModifiedEntries'] = len(ldif_modify_dns.keys()) stats['DeletedEntries'] = len(ldif_delete) stats['FailedAdds'] = add_error_counter stats['FailedModifies'] = modify_error_counter stats['FailedDeletes'] = delete_error_counter for key in stats.keys(): if key.find("_") == -1: log.info("%s: %i" % (key, stats[key])) infosys_output = "" if len(old_ldif) == 0: log.debug("ldapadd o=infosys updatestats") command = "ldapadd" infosys_output += "dn: Hostname=%s,o=infosys\n" % config[ 'BDII_HOSTNAME'] infosys_output += "objectClass: UpdateStats\n" infosys_output += "Hostname: %s\n" % config['BDII_HOSTNAME'] for key in stats.keys(): if key.find("_") == -1: infosys_output += "%s: %i\n" % (key, stats[key]) infosys_output += "\n" else: log.debug("ldapmodify o=infosys updatestats") command = "ldapmodify" infosys_output += "dn: Hostname=%s,o=infosys\n" % config[ 'BDII_HOSTNAME'] infosys_output += "changetype: Modify\n" for key in stats.keys(): if key.find("_") == -1: infosys_output += "replace: %s\n" % key infosys_output += "%s: %i\n" % (key, stats[key]) infosys_output += "-\n" infosys_output += "\n" try: output_fh = os.popen(("%s -x -c -h %s -p %s -D o=infosys -y %s" " >/dev/null") % ( command, config['BDII_HOSTNAME'], config['BDII_PORT'], config['BDII_PASSWD_FILE']['o=infosys']), 'w') output_fh.write(infosys_output) output_fh.close() except (IOError, KeyError): log.error("Could not add stats entries to the database.") old_ldif = None new_ldif = None new_dns = None ldif_delete = None ldif_add = None ldif_modify = None log.info("Sleeping for %i seconds" % int(config['BDII_BREATHE_TIME'])) time.sleep(config['BDII_BREATHE_TIME']) if __name__ == '__main__': config = parse_options() config = get_config(config) if config['BDII_DAEMON']: create_daemon(config['BDII_LOG_FILE']) # Giving some time for the init.d script to finish time.sleep(3) else: # connect stderr to log file e = os.open(config['BDII_LOG_FILE'], os.O_WRONLY | os.O_APPEND | os.O_CREAT, 0o644) os.dup2(e, 2) os.close(e) sys.stderr = os.fdopen(2, 'a', 0) log = get_logger(config['BDII_LOG_FILE'], config['BDII_LOG_LEVEL']) main(config, log) bdii-5.2.25/codemeta.json000066400000000000000000000026221335470071400151760ustar00rootroot00000000000000{ "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "Code", "name": "Berkeley Database Information Index (BDII)", "description": "The BDII implementation of the information", "provider": { "@type": "Organization", "name": "", "url": "" }, "maintainer": [ { "@type": "Person", "@id": "https://orcid.org/0000-0002-5686-3193", "name": " Baptiste", "familyName": "Grenier", "affiliation": { "@type": "Organization", "name": "EGI Foundation", "url": "http://www.egi.eu" } }, { "@type": "Role", "roleName": "Security and Vulnerability", "url": "" } ], "operatingSystem": [ "LINUX-2.4", "centos 6", "centos 7" ], "installUrl": "https://github.com/EGI-Foundation/glite-info-update-endpoints/releases", "buildInstructions": "http://gridinfo-documentation.readthedocs.io/", "releaseNotes": "", "codeRepository": "https://github.com/EGI-Foundation/bdii", "contIntegration": "https://travis-ci.org/EGI-Foundation/bdii", "networkStack": "", "supportUnit": { "@type": "contactPoint", "contactType": "GGUS", "name": "Information System Development" }, "developmentStatus": "supported", "author": [], "citation": "", "dateCreated": "", "dateModified": "", "keywords": [ "grid", "information", "index" ], "license": "Apache-2.0" } bdii-5.2.25/debian/000077500000000000000000000000001335470071400137425ustar00rootroot00000000000000bdii-5.2.25/debian/README.source000066400000000000000000000002061335470071400161170ustar00rootroot00000000000000The source is a svn snapshot downloaded using the following URL: http://svnweb.cern.ch/world/wsvn/gridinfo/bdii/trunk/?op=dl&rev=443 bdii-5.2.25/debian/bdii.default000066400000000000000000000000111335470071400162070ustar00rootroot00000000000000RUN="no" bdii-5.2.25/debian/bdii.postinst000066400000000000000000000003141335470071400164540ustar00rootroot00000000000000#!/bin/sh set -e sed "s/\(rootpw *\)secret/\1$(mkpasswd -s 0 | tr '/' 'x')/" -i /etc/bdii/bdii-slapd.conf chown -R openldap:openldap /var/lib/bdii chown -R openldap:openldap /var/log/bdii #DEBHELPER# bdii-5.2.25/debian/changelog000066400000000000000000000020331335470071400156120ustar00rootroot00000000000000bdii (5.2.24-1) UNRELEASED; urgency=low * Fix #3: init script failing on stale PID (Paolo Andreetto) * Update build, documetation and link to new GitHub repository (Baptiste Grenier) -- Baptiste Grenier Mon, 27 Aug 2018 18:44:00 +0100 bdii (5.2.23-1) UNRELEASED; urgency=low * New upstream release -- Baptiste Grenier Wed, 07 Jun 2017 15:15:00 +0100 bdii (5.2.10-1) UNRELEASED; urgency=low * New upstream release -- Andrew Elwell Thu, 15 Mar 2012 12:00:00 +0100 bdii (5.0.8+443-1) UNRELEASED; urgency=low * Updated packaging etc - svn revision 443 -- Daniel Johansson Thu, 25 Feb 2010 16:58:51 +0100 bdii (5.0.8+436-1) UNRELEASED; urgency=low * Update - svn revision 436 -- Mattias Ellert Wed, 24 Feb 2010 09:07:50 +0100 bdii (5.0.8+375-1) UNRELEASED; urgency=low * Initial release - svn revision 375 -- Mattias Ellert Mon, 08 Feb 2010 16:27:40 +0100 bdii-5.2.25/debian/compat000066400000000000000000000000021335470071400151400ustar00rootroot000000000000008 bdii-5.2.25/debian/control000066400000000000000000000016651335470071400153550ustar00rootroot00000000000000Source: bdii Maintainer: Baptiste Grenier Section: net Priority: optional Build-Depends: debhelper (>= 5), dh-python, python-all (>= 2.7), python-all-dev (>= 2.7) Standards-Version: 3.9.3 Homepage: https://twiki.cern.ch/twiki/bin/view/EGEE/BDII Vcs-Browser: http://svnweb.cern.ch/world/wsvn/gridinfo/bdii/ Vcs-Svn: https://svn.cern.ch/reps/gridinfo/bdii/ DM-Upload-Allowed: yes Package: bdii Architecture: all Depends: slapd, ldap-utils, glue-schema, whois, ${misc:Depends}, ${python:Depends} Suggests: logrotate Description: The Berkeley Database Information Index (BDII) The Berkeley Database Information Index (BDII) consists of a standard LDAP database which is updated by an external process. The update process obtains LDIF from a number of sources and merges them. It then compares this to the contents of the database and creates an LDIF file of the differences. This is then used to update the database. bdii-5.2.25/debian/copyright000066400000000000000000000014711335470071400157000ustar00rootroot00000000000000Name: bdii Copyright: © 2004, Members of the EGEE Collaboration as defined on http://www.eu-egee.org/partners License: Apache-2.0 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. On Debian and Ubuntu systems the Apache license is available on '/usr/share/common-licenses/Apache-2.0'. bdii-5.2.25/debian/patches/000077500000000000000000000000001335470071400153715ustar00rootroot00000000000000bdii-5.2.25/debian/patches/series000066400000000000000000000000221335470071400166000ustar00rootroot00000000000000#Add patches here bdii-5.2.25/debian/rules000077500000000000000000000007021335470071400150210ustar00rootroot00000000000000#!/usr/bin/make -f %: dh $@ override_dh_auto_install: $(MAKE) prefix=debian/bdii install slapd_modulepath="modulepath /usr/lib/ldap" ; \ slapd_moduleload="moduleload back_hdb" ; \ sed -e "/allow bind_v2/i$${slapd_modulepath}\n$${slapd_moduleload}" \ -e "s!etc/openldap/schema!etc/ldap/schema!" \ -i debian/bdii/etc/bdii/bdii-slapd.conf ; \ sed "s/BDII_USER=.*/BDII_USER=openldap/" \ -i debian/bdii/etc/bdii/bdii.conf bdii-5.2.25/docs/000077500000000000000000000000001335470071400134505ustar00rootroot00000000000000bdii-5.2.25/docs/Makefile000066400000000000000000000011311335470071400151040ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXPROJ = BDII SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)bdii-5.2.25/docs/conf.py000066400000000000000000000112461335470071400147530ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # BDII documentation build configuration file, created by # sphinx-quickstart on Wed Dec 21 17:13:25 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. # # import os # import sys # 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 = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'BDII' copyright = '2016, Laurence Field, Maria Alandes Pradillo' author = 'Laurence Field, Maria Alandes Pradillo' # 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. version = '5.2.23' # The full version, including alpha/beta/rc tags. release = '5.2.23-1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = 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 = 'alabaster' # 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 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'] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'BDIIdoc' # -- 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': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # 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 = [ (master_doc, 'BDII.tex', 'BDII Documentation', 'Laurence Field, Maria Alandes Pradillo', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'bdii', 'BDII Documentation', [author], 1) ] # -- 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 = [ (master_doc, 'BDII', 'BDII Documentation', author, 'BDII', 'One line description of project.', 'Miscellaneous'), ] bdii-5.2.25/docs/index.rst000066400000000000000000000007171335470071400153160ustar00rootroot00000000000000.. BDII documentation master file, created by sphinx-quickstart on Wed Dec 21 17:13:25 2016. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to BDII's documentation! ================================ .. toctree:: :maxdepth: 2 :caption: Contents: source/intro source/metadata Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` bdii-5.2.25/docs/source/000077500000000000000000000000001335470071400147505ustar00rootroot00000000000000bdii-5.2.25/docs/source/images/000077500000000000000000000000001335470071400162155ustar00rootroot00000000000000bdii-5.2.25/docs/source/images/InfoSysStructure.jpg000066400000000000000000001443571335470071400222500ustar00rootroot00000000000000ÿØÿàJFIF``ÿÛC    $ &%# #"(-90(*6+"#2D26;=@@@&0FKE>J9?@=ÿÛC  =)#)==================================================ÿÀÐÀ"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?öj(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¤f ¥˜€R{PÑXWþ3ÐôâVKä‘ÇðÃóŸÌqúÖÏÅ nE†›q7¡‘‚Ó5¤iN["H­ÙÝQ^k/Ä}bLýŸOµŒ¶Y¿¨ª­ãŸ¶pÖ«ŸHÇ¢ÃTd:ð=RŠòøLüKÿ?pÿߥÿ røÛÄ‹Ö{wúÄ)ýV ¾±Õ¨¯1‡â»Ö¶sP¤ý úUû‰ì¤ Ý%Ôwh¤ÏèGõ¨xz‹¡J´S¿¢¹›/ˆ:á ÓÉlÇ´ÉÔdWCosÜBKi£š3Ñ£`Ãó›‹ŽèÑI=‰h¢Š‘…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@T3]Áoþ¶ESéßò©œãÍ'd4›vDÔUQs4àxp§£Èp?!Í#ÂÅK]\£¨S±>¿­eíù•à›óÙ~?¥Êä¶ìš[˜aâIN§ò¨¾Ñ4¿ê-ÈÞ”í—Z‚;»XÉ[fèLKÇâÇ֤ơ?VŠÕ}çoðþu—´œöø¿‹Óò/‘Gu÷ÿ–ãþÓ,ëàaþÔ0ÿ–+ˆ§ÿU"·°<þU_:„DWJ=?vßáüª9.¬å`·‘4vóWoäÝ?Z=¤á»ÿÀ´üVŸƒDö_wùnhÑUV¶¹,§$ùüzÐ×R@ ¹‡ :ºË­kíÔWï_Šû×ëb9/³-QQCs ÀýÔŠÞïåRÖ±œf¹¢î‰i­QEB (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (®3â¹5¬:e£–ì#ÈN˜ü5P‹œ¬‰””UØx‡â6Rµ¦‹wr8iú´?‡_åï\Eý¾ýNöIQ8QôŠŠVÚ£ê}iõéÓ¡­)¥´QýÔõ<Ô”Q[QLŠ( Š( ¼I'Þ@ l)5”¢k™måш©(¤Ò{6Ž—Fø‡wfé¹à\F>aõè~µè6·PÞÛ%Å´«,.2®§ ׌²†0È=lø'X—G×£°f&Îñ¶…?Âýˆýåé\Uðé.hT«¶ùdzQ\'XQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEV~§{ª~úÚI†;&@ükB³õ;BÝ?ÐlÖ~>ñ~Ÿðÿc_øoü¯øRWšÿ;~%= ܆Kk˜í×þyFåˆúæ™+GevðÅy+•YË0ÿ€öª–ï§ÜܪÍ,Mÿ,Þ?%GÓüI­+}*[<Ë¥\ÄÈÜí‘A ÿäBƒq\‹îwÿÉ^‡|íï?ëÔ|Ú¼¢E†ÞÝ#$}û†Ú«ø•L¶\€×wòH§~í?1Éüê'Ô%vêZ{…îñëÿÖ¢±¹;¬.ŒN{#즺cR|ÖmKËásј5Ê´VóßñèHöw€µµû*à¹ù×óá‡çQC«¼›âžÔ9¬-½[ó¢a§Ú¶ëÛ“4ƒ³6Oýò)SP¸™véÖ'g—ä_˽¥Nk&£ä½ç÷l+«µ=¿¥[B—·DÙ6Pþ)Ò•yÛwï±U ÒÜü°)Uîæ­i0,"ÑÂòMÜy>¿:ÔTw‹4‚´‘í´QExç¤QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQU¬‚›™ã‹wMǤÚJìi9;"I Šá6OH¾Ž ŠÌÀŒ^Âiìßþ™9Ú~ Ö¤r$Ѭ‘ºº0Èe9ŸQ*pž­³§¢f6ýnËï$ñŽê|·ÿ ¯%ö‘w&Ëûg´œÿÏT(1ýk¡ª÷j–äÞ˜„=üÌcõ¬§CMî¼õ_æk ɽc¯–Ÿð xµ2ÙÊi–r]Ì;Ä„þlj|kw½LŸOÞIþ¥g-´¶àÙ´màyxÀö©XÒDC¾v©<œuÅ(Q÷~+/-ùþ!*¶zG_=_ù~\~µ.$¼y¯$õ™É•jE p H£TAÑT`Sè­áNøQŒêÎ (¤$($œÔ𲢫[j6—nÉos¬½B¶MLÒÆ’$lêóµIäã®*T“WL§'f‡ÑL2¢Ê±³¨wª“ɯó§Õ\¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯1ø‰ÿ#}§ýzý ëÓ«Ì~"Èßiÿ^ƒÿBzß üDc_à0j9-¢—–QŸQÅIEz‡žUû$‘ÿ©˜èhÝwUW¢­QJùWír/ß·aî(ûzޱ¸ü*ÕY÷ ¢¯ö„_ÝÈoŒôG?…Z¢‹0º*ý±ÜÍmÓýØBûšµEar¯‘q'úɰ=Ÿ”IÉûU=ì‚ìÀÀéRiÿò2èÿõ÷þ†µI§ÿÈË£ÿ×ÜúÔÔøYPø‘íQExǦQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE<ÑØk³\^‚"’X¥ØX. ܼt'ƒ[4TNÖ·BéÏ–÷êr3[Ï$ö¶WÌêŠJ¼c r:K›Ó¸–i#µ>fÉ™ð~]Ä ç=k¤{x¤ž9™s$@„lôÏ_åR×2µuÍýiýXéx¤ìù­«œÑ7³ÁtÓ\\+Ãf®ž^Ss|Üã׎•wS+&Ÿe-Áš6VWó£MÞSmêÃÒ¶(­ &¯¹v¶9”¹œÂÄ’-ÈónàŒÆd]¼’:õÀ$S¥¸¹a%Ô«‰¼³*ýî>£®3]%?Wv·0þ²¯~S’¢ÚS Ô¬ ǻثnÈàõÈ«W/ukssšçì‹,EäÉfD îÁëŒô®ŽŠK Òø¿­Ìo›ø­?Èænn%Û‚âãì%ß3J̼㸠Ûzà÷«“ÅwuáWŒ³K;'\. úrV¶¨«X}î÷V%â6²ÙÜæç¾I®íšÊ5(ˆÉŸ!• ;OéÛ§µ öØa°–)®&šhØ9Èݰ1Ûšé(¥õw{¹Zþ°’IGú×üÎwMe—S±tšâf?šeÜv¿ô>Þº*(­)SöjÆUj{G{QZ™Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@™o{¨]Û¤ðÙÚùr ˺éǸòÍKæêŸóçeÿmÿÆê¹Y<È»‘œdg®+̾"Èßiÿ^ƒÿBzí5rñ ®­¬¡1ýÙ…ë+'Ðù§zóÏMy?ˆ-ZýBÈ-ð„7¦_ r9ÏaÓ¥taái&cZ^íŠQEzGQEQVáÒïî"Y`²¹’6èé*¢ ÄâmÛ|½§v}1ëJèvdtUÉ4BÚIl.‘e™¡`÷8ªðÁ-Ä¢8#y$=’EÓ 2:*E‚W‰åH£Ü)!sÓ'µG@‚Š(¦Riÿò2èÿõ÷þ†µDdº‡TÓä°'ºYОÐí¹p íÍEO…—‰ÞΨf $ã“À§W¥Ëq©.® P×!ù沺˜À¶'±Ž=§¿GäžÌEu77ŒsB¶ÖÍæ³\ì({àm9ý+Æ=2õIçÔ¢±%”-fq™ÍÆqýͿֈgÔPxæ²…-vÌ.73z|›F?:»EPµ¹Ô¤3ýªÂBÝm¹ßæCò¿­$7Z™²šK:$¸Sû¸c¹ÜþTb€4(¬óy¨g †™›’Ø6ÿh^®î”\^jÚA$:g›3ÞEö…_/ñ<Т¨ÜÝßE4+o§yèàyçªù~¼´¯uzºŠÂ–­N3qç(ÇÝë@hª1]_> ðɧùvÃ;n<å;½>^´–·×“<Âm.{uŒŒòÆÞg°Ã~8  ôV|÷²ÙÍ,šLñJŸr–2Ò}lÄŠ>ß{ýçÿdÏçîÇÙ¼Ø÷c×;¶þ´¡EgÏ{œ2ǤÏ,¯÷áYc Ô–Áü ¥º½¼…àXt¹î@ ²Ëù~Ç,3øf€/ÑT¤¼ºME M6i 8ÍÈ’0«øÝúQõÄš‹Û6—yKœ]3Cå·Ð/ù¨  ´U ]BæwdÒomÄ`•i^%ö]²ÿ}b’ Jê[9¦}þ#û°;À^_÷JÈWó"€4(¬ÿí+¯ìï´ÿc_ù»¶ý—|f=sælÇü >ÔO©]Eg É£_Í$ŸzxÅþñiþDÐ…BëP¹àXô›Û š'„½›t€ÿß9§I¨I¢–£O¼tl¤(O-~¿6J»ERP’MEíNŸxˆ¹ÿH`ž[}>lþ”Û]JK‡[M½€D (LIì¸cúâ€/Öv¹¬aéÿjþÏÔ/þpžMŒ>lœ÷Û‘Ç.Ÿ~÷Èí%ݦÒAo¦Ö5r€8X¾*[Os5¼>ñL“Á:4ÓÁhò27 ù3]´ùöñ˱ãÞ¡¶H0Ë‘œØ×!჈Þ4?íYÿè£Yš.‘¨xòÂ]róĺ͂Ï,‰km§ÏäÇjì£pæn2Oá@Ey}‰~ÞK5ó NÇRMuÉæm™Fì)AÁêLj4½_ÁÖÖþ!Oj—ÓÇqÝÛÜ8û<Êì‚F¯q@Öµ¨ÿdh—Ú•æý’›ËÝ·vÕ'ÁÇOJv‘ý«£Y_ù~WÚ I¶nÝ·rƒŒñžµCÆŸò$kŸõá7þ€kŒÖµûÍ7ÁžÓ¬¿´ûFÖ5™ôè„—>ZĤˆÁèÇ?{¶  N¢¼ÇÁ÷z¢ø™lbÆ ¤Ý[¸šMvùJH:ddcŽI"ñEþðïS³šy¤×lnN˜Ží™$‘Û¾O'ålçý“@•HÇj“è3^eâÍNÿM½Ò|2³ø†hRÇκŸI_6òfhù‰ÊŒŒ“ß Ußßê§RÔlg‡ÄGK‰må× +2¾pË¿£„z0§Âzÿü$þ´Õ¾Ïöo´nýÖýûpÅzàg§¥ej_­¬5ËÍ*]Ô'³Ùç5 •r†îÏCÜv4Ï…òM´¯¤¿ú5ëøI¿ádx³þ¯ì|nµó¿´<ßùåòíÙøç>ÔØYx»LÔ<-6¿lÒµ¤¼’¡LH…YJŸâõǾ+ >*éâî®ô?YØ8Rog°Ä*­Ñ‹<Ž€õ¬}&O³ü?ñž™wÇ«[-Ä—äÈe’HÉó€0¤8®ß¨’x'GI26ŸeaG–2 .­â?IðÓk¬Ïsb2µ¶¸f É©ê[ÝzÚÇ\Ó4©Rc>¢%12µ|µ wç¡ã×—L> kq[œÚEª2[`äybáqƒéÖ»ÉKðoû—Ÿú,PgEPEPEPEPEU¿Ô­4»s=õÄpF;¹ëìR~•ÊÉãkýVW‡ÃZL— yóp ý8ñ?…T`å±.I¥C=ݽ·ü|O_ï¸_ç\ˆð߉µ_›U×M²·X­‡oCŒçSAðÛGOšáî®\òKÉŒþB«–+v.i=‘±/ŠtHNT´'ý™*€ø×@RAÔ¢ãÑXÿJ"ðNÂ鱟÷Ù›ùšœx[Dì»N=c~ïÌ=ÿ"ñž€ùƧ¡‡óni78òu+F'·œ þYÍBþÐßÒíxôL*©?€¼?>OØ|²{Ç+Ó8£÷~aïù:È¡£ee= œŠuq²|:·ÌšN©{e!î ~X?­0Ãã= îŽhuhð·ßÇèSG$^Ì9šÝ­Ëiž<²¸œÚê‘I¦]ƒ‚“ýÜýxÇâu Á”2Aw©”\w)I=…¢Š*FQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQÑä iÿ\…^ª:/ü­?ë«ÕRø˜£²"{Xež9¤ZHþá<íú{û×›|Dÿ‘¾Óþ½þ„õéÕæ??äo´ÿ¯Aÿ¡=m†þ"2¯ð3Š(¯PóŠ( Â8n¥ðΓö]V?]ÞeÁ‹ÍÛqýiº sã--ÿ©qÆ'ÁûãsUiZ†…§[ÝjŸešØI¹~ÎïÍž£éI>¯d5"+wv´ÓØfg\ù'qÅr(»¿ŸC¡µo¸Žû^Ô¬µû¦Šör±ÎàFîY1¸ñ´ñŠÖ‚£ñÕ¤°F#[˜<탠,šÏ¹ƒ@“Qšö]ZI‘äi ¼v̬Ù9ÀcÅ6Ï^†o¦¡uˆ-ÕY'bí!GÚº÷W@NÏWÔ‹Mÿ‘[[ÿzýÖkXÞÁª[I&&œÅå®Í†Éæ²kx-_¯èŒ¤ôAEU&Ÿÿ#.ÿ_qÿèkQÔšüŒº?ý}Çÿ¡­EO…—‰½u¦Ú^Ï×#ÍnÛâ“£!ö#œã¡ïV¨¢¼cÓ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€1´¿ÿføZÕ~Óæi˜O•åãÊòÓo\óž½`·€µ[ ®SÃ~)¸Ò¬.]¤kV´IÂ3»c Žzλz(ÎüqáûMádše“M勈KJ홚U%ËwbOZÐÀš…Åí¨Ö|Oy©i–r¬ÐZI#Sòy’dÇ|ŽO5ÚQ@u­;ûcD¾Ó¼ß+íp<>fÝÛw)ÆFzúÖ6¡àµ½ðö“c¡5­þ’ˆ-o¡\2º¦Ü•'•8åsø×OEsº­XêëZñ4ú®Ô)+l–ñŒ‘’Ás¸ð0ON}j­×"¹ñÔ^!ûk,JRI,ü¼¬’¢²¤…³Ø7Lv®²Šç|IáY5›Û]KMÔæÒµ[U1ÇsbEd=UÐðÃÓÐóRè&©§µÌºÆ¿>­<ÊsÃj3ÑÙ''¸Àí[´P?„ôøF<7i¤ý£í?gÝûÝ›7e‹tÉÇ_ZĽð^¶¶—â;¿ JšO‰cqü°]¨,¥}ýGê;Ším® ¼fµ™&‰º:6A¦ÝÙÛßÛ´pÇ4MÕ]r+•ŸÀ’ØÎ×Ôæ±sɉ‰(O¨4ïï£z>hìh®(k^/Ò~[ý!/Ñ奿Vÿ¾sÿ Š–?‰:r7—}g{k(꬀úƒúRöRé¨{EÔì(®r/xz\fø¡=ž'ÿ U„ñž€ùƧ æ*y%Ø|ñîmÑXMã]IR‹#ÑXÿJ©?ÄM/¹q,ßîBß×ýœŸ@çs¨¢¸³ñKÓ·FÑ/nÏ÷˜`ûç?ÎÅã=pâI Ò-Ï÷\Ã'?ˆ§ìÚø´:é©Ðë"Ó´(‹^Ü(|eb^]¾ƒúž+€ñÕLºe›ÜÀ¹üñUßÂzã:]·‰å[Us>âå]Œ•𶈘ƕiǬ@ÿ:·“§Û¨±µ‹ýÈU«tRæo¨Y Ò–Š) +Ÿñ×ü‰º‡Ñ?ô5®‚¹ÿÈ›¨}ÿCZ¨|H™ü,¿áïù´¿úô‹ÿ@£YÞÿ‘kKÿ¯H¿ôZ4¥»Ø(¢ŠC (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€(è¿ò´ÿ®B¯Uþ@ÖŸõÈUê©|LQÙ!‚È4´TŒáõÿ‡‰<¯w¢H¶óZáééôéô®*ò í*_+S³–蕾‡¡ü+Ûi²F’¡I]Uak¢ž&PÑêc:‘âIÙ«*¯qdçñ$*—ˆ¦º”¨Mô9V‘Pe˜©¨’á§”Ei “ÊßuQI&½ËáÆ‰lÁ¦Ý0ÿž¯ù.+£´°µ°Ë³¶Šôçò¬e‹_eG ú³Ï4o‡÷Ú‹,ÚË›[~¾J¾½‡óúW¢YY[éÖ©miÅ *¨©è®IÔ”Þ§L ¡°QE™aEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\wä`ð¿ý~ý :ìkŽñ¯üŒÿ¯Ñÿ¡GZRøˆ©ðeêÞ&Ò4&UÔ¯£…Û¢`³c×j‚@ã­i“€O¥qÞ0ÅáûŸÉm5ÕýÔ’<¦4ß)ðG§ŠQŠjìrodtGˆ4Íy$m2í'œ8ÁVãÞ«j^2д‹£m{¨Æ“/ÞEFr¾Çh8>Æ©Ûø–Úú-^k]:êÓP´¶Þÿj€Fì0Å{’G¯­Oà‹(-|-g4j ·Q‰¦òÒ;rI=úÕ8¥«“z#^ÃQ´Õ-æÆâ9ánŒ‡ô>‡ØóYº—Œ´-"èÛ^ê1¤Ë÷‘Qœ¯±Ú±¬d#DñGˆOEHΞ.Ìj>Q(Ï8ížµ©à›(-|/g,@n£M/V‘Û’IêzÐã«ØJMèlÙ^Ûê6‘ÝY̳A ʺžÿ_Ú²µèZMô–w×ÞUÄxÜžS¶229 GC[Áºl†42[j($äž=MsÚüŽž&ÿzÛÿEš˜¤îÙM½ 3V±Ö-…ÆsÄ]ÊžTúÔcYW>=ðí¥Ô¶óê;%‰Ê:ù08#!qU¥´‡Lø‡döh"þз—íƒ ÅpCë“Ö±ô«íf×íôÝ oâ“P¸Ì­rŠ0PòGO®jÔ"õ!ÍìwÐÍÌ)4,‘8 ®‡!‡¨5‡ã¯ùµ¢èkKà†·´ŠÖI\Cº7óSc«î%”¯8Á>´ž:ÿ‘7Pú'þ†µ1V¨—™MÞ¡E¨ŸéÆ+«EŒÚŵZÙ˜´c'Ìü…_òµOùü²ÿÀFÿã”Ïȵ¥ÿפ_ú­R“»бGÊÕ?çòËÿÿŽQåjŸóùeÿ€ÿÇ*õ¹˜ùQGÊÕ?çòËÿÿŽQåjŸóùeÿ€ÿÇ*õs0åE+îk˜nš'hY@hР ¨= ?ήÕ?ù êï'þ€*õÜ#°QE# (¢€+j¼6lñ¶ÖÜ Œ°þµö‹¯ùú“þùOþ&¯jŸñàßï§þ†+>¹+7Ï¿Oó3žã¾Ñuÿ?Rß)ÿÄÑö‹¯ùú“þùOþ&›UuF×I°–öú_*Ú—}¥°3Ž€Þ²¼»¿½“©sí_óõ'ýòŸüMhºÿŸ©?ï”ÿâk‘ÿ…£á/ú ä´ßüMtz~¡mªXC{e'›o2îGÚW#èpi¾u»ˆj[ûE×üýIÿ|§ÿGÚ.¿çêOûå?øšm®û¿½…Øï´]ÏÔŸ÷Êñ4}¢ëþ~¤ÿ¾Sÿ‰¦ÑEßw÷°»ö‹¯ùú“þùOþ&´]ÏÔŸ÷Êñ4Ú(»îþöc¾Ñuÿ?Rß)ÿÄÑö‹¯ùú“þùOþ&›E}ßÞÂìwÚ.¿çêOûå?øš>Ñuÿ?Rß)ÿÄÕ GW²Ò°½›Ê7S!ùY·9è8SÅ]¢òîþöŽûE×üýIÿ|§ÿGÚ.¿çêOûå?øšÍ´Ömï5›ý64”Ob#23´ïç=½hQyw{ G}¢ëþ~¤ÿ¾Sÿ‰£í_óõ'ýòŸüM6Š.û¿½…Øï´]ÏÔŸ÷Êñ4}¢ëþ~¤ÿ¾Sÿ‰¦ÑEßw÷°»,ØÜN×¢9&iÆÍ† 0A_@=kN²l?ä"¿õÉÿšÖµuPmÇSHlQElQGEÿ5§ýrz¨è¿ò´ÿ®B¯UKâbŽÈ(¢Š‘…Q@Q@Q@eÿõ§ý{Mÿ¡EWªŒ¿ò´ÿ¯i¿ô(ªõSÙ nŠ(©QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWã_ùöîãQ¸ûeõö>Ñ&Ý«€0G ÏãY¶Ú/‰4kMïMŸO´I|®’v‚½G¹®¶Š9Ør¢ž—ì6º•Â\]³¼iµFNvè:dòqX3i>"³ñ¥£¾”a½òÉfMÃbãøGÖºª))4Ø8ÜÁÑ´¨5)umbé.µËQmŽÎJ¦y<÷<Öm®‘â­*}At×ÑM½ÕÜ— g2—°Ç@+°¢Ÿ;Deø{EþÃÓL9¸žYi¥+·{±äãµSñ×ü‰º‡Ñ?ô5®‚¹ÿÈ›¨}ÿCZ"Ûšo¸IZ-ü=ÿ"Ö—ÿ^‘è´k;Ãßò-iõéþ€+F¦[±­‚Š(¤0¢Š(uí5kõhndË'1@îÈ;SmÃÿ>Ú‡þËÿÄÔ–?ñý©×uÿÑIW«I8ö!&fÿmÃÿ>Ú‡þËÿÄÑý·üûjø/ÿZTTÞ=‡gÜÍþÛ‡þ}µü—ÿ‰¨/u%¹„,#T·‘Nåt³óî àjÙª×ÖÒÝÀ"ŠæKpOÌѸ¯ '§Öšq¸š•Œâ#v_NºÒè20q*°Ü9!†WñãÞ®U‰ôûm?Kxíb ˆXõf;Ç$õ&«×%ÅÔ÷WOó3³[…Q\àr‹ÿ%iÿì ?ôuBnuÿjú„zF«“c§Ìm·‹ešI¤Èn±ZÃE¸8mg|_f:xµÛ“¿w™»8Æ1zÌ›Añ‘«ÞÝxjëNkkù<émïÕñ˜*S“Ÿ~•¢h¢¼C«CáÏA}$Úº:01 @Sr>ÓÀ>£¥Uº¸ñ…·‡×ÄGW³dޏm=m†Æ9üÛˆäàžœU›Ý mÀþ"¸¿¹ZôKs*®ÕÎÌQè:Z­k¡ø«Uðݦ™.§§ &{xÃΰ°¹òʃ³oÜÿg>œõªVÜgkغҒî,¨–*ç¶W"¸}&_랇W‡\µ·ahàªÞ~3’ì@ÚI|£Çzîž·Óš—lqű@ìÀ¯;ð‹'ð]”:f¡¦­Ìl7Ï ­ÁbM¿+w ·sS„CÅzµî…á«ÍɆëS˜E$r.cÎÒ{àž9â­ê7zö‘m§é ©Ã{«jSº¥ä¶Â5†0¹c±x$vÏ^õf_ ´6þ¶±’?'IdÉ\ <’sV¼O ÜêÂÎïMº[]JÂC%»È»‘²0U‡¡ãš.ƒC(\x‡ÃZÖ›­ªÇ«Xê}œ±µX •#osŸJ±¦x’[H¼C±7™>“#ɸ¨RБº>€ õÛm ^Õ5{;Ï\éâó`¶°WÚò`Ì_ž;ÿëgŠ<u­ë–÷V“ìѬ„nH2Ʋp1žçSÑèÃB»ëݧ‡ü3=Õ×ú^£¨B·ºP<¹7˜ÇëïVµ+ÍkXñEÆ‘¢ê1i°ØÂ’OpmÄÎÎù€ÜcúÖˆôIõs¤}•¢E²¿ŠåÃ’2‹œ€yçÚ©jº³oâ)uŸ ÜX¬·Q,W0^«lm¿u^sŽ1I4_‡./4Ïx®ãY’ç¶‚w·R¢ETbB@3\óüEº6çR_Ú­ÇúÁ¤ 5ÌîyØÎqߦ{â»]Â÷ÐßëSëWpÝR$ÌK³V@Ç9$ã&ªYèþ3Òí#Ò쵬bTWSE'Ú;£ä$™ëŽj¯@Öujó[Ñ,ô;˜­“R´y]¥Œ8Œ|§pIœ„žkªÓ º¶°Š+ë¿¶\¨ùçò„{Îzí ͹Ñngñf™ª "0Ú[Ë€ä33c czÖåfÚ²±,(¢Š‘QÚß#K¿7j3w_@j÷öŧ¬ßø'ÿUì?ä"¿õÉÿšÖµvaíÊ\oböŧ¬ßø'ÿGöŧ¬ßø'ÿW¨­ïÅêRÑÕ“G´VR¬" 0GáWh¢“ww¬¬QE!…Q@Q@Q@eÿõ§ý{Mÿ¡EWªŒ¿ò´ÿ¯i¿ô(ªõSÙ nŠ(©QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWã_ùÕbº)AÅY—dQEhPQEQEQEQEQEF_ùZ×´ßúUz¨Ëÿ!ëOúö›ÿBНU=–ì(¢Š‘…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@eø‹C‹ÄL–r6ÆÎèßØÃ¡þŸjQM6Кº³8½ÅriS ÄÀÁqËÃ}ɶOõüðk²GY^6 ¬2NAWRÒlµ‹&þÝ&AÓ=WèzŠå„u Ùü5ª‘ 9û5Ç#ðã úÖžìü™ô|ÎÖŠâÇ‹õÍ3åÖ´ H^³[ä¯õ­[¶ø‹ Ì Ë,Ö縒"qÿ|æ—²—aóÄêh¬h¼]¡J2º¥¸ÿy¶ÿ:œx‹F ík}nüjyeØ®eÜÒ¢³Äš2 VÇðOò5VèùÝ©FÄÏ5gþBŽI>Ì»›´W!7Ä}8¿—akwy)è0õý*#©xÃ[8±ÓãÒá?òÒ¼?1ÿ²Õ{)uОuÓS¨Ôµ[-"ÜÍ}p§mÇ–ú§ð®5Vïâ§²Döú³îP܈ÿ?€ÏzÑÓüÚ>×®]I©]~rvê—µuh‹*Fª¨£T`Nñ‡Ã«œ·ØP€;RÑEdhQEQEQ±ÿíKþ»¯þŠJ½TlãûRÿ®ëÿ¢’¯UKqG`¢Š*FQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQI@¥ÿõ§ý{Mÿ¡EWªŒ¿ò´ÿ¯i¿ô(ªè9éTöB[±h¢Š‘…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@U¸Ó,nÉ76vÓÞH•¿˜«TQ{‹/ƒ´N[L€ör¿ÈÔÀ^$Ÿìî¿ôÚOþ*º*¹åÜžXö9ôð/‡“8Ó—ŸY\ÿ6«Px[D·Ç—¥Úœ~0ßÏ5­Eò}C–=ˆ ¶†Ù6ÛÃKèŠ~•-T”QEQEQEQEQ±ÿíKþ»¯þŠJ½TlãûRÿ®ëÿ¢’¯UKqG`¢Š*FQEQEQEQEQEQEQEQEQEQEQEQEQEQTõPžàŒ‚èÿ ™K–-‰»+—(®{ìÐÏÿï‘GÙ ÿž1ÿß"¹þ°û~?ðç}ކŠç¾Íüñþù}šùãýò(úÃíøÿÀwØèh®{ìÐÏÿï‘GÙ ÿž1ÿß"¬>ßüç}ކŠç¾Íüñþù}šùãýò(úÃíøÿÀwØèi `<kŸû4óÆ?ûäQöh?çŒ÷È£ë·ãÿ9ßb)$¸³ÕíôøƒÇo!£ Ÿ÷B·×ÖºxÖÞ8b".zþ5‡öX3Ÿ",ŽûfƒþxÇÿ|йb›KÝüàc¡¢¹ï³Aÿßü¹ßc¡¢¹ï³Aÿ°û~?ðö:+žû4óÆ?ûäQöh?çŒ÷È£ë·ãÿ9ßc¡¢¹ï³Aÿ°û~?ðö:+žû4óÆ?ûäUÝ!;‹€ŠlC€1ݪ¡]ÊJ6ÝJ(¢º (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€(ØÿÇö¥ÿ]×ÿE%^ª6?ñý©×uÿÑIWª¥¸£°QE# (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ §ªǃ¾Ÿú«•OTÿÿ}?ô1QWà— ¥³3袊á12tísíúþ­¦ýŸgöqˆy›óæo]Ý1Æ>¦µ«”ðïü¾-ÿz×ÿEš¯âKX¼Cã[ Pi³…£ÝIÈPNÛ€ã’Z¾]J±°ÚÍÀñ²hû"û3X›’Ø;÷oÛŒçǵmפèvºÄÕ¶±iűÒÙ’)%.±0|«ž@ïŒõ&¹½RÆ VÛPÕ´ßjê¦V]^çSòäV\üÊ™åWqÐc­W"acØ(¯=×m_þ 3\Í×W‡žÛ -ËŽ=jKÏYxCÄš΄&ï.¾ÍsœÎ&B¤– “ÈÆjyBÇ}Er¿3ÿtÛX©óàã÷‹XÞ$ðµ—…ìíµ½*K¥Õb¹ˆ<òNÎ÷[˜“ƒœö…‰#Ðè® ÿAµñÄ«ÛmA¦k5°‰ÞÝ$(²Çˆ àuÇ®)Òh¶~$ñ}î‘©y­¦i6ð-½˜™• e?9Á84r®ã±ÝÑ\ އ¶¹­øVÖI•q§‰’râÝØB“ÈÏÞª­©Ï¯øWFÐ%Ü/n.þÅz ò© Ì„‘êÿßTrŽì\Þÿlo° ù;Åßœ¿?sg^œç¥\'šäãUâ»* *è ;:²|9á«éÓkº¤—O©Í<¾\épèö¡X…TÁÀÇ^AëG*Ü,u>Ög×¼;ýÒD’ÈΤD_•ÊŽ¤öµ^Wo{yað›M‹O2™n¯M±)(Èi_ 9áIÆ3ïSèžÕtߨ\iž“F„I¶í†ª³¬±ž ©9ã¨ÅS‚Ô,zmçÚo†luÿøõO6hmîÐÇn%d@ûÎv‘“øú× ÔIXL*ΗÿWî'ój­Vt¿øú¸ÿq?›S§ñ¯ë ãº4袊î5 (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ JZ¯wy ”k‡Úƒr}ï@æ¢[˜šCÊŒãøU†j¥®¥©‰nì’AVaï\ý¾uez—!†<Ň @ÁÏ=(sVÖ†˜Q~lŽ cvйÁ©ôÝDj6¾r©L6ÖSØý{ñÍQÔ †§§=×Í71¬;àû~ ÒhW°=„âVŒCó0VÞ\ã=qËqÓÜPqx­$¸UòÉb“pϱÇã[rÜGnšDAÆXàW7¢‹FþI>ɰ¦$\JY?úÞݺúTº¯•®ÓO¸Y%ƒqhÎWp8äÇ~´ÐCqè'WSÈe ƒRV6“lú5„Ïxàw§;Gùô©luëkëŸ%Dfå †ýhVŠ@ih¢Š(¢Š(¢Š(üj_õÝôRUê£cÿÚ—ýw_ý•zª[Š;QR0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ªz§üx7ûéÿ¡Š¹TõRžäœèI?ïŠÎ¯Á/F)lÌú*/´Áÿ=£ÿ¾…iƒþ{Gÿ} áæ]ÌnŽvÿÀÖ÷ºÅÖ¥±¬ØÍu·Í[;‘«´q·=sÜÔ·¾ ³¿±²†{ÝKíVYòoÖã#=røç=:VïÚ`ÿžÑÿßB´Áÿ=£ÿ¾…W´ó1‹¤x6ÃFÕ?´ašòk¶„Å,·ù.H;˜‘œðíTeøq¥ÊÓ'Û5D±™‹5‚]·ÉîëÏ^¾ÜWQö˜?ç´÷Уí0Ïhÿï¡G´ócñW†£ŸMðÞ‘½ÅÕ”‘Ç.$FŒ±\cëÅié^ ²Ó54Ô%½Ôµ ˜”¬-}peòAë·Œûæ·~Óüöþú}¦ùíýô(öšZáÌs߬¦Ô|#=µ½¼·òŘâRÌFõÏž”–~ÓmoíîëR¹ŠÕ·ÛZ\]†:SÏI®‹í0Ïhÿï¡GÚ`ÿžÑÿßBiedØ©o½>®¯/Ú&`e$l §#Ï>µG[ð–·{ïÚ/lo‘v}¦Êo*FOî“‚çÓ5³ö¨2ŸOmâ´Áÿ=£ÿ¾….{kq\Îм7eáøæû3O4ó¶éîndó%”öÜÞÞÕ—„ôûÝk™¾ÓrdfZÅF2 Ú3Ík}¦ùíýô(ûLóÚ?ûèQÏæ* Üxˆë;åûI¶û.Ü›wnÎ1œçÞ±çð™5üó¥Î£½Ë—¸²‚䥼ÄõÜ£ž{à×Gö˜?ç´÷Уí0Ïhÿï¡B©n¡s"iqødè2FóؒǸŸ `ž*¾™à«}:þ©u]büÛÑGyvdDlcpP8$~&·þÓüöþú}¦ùíýô(öžaÌTÓôk}6ÿP»…åi/äÊ‚/>¹­ ‹í0Ïhÿï¡GÚ`ÿžÑÿßB—2}BäµgKÿ«÷ùµQûLóÚ?ûèUÝ!ÖK‹‚ŒlAsݪé4ê/ë âõF¥Q]æÁEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE†€"¹ºŠÒ,òAԚΜÚx†Í£Šr66ìí ©ú29©õ[©Úy%ö2°u8ã#Ô~5[HÑΘòK4ªò0Æ ã@ µÒŸG¶¹ž7>YÚ í£ŒŸOZŽÒyµ >é5x¼¨TÇ—Žýý9÷¥‹M½Me®šçt˜ãyÉáqŒz~Ujqsq}äypɧ²í•‰ùçÐÐkmštAco%Ô'/qÀsŽp1ÀúS£{‹Y/c’{8”!x¥G@Hô÷=êHmDÿèñ'•aT¨!–àsÏ^*äzu¬q¬bÞ2« »ŽßBOQ@ââúÏN…´wsÊß8„€{äuúâ–ÎÎÊÎêqbC]¬g³ghôüñúzÕ¹´Ôä¶co+”.è2YW¢àñÒª–H%º±·‚ Eð%RáŠûq’ÿ"€#Ó^þýn Õ#a.2˰çÛÛßšu¦‹o¥HnåÜD¤‚ÃF9'ÔãÿÕRÞÛɪéKsÅædy›ÈÌ:¯Ó?Ò‹+DOm6{„•Ê0e ÈSéߊ’Ï\µ¼ºòcó ]péZ ×84ë-d½¿¿Ž4FÄf\'ÌAîO=ø¹eyô+5¬ÑÍtxÛp?ˆ§fÕì+«òܳERQEQEFÇþ?µ/úî¿ú)*õQ±ÿíKþ»¯þŠJ½U-Å‚Š(©QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEŸ?üŒ_õï?þ…hV|ÿò0Y×¼ÿúu¡U-‘+vQEIAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPMsÇãN¦¿4ÇG¦êcRV+(”8&oá#<œÿJèõ;¨Y4 û ôàã·Ò²µvñDÎÑĈd;œª€XúŸZó;¿Š·í)û´QŽ‚b]×ôçëUÿáik_óÃOÿ¿oÿÅÖËUô2xÊK©éÖ:}¾œŽ–ɵ]²A$óŒwªPè+ª×‚v ±p›qÉróïøZZÏüñÓÿïÛÿñtŸð´5“ÀƒOÏlFÿü]?ªUBúå3¨ñß…ï<@–²Ø:y–ûÇ–í·plr=øöêjçü=sáÝ2XnåF’iL›#$¬|€p3ОÕG]ñmö™á]'R‚+vžõc2V*3c´d£Œž•Àþ »ñqqz°«Ç9Œ”¨ÆÕ<‚O­KöžË]Š^ÏÚé¹ÓQEÎt…Q@Q@lãûRÿ®ëÿ¢’¯UøþÔ¿ëºÿ褫ÕRÜQØ(¢Š‘…Q@Q@Q@aëþ4Ð|/,Qk:ŒvòÊ7,aYÛ¥T¹ô5¹\>«§ëñ•ç‰tÍ9u{[È;˜ÂÜBø£ÏÞtrN>´ÐIâÍ/¦º×ÊtÇÆ'Tfêp>P3œñŒqÞ´^úÖ) †KˆR[Œù1»€Ò`dí“é\V·âèáYÝë^qhé*©S ††F•DŠèA¾cž½sïT|m¦ë7?|8lõß² ÌÂØ}$û1ç“óî÷é@•EyÏŒ|_uáÉ´Í ã_ŠÆâKo6çV’ÄÊI$JËsžéÍOð÷ÆRëz½ö•.¯´D³Å|–¦Ù˜†FBàãú€;ú(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€3çÿ‘‚Ëþ½çÿУ­ ÏŸþF /ú÷ŸÿBŽ´*¥²%nŠ(©((¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠŠêæ++I®nd0£HíŒá@É?•rgõRKK¨¼#v4k©Gyö¤i61Àsä„óÀæºû‹x®í¥·¸@ðÊ…OFR0EyýäZÇà ¹µ½Ÿ†¡‘Qí.WÈÌòÜ}þ[£tÜÐl.ïÿ· ©Ó¿â_äo¾zó&q³ËëÓœô¨ô}vÛ[{õ¶I”ØÝ=¬ž`.¸É'Ž{â¹¹`ŠóâõÄ.èfðöÇ\‘•3G¨àÖOÿèpjúÅôv8¹ÓuY µ:OÝ PÆì§““@›Exm¦›©ø¾Ú}^ë½ÔòÈ"¿ÒÁäáˆUHÿ„)žr{×­øUuHü1`šèa©$AgÜá‰#Œ’2 #¯EPEPEPEPEPEPEPEPEPEPEPEPL“„'9§Ó$û´<*Ùľ&g©½¸Ë¶z)9Ç=Âð? Ú¼ñ%Ž“}qeeáí?ʶ•¢ :nvÚH$’:dqÍVñ_‡î¼9«µÔ’Ѥó`8òÎs´žÄþ˜ïœL¾4‚Qæj^Óon3 RØîAS^Ä—´JQW^§Œ½Ë§£ÿ ªп¤ߪGñ¢2‘ÿþ3ë'ü&:_iøïÿ xÃKÿ¡CKü—ÿÒTßüûüEÏÞOÁÚuƧâ‹Y B"·˜M3(;cä/N èר®|F¼–5»“åÚĪŠr7õä~U­£üB yiao¢[ÚÃ,É"—70 ´õ/Ä_ Ïu*êÖ1YPGq œ ᱎz{ãõíWNª¶šû4è5}L‹‹› ÚXÀšUµíÕͺÜË5ÈÜ>lüª=±ÛÛ©5ü&Ñÿп¤ß¡Uôß{¬õ>ÏT‚D?hÁd€xãÓñâ­ÿÂa¥ù”4¿üwÿÓTßÚþfn¢èíò/ùèŸ÷Уϋþz'ýô*ì»ùò¶ÿ¿KþeØÏ•·ýú_ð£Ý Iüø¿ç¢ßB>/ùèŸ÷Ш?²ì?çÊÛþý/øQý—aÿ>Vß÷éÂt5'óâÿž‰ÿ} <ø¿ç¢ßB þ˰ÿŸ+oûô¿áGö]‡üù[ߥÿ =ÐÔŸÏ‹þz'ýô(óâÿž‰ÿ} ƒû.Ãþ|­¿ïÒÿ…Ùvóåmÿ~—ü(÷CR>/ùèŸ÷Уϋþz'ýô*ì»ùò¶ÿ¿KþeØÏ•·ýú_ð£Ý J—·¶öúÝ”’ϧ‘0Én3˜ÿ¬mißóùýõSÃemnåà·†&#Dãð©é·“(ÿmißóùýõGöÖÿ?ÿßUzŠWaêQþÚÓ¿çòûêí­;þ!ÿ¾ªõ^=ƒRöÖÿ?ÿßTmißóùýõW¨¢ñì”¶´ïùü‡þú£ûkNÿŸÈ諾E`Ô£ýµ§Ïä?÷ÕÛZwüþCÿ}Uê(¼{¥í­;þ!ÿ¾¨þÚÓ¿çòûê¯QEãØ5(ÿmißóùýõGöÖÿ?ÿßUzŠ/Á©GûkNÿŸÈïª?¶´ïùü‡þú«ÔQxö J?ÛZwüþCÿ}Qýµ§Ïä?÷Õ^¢‹Ç°jQþÚÓ¿çòû꟫c<«WQ3±Â¨nM[ª:—ß²ÿ¯•þMMr±;¢õQPPQEQE[P°ƒTÓî,®Ð½½Äf92¤`ò+˜´øk§Ãqo%ö­®jÛ°x­¯ïL«/Ým Ÿ—µvP`Ðm‡‰Î»¾oµO±ìÈÙ³~ìãÎ}ÿ ¡oà»KOË­Úßjp<Òf´Žãò¹\dÇ'¿^µÑQ@…ßË oî.l5]oI.dš >óʉÜõm¸<šéôû }/O‚ÊÍ [Û Ž5,Np9<š±EQEQEQEQEQEQEQEQEQEQEQEQE„f–ŠcC¡F@ÈF ‘ÁJÈè1c¤Y‚»_åZòJÆÒJꈣ,Ìp÷5Ì_üBÒíäòlVkùÉÀXWå'ê 5p翸DÔÄ^ÿ„3Ãÿô ¶ÿ¾iá ðÿý­¿ïšÆþÔñž§ÿš\ž9ù‡àOþËJ<5⛾oXt‡àmÃá-Þxæ‹L·I#`èÀr¬A­´ÕÇÿ¿–Cºã_Ô$~í“ýI¤ÿ…mgÿA+ÿûì…C³ÞE%m¢oÝxkF¾™¥¹Ómd•ŽYÌc-õ=êøC8Ó;YêH>€ãÿþ´ø> -¬¢ {M¹°—ûÛK)÷ì,Ðý¤—Ř%M}›|Ž—QÒ,uh’=BÚ;„FÜ¡ÇCŒZ4í&ÇI‰ãÓí£·Gm̨0 Àý:ÃR´Õ-ÄÖ7ϪŸQÔ~5j²æ’\·ÐÓ–-ó[P¢Š*J (¢€ (¢€(ØÿÇö¥ÿ]×ÿE%^ª6?ñý©×uÿÑIWª¥¸£°QE# (¢€ (¢€ (¨nã–[9ã·E3FË‘¬Gð45Å'‚õÆ@eñeê¹ê¹žñü«¥Ñ4ë/O÷WÒßH·&rAíÉ?ήQKgrSothQXZþƒ}«Ï ÙëW:z¢•e‹vç©Ã ̇Áº¼SÆíâ»çU`JøažŸ~…µ¸6ï±ØQEqóx7W–y|W|ŠÌHP 3ÓïÒŠOwa¶ÖÈì(¬-A¾Ò'™ï5«A]BªË» sÔe]ÖôëSO6ö·ÒØÈX7ríÁΆ•íp»¶Æ…ÆÂ¬ÐÝù?ÿ®§Mµ–ËO‚Þ{—¹’5ÃLùËŸS’Ilî$Ûݨ®s[ðÖ£ªjâ×_»±Œ¨La°ïÃåPé¾Õ,µ.'ñ-åÌq¶æ…Ãaǡ˟åO–6Ü.ï±ÔÑL™X$ErŒÊT0ê§kÿ„'Xÿ¡ºÿòþ9J)=ݶ¶GgTl?ãóSÿ¯•ÿÑ1Õ}H»Ò ™/5IõvZ\åF: ±ª:†‘w«Í|–z¤ú{%Ø,Ñg,<˜ø8aT’»WoGc¤¢¸ÏøBuú¯ÿ'ÿã•ØB£9vU Xõc޵2Ilî4ÛÝ¢¹mKÂz¥î¡=Ä%¼¶ŽFܰ l ôqüªmÃZŽ—¨ ‹­~îú0¤y2Á'¿.•>XÛp»¾ÇGEUÔ­e½Óç·‚åí¤‘p³&r‡Ô`ç\·ü!:Çý ×ÿ“ÿñÊQIîì µ²;:+?DÓ®4½<[Ý_K} bÞt™É·$ÿ:¥¯è7ڼ𽞵s§ª)VX·aŽzœ0¡%{\wvØÝ¢¸ø|«Å‘v.llü©€*Ívàõà’*ýËy“ï\Ôšh­âig‘#YÝ€{“Tá!Ñÿè-aÿ)þ5jòÎûI-®“|2®¹##ê9¬Oø@|;ÿ@ïü'ÿJ<¿h7CrÚêÞò/6Öx§Œœoà ýEGu©ØØ2­ååµ»0ʉeT${dÒiºe¦‘h-¬bò¡°]Ź=y$š¯ªøsLÖäŽMFÛÎxÆÕ>c.àE –þCÖÃ×_Ò‚®«bÌN ’:Ю~?ø~)DÓðèC)ó¤àøtK—ì‚¿S=µý!«j¶*Ààƒp™ó©­u;öe³¼¶¸eaªä|É“À¾–G‘ôü»’Ì|é9'þWt¯iš$’I§[y/ ÚÇÌfÈüI¦ù-¥Ä¹¯©væêÞÎ/6êx Œo‘ÂŒýMTÿ„‡Gÿ µ‡þ§øÔÚ–™i«Úkè¼ØI Wq^GNA²?áðïý¿ò<ŸüUå¶ ùºðÍÄK,$‘¸Êº0 b*µÎ¯§YËå]_ÚÁ Ù$ʧBj[;8l-#¶µMÄ6¢äœ©æ³õ/ èú½Ù¹¾³óf )o5×Ó€@¤¹o¨Ýí¡foK¸•bƒR³’G8TIÔ’}€4š£*6bEÊ’Ià ­T¬ü¡Ø]Çskc²hŽäo5Îж*æ¯Ê-#q”{€¬=AVª\¼ÚïmDÿ„‡Gÿ µ‡þ§øÕ›[ûKõf³º‚áTáŒRûâ±áðïý¿ò<ŸüUiéZ%†‰‘éÐy)!ÜÃ{6OâM'Ém.5ÍÔžêþÒÁU¯. ·V8S,>Ùªßðèÿô°ÿÀ”ÿv«¢XkqÇ£œ‘Ê7²àþVgü >ÿ wþG“ÿŠ¡r[[ƒæèt*ÊêHe# ƒÁJmoK·•¢ŸR³ŽD8dyÔ}Á5r8Ö(Ò4DTzX·ž Ðïî乺±ß4§s·šã'è£ËÔný m_N¼—ʵ¿µžB3²9•Ž>€Õ™¦ŠÞ&–y8Ðeع5—¦øWGÒ.ÅÍŸ•0Cy®Ü¼Eh^YÃi%µÒo†Qµ×$d}G4>[è öÔ«ÿ ÿAkü Oñ«v×V÷‘y¶³Å¢­×;àùì?í§þŒjè©ÉZM]ÒaETŒ(¢Š(¢Š(¢Š(¢Š(¢ ’@©4SÖ¨Ø.õBN¹ú&:n¡sa=«Å-õ¼Mœ«™TqÈ=z‚+ÇW¶¿¼¹ŠêâÞþвͺPÊÆŠI<ÊOÐZÒºl‰JÍm^-BÒ|yWPIž›d¬Ve…Q@Q@Q@Q@Q@çôK½{F†ÚÄ!‘.C½°0‡õÑÕ[ýJÓKf¾ ›`gèN ÇèjÕSnÉ %{…QR0¢Š(¢Š(¢Š( Ó_–ãÆWº)…vЉD€œ’Bqøé[µÇé°Ê¿õiLn#kUÊœˆ»þØUÍ%kv&-»Ü(¢Š‚‚Š( Š( Š( Š( Š* ‹Û[Oøù¹†úèáOY·“Çpm m—Œ@`j9|U¡ÄpÚ¥©?ìÈùW;«øŸL7QM¦ê0’ò)”2¶€@“§8‘ßµ§ÞÆs’Hìáž9Ìž[nòÜ£q‘Ö¥®zÏÅ~··H!Ô¢Úƒ«õ$‘Ôõ­5ý&çN¥fäö.,Ô8µÐ¥$úšR+©„RÔ”QEQEQEQEŸ§h–š]ÝíͰq%ìžd»› œ“ǧÞ5¡XZ¿.¯ªjö’B‘­„ÞR²’K Ì2„ítnÑE# (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ó ~[ëOˆ—·ºlfI-#I¤AÝ6¢·ð/ëÚ½>¸í?þJ¶©ÿ^Kÿ´«ZN×~Fuìnè~ ²×í¶r à~ò&ûÈ}Çõ­JåõoÚÝÜ›Ý.wÓ¯AÈxxR~ƒ§áúÕ­x«@ùuM5uuÿ–öÿ{§Ô \Š_ 3_ÛQ\½—Ä=ëYeµI£=~£"¶ Ö´Ë  …¬™ì³)?–j\$·CROf^¢‘X0H ÷µ%Uiõ;\ý¢îÞ,uó$ üÍY¢¹ûÏh6yùeaü0©ÔqúÖYñž««’žÑe‘O{Žü?ZµNO¡.qG]wwocnÓÝLÄ]ÎyOŒu«ÿ¥A¦•m'•°Æ÷#9ü‡áø×Qmà›½VánüQ¨=ËŽE¼g¾Ùÿ>´Ÿ­¡´ð¼ñ$Q%ÊDåjÖŸ,d’Õ™Îò‹èŽÖŠ(®s`¢Š(¢Š(üj_õÝôRUê£cÿÚ—ýw_ý•zª[Š;QR0¢Š(¢Š(¦M wI Ê9£©èA"ŸTõ{™,ôkë˜p$†ÞI##!IÊšÜ-œ©miÅ gj/A““úšž²<+©O«ørÖö쩚]ûŠŒ€ãè+^‰&›LIÝhQE!…Q@Q@UMWPM+K¸½”eaBÛGsØ~&„®¨ê–zM±žþá!±n¤úÔþÅj?ÞFdÑìwÿ-géÿ|ñ®Jîîë\»kÝFC#1ùøPzØR€ÀèS¤¯#Žx‡´K—^"ññ>v§$J†“–?fÉnÓ×JÝrÍŸçSQ]*œVÈÁÎOvW0ŽÄþ4¿b‡û§ó©èª²&ì®la=˜~5,æÐƒg}s6HGòÅ>ŠN)™£JÓÅþ"°#ý).ÊëÁýk¤Ò¾$ÚLâ-ZÝìÜÿËEË!þ£õ®&‘Ñ]pÀïXÏ t4y#Úaž+˜RX$Y#q•t9}jJòk³økSˆyŒÚ|ÎXÉásüCÜ~µëÕÁV“¦ìÎÊuÕŠ(¬Š( Š( 3â‡ü‹Vÿõö¿ú×gYúΉi¯Z%µðsH$ ýMhU¹'‰KÞl(¢Š‚‚Š( Š( Š( ŠãôÙ¥oŠz´FG1­ª…ŽÄ]¿ì*¥Q'p¢Š*FQEQEQEEsu »Ïs*E ³¹ÀÃj¿·3E¢Zy¤qçÌ0¿‚ÿ‰JÅñ–©.³â)­K‘idÆ5@x,8$ûç#è+)T*€ ; MsHä«]§h–¯5½oS$ÝêRªŸàˆìÅg}Š2IrîORMX¢ºÔ#‘Ìç'»"°ùf?_³Åÿ<×ò©(ª²ÙÙ¡?òÍ*a³€ÿ>„ÔôQddp$ömºÊòâë”r?•nXxç]Óˆ>]ô#¨q†ÇÔPkЉR„·EF¤£³=7@ñޝŸ*60]w†SÉÿt÷þ~Õ¿^4ˆ’2RU9VSƒšõ/ë2랎kƒºxœÃ#x€"+‚½gªØì£WŸFoÑEÌnQEQEÇø6bñ‰ÚHÝîò¥”€Ã|+°¢©JÉ®âjí0¢Š*FQEQEQEQEQEQEQEQEQEQEÇiÿòUµOúò_ý¥]qÚü•mSþ¼—ÿiVëèDúzQY–S¼Ò4ýC?k²·˜žï'óëXÓü>Ð&É[GˆžñÊßÔšéhªS’Ù‰Å=ÑÆŸ†ZZ’`¼¿ˆž¸uÿâi?á]"ñ³~‹Øn®ÎНk>äû8ö8ÑðÖÄäI¨ê §¨Þ¼þ•b‡XßÄßïÊGþƒŠê¨£Úϸ{8ö2ìü3£ØcìÚuº°èÌ»Ûó95¦0- ·¹I%°Wñ;þE˜¿ëéô®Æ¸ï‰ßò,Åÿ_Kÿ µ]/SágcEVe…Q@Q@lãûRÿ®ëÿ¢’¯UøþÔ¿ëºÿ褫ÕRÜQØ(¢Š‘…Q@Q@QÖá’ãAÔ!…KÉ%´ˆŠ:’T€*õ#2¢–b@É$ð4ìÁ˜ž ³žÃ¶v×q4S&ýÈÝF]ˆý nSc‘%@ñººŒ§ Ó¨“»lIYX(¢ŠC (¢€ (¢€ ç|{ÿ"]ÿý³ÿÑ‹]s¾>ÿ‘.ÿþÙÿèÅ«§ñ¢gð³Êìî‘cnp3Þ­Uqn“[Ǹa¶ŽG^”Ý—0}Ò%_~µë¦Ñæ»2ÕT_âXÝMH/!?Ǩ§t+2j*1qÿ–‰ùÒùñÏDÿ¾… ¢£7ùh¿¦5ì+üDýAfOGJ«ö·“ýL,}ÍDÓÿ®}«ýÕ¥~Ánä7· &9PrO½{åx=äk²ª Ãù÷ŠáÅîŽÌ6Ì(¢Šã:BŠ( Š(  /ëòøsKŠîRfy„[\0UŽJÝ®?â\2ÏáÛu†7‘…Úœ"’q±ë°«ir¦Jo™…QPPQEQEQE…i Koã+Ýh̆;˜DB0A9Ïüõ­Úχ[´Ÿ\ŸIBÿkŽ ñ—¿üV…T›ê%nETŒ(¢Š(¢Š(¢Šñ bV‡Äú«í%>×(l¾ic•%\£Rêø“Xÿ¯¹?ô6ªd¤îŒmí^Å;ò£Í¹™bŠ«›¸»,‚¶²ÿ¬…ÖªäXµEVðŸïÂöè¼*wAfOEWût>§ò¦ý¾?áW'éEÐY–¨ª¿h¸õpcݨû<Ó®—û«Jý‚ÂÏtîáù¤B@éœâ‹_ ê:¢]ê~ Õb¼™C˜í&ò¢#… Žß­>KnÅÏ}Ž¶ŠŠÖ9bµŠ;‰¼ù•yv…Þ{œ™ô®3HÑçñúµÅƹ¬ÀbÔ&…ÞìªcŒZ˜Å;Ý»ÅÍhÚ¾¥{¢jÓ‹©­¢Ya¹ ´Ëã‘ê®BÆu ®£ÿ f¢5Œ§ÛD›œ1 ¾WSž8÷ô«T¯Ô—RǪWñ4gÃPÿ?Kÿ µuO.Ÿm%Òyw ™Sû­ŽGç\·Äïùbÿ¯¥ÿÐZ•/§ÀÎûbÓÖoü“ÿ‰£ûbÓÖoü“ÿ‰«ÔTÞ=ŠÔ£ý±ië7þÉÿÄÑý±ië7þÉÿÄÕê(¼{¥í‹OY¿ðOþ&í‹OY¿ðOþ&¯QEãØ53ôÇ\_̪â9&K!\â4õ´(¢“w`•‚Š(¤0¢Š(¢Š(¬ïÿȵªפ¿ú­¯j·ú}Í£1Už&ˆ°ê3úÓZ1=Œ_ÿÈ—aÿm?ôcWET4M)4M" äiØf',[úÕúsw“h"¬’ (¢¤aEPEP\ï¿äK¿ÿ¶ú1k¢®wÇßò%ßÿÛ?ýµtþ4Lþy”ê#ÿt*}2õÿº?•>½„yŒ:Ó 7X×ò§Ñ@ˆMœþYþ¦±ÁýÏÔÔÔQd;²iÿ–có©×î¢ÂE …QLE]CýBÿ½ý {µxN¡þ¡Þþ†½Ú¸1›£³ ³ (¢¸Ž ¢Š(¢Š(¢¸ÿ‰sK‡mÚ67j2ŒAÆÇ®Â©ÆÉ1'vÐQE# (¢€ (¢€ (¢€8Í3þJƯÿ^‹ü¢®ÎªÇ¦ÚE¨I~"ÝÊ»QÕ‡~ƒò«UR•ìLU‚Š(©((¢Š(¢Š(¢Šñ}CþF]cþ¾äÿÐÚ£©5ùuúû“ÿCj޽š <ÉüL(¢§¶±º¼Ýö[i¦Û÷¼¸Ëc늦ìNårªz€ O)?¸¿•X¸µ¸´p—0K ²!RGãS&‘¨Ë¼vnŒ2¬°±{Rº 2–ƒ¢/åNéNderŒ¤08 ŽA©~ÅuöŸ³}šo?þyl;ºg§^”îƒR (¢˜‚»Ÿ…ÿò-\×Ûè \5kx'ÅËalt;;v—R¸¸.­"°Š5!,Tyôõ"¹qцøV¢±ôûôµ»:uþ¦nõmÄ6*ü¹Ú ybyëVíõ‹«ùl¡¹Fº‹;âä0ÁÁ8=³Þ¼Ó¸»EgÚëÚ]ìw[_ÛɲŒF9åaÁü©`×t««)/-õ;)mbm²OÂ2!ã‚ÀàGæ(ýUu;´[¥½¶6îv¬¢UØO lã±ü©ÒêpG’ÝAH2ŒÒ{ôb³ôínÓT»½¶¶.d²“Ë—rà’8õû¦®4ñ#ª´ˆ¬Ýa“\‡‚äeñOý}ÿìòUÆ)¦ÉnÍ#³¢Š* (¢€ +™ñ÷ˆn¼7á帱òRyî#¶Ì327°ÿ9è_ i¾$°¿-«x‚ ^ÆH¸ÍšÀñ¾F6ìà‚3œú w ŽŠâæñt:‡5½Fë\]A ¼šÞömŠP>XQ–ÿ|õ«¾ñÆ›â­(IÔFòKÔXÝ#žXc¹é@=Íiÿ×+°¸Áù±Ø“zé iVö1雋MB$ %«Àå÷Ð`cžÙÇZ먦æ¥ñ!(5±ÄÁ Þë>צ¸…í®µY<Èa—†E\l èN9«4«k4‡Xiìµ”,¶ò@û·`tÀ#µuÔQΟă•­™ ¥ÇÚí"¸ò¥‡ÌPÞ\˵×=ˆìkˆÐ|W£è3ë6ÚÙ‚fÔç/”íò’0rw´RŒ’ºhm7k¾‚·:¦·®Ïk-¬B¶öÑÌ»]rXŽÙ'Šä´=KÂKá(í/í ŸSdu(–…¥v,v€ázò1Íz­J§‘.O…¡¼·ðÍ„ZŽï´¬@8¼=÷±>'ȳý}/þ‚ÕØ×ñ;þE˜¿ëéô¢›½DÂjбØÑE‘ QEQEQEQEQEQEU{û¥°ÓînÙK,4¥GRÒ¬VwˆäZÕ?ëÒ_ýÓZ±=‡hšªkzDñÆÑ¤Û°¬rF¯ô«õÎøþD»ûiÿ£º*sV“H"î“ (¢¤aEPEP\ï¿äK¿ÿ¶ú1k¢®wÇßò%ßÿÛ?ýµtþ4Lþy”ê#ÿt*}2õÿº?•>½„yŒ+µ‘&€i‘Yè—QKoI+Zäî=rÝÔ×]µí޽sšúKΰ 8Ù8EÝŽã#¶;V5·W5¥Ô¡ce`|tmàHæ³¶«|ë÷ Ç=p•Em­Ù_\-¶£¤Ø¤¾e´~[¡Ï9çéZqMþ>µ0¼rH!+4‘ýדaÉ¥fZøVòÞé&Õ|»;D`Ï#ʼóÐ`žMEâþ'Ñ~¥Yýžìuž•ž£­Ú̉/Ùí$1³¨8<`CƒU ³ùIç}·o™´nÛ³8Ï¥hYß.¥ªk÷HI,¥Ú\ üª‘ÿ‘ëÿÿdªMß_!4­§™‡EWAWPÿP¿ïC^í^¨¨_÷¿¡¯v® fèìÃlŠ(®#¨(¢Š(¢ŠÂñn/ˆô¸­!™!d˜K¹Á#Xcõ­ÚÏÖu»MÑ.o‹ˆÞAع9 ŸèkB©·eØJ× (¢¤aEPEPEP9e­ÝÏãÝCIrŸe‚ÜH€/9Ä}ÿàFº:å¬4Û¸¾$jwï­¬¶Ê‰)èÇñúʺš¹ÛKv&7ÖáETQEQEQEx¾¡ÿ#.±ÿ_rèmQÔš‡üŒºÇý}Éÿ¡µG^Í?…dþ&Óxe%“DÕ– ´´˜±3Èc Éþ!ÓÒ¹šÜÑ'±þÉÔl¯¯>Ëöƒ×ò™þé$ð*j«ÇîüÇMÚEÝu.Ã6É=ÀÔg$ÝG ‘S¹»¯¿5¿¨ÞZf%µÜ𡱈•ŽB£<óP]]iú~‡6Ÿar÷rêï)ŒÆª`9«¿Øú¬VO.¯öy!¶HZ?³;ò:óøÖIZ×ZkÓô4n÷³íÔf«9Õt+Ju_µ‰Œ8óâ´‡ü”Æúí*ÂÕµ;Y`µ±Ó‘ÖÎÜ–Ý&7HÇ«V€Ö,á9mCÏÿD#ýfÆÿžxéŒõ¡ÅÛnŒ•÷êŽ`õ4”§©¤®£œ+­ð“g«xVd»‹sGxÍŠJÉlO™r§é\•w? ÿäZ¸ÿ¯¶ÿйqцøŽ£N¶¹´µònïñ•ŽÉ^0¯·°lpO¸éV袼Ӹ)®‹"•u §¨#"EA%´Ðˆe·…â!P}qI.Ÿg+½Å¼øí‘dÉêwœœšÑ<ðhü3£x ¼þ©ymm­,r9’;öYãpͰ¬a¾öàmô­ýKQ»“Á>¿×¥Oý«m$ÒÌBü£~‰éòàšï"𗇡™%‹AÒ’D`Êëg*G ƒŽ ^¿ÓlµH½Ü!·ç‰d\úàŽ¼Ðãi7ÞŸGѯmõ=KTCko¤«)ËpKÈPÏ>Ÿ\…¡°ñÇ€íƒ-®¢$wÛ é]vŸ¡ézK»iºm•›HsoFX{í5<–vÓ]Cs-¼/qï*V@^<Œ§¨ÈëŠå<1ÿ%ÆŸïYÿè£]•A´3\Co sÏ:D@L Ç©Àéšž€ (¢€ (¢€ (¢€ ã´ÿù*Ú§ýy/þҮƸí<ãâ¾§žökü‡ZC¯¡éêv4QEfXQEQEQEQEWñ;þE˜¿ëéô®Æ¸ï‰Çþ)˜Gst¸ÿ¾Z´¥ñ¢*|,ìh¢ŠÌ°¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¤eWR¬) Ž¥ª:ÜÒ[è:„бI#¶‘чPB’ 4®Á—#"@‘¢¢Š£S«Á·“ßøVÎæîV–g߹۩ð ­Ê$¬ÚwW (¢ÂŠ( Š( ¹ßÈ—ÿlÿôb×E\ï¿äK¿ÿ¶ú1jéüh™ü,ó(?ÔGþèþTúdê#ÿt*}{óQEÄQEQEQEUÔ?Ô/ûßÐ×»W„êêýïèkÝ«ƒº;0Û0¢Š+ˆê (¢€ (¢€8ÏŠò-[ÿ×Úÿè]U¿Óm5HèxÕ·…~€àŒþ¦­U9^)–­…QRPQEQEQEÁ4M+D$C"Œ” 2Óñ§×¦ÉXÕÿëÑ”UÙÕJ<¤ÅÜ(¢Š’‚Š( Š( Š( Ô?äeÖ?ëîOý ª:“Qñ.°írèmQ׳OáG™?‰…QV@QEQEQEWsð¿þE«úûoý+†®çáü‹WõößúW./à:0ßÙÑEæÁEPEP\·„ôÛ»-{Ä3\ÀñÇss¾&nŽ7Hr?1ù×SLI¢•c‘á‚°%O½R•“Bjí1ôQEHŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¸ÏZÝhúÕ¿‰lPȱ/—uî½3ù{`ìé `<{ÕF\®äÉ]ô­^ÓZ³[›C¡ê?‰¡]®CPðCÛݵ÷†ïO¹<˜óû¶ÿí‚=ªã _EÄ~"Ñ䨼›º§ê*¹¾.{|GmE`Ùx×B¾múDÇøf1øž?ZÚ†x®t2¤‹êŒý*ZÝš{QE†QH̨¤±äÐÑYWž'Ѭ3öJÜÕQ·ŸÉrk oˆîCƒ¦\ßKýâ¤(÷ã'\Uªr} sŠêuòË4“:Ç ³1ÀÜ× spÞ;ñöв,|²‘#{}zlš•|/­øŽE›Ä·Æ|äZ@z}{¯&ºû m2Ñ-¬áX¡NŠ?™õ5ZCmY:ÏгEVFEPEPEPEPEPEPTõ{i/4këhpdšÞHÓ'%HήS&š;x$šf j]Øô “Mn Ëð®›>‘áË[+°¢h·î r9v#Ÿ¡­z‚Îò ûT¹´•e…óµ×¡ÁÁýEOD›m¶$¬´ (¢ÂŠ( Š( ³µý4êúÝ’TùIé¸r?P+FŠiÙܾ‡†Ç¾ÚF´ºFŠxŽÖFàÔÕêz÷…´ï&nc)8,ñðãØú­pÚ‡5½9‰³hïá0v¿äOò&½x˜ÉZZ3¡%±‹EG;Odû/­'·lã„:h¼„ÿ>¢º“0qhšŠŒ\Då¢~t¾|_óÑ?ï¡NáaôTfâ!ÿ-ð4ƽ…ˆŸ ¢è,ÉèéKik¨êD ?Ož`z>Ó·óéú×G¦ü9¿¼!õ‹•·þyBC1üzÖ³hGv\iJ[#žÓ4Ù|C«Cel¥£ iE^çü÷¯iªZ^“g£Ú‹{$îGV>¤÷5v¼êÕ}£¹ÝJŸ" (¢±4 (¢€ (¢€9Ïëwz ͉A#Ü,gzä`«è+£®[â›wªh0CcÏ"Ü«•N maŸÔWSVíʉWæaETQEQEQEdÛè[øžëZ9’æ!Œ€ÞsÿýkZ²mõø®¸ÏŠò-[ÿ×Úÿè]S¢™)Ý´QEIAEPEPEP¦ÉXÕÿëÑ”UÙÓ1,­(Œ0\(É_ŸU)s`¢Š*J (¢€ (¢€ (¢€ (¢€ (¢€ £ªó ësèàÿJ½X——f;…‚íÀ1\¬ªç€bÃ0?†üïWvL‘·EV±–ià3L»º4Æ §lû÷öÎ;Uš–¬Rw (¢Q@Q@Q@Q@déüZ¾¡©ZG ÆÖùLÌA rÃ#þù­jã<ÿ#/Šëïÿg’®)8¶KviQPPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEU]Né¬4»»µPÍ/(SЕRqúUªÏ×՟úš¨,ÆÖP“°Ó[‰ì3Ú«ëzµü‘¬o6쪜†+ý+N¹ÿÆñx>Å$FGfU†ýãWANjÒi :¤QEIEKA-¦òŒR;m vã€sêG¡¨ÿµ—þ}§ÿÇ?øª‚ÿþB-ÿ\“ùµC\’«>g©›“¹wûYçÚüsÿŠ£ûYçÚüsÿŠªTTûY÷3.ÿk/üûOÿŽñTk/üûOÿŽñUJŠ=¬û‡3.ÿk/üûOÿŽñTk/üûOÿŽñUJŠ=¬û‡3.ÿk/üûOÿŽñTk/üûOÿŽñUJŠ=¬û‡3,ͨCq Ã-¤í©Vo ÿÀ«;Fß`e’ígžvf ß'N}zž§ðô«U*õµÄõw.ÿk/üûOÿŽñTk/üûOÿŽñUJŠŸk>ãæeßíeÿŸiÿñÏþ*íeÿŸiÿñÏþ*©QGµŸpæeßíeÿŸiÿñÏþ*íeÿŸiÿñÏþ*©QGµŸpæeßíeÿŸiÿñÏþ*íeÿŸiÿñÏþ*©QGµŸpæeßíeÿŸiÿñÏþ*íeÿŸiÿñÏþ*©QGµŸpæfÌ2¬Ð¤«®¡†} >«éÿò¶ÿ®Iü…X®¸»Å3E±“â=/éñÚM3©(—rN@#­kVOˆõø¼9§Çw4/2¼¢-¨@9 œþ•­Z;Ùvk…QR0¢Š(¢Š(¢Š(–°Ô®åø‘©Ø<îÖ±[+¤G¢œGÏê:êkœ²Ñ.àñî¡«8O²Ïn#Bœâ>ßð]\í¥»ëp¢Š* +ßNöö­${w n°Ö¨ý¾óûÐß³ÿÅU­Sþ<ýôÿÐÅg×-i5;'ÐÎMÜ›í÷ŸÞƒþýŸþ*·Þzûöøª†¡û]¿ÚþËçÅöžg“¼oÛœnÛ×ã5—4»“v\û}ç÷ ÿ¿gÿŠ£í÷ŸÞƒþýŸþ*¡ª—:¥¥õ¥œòí¸», M¤ïÚ2yZ9¥Ü.Í·Þzûöøª>ßyýè?ïÙÿâª(æ—p»&û}ç÷ ÿ¿gÿŠ£í÷ŸÞƒþýŸþ*¡¢Žiw ²o·Þzûöøª¥ jRÛIqä“nûמ}ÍÓ§åSÑMTšwM‰ë¹7Ûï?½ýû?üUo¼þô÷ìÿñU ¹¥Üwdßo¼þô÷ìÿñT}¾óûÐß³ÿÅT5—–ÐÜÅo-Ä)<Ùò¢gŸvާÔsK¸]—~ßyýè?ïÙÿâ¨û}ç÷ ÿ¿gÿЍj¥Ž§i©5È´—Ì6Ó%ùHÚã¨äsרâŽiw ³Gí÷ŸÞƒþýŸþ*·Þzûöøª†Š9¥Ü.ɾßyýè?ïÙÿâ¨û}ç÷ ÿ¿gÿЍh£š]Âì¹gy<·^TÞY ªGB=Ï­hVM‡ü„Wþ¹?óZÖ®š-¸êi S¢ghãDg9bªcïO®[Âz•Ýî½ângy#·¹Ù·D¤ü«¡Fé±·f‘ÔÑE# (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¬ý}™<;©²’¬-e ƒÈ; 4®ìCBŠçü #Ëàû‘ÙÜù™f9'÷]Vm ;«…QHfMÿü„[þ¹'ój†¦¿ÿ‹×$þmP×¾&b÷2üO4–þÕe†GŽXí%dt$2§ÐÓ¼9,“økK–gi${H™ÎKƒ$žæ¢ñoüŠÇýyMÿ Á𧎼=.™¤iI¨fûÈŠ/È“ï…ÛŽ¾ø¦“qÐ} —~?Ó-ïn K]Jâ f)qyoj^}àÌ9㾪 Ö-t†ö··&C™ U ³³JÁTÜœ ©áŸÙøcO›BÕcº]^+‰JÛÇnî÷;˜°d `ç=ÈéUl/µ ;á=¤Ö ,íN·3Gw‚/5·°ßçÖ¯— ìušOŒ­uMI,%Óõ=:æE-ßÛù^hvòsŠŠûÇz}–¥u§%¦£w}lÁM½­¿˜ì0 `û£ sŽO®;I—O¸ñ΃&“«ëz´æVžý™£VòÏÊ¥”|Øäûbµm|O§øsÆž&þÔ3óC‹ q»鞣šÊÒn£×üC­k¶ 'öq°±Ìñ”°É,3ÎJµáKW¹øYkmm„–k)ãæmÜþf‡‚ÂÉñMS$Øj÷61¶Ö¿†Ðµ¸©Ýœàwã·­ŸøH¬›PÓ­P»F&–ÚeËp 3œç=+‘Ò|q¤é~ƒG¼‚é5h ÇN6Î^GÆùºõïN»Ño4¯†º]Ã)]GE)x@.‡þN~”ÜPXëäÖmθÚ:¬íqöcpî€ms“ž§·•¢kÚm‡¢Õ¥½¾{ |ëöó'?98ëÏÛƒ?âdº¯ˆ˜ø™Ì|ÃCU?>MsVp\IðE¸·îÎì\Íc,ñ¬¬N_ÂŽU°Xµâ/[kRh–ÆÇR°œêH‰{lcóJžAÆG~õÖk^*´Ñn’ÐZß_Þ2ù†ÞÆ,Š7Æx®WÄ>-Ó'¼Òo5KØ®#;®oùóŠ®2¤€HÆOu®óEPš‚¨E´`8(©’I —h¢ŠÌ“SOÿu·ýrOä*ÅWÓÿämÿ\“ù ±]ðøQ²Øã>(ȵoÿ_kÿ =vtÉaŠu 4i"ƒœ:‚3O­¯„•›aET”QEQEQEUR´—P’Á'F»‰w¼CªŽ9ýGçV«ŒÓ?ä¬jÿõè¿Ê*ìê¥X˜»…QRQOTÿÿ}?ô1Yõ¡ªǃ¾Ÿú¬úã¯ñü¿ÌÊ{…r‹ÿ%iÿì ?ôuuuÆë0k–9¾•¢ÿi@Úx¶#íQõ¼ÂßÅϧnõ5|O®\é)gm¦ÛÇq¨ßËäÛ¤­µ%›¾ì+›žmy¼uᨵûkeiÚ9ìäb˜ù]­ó1סÍ^Õ-uýZ 7YJŽÓUÓn–ÆK¥q4l¸#xàÛ4‹mâ=cÅ.¥¨iPXÚÚ4¡¢[•–EÜ„nb0' ëšµd†‡øƒÄõ†£$pKáÍ>ÝÕj]‘%Àþò…#¶qNŸÆ³7€!ñ¥´~sº#BçpÏ™±°Aø9ô¬»Ÿêvºö¨ÿðŒéÚ×Ûçó"½º™1#Y…ÿg¨ý+êzUî‹ðlnâŽ+¸îå ó>GNÜŠvŽƒÐ׺ñ‰ôi-¯µ­;NJšeÒ Y§¶ p 7ÝlÎÑ]¿¨K¥x~þúF–Þ‘‚T3Î1\Íý·‰üJ-ô­OIµ²³I’K›´º³`q}åÉûV猿äLÖ?ëÒOýÔ´®„`\x—ŶúJë¥é£LTYd·óXÜû°#åsŽHµ{[ñ>§¯¥XèV–÷GR·yQ¦%Bt!˜ƒ÷@$‘Œž‚²x³V𭾊4›4‚æÙ#:ŠÝ ‹Qÿ,ÏÍ»o™ö­é4;ˆ|[¢\[ÄM•œ4…‡ÊHF3“Ò›²íw]·{"ÚÛOŸ^¸‰¦•÷8¶…cqxç§Ö—M×u¸u±£köö)uq Miqh\Äûz© ÎGZwˆ4ÝZßĺþ‰oä±Àm§´’A‘ dcÀ úÓ4ËkWñ$ηe›”O½¢Î%rÏÎÌ8ÆëýV–·ñ‹ s¬ÝÇÞZŠhW!DÊÛBõ$d•üéfÔ.SÄ>ƒP²°k»¸¦ieò‰hYP#$åG8=sY×¾Ôeñ‹Ç*t »˜¯®zŒJŠA]¹ÉÜÁIâ¶µ}.îëÆ^¾†-ÖÖ‚àNû€Ù¹^ ÉÉô£@Ч>»â OY¾µðå®›öm>AÒÞ»æWÀ%P/Ltæ³|-­dè>&Õu+cŨË$Uˆ_“pàòqš¸–ž!ðÖ·©+K‹T±ÔgûJ±ºXZ# =GÒ¡±ðž§yáéÚ¯“ Ö¡vó$‘œÆI A$…Ü;óŽÔô°cñö£k=½ÅýLJ'²šEF·±¼ßscÁ<á¶çœ*ÕÕµØ/?»ýü?üMqJ2æz4îCEMö ÏîÁÿÿGØ/?»ýü?üMO,» Ì†Š›ìŸÝƒþþþ&°^vûøøš9eØ,Èk+LÑ?³µVÿÏó?´$GÙ³^ÕÛŒçœþ·ö ÏîÁÿÿGØ/?»ýü?üM²ìd5•‰³ÅRë^~|ËE¶òvtÃnÝ»?¦+oìŸÝƒþþþ&°^vûøøš|²]Ì†Š›ìŸÝƒþþþ&«Y ›å˜ÇKäÌðÒJœ÷h䕯`EMö ÏîÁÿÿGØ/?»ýü?üM.Yv 2*o°^vûøøš>ÁyýØ?ïáÿâhå—`³!¢¦ûç÷`ÿ¿‡ÿ‰£ìŸÝƒþþþ&ŽYv 2*o°^vûøøš>ÁyýØ?ïáÿâhå—`³!¢¦ûç÷`ÿ¿‡ÿ‰£ìŸÝƒþþþ&ŽYv 2öŸÿ ëoúäŸÈUŠŠÖ#¤11‘I8-vÁZ)­Ž[â¥w¥è0Mc;Á#\ªN¤mcÐWS\çôK½{F†ÚÄ!‘.C½°0‡õÑ֮ܨJüÌ(¢Š‚‚Š( Š( Š(  øtKH5Éõdö¹ÐFä·ù{ÀEhV¦¿-ÇŒ¯tS í¡‰9$„ãð?Ò·j¤ŸQ+t (¢¤e=Sþ<ýôÿÐÅgÖ½Ì s‰™”W¨ÁÏôª¿Ù+ÿ?3ÿãŸüMsU§)Jé$Û)QW²Wþ~gÿÇ?øš?²Wþ~gÿÇ?øšÏÙO±<¬¥E]þÉ_ùùŸÿÿâhþÉ_ùùŸÿÿâhöS쬥U5-2ÓW³6·Ñy°3+ÜWr9¢¶?²Wþ~gÿÇ?øš?²Wþ~gÿÇ?øš=”û+)T¶pjSZ]'™ÈREÉSÔdsZŸÙ+ÿ?3ÿãŸüMÙ+ÿ?3ÿãŸüMÊ}ƒ•™ÖðGko®Ø¢Pˆ¹Îš’®ÿd¯üüÏÿŽñ5JòÐÁ¨XB—l¸‘Õò8Ì1òúŠjŒßA4ÐQW²Wþ~gÿÇ?øš?²Wþ~gÿÇ?øš^Ê}‡ÊÊTUßì•ÿŸ™ÿñÏþ&ì•ÿŸ™ÿñÏþ&e>ÁÊÊTUßì•ÿŸ™ÿñÏþ&ì•ÿŸ™ÿñÏþ&e>ÁÊÊTUßì•ÿŸ™ÿñÏþ&ì•ÿŸ™ÿñÏþ&e>ÁÊÊTUßì•ÿŸ™ÿñÏþ&ì•ÿŸ™ÿñÏþ&e>ÁÊÈ,?ä"¿õÉÿšÖµT¶ÓÒÚo4K#¶Ò¿60Ç •nº)EÆ:—dÆx'þF_ÿ×ßþÏ%vuŸ§h–š]ÝíͰq%ìžd»› œ“ǧÞ5Ñ$š®Ó4(¢Š‚‚Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ³¼Cÿ"Ö©ÿ^’ÿè´i“CÄC2‡ŽE(êzF¦˜ž¨Àðü‰vöÓÿF5tUœ©miÅ gj/A““úšžœÛadQE# (¢€ (¢€ (¢€ (¢€ (¢€ Èð÷ú­Cþ¿çÿЫ^²<=þ«Pÿ¯ùÿô*µð²_Äz(¢  ¢Š(¢Š(¢Š(¢Š(¢Š(­þ¥i¥À³_NFͰ3ô'ãô5j¸ÏŠò-[ÿ×Úÿè]S¢™)êÐQE%Q@Q@Q@~› «ñOV”Æâ6µP©Á8‹¿á]…UJ\ÂJÁETŒ(¢Š(¢Š(¨n. ´@÷¤JNÐXã'ÓôªÿÛZwüþCÿ}SQod&Ò/QT¶´ïùü‡þú£ûkNÿŸÈïª|²ì˹zŠ£ýµ§Ïä?÷ÕÛZwüþCÿ}QË.ÁÌ»—«3RÿÆ‘ÿ]¤ÿÑMRÿmißóùýõY÷ú¥”š¦–és$r¹r…6?‰ª„e}»þDÊJÛ›ÔUí­;þ!ÿ¾¨þÚÓ¿çòûê§–]Šæ]ËÔUí­;þ!ÿ¾¨þÚÓ¿çòûêŽYveܽEQþÚÓ¿çòûêí­;þ!ÿ¾¨å—`æ]ËÔUk}FÒîC½Är8Š«dãÖ¬Òi­ÆÂŠ(¤EPXZ¿.¯ªjö’B‘­„ÞR²’K Ì2ïšÝ®?Á°Ëˆ¼NÒFè¯w•,¤äéWœY-»£°¢Š* (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ §«ÜÉg£_\Ã$6òH™ HþUr¨ëpÉq êÂ¥ä’ÚDEI*@Öâ{¼+©O«ørÖö쩚]ûŠŒ€ãè+^°üg=‡…lí®âh¦Mû‘ºŒ»úܧ;s; ;+…QRPQEQEAyt,íÌΤƤo#øW<·Ðu¡+†Ål⼿Ôs+Q@™Ôå!Æõ'ó©ÿ±í=&ÿÀ‰?øª‚+¥µ¸Ô[iw’ñubbúdý­ZÒNH„“(ÿcÚzMÿñTcÚzMÿñUzŠžiw+•v(ÿcÚzMÿñTcÚzMÿñUzŠ9¥Ü9Wbö=§¤ßø'ÿLB±‹w—«¹‹O É=OÞ­(ç—qr®Åì{OI¿ð"Oþ*ì{OI¿ð"Oþ*¯QG4»•v(ÿcÚzMÿñTcÚzMÿñUzŠ9¥Ü9Wbö=§¤ßø'ÿHúE F M?çâOþ*¯Õiî–;„·u#ÍF(ý‰WëŽéB”ŸQ5Ãt¦gÒ,™Ø³4I'$¢­Öf“t¿cÓí•K7ÙÜŽˆ6€3õ9ÇÐÖVa QE%Q@úΉi¯Z%µðsH$ ýMhV‹uù|9¥Åw )3<Â-®H*Ç?¥nÕ4ì» ZáETŒ(¢Š(¢Š(¢ŠãôÙ¥oŠz´FG1­ª…ŽÄ]¿ì+ Ó@–߯WºÑ™ w0ˆ„`‚sŸøë[µsiÚ݉Šj÷ (¢  ¢Š(¢Š(ÿü~iŸõòßú&J½Xw7Þ]å¬wŒªö÷,庌Ã) ú}Á­Kž[a-Âìi `˜ÁE=÷Ç_z¹E¤ˆ‹»eŠ(¢ °¢Š(¢Š(¢Š(¢Š(¢Šk‚Q‚¶Ö#ƒŒàÐ9?ä=oÿ^ÒÿèQÕêÁ:žËØ¥?Ò!·–'‰z™7ÆïdìElÛ‰E¼bᕦÇÎT`gÚ®Qi"bîÙ-QPPQEQEqþ šY|Eâu’GuK¼(f$(ß'J¥¦û »4ŽÂŠ(©QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQER3*)f!T ’ORÖwˆäZÕ?ëÒ_ýÓJîÂz"ür$¨7WCÑ”äus¾ÿ‘.ÃþÚèÆ®Š‰+6;«…QHaP]ßZØEæ^\EzG üë“ñ‡ŒäÓn›¥kÌ~òSÈ‹=€î•pSG-äæ{é丙º³±5ÓK)«½Œ*WQÐô‹¿ˆš©ÂM5ÁóÆ3üÛ³føhÊV-.æE<ìGë\ZƉ÷QGÐS«¥a ·0x‰ZoRÂîYžÂâe'0©|ych^¸äáTgëë[üPÓŽÍ•ÜGý¬?˜®&Š©aá-ÉyDôûhWì;ôÏðÌ ~§Ö·ƒ(e ƒÈ#½xƒÛDÿyÔqVtÝ[SÐ$§\³BZåðÿ ÂxK|,Ú8Ÿæ=žŠÊðö½ˆtź„lpvË9(ÞŸJÕ®6švgJwÕQHaE„€ 'u&€¨jÞ›¥ôëØa?Ý-–ÿ¾G5Áx‹Æ÷š•Ì–š4†ÞÑÓ:ýé>‡°úsü«˜[HÓ299,Ç©®ªxW%yóÄ(èE¹ø•¢ÂÅa[«B‘€?ñâéYZÄX/mŒQé—²$Þ2Œ:cüò+•UUáT ¥®˜á`Œ"Lè´G¦Z,O¦Ï,œ—~2ÀÀÇ@ÿ&¶mþ&é³ÁwîJò9ý+„ €Ã=è–w×’Ðõ;ÄšNªBÙßBîz!;Xþµ+Ãd³…ÿ‡iõ^+gEñv¥ J‰s#ÞXd®rÈ?Ù?Ó§Ò¹êaÖ&ÐÄ'£=fŠŠÚæ+Ëh®-ÜuõïYK ±¤q[žÜ r -y‡‚üCs¦j°iW24¶wd[1±ébxǽz}pÔ¦é»3®SWAEVe…Q@dêþ'ÒôO–öéD½¢O™ÿ!ÓñÅcxïÄ—L0ØØ6Ë«IuEéǹõö¯°Ë7>TǤS Œ~àkÌ*)m£˜r¸>¢²–cHâ$·=ÆŠóÿxŽäÞÿc_ÈÓ¥­äc’09_¦?,W Wàá+3²RWAXZ.‘ªj÷rL’-üÞjª‚ ÌpïªÝ¬ý;[´Õ.ïm­‹™,¤òåܸäŽ=~餛³°Ý® (¢¤aEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPUïíVÿO¹´f*³ÄÑ@`FZ±Uïî–ÃO¹»e,°DÒ•HPN?Jk}hšRhšDÈÒ$;°Ì0NX·õ«õCDÕS[Ò ¿Ž6&Ý…c’0Å¥_¢W»¸•­ QE†x¦­qê¦VäÝH Ÿf ~””º¤I7ˆµ…qŸô¹?ªŸ“qú—¿Ýjõééy³ÖL·EUû\‰þ²â”_ÅÜ8ü*ù‘eš*¿ÛáõoÊšoÐýÔv?J9Y–©¯"Æ¥œàU:æOõpí­J¶…Ûu×>ƒ¥ìîvŸ ܹÕÿ»º"¦wנן|.O¬À þÏ^ƒ^]â3ѥ𠢊+@¬ýyÚ?jN‡ ¶²Gc°Ö…gx‡þE­Sþ½%ÿÐ 8î„öfQEAAEPEPEPXôÛHµ /Ò[¹WcÊ:°ãÐ~Uj¹Ë-nîêK”û,âDyÎ#ïÿ5ÑÕI5¸“O`¢Š*FyïÅ\îÒ6õ̸ÿÇ+†é%àü¯Üì¾(¯Ñ¿Þ—ù¥qÒÛG7,0}GZôð×öhá¯nrZ*¯Ù§ýTÜz5îת#V÷0±jŠ«çÜŽ°QæÝ¨úš.-S^Erìªû.ßï: ö¥K$tŒÒ7½adM¤Lgñ>”@!Ü@g¿Î+ÛëÅ´à‰4`»§ûë^Ó\¿‰¸„(¢Šä:Š( .ø–̾)µd"ÑN?ào\ü7Ì>S†þéë]ÄOùí?ëÐèO\ܶ‘Ês­ê+Ô¡f>µ¹Ù5Wȸý\Û‡£Qæ]¯XѾŸþºÚæV-QU|ûŸùáG™vzD£êúô\,Z¦I2D2ínõ•u'Þ” ö§GgÍ—oö¨» #SÁR™|q`ä`&>ž[W°W’xKþG½;ýÙ?ô¯[¯;ñØ€+ŒðOüŒ¾)ÿ¯¿ýžJìê­®›ie=ÄÖÐ$r\¶ùYz¹É9?™üë+&»šµv™jŠ(©((¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+;Ä?ò-jŸõé/þ€kF‘•]J° ¤`‚8"švwÕ÷€äK°ÿ¶Ÿú1«¢¦ÇD#EDF§Q'vØ%e`¢Š) ñ}CþF]cþ¾äÿÐÚ£©5ùuúû“ÿCjŸJ‚;ZÒ—trLªÃ8È'šö íÏ2Jò±R‚êu±Yèú†©w¦E¥½³Eær· Ûvž¤ÿÎÑmìF•¨ÞßZ}«ìæ0©æ²}âAäRöªÛL~Íßs bá•/J袴ҵÛy—O·’Æö$.±™L‰(y<çüóÚ‘²ƒþE¼Ùþo<­ù?wfqŽiªˆN Ê¢µuÛ(,šÄ[¦Ï6Î9_’rÇ9<ÖUT_2¹-YØë>ÿ¯ÖÞ‹ù½zyÿÂÿõúÏûÑ7¯@¯.¿ñèÑøQE‰ VwˆäZÕ?ëÒ_ýÖgx‡þE­Sþ½%ÿÐ 8î„öõ+ê¤óU¯¬ ‡EÒgŽÏ¿8ÿ]œmÿk  sÅ—6²èú>³©,BiPL°¤Qœ€ZFãq#Þ—CñƒêgPµÔ4ÉtÝWOO2kI$ J²¸áÇ\p}k&ö}KÂ~4¿Ö‹}¨éú¬0†û e‚DR6”ÈÈ#¿J~£­ëz¿ˆî´Û> ¬>Çimp¸ž@ Ì£îœðÿZ€'ðÏŽ¯!jKjuUð¥ÙððþÜnPIåtßäýìwëÓš·áM"â_…vºUÌrZÜKbð²Ê…Y nƒÏzóë èÖvQØjŸµËf5òÚHO³Lý›ÍTðIÆ'Ò€=²’â扷G"†VÁäðœøo©Ô®Væå£„<©0“pó—pH'n9ëšÓÔ>èö2]øcíF«™!¸†æC’Ý`Ä‚§¿ù&ëÃ:µŸÁíNѬ$w7jŽÂÜy†3)Ø¡zàx­‹ïkÕŒ¶…u¸/¦B‚}JÜ[Ã#÷rF~ïÒ€(jºÄšÿ†< ©LšãW¶2†ðqøƒ[+ÿ%¥ÿì?ô}SÖ<56•¡x7L²Šk¡aªÛ4ÏdàÛœã¢äõ=3ZKgsÿ qï>Ï7ÙNŠ"óö›üìíÝÓ8çÖQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEUni-ôBhX¤‘ÛHèè!I¯U=^ÚKÍúÚ&·’4ÉÀÉRó¦·ØÏðmä÷þ³¹»•¥™÷îvêpìè+r²<+¦Ï¤xrÖÊì(š-û‚œŽ]ˆçèk^œíÌì(ì®QEIG‹êò2ëõ÷'þ†ÕoCÿõ‡ý|'þ„*¦¡ÿ#.±ÿ_rèmMŽGŠExÙ‘ÔåYN> ×±xXódí;Ôw:å׈%´»·ytƕՄ¶à'—“ƒ»ðæ²,m¼íÄX#̾lb%@X²‡8éíX²ê·óÄÑÍ}u$mÕ^f þ¨í¯n¬÷}–æhw}ï-ÊçëŠÍRiiåøê&ÍíO¸ÒZ}SP‰í¡‚6U.Ö‘ˆÀ{Õfÿ‘ëÿÿd¬›‹Ë›ÆVº¸–b£Èå±ùÓ|ù|#Ì'vï/qÛ»¦qëWÈÛ»'%dlxŸïéŸõáõ¬:’YåŸo#ɱB®æ'jŽ€{Tup*±2ww:Ï…ÿëõŸ÷¢þo^^ð¿ý~³þô_ÍëÐ+˯üFz~QEbhâùµOúô—ÿ@5£YÞ!ÿ‘kTÿ¯IôN;¡=³ÿTüt^ÿ‘šÛpÿú ®vÏþ=Sñþujå¶”K¯ƒ££#ñìJ<бæ§i\ìtùõ}E®!ñtñ3´ðö8 àsYpÚÏwà –ÐK3‹òJÆ…Ž6uÀ¬yõ+Û¨ü»‹Ë‰“9Û$¬Ã?Bh·Ô/-¥µÝÄ(NJÇ!POàk5M­Šö‰îmÙYÍ¢èz…Õú4-u‘ N0ÌIäã¨ÅC©ÿȹ ÿÛoý V4÷]Iæ\K$²c¤bÇó4<¯q¼®Év)bBç®j¥{¿ëK ZÈÖñüŒ÷ðýVÿê$ÿtÿ*ži¥¸”É<$Õ‰'ñ5ÿê$ÿtÿ*¨®X¤LåsÓ|ÿ"]‡ý´ÿÑ]s¾ÿ‘.ÃþÚèÆ®Š¼šŸ=(|(«¦Új,7Ð$ñ«o ýÁýMZ®sÆúÝÞƒ£CsbPH÷ Þ¹*Çú èé4ì˜ÓW°QE# (¢€ (¢€ (¢€8Í3þJƯÿ^‹ü¢®Î²mô­üOu­ œÉsˆÆ@Ào9ÿ€~µ­W6­Ø˜«\(¢Š‚?ø¡þ¿Fÿz_æ•É×YñCý~þô¿Í+“¯S ü4pb>3£°‚-CŸdûu¼ËxdÅÄÁ2»qýiº¬ÐZè–z`»Žîx¥23Äw"ü ÷ë\õ~Ï[ÜŽ}6:ÍoJ·Ôõ™¯XÓ 6“™òàü£©ã¦j´º•¤šæ“ «âÊÉÑD¯òîùf9è+œ¢…KK7°:šÝ#¤µºu_;OY`œFÅÆ–à\×7EQ).W$ÓÿäeÑÿëî?ý kÚ+ÅôÿùtúûÿCZöŠáÅüHìÃ|!EW!ÐQEyÄOùí?ëÐèOX5½ñþFûOúôúÖ z¸á£Î­ñ³ªÔ4øµUÓ¦‡UÓaòí#–K€Xuãñ©cÕlçñ¥¬Ëp¾LPùMq!Ú…?1ÏÖ¹ )û-,Ø{Mn‘ÐÛh¶V7)s¨êÖ2Anòí¤2;žÃà{Ó¬µXï5 nêwHŤ˜ ž0£ÔàW9E?g}عí²6Lñ°y‰ç}·w—¸n۳ǥcQEZV!»š>ÿ‘ïNÿvOýëÖëÉ<%ÿ#ÞþìŸú׭׊øÎì?ÀÄš)YÖ9Ù+TûÓëŒðOüŒ¾)ÿ¯¿ýžJÁF龯­Ù¤vtQEIAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPLšhíà’i˜$q©wcÐ2M>³¼Cÿ"Ö©ÿ^’ÿèšWb{¬ï ¿µK›IVX_;]zÔTõÎøþD»ûiÿ£º*rVm]ÒaETŒñQ/jèã n]€ö,Hý¨«Ð|[àÁ­Éöëõ\ýÙ@éŸCï^{y î“/•©ÚKt GÊßCÐþêQ«E.§ZrNâÑQ­ÌOÒEüx©¡é[˜RUûÌÔÓh¨^òþ-ÇÐVž—á­_^eò k[SÖiF8öOáùÔJqŠ»eF[Âå%õ‡ÇÈÏ×ïÿˆ®þ³ôMÛBÓRÎÔp9w=]»“ZäÔ—4›G£òÅ ¢Š* ¡®FÓhŒh2ïk*¨õ%M_¢šv`õ<2ȃj£Ó9üêzê|Gà+ˆnd½Ð€ds¹íOÿwÛÛò®FIÚÞS ä2[ʽUÔ‚+Õ§V3ZléÊ,–ŠbÍ}×SøÓúÖ¦aEÖ–5ûΣñ Tw y3ýÓLûZ³„…ZW<Aœ×G¢xPÕ¦IµUkK0såž$lvü*‰ÔŒ¬Òå'¡×øY<§†8søb+¡¦C vð¤0¢¤h¡UT`: }ywmžŠVV9oˆ:mÞ© Á Œ<‹r®U:µ†Q]M2Y¢CM"F¤ã.À Óé¹]$ kp¢Š*FQEQEQEdÛëñ\xžëE¸’Ú!)‘‚Þ1ÿý+Z¸Í3þJƯÿ^‹ü¢®Î®i+[±1w¸QE7ÄëIÏO½E-´Œ¦í¸'þùÇã\Z°u § ÷¯iž®`xgdŠAµ•†AÁjÿ%ŠGŸBœm'?g”ôú7øþuÙ‡®¢¹drÖ¤äù‘ÊQO»±Õ4ÒEö›q?Œ.Wó~µP_Bz–Q]Êq{3•Å­ËT?lƒûÿ¡¤7°â'ð4î…fOEE ²Ý¾Ë;YçHГúVæŸàsS Ü*X@z—9lº9üñQ*±Žì¨Ó”¶E ¿ñ^™ XÃ:ÊäT‚sù~µì•‘áÿ XøvÜ¥ª—•ÿÖLÿy¿À{V½yµê{I]Ô¡ÉQXš…Q@qñ*ÖHu›@©òZ/$¶8“~•ÍÈ9½’úÂÛR´{[È–X_ªŸóÁ¯>Õ>ßY»I£N."ÎD2¬?‡ô®ì=x¥Ë#’µß29Ê(º‚ÿO$_é÷ã«8üOÖ«‹èORGÔWb’{3™Å¢ÅÛ þÿèi ì#ø‰ú wB³'¢™oö›æÛceqpØB•oéÞÖ5 ¨:XÀz¨;œ þ§ð¬åVÝ—r–ȃÁ=ߌàž%&+XØÈÀp2¥GêC^¯Yú6‰g¡Y kö©åÜòÎ}I­ ókTö’¹ÝN‘°VN‘ E¤j•Üs</šÊÀ§,p?ïªÖ¬#_‹WÔ5+HáxÚÂ_)™ˆ!ŽXdß5 öv-ÚêæµQR0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¨n­£¼´šÚl˜æFðppF󩪮§tÖ]ÝÚ¨f‚”)èJ©8ý)­Á‰¦i°i|VV„1ghc“É$óõ5n³<9ª¾·¡[_ÉÆónÊ©Èb¿Ò´è•îî%khQE!…2H’hÊJŠèÝU†Aü)ôPׂ´²KéÑ¡õˆ”ý³%øe¢Èr²^F=Eþªk°¢­TšêK„_CŒÿ…_£ÿÏÍÿýüOþ&¬AðãB‹Òâo÷åÇþƒŠêè§ígÜ^Î=ŒÛé:i i§ÛÆã£ìÜÃñ<Ö•T6Þå$–ÁERQEQETv6·ÑùwvñNŸÝ‘Ö§¢€9ËŸh·ØŒD÷ŽF¦qTᆌ͑=òA"ñ5ÙQVªMu'’/¡Æ…ú0 ý¢üûy‰ÿÄÕˇº¶’b?礭ý1]5:³}EÉÅK-.ÇN\YZAz˜Ð~§½[¢Š†îXQEÆ|Pÿ‘jßþ¾×ÿ@zìë'Äz^#Óã´šg…RQ.䜀F?ZÖ«mr¤J^óaETQEQEQE0CÊÒˆÐHÃÂŒ‘õü)õËXjWrüHÔìwkX­•Ò#ÑN#çõ?u5R„Š(©QEUiôë;œý¢ÒÞ\õßoæ*Íÿöÿ@›üOð§Ç¢ip¶è´Û$>«éW¨§ÌûŠÈj¢¢…E `1N¢ŠC (¢€ (¢€ (¢€ (¢€ ©6•asþ¾ÆÚ_÷âVþb­ÑEìwü#Ú?ýl?ð?¤‡FÓmÎaÓí#>© ä*íùŸqYPÐ Z(¤0¢Š(®3Á?ò2ø§þ¾ÿöy+³¦$1DÎÑÆˆÎrÅTÇÞ©JÉ®äµv˜ú(¢¤ ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¬ý}Yü;©ª‚ÌmeÉ; hQM;;ƒÔæüá¼fƒïFÒ+:ì‘£ªøLÑ$Ž=FçÉyå[6Gà ^‚Þd)o q!%Š¢…=OÖ™c~Ê×–v× £ e‰\í‘TÚrm’“Q²2cñׇå‘#MC.ä*&NIÿ€×AYë iÁ—J±V ‹tÈ?•hR—/Ù¿SŸ“Ç^ŠGõ :¬<™8#þWt¯éšÜ’G§\ùÏÜÃËeÀü@§¶¤;m*Řœ’mÓ$þU5®™c`ÌÖvvÖìà b‰P‘ïMò[K‰s_Q5-NÓH´77ÒùP‚¶ÒÜžœMdÂ{áßúÿä ?øšÜ¹µ·¼‹Êº‚)ã';$@Ã?CU?áÑÿèaÿ€ÉþG–ÚƒæèZ³¼†þÒ;›Wß £r6Èúk?RñV¤]›këÏ*`ò¸=9ŠÔ†­âX #@{U®t:ò_6êÂÖyÆù!V8ú‘IrßQ»ÛC>ÏÆZýÜvÖ·Ûæ”íEòœdýJⶤ‘bäs„@Y NK·•eƒM³ŽD9WH}ˆu•]J° ¤`‚8"‰rô~§=ÿ ï‡è#ÿ$ÿâkOJÖì5¸ä“NŸÎHÎÖ;p)¿ðhÿô °ÿÀdÿ ³kai`¬¶v°[«°Š0€Ÿ|S|–Òâ\ÝH5]nÃDŽ95ü”íS±›'ð³?á=ðïýòŸüMm]XZ_ª­å¬ §*%Œ8Û5[þíþ6ø ŸáBä¶·Íп‹,i"£€Ê}A¬[Ïhvr[]_lš#µ×Êsƒõ ŠÛUTPª¨ªShš]Ä­,úmœ’9Ë;À¤“îH¥^£wèVÓ|U£ê÷bÚÆó͘‚Á|§^^H´//!°´’æéöCÜí‚p>ƒšŠÛHÓ¬åóml-` oŽS¨fhb¸‰¢ž4’7duî –ú½µ0?á=ðïýòŸüMk麦¯h.leóa$¨m¥yx ‡þíþ6ø ŸáVí­mìâò­`ŠÁÎÈÐ(ÏÐS—-´æêRÕ|G¦h’G£s伃r-›#ð©Gã¯Ë"Fš†]ÈULœ“ÿ­k­2Æý•¯,í®F˹Û"¡]HF ºUа9[¦Aü¨\–Öàù¯¡¡\üž:ðüRéâ»z(ª”¹¬JŽ>ojñO"/…/Uˆ á†zýÊÓÐ5ëí^y’óE¹ÓÕiwaŽz ¨­Ú)¹FÛNû™úÞ£q¥éæâÖÆ[éäÇœ{ðò®sþmcþ…ÿÍÿøÝvtQE-P4ÞÌ«¦ÝK{§Áq=³ÛI"å¡|å¡Èʱµ¿ê:^ mít »èƒçG»žÜ!þuÑÑI4ž¨m;nrÚo‹5KÝB yü5ym†™Ëa©Êç]4ÎÑA#ªeRBެqÒŸEiì&·gÿ ¶±ÿBÿæÿün·t bïW‚g¼ÒçÓÙYs–ê2¢µ¨¦å´BI­Ù“¯ëzD½ž—> ÎÄ2Åœ¨ÇS…5…ÿ ¶±ÿBÿæÿün»:(RŠZ i½˜È]¥‚7d(Ì •=Tã¥s:—‹5K-B{x<5ysm…™ aǨÂç]M¢ÒÝ ¦ög9¢x—QÕ5ou ]ØÆTŸ:MØvåó­Jê[->{ˆ-žæH×+ g.}þUjŠMè'mÎ3þmcþ…ÿÍÿøÝtz&£qªiââêÆ[ äÉœ€;òò­ )ÊQkD$šÝ˜Zþ½}¤O Yè·:‚º’Íì)ÏC…5™Œµyg—ȬÀ%ð£=~åvP¥¶÷ ãæñ–¯ò"øRùÕX€À¾g¯Ü®ÂŠQin®6›Ù˜Z½}«Ï2^h·:z¢‚­.ì1ÏA•w[Ôn4½<ÜZØË} `<˜ó’~þU¡E «ÞÁgmÎ3þmcþ…ÿÍÿøÝu:mÔ·º|Û=´’.ZÎPúüªÕI§²°’kvsšß‰u/P6öºÝôaAó£Ý‚Onÿ:‹Á×zÅíΩ>­ Ô<ŠÖñN¤l± d ã客Š|Ë–ÖW{Ü(¢Š‚‚Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿÙbdii-5.2.25/docs/source/intro.rst000066400000000000000000000043751335470071400166460ustar00rootroot00000000000000Grid Information System Introduction ==================================== The grid information system is a mission-critical component in the WLCG grid infrastructure. It provides detailed information about grid services which is needed for various different tasks. The grid information system has a hierarchical structure of three levels. The fundamental building block used in this hierarchy is the Berkley Database Information Index (BDII). Although the BDII has additional complexity, it can be visualized as an LDAP database. The resource level or core BDII is usually co-located with the grid service and provides information about that service. Each grid site runs a site level BDII. This aggregates the information from all the resource level BDIIs running at that site. The top level BDII aggregates all the information from all the site level BDIIs and hence contains information about all grid services. There are multiple instances of the top level BDII in order to provide a fault tolerant, load balanced service. The information system clients query a top level BDII to find the information that they require. .. image:: images/InfoSysStructure.jpg The BDIIs are populated with information by running information providers. These are scripts which obtain information, format it as LDIF and print the result to standard out. These information providers can also be used to query other BDIIs which is how the hierarchy is built. The order in which these information providers are run is random. The information in the information systems conforms to a schema called the GLUE schema. The GLUE schema started as collaboration effort between European and US grid projects to facilitate interoperation between them. The Open Grid Forum (OGF) is now responsible for the GLUE schema. The information system is bootstrapped from the information registered in the Operations Databases of EGI and OSG grid infrastructures (GOCDB and OIM). When a site registers, it enters the URL for the site level BDII into the GOCDB/OIM. GOCDB/OIM then generates a list of LDAP URLs for all the sites in the grid and this is downloaded by the information provider running on the top level BDII. These URLs are then used to query all the site level BDII and the result is used to populate the top level BDII. bdii-5.2.25/docs/source/metadata.rst000066400000000000000000000000231335470071400172550ustar00rootroot00000000000000Metadata ======== bdii-5.2.25/etc/000077500000000000000000000000001335470071400132735ustar00rootroot00000000000000bdii-5.2.25/etc/BDII.schema000066400000000000000000000076021335470071400151710ustar00rootroot00000000000000# # BDII Update Process Monitoring Schema # attributetype ( 1.3.6.1.4.1.8006.100.3.1 NAME 'Hostname' DESC 'The hostname of the BDII this data refers to' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.3.2 NAME 'TotalEntries' DESC 'The number of Entries in the LDAP database' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.3.3 NAME 'UpdateTime' DESC 'The time in seconds for the update process to complete' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.3.4 NAME 'DBUpdateTime' DESC 'The time in seconds to update the LDAP database' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.3.5 NAME 'NewEntries' DESC 'The number of new entries this update' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.3.6 NAME 'QueryTime' DESC 'The time to query the LDAP database' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.3.7 NAME 'ProvidersTime' DESC 'The time in seconds to run the information providers' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.3.8 NAME 'PluginsTime' DESC 'The time in seconds to run the information plugins' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.3.9 NAME 'FailedAdds' DESC 'The number failed add entries' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.3.10 NAME 'ModifiedEntries' DESC 'The number entries which were modified' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.3.11 NAME 'DeletedEntries' DESC 'The number entries which were deleted' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.3.12 NAME 'FailedDeletes' DESC 'The entries that failed to delete' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.3.13 NAME 'FailedModifies' DESC 'The number of entries which failed to be modified' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.3.14 NAME 'ReadTime' DESC 'The time taken to read the old entries' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) objectclass ( 1.2.6.1.4.1.8006.100.3 NAME 'UpdateStats' DESC 'An entity which keeps statistical data for a BDII instance' MUST ( Hostname $ TotalEntries $ UpdateTime $ DBUpdateTime $ NewEntries $ QueryTime $ ProvidersTime $ FailedAdds $ ModifiedEntries $ DeletedEntries $ FailedDeletes $ FailedModifies $ PluginsTime $ ReadTime) ) # # BDII Compresses Content # attributetype ( 1.3.6.1.4.1.8006.100.1.1 NAME 'CompressionType' DESC 'The compression type which the data has been created with' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE) attributetype ( 1.3.6.1.4.1.8006.100.1.2 NAME 'Data' DESC 'The compressed data' SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 SINGLE-VALUE) objectclass ( 1.2.6.1.4.1.8006.100.1 NAME 'CompressedContent' DESC 'An entity which keeps the content of a BDII in a compressd format' MUST ( Hostname $ CompressionType $ Data ) ) bdii-5.2.25/etc/DB_CONFIG000066400000000000000000000004241335470071400145300ustar00rootroot00000000000000# Maintain transaction logs in memory rather than on disk set_flags DB_LOG_INMEMORY # Automatically remove log files as soon as they are no longer needed set_flags DB_LOG_AUTOREMOVE # Do not write or synchronously flush the log on transaction commit set_flags DB_TXN_NOSYNC bdii-5.2.25/etc/DB_CONFIG_top000066400000000000000000000007761335470071400154240ustar00rootroot00000000000000# Maintain transaction logs in memory rather than on disk set_flags DB_LOG_INMEMORY # Set in-memory transaction log cache (10MB) set_lg_bsize 10485760 # Set the maximum size of log files (40MB) set_lg_max 41943040 # Automatically remove log files as soon as they are no longer needed set_flags DB_LOG_AUTOREMOVE # Do not write or synchronously flush the log on transaction commit set_flags DB_TXN_NOSYNC # Set the size of the shared memory buffer pool (gbytes, bytes, ncache) set_cachesize 0 524288000 1 bdii-5.2.25/etc/bdii-slapd.conf000066400000000000000000000071541335470071400161610ustar00rootroot00000000000000include /etc/openldap/schema/core.schema include /etc/openldap/schema/cosine.schema include /etc/openldap/schema/nis.schema include /etc/bdii/BDII.schema include /etc/ldap/schema/Glue-CORE.schema include /etc/ldap/schema/Glue-MDS.schema include /etc/ldap/schema/Glue-CE.schema include /etc/ldap/schema/Glue-CESEBind.schema include /etc/ldap/schema/Glue-SE.schema include /etc/ldap/schema/GLUE20.schema allow bind_v2 pidfile /var/run/bdii/db/slapd.pid argsfile /var/run/bdii/db/slapd.args loglevel 0 idletimeout 120 sizelimit unlimited timelimit 2400 moduleload rwm moduleload back_relay ####################################################################### # GLUE 1.3 database definitions ####################################################################### database hdb suffix "o=grid" cachesize 30000 checkpoint 1024 0 dbnosync rootdn "o=grid" rootpw secret directory /var/lib/bdii/db/grid index GlueCEAccessControlBaseRule eq index GlueCESEBindCEUniqueID eq index GlueCESEBindSEUniqueID eq index GlueCEUniqueID eq index GlueChunkKey eq index GlueClusterUniqueID eq index GlueSAAccessControlBaseRule eq index GlueSALocalID eq index GlueSEAccessProtocolType pres index GlueSEUniqueID eq index GlueServiceAccessControlRule eq index GlueServiceAccessControlBaseRule eq index GlueServiceType eq,sub index GlueServiceEndpoint eq,sub index GlueServiceURI eq,sub index GlueServiceDataKey eq index GlueSubClusterUniqueID eq index GlueVOInfoAccessControlBaseRule eq index objectClass eq,pres ####################################################################### # Relay DB to address DIT changes requested by ARC ####################################################################### database relay suffix "GLUE2GroupName=services,o=glue" overlay rwm suffixmassage "GLUE2GroupID=resource,o=glue" database relay suffix "GLUE2GroupName=services,GLUE2DomainID=*,o=glue" overlay rwm suffixmassage "GLUE2GroupID=resource,GLUE2DomainID=*,o=glue" database relay suffix "GLUE2GroupName=services,GLUE2DomainID=*,GLUE2GroupName=grid,o=glue" overlay rwm suffixmassage "GLUE2GroupID=resource,GLUE2DomainID=*,GLUE2GroupID=grid,o=glue" ####################################################################### # GLUE 2.0 database definitions ####################################################################### database hdb suffix "o=glue" cachesize 30000 checkpoint 1024 0 dbnosync rootdn "o=glue" rootpw secret directory /var/lib/bdii/db/glue index GLUE2GroupID eq index GLUE2ExtensionLocalID eq index GLUE2LocationID eq index GLUE2ContactID eq index GLUE2DomainID eq index GLUE2ServiceID eq index GLUE2EndpointID eq index GLUE2ShareID eq index GLUE2ManagerID eq index GLUE2ResourceID eq index GLUE2ActivityID eq index GLUE2PolicyID eq index GLUE2BenchmarkID eq index GLUE2ApplicationEnvironmentID eq index GLUE2ApplicationHandleID eq index GLUE2ToStorageServiceID eq index GLUE2StorageServiceCapacityID eq index GLUE2StorageAccessProtocolID eq index GLUE2StorageShareSharingID eq index GLUE2StorageShareCapacityID eq index GLUE2EndpointInterfaceName eq index GLUE2PolicyRule eq index objectClass eq,pres ####################################################################### # Stats database definitions ####################################################################### database hdb suffix "o=infosys" cachesize 10 checkpoint 1024 0 dbnosync rootdn "o=infosys" rootpw secret directory /var/lib/bdii/db/stats bdii-5.2.25/etc/bdii-top-slapd.conf000066400000000000000000000076431335470071400167640ustar00rootroot00000000000000include /etc/openldap/schema/core.schema include /etc/openldap/schema/cosine.schema include /etc/openldap/schema/nis.schema include /etc/bdii/BDII.schema include /etc/ldap/schema/Glue-CORE.schema include /etc/ldap/schema/Glue-MDS.schema include /etc/ldap/schema/Glue-CE.schema include /etc/ldap/schema/Glue-CESEBind.schema include /etc/ldap/schema/Glue-SE.schema include /etc/ldap/schema/GLUE20.schema allow bind_v2 pidfile /var/run/bdii/db/slapd.pid argsfile /var/run/bdii/db/slapd.args loglevel 0 idletimeout 120 sizelimit unlimited timelimit 2400 threads 64 moduleload rwm moduleload back_relay ####################################################################### # GLUE 1.3 database definitions ####################################################################### database hdb cachesize 300000 dbnosync suffix "o=shadow" checkpoint 1024 0 rootdn "o=shadow" rootpw secret directory /var/lib/bdii/db/grid index GlueCEAccessControlBaseRule eq index GlueCESEBindCEUniqueID eq index GlueCESEBindSEUniqueID eq index GlueCEUniqueID eq index GlueChunkKey eq index GlueClusterUniqueID eq index GlueSAAccessControlBaseRule eq index GlueSALocalID eq index GlueSEAccessProtocolType pres index GlueSEUniqueID eq index GlueServiceAccessControlRule eq index GlueServiceAccessControlBaseRule eq index GlueServiceType eq,sub index GlueServiceEndpoint eq,sub index GlueServiceURI eq,sub index GlueServiceDataKey eq index GlueSubClusterUniqueID eq index GlueVOInfoAccessControlBaseRule eq index objectClass eq,pres ####################################################################### # Relay DB to address performance issues ####################################################################### database relay suffix "o=grid" overlay rwm suffixmassage "o=grid,o=shadow" ####################################################################### # Relay DB to address DIT changes requested by ARC ####################################################################### database relay suffix "GLUE2GroupName=services,o=glue" overlay rwm suffixmassage "GLUE2GroupID=resource,o=glue" database relay suffix "GLUE2GroupName=services,GLUE2DomainID=*,o=glue" overlay rwm suffixmassage "GLUE2GroupID=resource,GLUE2DomainID=*,o=glue" database relay suffix "GLUE2GroupName=services,GLUE2DomainID=*,GLUE2GroupName=grid,o=glue" overlay rwm suffixmassage "GLUE2GroupID=resource,GLUE2DomainID=*,GLUE2GroupID=grid,o=glue" ####################################################################### # GLUE 2.0 database definitions ####################################################################### database hdb cachesize 300000 dbnosync suffix "o=glue" checkpoint 1024 0 rootdn "o=glue" rootpw secret directory /var/lib/bdii/db/glue index GLUE2GroupID eq index GLUE2ExtensionLocalID eq index GLUE2LocationID eq index GLUE2ContactID eq index GLUE2DomainID eq index GLUE2ServiceID eq index GLUE2EndpointID eq index GLUE2ShareID eq index GLUE2ManagerID eq index GLUE2ResourceID eq index GLUE2ActivityID eq index GLUE2PolicyID eq index GLUE2BenchmarkID eq index GLUE2ApplicationEnvironmentID eq index GLUE2ApplicationHandleID eq index GLUE2ToStorageServiceID eq index GLUE2StorageServiceCapacityID eq index GLUE2StorageAccessProtocolID eq index GLUE2StorageShareSharingID eq index GLUE2StorageShareCapacityID eq index GLUE2EndpointInterfaceName eq index GLUE2PolicyRule eq index objectClass eq,pres ####################################################################### # Stats database definitions ####################################################################### database hdb cachesize 10 dbnosync suffix "o=infosys" checkpoint 1024 0 rootdn "o=infosys" rootpw secret directory /var/lib/bdii/db/stats bdii-5.2.25/etc/bdii.conf000066400000000000000000000005651335470071400150570ustar00rootroot00000000000000BDII_LOG_FILE=/var/log/bdii/bdii-update.log BDII_PID_FILE=/var/run/bdii/bdii-update.pid BDII_LOG_LEVEL=ERROR BDII_LDIF_DIR=/var/lib/bdii/gip/ldif BDII_PROVIDER_DIR=/var/lib/bdii/gip/provider BDII_PLUGIN_DIR=/var/lib/bdii/gip/plugin BDII_PORT=2170 BDII_BREATHE_TIME=120 BDII_READ_TIMEOUT=300 BDII_ARCHIVE_SIZE=0 BDII_DELETE_DELAY=0 BDII_USER=ldap BDII_VAR_DIR=/var/lib/bdii bdii-5.2.25/etc/default.ldif000066400000000000000000000006671335470071400155700ustar00rootroot00000000000000dn: o=shadow objectClass: organization o: o=shadow dn: o=grid objectClass: organization o: grid dn: mds-vo-name=local,o=grid objectClass: MDS mds-vo-name: local dn: mds-vo-name=resource,o=grid objectClass: MDS mds-vo-name: resource dn: o=glue objectClass: organization o: glue dn: GLUE2GroupID=resource, o=glue objectClass: GLUE2Group GLUE2GroupID: resource dn: GLUE2GroupID=grid, o=glue objectClass: GLUE2Group GLUE2GroupID: grid bdii-5.2.25/etc/init.d/000077500000000000000000000000001335470071400144605ustar00rootroot00000000000000bdii-5.2.25/etc/init.d/bdii000077500000000000000000000265661335470071400153340ustar00rootroot00000000000000#! /bin/bash # # BDII system startup script # $Id: bdii,v 1.9 2009/06/18 14:26:52 lfield Exp $ # chkconfig: - 95 5 # description: BDII Service # config: /etc/bdii/bdii.conf ### BEGIN INIT INFO # Provides: bdii # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Stop: 0 1 2 3 4 5 6 # Short-Description: BDII # Description: Berkeley Database Information Index ### END INIT INFO shopt -s expand_aliases if [ -f /etc/init.d/functions ]; then . /etc/init.d/functions else echo "Error: Cannot source /etc/init.d/functions" fi log_success_msg () { success echo } log_failure_msg() { failure echo } prog=bdii # Debian does not have /var/lock/subsys if [ -d /var/lock/subsys ] ; then LOCK_DIR=/var/lock/subsys else LOCK_DIR=/var/lock fi lockfile=${LOCK_DIR}/$prog # TODO is it necessary?? RUN=yes if [ -r /etc/default/bdii ] ; then . /etc/default/bdii fi if [ -r /etc/sysconfig/bdii ] ; then . /etc/sysconfig/bdii fi if [ "x$RUN" != "xyes" ] ; then echo "bdii disabled, please adjust the configuration to your needs " echo "and then set RUN to 'yes' in /etc/default/bdii to enable it." exit 0 fi BDII_CONF=${BDII_CONF:-/etc/bdii/bdii.conf} if [ -f "${BDII_CONF}" ]; then . "${BDII_CONF}" fi UPDATE_LOCK_FILE=${UPDATE_LOCK_FILE:-${LOCK_DIR}/bdii-update} SLAPD_LOCK_FILE=${SLAPD_LOCK_FILE:-${LOCK_DIR}/bdii-slapd} UPDATE_PID_FILE=${BDII_PID_FILE:-/var/run/bdii/bdii-update.pid} BDII_USER=${BDII_USER:-ldap} BDII_VAR_DIR=${BDII_VAR_DIR:-/var/lib/bdii} BDII_UPDATE=${BDII_UPDATE:-/usr/sbin/bdii-update} SLAPD=${SLAPD:-/usr/sbin/slapd} SLAPD_CONF=${SLAPD_CONF:-/etc/bdii/bdii-slapd.conf} SLAPD_HOST=${SLAPD_HOST:-0.0.0.0} SLAPD_PORT=${SLAPD_PORT:-2170} BDII_IPV6_SUPPORT=${BDII_IPV6_SUPPORT:-no} SLAPD_HOST6=${SLAPD_HOST6:-::} SLAPD_DB_DIR=${SLAPD_DB_DIR:-$BDII_VAR_DIR/db} SLAPD_PID_FILE=${SLAPD_PID_FILE:-/var/run/bdii/db/slapd.pid} DB_CONFIG=${DB_CONFIG:-/etc/bdii/DB_CONFIG} DELAYED_DELETE=${DELAYED_DELETE:-${BDII_VAR_DIR}/delayed_delete.pkl} BDII_RAM_SIZE=${BDII_RAM_SIZE:-1500M} if [ "x${BDII_IPV6_SUPPORT}" == "xyes" ]; then SLAPD_HOST_STRING="'ldap://${SLAPD_HOST}:${SLAPD_PORT} ldap://[${SLAPD_HOST6}]:${SLAPD_PORT}'" else SLAPD_HOST_STRING="ldap://${SLAPD_HOST}:${SLAPD_PORT}" fi if [ -x /sbin/runuser ] ; then RUNUSER=/sbin/runuser else RUNUSER=su fi # Return code # 0 process is running # 1 process has been stopped (correctly) # 2 process has been aborted function check_slapd(){ if [ ! -f "${SLAPD_PID_FILE}" ] ; then [ ! -f "${SLAPD_LOCK_FILE}" ] || return 2 return 1 fi ps $(cat ${SLAPD_PID_FILE}) >/dev/null 2>&1 if [ $? -eq 0 ]; then return 0 fi if [ -n "${1}" -a "${1}" == "reset" ]; then rm -f ${SLAPD_PID_FILE} rm -f ${SLAPD_LOCK_FILE} fi return 2 } function check_updater(){ if [ ! -f "${UPDATE_PID_FILE}" ]; then [ ! -f "${UPDATE_LOCK_FILE}" ] || return 2 return 1 fi ps $(cat ${UPDATE_PID_FILE}) >/dev/null 2>&1 if [ $? -eq 0 ]; then return 0 fi if [ -n "${1}" -a "${1}" == "reset" ]; then rm -f ${UPDATE_PID_FILE} rm -f ${UPDATE_LOCK_FILE} fi return 2 } function check_updater_hanging(){ # Check for hanging process response=$(ldapsearch -LLL -x -h ${SLAPD_HOST} -p ${SLAPD_PORT} -b o=infosys objectClass=UpdateStats modifyTimestamp 2>/dev/null | grep modifyTimestamp ) if [ $? -eq 0 ]; then time_stamp=$(echo ${response} | cut -d" " -f2) time_string=$(echo ${time_stamp} | sed 's/^\([0-9][0-9][0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\).*/\1-\2-\3 \4:\5/') time_int=$(date --utc --date "${time_string}" +%s) let time_threshold=${time_int}+1200 time_now=$(date --utc +%s) if [ ${time_now} -gt ${time_threshold} ]; then return 1 fi fi return 0 # TODO failsafe? } function start(){ # Check status check_slapd reset RETVAL1=$? check_updater reset RETVAL2=$? if [ $RETVAL1 -eq 0 -a $RETVAL2 -eq 0 ] ; then echo "BDII already started" exit 0 fi # Create RAM Disk if [ "${BDII_RAM_DISK}" = "yes" ]; then mkdir -p ${SLAPD_DB_DIR} mount -t tmpfs -o size=${BDII_RAM_SIZE},mode=0744 tmpfs ${SLAPD_DB_DIR} fi # Remove delayed_delete.pkl if it exists if [ -f "${DELAYED_DELETE}" ] ; then rm -f ${DELAYED_DELETE} fi #Initialize the database directory. mkdir -p ${SLAPD_DB_DIR}/stats mkdir -p ${SLAPD_DB_DIR}/glue mkdir -p ${SLAPD_DB_DIR}/grid mkdir -p ${BDII_VAR_DIR}/archive chown -R ${BDII_USER}:${BDII_USER} ${BDII_VAR_DIR} chown -R ${BDII_USER}:${BDII_USER} ${SLAPD_DB_DIR} [ -x /sbin/restorecon ] && /sbin/restorecon -R ${BDII_VAR_DIR} mkdir -p /var/run/bdii/db chown -R ${BDII_USER}:${BDII_USER} /var/run/bdii [ -x /sbin/restorecon ] && /sbin/restorecon -R /var/run/bdii/db $RUNUSER -s /bin/sh ${BDII_USER} -c "rm -f ${SLAPD_DB_DIR}/stats/* 2>/dev/null" $RUNUSER -s /bin/sh ${BDII_USER} -c "rm -f ${SLAPD_DB_DIR}/glue/* 2>/dev/null" $RUNUSER -s /bin/sh ${BDII_USER} -c "rm -f ${SLAPD_DB_DIR}/grid/* 2>/dev/null" $RUNUSER -s /bin/sh ${BDII_USER} -c "rm -f ${BDII_VAR_DIR}/old.ldif 2>/dev/null" $RUNUSER -s /bin/sh ${BDII_USER} -c "ln -sf ${DB_CONFIG} ${SLAPD_DB_DIR}/grid/" $RUNUSER -s /bin/sh ${BDII_USER} -c "ln -sf ${DB_CONFIG} ${SLAPD_DB_DIR}/stats/" $RUNUSER -s /bin/sh ${BDII_USER} -c "ln -sf ${DB_CONFIG} ${SLAPD_DB_DIR}/glue/" if [ ${SLAPD_CONF} = "/etc/bdii/bdii-top-slapd.conf" ] ; then $RUNUSER -s /bin/sh ${BDII_USER} -c "ln -sf ${DB_CONFIG}_top ${SLAPD_DB_DIR}/grid/DB_CONFIG" $RUNUSER -s /bin/sh ${BDII_USER} -c "ln -sf ${DB_CONFIG}_top ${SLAPD_DB_DIR}/stats/DB_CONFIG" $RUNUSER -s /bin/sh ${BDII_USER} -c "ln -sf ${DB_CONFIG}_top ${SLAPD_DB_DIR}/glue/DB_CONFIG" $RUNUSER -s /bin/sh ${BDII_USER} -c "rm -f ${BDII_VAR_DIR}/gip/cache/gip/top-urls.conf/* 2>/dev/null" $RUNUSER -s /bin/sh ${BDII_USER} -c "rm -f ${BDII_VAR_DIR}/gip/cache/gip/top-urls.conf-glue2/* 2>/dev/null" else if [ -r "${BDII_VAR_DIR}/gip/cache" ]; then $RUNUSER -s /bin/sh ${BDII_USER} -c "rm -f ${BDII_VAR_DIR}/gip/cache/gip/site-urls.conf/* 2>/dev/null" $RUNUSER -s /bin/sh ${BDII_USER} -c "rm -f ${BDII_VAR_DIR}/gip/cache/gip/site-urls.conf-glue2/* 2>/dev/null" fi fi if [ $RETVAL1 -ne 0 ] ; then cd /tmp echo -n "Starting BDII slapd: " COMMAND="${SLAPD} -f ${SLAPD_CONF} -h ${SLAPD_HOST_STRING} -u ${BDII_USER}" eval ${COMMAND} touch ${SLAPD_LOCK_FILE} [ -f "${SLAPD_PID_FILE}" ] || sleep 2 check_slapd reset RETVAL=$? if [ ${RETVAL} -gt 0 ]; then echo -n "BDII slapd failed to start" 1>&2 eval log_failure_msg # TODO check a sysconfig option DEBUG_MODE echo "${COMMAND} -d 256" ${COMMAND} -d 256 return 1 else eval log_success_msg fi fi if [ $RETVAL2 -ne 0 ] ; then cd /tmp echo -n "Starting BDII update process: " export SLAPD_CONF=${SLAPD_CONF} $RUNUSER -s /bin/sh ${BDII_USER} -c "sh -l -c '${BDII_UPDATE} -c ${BDII_CONF} -d'" touch ${UPDATE_LOCK_FILE} [ -f ${UPDATE_PID_FILE} ] || sleep 2 check_updater reset RETVAL=$? if [ ${RETVAL} -gt 0 ]; then echo -n "BDII update process failed to start" 1>&2 eval log_failure_msg return 1 else eval log_success_msg fi fi touch $lockfile return 0 } function stop(){ check_slapd RETVAL1=$? check_updater RETVAL2=$? RETVAL=0 echo -n "Stopping BDII update process: " if [ $RETVAL1 -gt 0 ] ; then echo -n "already stopped" 1>&2 eval log_success_msg else UPDATE_PID=$(cat ${UPDATE_PID_FILE}) $RUNUSER -s /bin/sh ${BDII_USER} -c "kill -15 ${UPDATE_PID} 2>/dev/null" ps ${UPDATE_PID} >/dev/null 2>&1 if [ $? = 0 ]; then sleep 2 ps ${UPDATE_PID} >/dev/null 2>&1 if [ $? = 0 ]; then $RUNUSER -s /bin/sh ${BDII_USER} -c "kill -9 ${UPDATE_PID} 2>/dev/null" sleep 2 ps ${UPDATE_PID} >/dev/null 2>&1 if [ $? = 0 ]; then echo -n "Could not kill BDII update process ${UPDATE_PID}" 1>&2 eval log_failure_msg RETVAL=1 fi fi fi if [ ${RETVAL} = 0 ]; then rm -f ${UPDATE_PID_FILE} rm -f ${UPDATE_LOCK_FILE} eval log_success_msg fi fi echo -n "Stopping BDII slapd: " if [ $RETVAL1 -gt 0 ] ; then echo -n "already stopped" 1>&2 eval log_success_msg else SLAPD_PID=$(cat ${SLAPD_PID_FILE}) $RUNUSER -s /bin/sh ${BDII_USER} -c "kill -15 ${SLAPD_PID} 2>/dev/null" ps ${SLAPD_PID} >/dev/null 2>&1 if [ $? = 0 ]; then sleep 2 ps ${SLAPD_PID} >/dev/null 2>&1 if [ $? = 0 ]; then $RUNUSER -s /bin/sh ${BDII_USER} -c "kill -9 ${SLAPD_PID} 2>/dev/null" sleep 2 ps ${SLAPD_PID} >/dev/null 2>&1 if [ $? = 0 ]; then echo -n "Could not kill BDII slapd process ${SLAPD_PID}" 1>&2 eval log_failure_msg RETVAL=2 fi fi fi if [ ${RETVAL} -ne 2 ]; then rm -f ${SLAPD_PID_FILE} rm -f ${SLAPD_LOCK_FILE} eval log_success_msg fi fi if [ ${RETVAL} -ne 0 ]; then return 1 else mountpoint -q ${SLAPD_DB_DIR} && umount ${SLAPD_DB_DIR} rm -f $lockfile return 0 fi } function status(){ check_slapd RETVAL1=$? check_updater RETVAL2=$? if [ $RETVAL1 -eq 1 ] ; then echo -n "BDII slapd stopped" 1>&2 eval log_success_msg elif [ $RETVAL1 -eq 2 ] ; then echo -n "BDII slapd aborted" 1>&2 eval log_failure_msg else echo -n "BDII slapd running " eval log_success_msg fi if [ $RETVAL2 -eq 1 ] ; then echo -n "BDII updater stopped" 1>&2 eval log_success_msg elif [ $RETVAL2 -eq 2 ] ; then echo -n "BDII updater aborted" 1>&2 eval log_failure_msg else check_updater_hanging RETVAL=$? if [ $RETVAL -eq 1 ] ; then echo -n "BDII update process hanging" 1>&2 eval log_failure_msg else echo -n "BDII updater running " eval log_success_msg fi fi if [ $RETVAL1 -eq 1 -a $RETVAL2 -eq 1 ] ; then return 3 fi if [ $RETVAL1 -eq 2 -o $RETVAL2 -eq 2 ] ; then return 1 fi return 0 } case "$1" in start) start RETVAL=$? ;; stop) stop RETVAL=$? ;; status) status RETVAL=$? ;; reload) ;; restart | force-reload) stop start RETVAL=$? ;; condrestart | try-restart) if [ -f ${SLAPD_LOCK_FILE} ] || [ -f ${UPDATE_LOCK_FILE} ]; then stop start RETVAL=$? fi ;; *) echo $"Usage: $0 {start|stop|restart|status|condrestart}" RETVAL=1 esac exit ${RETVAL} bdii-5.2.25/etc/logrotate.d/000077500000000000000000000000001335470071400155155ustar00rootroot00000000000000bdii-5.2.25/etc/logrotate.d/bdii000066400000000000000000000001521335470071400163450ustar00rootroot00000000000000/var/log/bdii/bdii-update.log { daily rotate 30 missingok compress copytruncate } bdii-5.2.25/etc/sysconfig/000077500000000000000000000000001335470071400152775ustar00rootroot00000000000000bdii-5.2.25/etc/sysconfig/bdii000066400000000000000000000001171335470071400161300ustar00rootroot00000000000000#SLAPD_CONF=/etc/bdii/bdii-slapd.conf #SLAPD=/usr/sbin/slapd #BDII_RAM_DISK=no bdii-5.2.25/man/000077500000000000000000000000001335470071400132735ustar00rootroot00000000000000bdii-5.2.25/man/bdii-update.1000066400000000000000000000015011335470071400155410ustar00rootroot00000000000000.TH BDII_UPDATE 1 .SH NAME bdii-update \- the bdii update process .SH SYNOPSIS .B bdii-update [-d ] -c .I config-file .SH DESCRIPTION The .B bdii-update process obtains the LDIF by reading files found the .B ldif directory, running providers found in the .B provider directory and running plugins found in the .B plugin directory. The difference between providers and plugins is that providers return complete entries and plugins provide modifications to existing entries. The process can be run either as a daemon that periodically syncronizes an LDAP database or as a command that will print the result to stdout. .SH OPTIONS .IP -d Run as a daemon process. .IP "-c config" The configuration to use. .SH FILES .I /etc/bdii.conf .RS The default configuration file. .SH AUTHOR Laurence Field bdii-5.2.25/tests/000077500000000000000000000000001335470071400136625ustar00rootroot00000000000000bdii-5.2.25/tests/ldif/000077500000000000000000000000001335470071400146005ustar00rootroot00000000000000bdii-5.2.25/tests/ldif/bad-data.ldif000066400000000000000000000022011335470071400170700ustar00rootroot00000000000000ofjsdafpojwfpjepjftg409uj4tg0p9ujq-p09jue43oimnvlfk lde'asjg 89u05u09u50950u8t0wre9ifvujaeifoweoifjerfw20povj -pfjrf u0m utofewfwe f]pewfg089uf 4e03thg430uvj 09w5sreu ghqa034jgh 0e9r ugv04u tg gegergepojewrgpjegtfg34]- au f9u09t0u t0u g4r utc4 30r9ti43q09t 8 q4309iotuj43ep0ijgt43aoi WEFJ E4ORFJVOEDJ gc jarvoj4a3 \gjo4rigfgt4hq4 ju6w5j[ kjapo4tgjverw-[p0iugje0rwp9iugt-43it fewnat fewgerj4nwg regrqegrjheqgrqegqregrqe grehytr5yh yg gregregregregreg grhtr5ge3a[p'\orgm ,i-035u4i w-y bhreAeqgq5 email mat regu77uquij2n w0ms8 qu9j4u[smjvy5[m 209q y0ub62[VLREOI;KARIJTG09 y9wubv2q0wuj g[ ywv]0 erfy6 htrREFE-0RO9U3E0O T3209U7FGTE439UT5 1QITUGE09W[RUT[0U TG4WEUGT340UTG0[WE9U[05N TGW0EUG43Q09UTGERW0[9UG[09TUGFVSDF[0 G4[3UGV BD[FZ09UT1[03J GV OISJRT[JV OIJf09uj4[G0UBH[09UFvb09ujRD{f)q(ujV[09URf[0q(ufv[091URDF0[qb098OIHOoijoj jhyyu7t jj5yt kw45v[ w094u0w945u m[0j v[0w c0jw40 ujyv540uj vq1]u yh-09 w] eytjytj jwrjtrjhtrghlkermjglkerwmjflkrwejflkrwejg \r ;ljgrel;pkjrelknjregt ikjw-9 ]pf2pojne[poj][pkf ]polprnpomjfgpomjfpoj EPOLZM4[PKJFpinhg]PKIGpi3REPNJGpnj G[OJGponhgPONJP[G[pjG]PK] [klgEPONJ[POkj[fg;x-]f[4reolf;pem3 t';epofjEOLMJFGEPIAJGP4RIJNG bdii-5.2.25/tests/ldif/default.ldif000066400000000000000000000006631335470071400170710ustar00rootroot00000000000000dn: o=shadow objectClass: organization o: shadow dn: o=grid objectClass: organization o: grid dn: mds-vo-name=local,o=grid objectClass: MDS mds-vo-name: local dn: mds-vo-name=resource,o=grid objectClass: MDS mds-vo-name: resource dn: o=glue objectClass: organization o: glue dn: GLUE2GroupID=resource, o=glue objectClass: GLUE2Group GLUE2GroupID: resource dn: GLUE2GroupID=grid, o=glue objectClass: GLUE2Group GLUE2GroupID: grid bdii-5.2.25/tests/ldif/nordugrid.ldif000066400000000000000000000001411335470071400174310ustar00rootroot00000000000000dn: mds-vo-name=nordugrid_1,o=Grid objectclass: Mds objectclass: GlueTop mds-vo-name: nordugrid_1bdii-5.2.25/tests/ldif/service-bad-suffix.ldif000066400000000000000000000007261335470071400211330ustar00rootroot00000000000000dn: GlueServiceUniqueID=service_bad_suffix,mds-vo-name=resource,o=bad objectClass: GlueTop objectClass: GlueService objectClass: GlueKey objectClass: GlueSchemaVersion GlueServiceUniqueID: service_bad_suffix GlueServiceName: Test Service Bad Suffix GlueServiceType: bdii GlueServiceVersion: 3.0.0 GlueServiceEndpoint: ldap://host-invalid:2170/mds-vo-name=resource,o=grid GlueForeignKey: GlueSiteUniqueID=my-site-name GlueSchemaVersionMajor: 1 GlueSchemaVersionMinor: 3 bdii-5.2.25/tests/ldif/service-encoding.ldif000066400000000000000000000022721335470071400206670ustar00rootroot00000000000000dn: GlueServiceUniqueID=service_\\slash,mds-vo-name=resource,o=grid objectClass: GlueTop objectClass: GlueService GlueServiceUniqueID: service_\slash dn: GlueServiceUniqueID=service_\,comma,mds-vo-name=resource,o=grid objectClass: GlueTop objectClass: GlueService GlueServiceUniqueID: service_,comma dn: GlueServiceUniqueID=service_\=equals,mds-vo-name=resource,o=grid objectClass: GlueTop objectClass: GlueService GlueServiceUniqueID: service_=equals dn: GlueServiceUniqueID=service_\+plus,mds-vo-name=resource,o=grid objectClass: GlueTop objectClass: GlueService GlueServiceUniqueID: service_+plus dn: GlueServiceUniqueID=service_\;semi,mds-vo-name=resource,o=grid objectClass: GlueTop objectClass: GlueService GlueServiceUniqueID: service_;semi dn: GlueServiceUniqueID=service_\"quote,mds-vo-name=resource,o=grid objectClass: GlueTop objectClass: GlueService GlueServiceUniqueID: service_"quote dn: GlueServiceUniqueID=service_\>greater,mds-vo-name=resource,o=grid objectClass: GlueTop objectClass: GlueService GlueServiceUniqueID: service_>greater dn: GlueServiceUniqueID=service_\ ${working_dir}/bdii.conf sed -i "s#BDII_READ_TIMEOUT=.*#BDII_READ_TIMEOUT=3#" ${working_dir}/bdii.conf sed -i "s#BDII_BREATHE_TIME=.*#BDII_BREATHE_TIME=10#" ${working_dir}/bdii.conf sed -i "s#BDII_DELETE_DELAY=.*#BDII_DELETE_DELAY=2#" ${working_dir}/bdii.conf sed -i "s#ERROR#DEBUG#" ${working_dir}/bdii.conf sed -i "s#/var/log/bdii#${working_dir}#" ${working_dir}/bdii.conf export BDII_CONF=${working_dir}/bdii.conf /etc/init.d/bdii restart command="ldapsearch -LLL -x -h $(hostname -f) -p 2170 -b o=grid" command_glue2="ldapsearch -LLL -x -h $(hostname -f) -p 2170 -b o=glue" RETVAL=0 echo "Waiting 10 seconds for the BDII to start." sleep 10 echo -n "Testing the timeout for hanging providers: " ${command} >/dev/null 2>/dev/null if [ $? -eq 32 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing static LDIF file: " filter=GlueServiceUniqueID ${command} ${filter} | grep "service_1" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing GLUE2 service: " filter=objectClass=GLUE2Service ${command_glue2} ${filter} | grep "glue2-service" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing GlueTop modification: " filter="objectClass=MDS" ${command} ${filter} | grep "nordugrid_1" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing provider: " filter="GlueServiceUniqueID=service_2" ${command} ${filter} | grep "service_2" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing GLUE2 provider: " filter="objectClass=GLUE2Service" ${command_glue2} ${filter} | grep "cream-06" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing the handling of long DNs: " filter="GlueServiceUniqueID" ${command} ${filter} | grep "really_long" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing ignoring of junk files: " filter="GlueServiceUniqueID=service_4" ${command} ${filter} | grep "service_4" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "OK" else echo "FAIL" RETVAL=1 fi echo -n "Testing basic plugin: " filter=GlueServiceStatus=Failed ${command} ${filter} | grep "Failed" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing plugin mutlivalued delete: " filter=GlueServiceAccessControlBaseRule ${command} ${filter} | grep "atlas" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "OK" else echo "FAIL" RETVAL=1 fi echo -n "Testing plugin mutlivalued add: " filter=GlueServiceAccessControlBaseRule=cms ${command} ${filter} | grep "cms" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing plugin modify: " filter=GlueServiceStatusInfo ${command} ${filter} | grep "Broken" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing two plugins extending the attribute: " filter="GlueServiceUniqueID=service_7" ${command} ${filter} | grep "vo_1" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi rm -f ${working_dir}/ldif/service-long-dn.ldif rm -f ${working_dir}/ldif/service-unstable.ldif rm -f ${working_dir}/ldif/service-spaces-2.ldif sed -i "s#Failed#Unknown#" ${working_dir}/plugin/service-status sed -i "s#=# = #" ${working_dir}/ldif/service-spaces-1.ldif sed -i "s#2011-02-07T10:57:48Z#2011-02-07T10:58:57Z#" ${working_dir}/provider/glue2-provider echo "Wating for update ..." sleep 14 echo -n "Testing modify on update: " filter=GlueServiceStatus=Unknown ${command} ${filter} | grep "Unknown" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing modify GLUE2 Service: " filter=objectClass=GLUE2Service ${command_glue2} ${filter} | grep "GLUE2_Serivce_OK" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing GLUE2 provider updated: " filter="objectClass=GLUE2Service" ${command_glue2} ${filter} | grep "2011-02-07T10:58:57Z" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing delayed delete: " filter=GlueServiceUniqueID ${command} ${filter} | grep "_long_" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing ignoring spaces in dn: " filter=GlueServiceUniqueID ${command} ${filter} | grep "service_5" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing dn special character '\': " filter=GlueServiceUniqueID ${command} ${filter} | grep "slash" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing dn special character ',': " filter=GlueServiceUniqueID ${command} ${filter} | grep "comma" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing dn special character '=': " filter=GlueServiceUniqueID ${command} ${filter} | grep "equal" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing dn special character '+': " filter=GlueServiceUniqueID ${command} ${filter} | grep "plus" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing dn special character '\"': " filter=GlueServiceUniqueID ${command} ${filter} | grep "quote" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing dn special character ';': " filter=GlueServiceUniqueID ${command} ${filter} | grep "semi" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing dn special character '<': " filter=GlueServiceUniqueID ${command} ${filter} | grep "less" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing dn special character '>': " filter=GlueServiceUniqueID ${command} ${filter} | grep "greater" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi cp ldif/service-unstable.ldif -f ${working_dir}/ldif/ echo "Wating for update ..." sleep 13 echo -n "Testing deleting obsolete entry: " filter=GlueServiceUniqueID ${command} ${filter} | grep "_long_" >/dev/null 2>/dev/null if [ ! $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing delete with space in uniqueID: " filter=GlueServiceUniqueID ${command} ${filter} | grep "service 6" >/dev/null 2>/dev/null if [ ! $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo -n "Testing unstable service is not deleted: " filter=GlueServiceUniqueID ${command} ${filter} | grep "service_7" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi rm -f ${working_dir}/ldif/service-unstable.ldif echo "Wating for update ..." sleep 13 echo -n "Testing unstable service is not deleted: " filter=GlueServiceUniqueID ${command} ${filter} | grep "service_7" >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi echo "Wating for update ..." sleep 13 echo -n "Testing unstable service is deleted: " filter=GlueServiceUniqueID ${command} ${filter} | grep "service_7" >/dev/null 2>/dev/null if [ ! $? -gt 0 ]; then echo "FAIL" RETVAL=1 else echo "OK" fi /etc/init.d/bdii stop mv ${working_dir}/bdii-update.log /tmp rm -rf ${working_dir} if [ ${RETVAL} -eq 1 ]; then echo "Test Failed" exit 1 else echo "Test Passed" exit 0 fi