pax_global_header00006660000000000000000000000064122367174650014527gustar00rootroot0000000000000052 comment=7838d063e33246ec21cf58060ced35e3771b5e82 bcfg2-1.3.3/000077500000000000000000000000001223671746500125165ustar00rootroot00000000000000bcfg2-1.3.3/.gitignore000066400000000000000000000003241223671746500145050ustar00rootroot00000000000000*.py[co] # vim *.swp *.swo # Packages build/ dist/ src/Bcfg2 *.egg *.egg-info # ctags tags TAGS # test artifacts testsuite/test.sqlite # rope project metadata .ropeproject # sphinx build data man/.doctrees bcfg2-1.3.3/.travis.yml000066400000000000000000000006741223671746500146360ustar00rootroot00000000000000language: python python: - "2.5" - "2.6" - "2.7" env: - WITH_OPTIONAL_DEPS=yes - WITH_OPTIONAL_DEPS=no before_install: - testsuite/before_install.sh install: - testsuite/install.sh - pip install --use-mirrors -e . script: - nosetests testsuite branches: except: - maint-1.2 - 1.1.0-stable notifications: email: chris.a.st.pierre@gmail.com irc: channels: - "irc.freenode.org#bcfg2" use_notice: true bcfg2-1.3.3/COPYRIGHT000066400000000000000000000126741223671746500140230ustar00rootroot00000000000000This file contains a list of copyright holders. Anyone who contributes more than trivial fixes (typos, etc.) to Bcfg2 should also add themselves to this file. See LICENSE for the full license. - Narayan Desai has written most of Bcfg2, including all parts not explicitly mentioned in this file. - Sol Jerome squashes bugs, helps manage the project roadmap, and implements various interesting features. - Tim Laszlo worked on the reporting system and made plugins. - Fabian Affolter made some patches, added some new features and plugins, and restructured the manual for Bcfg2. - Andrew Brestick fixed bugs and completed plugins. - James Yang worked on bcfg2-admin and bcfg2-reports. - Robert Gogolok fixed bugs and made the code more robust. - Jack Neely worked on the YUM driver. - Joey Hagedorn has written the reporting subsystem, including StatReports, GenerateHostinfo, and the xslt, css and javascript associated with it. - Ed Smith has done substantial hardening of the Bcfg2 client and server and implemented a common logging infrastructure. - Rick Bradshaw has written several of the tools included in the tools/ subdirectory. - Ken Raffenetti , Rick Bradshaw, Rene Martin, and David Dahl have written the Hostbase plugin. - Scott Behrens and Rick Bradshaw have written the VHost plugin. - Cory Lueninghoener wrote the showentries function in bcfg2-info. - Chris Vuletich wrote some SSL code and the verification debugging code. - Daniel Clark created encap packages for Bcfg2 and deps, wrote fossil-scm dvcs support, and helps with debian packaging - Jason Pepas has written a rpm package list creator has contributed patches to the Red Hat toolset. - Sami Haahtinen has writen debian packaging logic. - Brian Pellin and Andrew Lusk did substantial work on Bcfg1, some of which was used in the Bcfg2 client. - Michael Jinks wrote the gentoo tool drivers. - Chris St. Pierre has (re)written vast swaths of more recent Bcfg2 releases. - Anatoly Techtonik has fixed various bugs. - Arto Jantunen maintains the Debian packages. - Asaf Ohaion added Pacman support. - Brent Bloxam fixed bugs, particularly in the documentation. - Calen Pennington write bcfg2-test and contributed performance enhancements. - Calvin Cheng worked on Python packaging. - Carl Jackson fixed client-side bugs. - Chris Brinker added support for client profile assertion to bcfg2.conf - Christopher 'm4z' Holm greatly improved the RPM build logic. - Dan Foster contributed Solaris 10 build fixes. - David Strauss wrote the Bzr plugin and contributed other various fixes. - Gordon Messmer contributed documentation fixes. - Graham Hagger wrote the SSLCA plugin. - Holger Weiß has fixed a tremendous number and variety of bugs, particularly with unicode handling, SSHbase, and bcfg2-reports. - Jake Davis has fixed various bugs. - Jason Kincl added conflict resolution to the Svn plugin. - Jeffrey C. Ollie wrote systemd support. - Jeroen Dekkers worked on the APT driver. - Joe Digilio worked on Cheetah support and fixed other bugs. - John Morris fixed bugs in the Chkconfig driver. - John 'Skip' Reddy worked on DBStats. - Jonathan Billings worked on systemd support, RPM builds, and fixed other bugs. - Kamil Kisiel worked on documentation, Py3k support, launchd support, and other bugs. - Kioob fixed various bugs. - Luke Cyca worked on MacPorts and launchd support. - Mike Brady worked on the YUM and RPM drivers. - Mike McCallister worked on the Packages plugin. - Phillip Steinbachs wrote Solaris packaging manifests. - Raul Cuza worked on Python packaging, documentation, and various bugs. - Remi Broemeling worked on handling restarts of Service entries. - Richard Connon worked on handling of Apt repositories. - Steve Tousignant worked on several of the Debian package list tools and contributed bug fixes. - Ti Legget worked on ebuild packaging and bugfixes, RPM packaging. - Torsten Rehn wrote the Ldap plugin and fixed bugs. - Zach Lowry wrote Solaris support and general hardening. - Michael Fenn fixed various small bugs related to bcfg2 on CentOS 5 - Alexander Sulfrian fixed various bugs. bcfg2-1.3.3/LICENSE000066400000000000000000000044431223671746500135300ustar00rootroot00000000000000Copyright (c) 2004, University of Chicago. See the COPYRIGHT file at the top-level directory of this distribution and at https://github.com/Bcfg2/bcfg2/blob/master/COPYRIGHT. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. GOVERNMENT LICENSE Portions of this material resulted from work developed under a U.S. Government Contract and are subject to the following license: the Government is granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable worldwide license in this computer software to reproduce, prepare derivative works, and perform publicly and display publicly. DISCLAIMER This computer code material was prepared, in part, as an account of work sponsored by an agency of the United States Government. Neither the United States, nor the University of Chicago, nor any of their employees, makes any warranty express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately owned rights. bcfg2-1.3.3/README000066400000000000000000000032051223671746500133760ustar00rootroot00000000000000Bcfg2 - A Configuration Management System ----------------------------------------- Bcfg2 (bee-config two) helps system administrators produce a consistent, reproducible, and verifiable description of their environment, and offers visualization and reporting tools to aid in day-to-day administrative tasks. It is the fifth generation of configuration management tools developed in the Mathematics and Computer Science Division of Argonne National Laboratory. * Homepage: http://bcfg2.org Bcfg2 is fairly portable. It has been successfully run on: * AIX, FreeBSD, OpenBSD Mac OS X, OpenSolaris, Solaris * Many GNU/Linux distributions, including ArchLinux, Blag, CentOS, Debian, Fedora, Gentoo, gNewSense, Mandriva, openSUSE, Red Hat/RHEL, SuSE/SLES, Trisquel, and Ubuntu. Installation ------------ For details about the installation of Bcfg2 please refer to the following pages in the Bcfg2 wiki. * Prerequisites: http://bcfg2.org/wiki/Prereqs * Download: http://bcfg2.org/wiki/Download * Installation: http://bcfg2.org/wiki/Install Need help --------- A lot of documentation is available in the Bcfg2 manual and the Bcfg2 wiki. * Documentation: http://docs.bcfg2.org/ * Wiki: http://bcfg2.org/wiki/ * FAQ: http://bcfg2.org/wiki/FAQ * IRC: #bcfg2 on chat.freenode.net * Mailing list: https://lists.mcs.anl.gov/mailman/listinfo/bcfg-dev Want to help ------------- * Bug tracker: http://bcfg2.org/report * Development: http://docs.bcfg2.org/development/ * Wiki: http://bcfg2.org/wiki/Contribute Bcfg2 is licensed under a Simplified (2-clause) BSD license. For more details check LICENSE. bcfg2-1.3.3/debian/000077500000000000000000000000001223671746500137405ustar00rootroot00000000000000bcfg2-1.3.3/debian/NEWS000066400000000000000000000011411223671746500144340ustar00rootroot00000000000000bcfg2 (1.1.0-1) unstable; urgency=low Due to repository changes made to support Path entries it is recommended to upgrade the clients before upgrading the server. After that, you can use the posixunified.py script (included as an example in the server package) to convert your repository. -- Arto Jantunen Fri, 05 Nov 2010 19:07:59 +0200 bcfg2 (1.0.1-1) unstable; urgency=low Since version 1.0, Bcfg2 no longer supports agent mode. Please update your configuration accordingly if you were using it. -- Arto Jantunen Tue, 03 Aug 2010 12:28:54 +0300 bcfg2-1.3.3/debian/bcfg2-doc.docs000066400000000000000000000000221223671746500163320ustar00rootroot00000000000000build/sphinx/html bcfg2-1.3.3/debian/bcfg2-server.README.Debian000066400000000000000000000015561223671746500202760ustar00rootroot00000000000000Getting started =============== The first thing to do is to create the repository and SSL key, these can be done by running "bcfg2-admin init" as root on the server. The script will ask questions about the SSL key, and a few things about the repository. After that is done, you should be able to start the server with the init script by running "/etc/init.d/bcfg2-server start" You can look at /var/log/daemon.log to see what the server said while starting up. If the server started without problems, you can run "bcfg2 -q -v -n" to see if the client and server are communicating properly. The next step after that is to actually populate the repository (by default in /var/lib/bcfg2/) with configuration files, see the Bcfg2 documentation at http://docs.bcfg2.org/ for information on how that is done. -- Arto Jantunen , Sat, 24 Apr 2010 09:43:44 +0300 bcfg2-1.3.3/debian/bcfg2-server.bcfg2-report-collector.init000077500000000000000000000051201223671746500233740ustar00rootroot00000000000000#!/bin/sh # # bcfg-report-collector - Bcfg2 reporting collector daemon # # chkconfig: 2345 19 81 # description: bcfg2 server for reporting data # ### BEGIN INIT INFO # Provides: bcfg2-report-collector # Required-Start: $network $remote_fs $named # Required-Stop: $network $remote_fs $named # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Configuration management Server # Description: Bcfg2 is a configuration management system that builds # installs configuration files served by bcfg2-server ### END INIT INFO # Include lsb functions . /lib/lsb/init-functions # Commonly used stuff DAEMON=/usr/sbin/bcfg2-report-collector PIDFILE=/var/run/bcfg2-server/bcfg2-report-collector.pid PARAMS="-D $PIDFILE" # Include default startup configuration if exists test -f "/etc/default/bcfg2-server" && . /etc/default/bcfg2-server # Exit if $DAEMON doesn't exist and is not executable test -x $DAEMON || exit 5 # Internal variables BINARY=$(basename $DAEMON) RETVAL=0 start () { echo -n "Starting Configuration Report Collector: " start_daemon ${DAEMON} ${PARAMS} ${BCFG2_REPORT_OPTIONS} STATUS=$? if [ "$STATUS" = 0 ] then log_success_msg "bcfg2-report-collector" test -d /var/lock/subsys && touch /var/lock/subsys/bcfg2-report-collector else log_failure_msg "bcfg2-report-collector" fi return $STATUS } stop () { echo -n "Stopping Configuration Report Collector: " if [ -f $PIDFILE ]; then killproc -p $PIDFILE ${BINARY} else killproc ${BINARY} fi STATUS=$? if [ "$STATUS" = 0 ]; then log_success_msg "bcfg2-report-collector" test -e /var/lock/subsys/bcfg2-report-collector && rm /var/lock/subsys/bcfg2-report-collector else log_failure_msg "bcfg2-report-collector" fi return $STATUS } status () { PID=$(pidofproc -p "$PIDFILE" $BINARY) if [ -n "$PID" ]; then echo "$BINARY (pid $PID) is running..." return 0 fi if [ -f $PIDFILE ]; then if [ -n "$PID" ]; then log_failure_msg "$BINARY dead but pid file exists..." return 1 fi fi log_failure_msg "$BINARY is not running" return 3 } case "$1" in start) start RETVAL=$? ;; stop) stop RETVAL=$? ;; status) status RETVAL=$? ;; restart|reload|force-reload) stop sleep 5 start RETVAL=$? ;; *) log_success_msg "Usage: $0 {start|stop|status|reload|restart|force-reload}" RETVAL=1 ;; esac exit $RETVAL bcfg2-1.3.3/debian/bcfg2-server.default000066400000000000000000000007551223671746500176040ustar00rootroot00000000000000# Configuration options for bcfg2 server # BCFG2_SERVER_OPTIONS: # Set the default options for Bcfg2 Server on startup # Default: "" #BCFG2_SERVER_OPTIONS="" # BCFG2_SERVER_ENABLED: # Should Bcfg2 Server be run automatically by system scripts # # Uncomment the following line to enable any of the below selections # Default: 0 (disable) BCFG2_SERVER_ENABLED=1 # BCFG2_REPORT_OPTIONS: # Set the default options for Bcfg2 Report Collector on startup # Default: "" #BCFG2_REPORT_OPTIONS bcfg2-1.3.3/debian/bcfg2-server.dirs000066400000000000000000000000161223671746500171070ustar00rootroot00000000000000var/lib/bcfg2 bcfg2-1.3.3/debian/bcfg2-server.docs000066400000000000000000000000221223671746500170730ustar00rootroot00000000000000LICENSE COPYRIGHT bcfg2-1.3.3/debian/bcfg2-server.examples000066400000000000000000000000161223671746500177640ustar00rootroot00000000000000tools/upgrade bcfg2-1.3.3/debian/bcfg2-server.init000077500000000000000000000053711223671746500171250ustar00rootroot00000000000000#!/bin/sh # # bcfg-server - Bcfg2 configuration daemon # # chkconfig: 2345 19 81 # description: bcfg2 server for configuration requests # ### BEGIN INIT INFO # Provides: bcfg2-server # Required-Start: $network $remote_fs $named $syslog # Required-Stop: $network $remote_fs $named $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Configuration management Server # Description: The server component of the Bcfg2 configuration management # system ### END INIT INFO # Include lsb functions test -f "/lib/lsb/init-functions" && . /lib/lsb/init-functions # debian test -f "/etc/init.d/functions" && . /etc/init.d/functions # redhat # Commonly used stuff DAEMON=/usr/sbin/bcfg2-server PIDFILE=/var/run/bcfg2-server/bcfg2-server.pid PARAMS="-D $PIDFILE" # Disabled per default BCFG2_SERVER_OPTIONS="" BCFG2_SERVER_ENABLED=0 # Include default startup configuration if exists test -f "/etc/default/bcfg2-server" && . /etc/default/bcfg2-server if [ "$BCFG2_SERVER_ENABLED" -eq 0 ] ; then log_failure_msg "bcfg2-server is disabled - see /etc/default/bcfg2-server" exit 0 fi # Exit if $DAEMON doesn't exist and is not executable test -x $DAEMON || exit 5 # Internal variables BINARY=$(basename $DAEMON) RETVAL=0 start () { echo -n "Starting Configuration Management Server: " start_daemon ${DAEMON} ${PARAMS} ${BCFG2_SERVER_OPTIONS} STATUS=$? if [ "$STATUS" = 0 ] then log_success_msg "bcfg2-server" test -d /var/lock/subsys && touch /var/lock/subsys/bcfg2-server else log_failure_msg "bcfg2-server" fi return $STATUS } stop () { echo -n "Stopping Configuration Management Server: " killproc -p $PIDFILE ${BINARY} STATUS=$? if [ "$STATUS" = 0 ]; then log_success_msg "bcfg2-server" test -d /var/lock/subsys && touch /var/lock/subsys/bcfg2-server else log_failure_msg "bcfg2-server" fi return $STATUS } status () { # Inspired by redhat /etc/init.d/functions status() call PID=$(pidof -x $BINARY -o %PPID) if [ -n "$PID" ]; then echo "$BINARY (pid $PID) is running..." return 0 fi if [ -f $PIDFILE ]; then if [ -n "$PID" ]; then log_failure_msg "$BINARY dead but pid file exists..." return 1 fi fi log_failure_msg "$BINARY is not running" return 3 } case "$1" in start) start RETVAL=$? ;; stop) stop RETVAL=$? ;; status) status RETVAL=$? ;; restart|reload|force-reload) stop sleep 5 start RETVAL=$? ;; *) log_success_msg "Usage: $0 {start|stop|status|reload|restart|force-reload}" RETVAL=1 ;; esac exit $RETVAL bcfg2-1.3.3/debian/bcfg2-server.install000066400000000000000000000005241223671746500176200ustar00rootroot00000000000000debian/bcfg2-server.default usr/share/bcfg2 debian/tmp/usr/bin/bcfg2-* usr/sbin debian/tmp/usr/lib/python*/*-packages/Bcfg2/Server/* debian/tmp/usr/lib/python*/*-packages/Bcfg2/Reporting/* debian/tmp/usr/share/bcfg2/Hostbase/* debian/tmp/usr/share/bcfg2/schemas/* debian/tmp/usr/share/bcfg2/xsl-transforms/* debian/tmp/usr/share/man/man8/* bcfg2-1.3.3/debian/bcfg2-server.logcheck.ignore.server000066400000000000000000000007041223671746500225200ustar00rootroot00000000000000^\w{3} [ :0-9]{11} [._[:alnum:]-]+ bcfg2-server\[[0-9]+\]: Processed [0-9]+ (fam|gamin) events in [0-9.]+ seconds\. [0-9]+ coalesced$ ^\w{3} [ :0-9]{11} [._[:alnum:]-]+ bcfg2-server\[[0-9]+\]: Generated config for [._[:alnum:]-]+ in [0-9.]+ s$ ^\w{3} [ :0-9]{11} [._[:alnum:]-]+ bcfg2-server\[[0-9]+\]: Client [._[:alnum:]-]+ reported state (clean|dirty)$ ^\w{3} [ :0-9]{11} [._[:alnum:]-]+ bcfg2-server\[[0-9]+\]: Suppressing event for bogus file .*$ bcfg2-1.3.3/debian/bcfg2-server.postinst000066400000000000000000000021071223671746500200340ustar00rootroot00000000000000#!/bin/sh # postinst script for bcfg2-server # # see: dh_installdeb(1) set -e # summary of how this script can be called: # * `configure' # * `abort-upgrade' # * `abort-remove' `in-favour' # # * `abort-deconfigure' `in-favour' # `removing' # # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package # case "$1" in configure) ucf /usr/share/bcfg2/bcfg2-server.default /etc/default/bcfg2-server if [ -x /usr/bin/ucfr ] ; then ucfr bcfg2 /etc/default/bcfg2-server fi ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 bcfg2-1.3.3/debian/bcfg2-server.postrm000066400000000000000000000021661223671746500175020ustar00rootroot00000000000000#!/bin/sh # postrm script for bcfg2-server # # see: dh_installdeb(1) set -e # summary of how this script can be called: # * `remove' # * `purge' # * `upgrade' # * `failed-upgrade' # * `abort-install' # * `abort-install' # * `abort-upgrade' # * `disappear' overwrit>r> # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package case "$1" in purge) for i in /etc/default/bcfg2-server; do rm -f $i if which ucf >/dev/null; then ucf --purge $i fi if which ucfr >/dev/null; then ucfr --purge bcfg2 $i fi done ;; remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) ;; *) echo "postrm called with unknown argument \`$1'" >&2 exit 1 esac # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 bcfg2-1.3.3/debian/bcfg2-web.install000066400000000000000000000001731223671746500170670ustar00rootroot00000000000000misc/apache/bcfg2.conf etc/apache2/conf.d/ debian/tmp/usr/share/bcfg2/reports.wsgi debian/tmp/usr/share/bcfg2/site_media/* bcfg2-1.3.3/debian/bcfg2.cron.daily000077500000000000000000000005661223671746500167210ustar00rootroot00000000000000#!/bin/sh BCFG2CRON= if [ -x /usr/libexec/bcfg2-cron ]; then BCFG2CRON=/usr/libexec/bcfg2-cron elif [ -x /usr/lib/bcfg2/bcfg2-cron ]; then BCFG2CRON=/usr/lib/bcfg2/bcfg2-cron elif type bcfg2-cron 2>&1 >/dev/null; then BCFG2CRON=bcfg2-cron else echo "No bcfg2-cron command found" exit 1 fi $BCFG2CRON --daily 2>&1 | logger -t bcfg2-cron -p daemon.info -i bcfg2-1.3.3/debian/bcfg2.cron.hourly000077500000000000000000000005671223671746500171420ustar00rootroot00000000000000#!/bin/sh BCFG2CRON= if [ -x /usr/libexec/bcfg2-cron ]; then BCFG2CRON=/usr/libexec/bcfg2-cron elif [ -x /usr/lib/bcfg2/bcfg2-cron ]; then BCFG2CRON=/usr/lib/bcfg2/bcfg2-cron elif type bcfg2-cron 2>&1 >/dev/null; then BCFG2CRON=bcfg2-cron else echo "No bcfg2-cron command found" exit 1 fi $BCFG2CRON --hourly 2>&1 | logger -t bcfg2-cron -p daemon.info -i bcfg2-1.3.3/debian/bcfg2.default000066400000000000000000000012161223671746500162710ustar00rootroot00000000000000# Configuration options for bcfg2 client # BCFG2_OPTIONS: # Set the default options for Bcfg2 on startup # Default: "-q" #BCFG2_OPTIONS="-q" # BCFG2_ENABLED: # Should Bcfg2 be run automatically by system scripts # # Uncomment the following line to enable any of the below selections # Default: 0 (disable) #BCFG2_ENABLED=1 # BCFG2_INIT: # Enable bcfg2 during system bootup # # Set value to 1 to enable # Default: 0 (disable) #BCFG2_INIT=1 # BCFG2_AGENT: # Bcfg2 no longer supports agent mode, please see NEWS.Debian # BCFG2_CRON: # Set the frequency of cron runs. # # Can be set to off, hourly, daily or both # Default: off #BCFG2_CRON=off bcfg2-1.3.3/debian/bcfg2.docs000066400000000000000000000000231223671746500155700ustar00rootroot00000000000000LICENSE COPYRIGHT bcfg2-1.3.3/debian/bcfg2.init000077500000000000000000000040521223671746500156140ustar00rootroot00000000000000#!/bin/sh # # bcfg2 - bcfg2 configuration client # # chkconfig: 2345 19 81 # description: bcfg2 client for configuration requests # ### BEGIN INIT INFO # Provides: bcfg2 # Required-Start: $network $remote_fs $named # Required-Stop: $network $remote_fs $named # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Configuration management client # Description: Bcfg2 is a configuration management system that builds # installs configuration files served by bcfg2-server # This is a client that installs the server provided # Configuration. ### END INIT INFO # This might need some better logic BCFG2=/usr/sbin/bcfg2 # Set default options # You can set script specific options with BCFG2_OPTIONS_INIT # You can set agent-mode specific options with BCFG2_OPTIONS_AGENT BCFG2_OPTIONS="-q" # Disabled per default BCFG2_ENABLED=0 BCFG2_INIT=0 BCFG2_AGENT=0 # Include default startup configuration if exists test -f "/etc/default/bcfg2" && . /etc/default/bcfg2 [ "$BCFG2_ENABLED" -eq 0 ] && exit 0 [ "$BCFG2_AGENT" -eq 0 -a "$BCFG2_INIT" -eq 0 ] && exit 0 # Exit if bcfg2 doesn't exist and is not executable test -x $BCFG2 || exit 0 if [ "$BCFG2_AGENT" != 0 ]; then echo "Bcfg2 no longer supports agent mode, please update your configuration!" exit 1 fi # Internal variables BINARY=$(basename $BCFG2) RETVAL=0 # Include lsb functions . /lib/lsb/init-functions start () { echo -n "Running configuration management client: " if [ "$BCFG2_INIT" -eq 1 ]; then ${BCFG2} ${BCFG2_OPTIONS} ${BCFG2_OPTIONS_INIT} STATUS=$? fi if [ "$STATUS" -eq 0 ] then log_success_msg "bcfg2" else log_failure_msg "bcfg2" fi return $STATUS } case "$1" in start) start RETVAL=$? ;; stop|status) RETVAL=0 ;; restart|force-reload) start RETVAL=$? ;; *) echo "Usage: $0 {start|stop|status|restart|force-reload}" RETVAL=1 ;; esac exit $RETVAL bcfg2-1.3.3/debian/bcfg2.install000066400000000000000000000005351223671746500163160ustar00rootroot00000000000000debian/tmp/usr/bin/bcfg2 usr/sbin debian/tmp/usr/lib/python*/*-packages/Bcfg2/*.py debian/tmp/usr/lib/python*/*-packages/Bcfg2/Client/* debian/tmp/usr/share/man/man1/* debian/tmp/usr/share/man/man5/* examples/bcfg2.conf usr/share/bcfg2 debian/bcfg2.default usr/share/bcfg2 tools/bcfg2-cron usr/lib/bcfg2 tools/bcfg2-import-config usr/share/bcfg2 bcfg2-1.3.3/debian/bcfg2.postinst000066400000000000000000000022551223671746500165340ustar00rootroot00000000000000#!/bin/sh # postinst script for bcfg2 # # see: dh_installdeb(1) set -e # summary of how this script can be called: # * `configure' # * `abort-upgrade' # * `abort-remove' `in-favour' # # * `abort-deconfigure' `in-favour' # `removing' # # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package # case "$1" in configure) OLDUMASK=$(umask) umask 027 ucf /usr/share/bcfg2/bcfg2.conf /etc/bcfg2.conf umask $OLDUMASK ucf /usr/share/bcfg2/bcfg2.default /etc/default/bcfg2 if [ -x /usr/bin/ucfr ] ; then ucfr bcfg2 /etc/bcfg2.conf ucfr bcfg2 /etc/default/bcfg2 fi ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 bcfg2-1.3.3/debian/bcfg2.postrm000066400000000000000000000021701223671746500161710ustar00rootroot00000000000000#!/bin/sh # postrm script for bcfg2 # # see: dh_installdeb(1) set -e # summary of how this script can be called: # * `remove' # * `purge' # * `upgrade' # * `failed-upgrade' # * `abort-install' # * `abort-install' # * `abort-upgrade' # * `disappear' overwrit>r> # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package case "$1" in purge) for i in /etc/default/bcfg2 /etc/bcfg2.conf; do rm -f $i if which ucf >/dev/null; then ucf --purge $i fi if which ucfr >/dev/null; then ucfr --purge bcfg2 $i fi done ;; remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) ;; *) echo "postrm called with unknown argument \`$1'" >&2 exit 1 esac # dh_installdeb will replace this with shell code automatically # generated by other debhelper scripts. #DEBHELPER# exit 0 bcfg2-1.3.3/debian/changelog000066400000000000000000000720631223671746500156220ustar00rootroot00000000000000bcfg2 (1.3.3-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Thu, 07 Nov 2013 08:09:57 -0600 bcfg2 (1.3.2-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Mon, 01 Jul 2013 16:24:46 -0500 bcfg2 (1.3.1-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Thu, 21 Mar 2013 09:32:16 -0500 bcfg2 (1.3.0-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Fri, 15 Mar 2013 08:45:18 -0500 bcfg2 (1.3.0rc2-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Tue, 29 Jan 2013 10:43:55 -0600 bcfg2 (1.3.0rc1-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Wed, 09 Jan 2013 10:48:22 -0600 bcfg2 (1.3.0pre2-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Tue, 30 Oct 2012 17:19:01 -0500 bcfg2 (1.3.0pre1-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Fri, 31 Aug 2012 13:16:05 -0500 bcfg2 (1.2.3-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Tue, 03 Jul 2012 09:33:50 -0500 bcfg2 (1.2.2-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Sat, 17 Mar 2012 14:41:17 -0500 bcfg2 (1.2.1-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Fri, 27 Jan 2012 13:55:45 -0600 bcfg2 (1.2.0-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Wed, 07 Dec 2011 20:22:57 -0600 bcfg2 (1.2.0rc2-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Fri, 28 Oct 2011 14:30:45 -0500 bcfg2 (1.2.0rc1-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Thu, 01 Sep 2011 20:59:16 -0500 bcfg2 (1.2.0pre3-0.0) unstable; urgency=low * New upstream release -- Sol Jerome Sat, 18 Jun 2011 22:41:29 -0500 bcfg2 (1.2.0-0.0pre2) unstable; urgency=low * New upstream release -- Sol Jerome Mon, 25 Apr 2011 11:21:13 -0500 bcfg2 (1.2.0-0.0pre1) unstable; urgency=low * New upstream release -- Sol Jerome Sun, 23 Jan 2011 16:33:00 -0600 bcfg2 (1.1.0-0.0) unstable; urgency=low * New upstream release -- Narayan Desai Sun, 26 Sep 2010 20:56:09 -0500 bcfg2 (1.1.0-0.0rc5) unstable; urgency=low * New upstream release -- Narayan Desai Tue, 14 Sep 2010 10:53:22 -0500 bcfg2 (1.1.0-0.0rc4) unstable; urgency=low * New upstream release -- Sol Jerome Mon, 19 Jul 2010 14:46:00 -0500 bcfg2 (1.1.0-0.0rc3) unstable; urgency=low * New upstream release -- Narayan Desai Thu, 17 Jun 2010 11:22:34 -0500 bcfg2 (1.1.0-0.0rc2) unstable; urgency=low * New upstream release -- Sol Jerome Wed, 09 Jun 2010 12:42:54 -0500 bcfg2 (1.1.0-0.1rc1) unstable; urgency=low * release candidate -- Narayan Desai Tue, 27 Apr 2010 11:17:26 -0600 bcfg2 (1.0.1-1) unstable; urgency=low * Remove unnecessary python-lxml dependency (for the client package) * Remove duplicate Recommends items -- Sol Jerome Thu, 08 Apr 2010 08:31:45 -0600 bcfg2 (1.0.0-6) unstable; urgency=low * final 1.0.0 release + debian directory from r5630 -- Daniel Clark Sun, 23 Dec 2009 02:13:44 -0500 bcfg2 (1.0.0-4) unstable; urgency=low * final 1.0.0 release + debian directory from r5621 -- Daniel Clark Sun, 15 Dec 2009 06:12:33 -0500 bcfg2 (1.0.0-2) unstable; urgency=low * final 1.0.0 release + debian directory from r5619 -- Daniel Clark Sun, 11 Dec 2009 15:59:33 -0500 bcfg2 (1.0.0-1) unstable; urgency=low * final release -- Narayan Desai Thu, 05 Nov 2009 17:40:46 -0600 bcfg2 (1.0.0~rc4-1) unstable; urgency=low * last release candidate -- Narayan Desai Thu, 05 Nov 2009 16:10:39 -0600 bcfg2 (1.0.0~rc3-1) unstable; urgency=low [ Narayan Desai ] * new release candidate -- Narayan Desai Wed, 04 Nov 2009 23:47:56 -0600 bcfg2 (1.0.0~rc2-1) unstable; urgency=low [ Narayan Desai ] * new release candidate [ Sami Haahtinen ] * Update packaging * Switch to plain debhelper * Switch from pycentral to python-support * Move homepage to the dedicated Homepage field * Update Standards-Version to 3.8.3.0 * Add python-m2crypto as dependency * bcfg2-server needs python2.4+ -- Sami Haahtinen Sat, 24 Oct 2009 00:20:51 +0300 bcfg2 (1.0.0rc1-0.1) unstable; urgency=low * first release candidate -- Narayan Desai Sun, 25 Oct 2009 21:40:44 -0500 bcfg2 (1.0pre5-0.2) unstable; urgency=low * version bump to 1.0pre5 -- Narayan Desai Thu, 23 Jul 2009 10:54:06 -0500 bcfg2 (1.0pre4-0.2) unstable; urgency=low * Remove python-cheetah dependency -- Sol Jerome Wed, 15 Jun 2009 14:30:25 -0500 bcfg2 (1.0pre4-0.1) unstable; urgency=low * new prerelease -- Narayan Desai Wed, 24 Jun 2009 20:58:52 -0500 bcfg2 (1.0pre3-0.1) unstable; urgency=low * new prerelease -- Narayan Desai Fri, 22 May 2009 12:37:13 -0500 bcfg2 (1.0pre2-0.1) unstable; urgency=low * new prerelease -- Narayan Desai Wed, 25 Mar 2009 19:17:14 -0500 bcfg2 (1.0pre1-0.0) unstable; urgency=low * new prerelease -- Narayan Desai Thu, 29 Jan 2009 15:36:44 -0600 bcfg2 (0.9.6-0.1) unstable; urgency=low * Final release! -- Narayan Desai Thu, 13 Nov 2008 18:36:50 -0600 bcfg2 (0.9.6-0.0rc1) unstable; urgency=low * Release candidate -- Narayan Desai Fri, 07 Nov 2008 13:52:58 -0600 bcfg2 (0.9.6-0.0pre3) unstable; urgency=low * new prerelease (hopefully rc) -- Narayan Desai Fri, 10 Oct 2008 16:35:17 -0600 bcfg2 (0.9.6-0.0pre2) unstable; urgency=low * new prerelease -- Narayan Desai Thu, 07 Aug 2008 15:49:22 -0500 bcfg2 (0.9.5-0.1) unstable; urgency=low * final release -- Narayan Desai Fri, 09 Nov 2007 19:44:25 -0600 bcfg2 (0.9.5-0.0pre7) unstable; urgency=low * final pre ;) -- Narayan Desai Mon, 05 Nov 2007 17:58:03 -0600 bcfg2 (0.9.5-0.0pre6) unstable; urgency=low * new pre (double hopefully the final 0.9.5pre) -- Narayan Desai Tue, 30 Oct 2007 12:42:27 -0500 bcfg2 (0.9.5-0.0pre5) unstable; urgency=low * new pre (hopefully the final 0.9.5pre) -- Narayan Desai Thu, 11 Oct 2007 16:31:33 -0500 bcfg2 (0.9.5-0.0pre4) unstable; urgency=low * new pre (final feature addition for 0.9.5) -- Narayan Desai Sun, 26 Aug 2007 16:25:33 -0500 bcfg2 (0.9.5-0.0pre3) unstable; urgency=low * new pre -- Narayan Desai Tue, 07 Aug 2007 22:32:16 -0500 bcfg2 (0.9.5-0.0pre2) unstable; urgency=low * new pre -- Narayan Desai Wed, 25 Jul 2007 15:01:48 -0500 bcfg2 (0.9.5-0.0pre1) unstable; urgency=low * new pre -- Narayan Desai Thu, 19 Jul 2007 19:25:16 -0500 bcfg2 (0.9.4-0.1) unstable; urgency=low * final release -- Narayan Desai Mon, 25 Jun 2007 10:12:27 -0500 bcfg2 (0.9.4-0.0pre4) unstable; urgency=low * new pre -- Narayan Desai Wed, 20 Jun 2007 16:15:39 -0500 bcfg2 (0.9.4-0.0pre3) unstable; urgency=low * new pre -- Narayan Desai Thu, 14 Jun 2007 09:12:50 -0500 bcfg2 (0.9.4-0.0pre2) unstable; urgency=low * new pre -- Narayan Desai Wed, 06 Jun 2007 10:28:05 -0500 bcfg2 (0.9.4-0.0pre1) unstable; urgency=low * new pre -- Narayan Desai Mon, 14 May 2007 21:48:23 -0500 bcfg2 (0.9.3-0.1) unstable; urgency=low * final release -- Narayan Desai Mon, 30 Apr 2007 13:21:30 -0500 bcfg2 (0.9.3-0.0pre7) unstable; urgency=low * new pre -- Narayan Desai Wed, 18 Apr 2007 19:49:05 -0500 bcfg2 (0.9.3-0.0pre6) unstable; urgency=low * new pre -- Narayan Desai Wed, 11 Apr 2007 13:17:10 -0500 bcfg2 (0.9.3-0.0pre5) unstable; urgency=low * new prerelease -- Narayan Desai Fri, 6 Apr 2007 14:07:26 -0500 bcfg2 (0.9.3-0.0pre4) unstable; urgency=low * new prerelease -- Narayan Desai Thu, 5 Apr 2007 14:24:37 -0500 bcfg2 (0.9.3-0.0pre3) unstable; urgency=low * new prerelease -- Narayan Desai Tue, 20 Mar 2007 10:50:37 -0500 bcfg2 (0.9.3-0.0pre2) unstable; urgency=low * new prerelease -- Narayan Desai Mon, 12 Mar 2007 11:25:13 -0500 bcfg2 (0.9.3-0.0pre1) unstable; urgency=low * new prerelease -- Narayan Desai Mon, 12 Mar 2007 10:10:05 -0500 bcfg2 (0.9.2-0.1) unstable; urgency=low * final release -- Narayan Desai Mon, 19 Feb 2007 13:35:24 -0600 bcfg2 (0.9.2-0.0rc6) unstable; urgency=low * final(?) release candidate -- Narayan Desai Fri, 16 Feb 2007 16:24:53 -0600 bcfg2 (0.9.2-0.0rc5) unstable; urgency=low * release candidate -- Narayan Desai Thu, 15 Feb 2007 14:59:48 -0600 bcfg2 (0.9.2-0.0rc4) unstable; urgency=low * release candidate -- Narayan Desai Thu, 15 Feb 2007 13:53:17 -0600 bcfg2 (0.9.2-0.0rc3) unstable; urgency=low * release candidate -- Narayan Desai Thu, 15 Feb 2007 13:29:00 -0600 bcfg2 (0.9.2-0.0rc2) unstable; urgency=low * release candidate -- Narayan Desai Thu, 15 Feb 2007 11:03:51 -0600 bcfg2 (0.9.2-0.0rc1) unstable; urgency=low * prerelease -- Narayan Desai Tue, 6 Feb 2007 15:18:53 -0600 bcfg2 (0.9.1d-0.1) unstable; urgency=low * another patch release -- Narayan Desai Tue, 6 Feb 2007 10:00:51 -0600 bcfg2 (0.9.1c-0.1) unstable; urgency=low * another patch release -- Narayan Desai Mon, 5 Feb 2007 12:26:43 -0600 bcfg2 (0.9.1b-0.3) unstable; urgency=low * doubly brown paper bag release -- Narayan Desai Fri, 2 Feb 2007 19:26:12 -0600 bcfg2 (0.9.1a-0.2) unstable; urgency=low * brown paper bag release -- Narayan Desai Thu, 1 Feb 2007 17:49:01 -0600 bcfg2 (0.9.1-0.1) unstable; urgency=low * new release -- Narayan Desai Thu, 1 Feb 2007 15:38:06 -0600 bcfg2 (0.9.0-0.1) unstable; urgency=low * final release -- Narayan Desai Mon, 29 Jan 2007 13:18:38 -0600 bcfg2 (0.9.0-0pre6) unstable; urgency=low * new pre, hopefully near final -- Narayan Desai Wed, 24 Jan 2007 13:17:08 -0600 bcfg2 (0.9.0-0pre5) unstable; urgency=low * new pre -- Narayan Desai Tue, 16 Jan 2007 10:44:54 -0600 bcfg2 (0.9.0-0pre4) unstable; urgency=low * new pre, this one works -- Narayan Desai Thu, 11 Jan 2007 14:58:11 -0600 bcfg2 (0.9.0-0.0pre3) unstable; urgency=low * new pre -- Narayan Desai Thu, 11 Jan 2007 14:01:53 -0600 bcfg2 (0.9.0-0.0pre2) unstable; urgency=low * second pre of 0.9.0 -- Narayan Desai Thu, 11 Jan 2007 12:35:19 -0600 bcfg2 (0.9.0-0.0pre1) unstable; urgency=low * first prerelease of 0.9.0 -- Narayan Desai Tue, 9 Jan 2007 10:07:45 -0600 bcfg2 (0.8.7.3-0.1) unstable; urgency=low * license changes -- Narayan Desai Wed, 27 Dec 2006 15:33:22 -0600 bcfg2 (0.8.7.2-0.1) unstable; urgency=low * new upstream -- Narayan Desai Thu, 21 Dec 2006 09:23:56 -0600 bcfg2 (0.8.7.1-0.1) unstable; urgency=low * new upstream -- Narayan Desai Mon, 11 Dec 2006 12:26:28 -0600 bcfg2 (0.8.7-0.1) unstable; urgency=low * final release -- Narayan Desai Fri, 1 Dec 2006 21:22:16 -0600 bcfg2 (0.8.7-0pre2) unstable; urgency=low * new upstream -- Narayan Desai Thu, 30 Nov 2006 14:48:49 -0600 bcfg2 (0.8.7-0pre1) unstable; urgency=low * new upstream -- Narayan Desai Mon, 27 Nov 2006 10:36:56 -0600 bcfg2 (0.8.6.1-0) unstable; urgency=low * new upstream due to another bug -- Narayan Desai Sun, 12 Nov 2006 10:24:29 -0600 bcfg2 (0.8.6-0) unstable; urgency=low * new upstream -- Narayan Desai Sat, 11 Nov 2006 10:07:40 -0600 bcfg2 (0.8.5-0) unstable; urgency=low * Final release -- Narayan Desai Fri, 3 Nov 2006 10:43:12 -0600 bcfg2 (0.8.5-0pre4) unstable; urgency=low * new upstream -- Narayan Desai Wed, 1 Nov 2006 16:46:45 -0600 bcfg2 (0.8.5-0pre3) unstable; urgency=low * new upstream -- Narayan Desai Wed, 18 Oct 2006 18:42:46 -0500 bcfg2 (0.8.5-0pre2) unstable; urgency=low * new upstream * Fixed bugs reported by gogo, Cory, and mjung -- Narayan Desai Mon, 9 Oct 2006 13:44:46 -0500 bcfg2 (0.8.5-0pre1) unstable; urgency=low * new upstream * client refactor -- Narayan Desai Fri, 6 Oct 2006 16:08:08 -0500 bcfg2 (0.8.4-1) unstable; urgency=low * Change paths to the reporting system * Add server dep on python-cheetah -- Narayan Desai Fri, 15 Sep 2006 12:53:56 -0500 bcfg2 (0.8.3-2) unstable; urgency=low * fix an init script bug -- Narayan Desai Sun, 10 Sep 2006 16:08:04 -0500 bcfg2 (0.8.3-1) unstable; urgency=low * final release! -- Narayan Desai Tue, 5 Sep 2006 10:08:29 -0500 bcfg2 (0.8.3pre7-1) unstable; urgency=low * new upstream -- Narayan Desai Fri, 1 Sep 2006 14:44:56 -0500 bcfg2 (0.8.3pre6-1) unstable; urgency=low * fix debian cron stuff -- Narayan Desai Fri, 1 Sep 2006 09:31:49 -0500 bcfg2 (0.8.3pre5-1) unstable; urgency=low * new upstream -- Narayan Desai Thu, 31 Aug 2006 09:48:52 -0500 bcfg2 (0.8.3pre4-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 29 Aug 2006 16:22:58 -0500 bcfg2 (0.8.3pre3-1) unstable; urgency=low * new upstream -- Narayan Desai Wed, 23 Aug 2006 21:36:48 -0500 bcfg2 (0.8.3pre2-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 22 Aug 2006 16:14:09 -0500 bcfg2 (0.8.3pre1-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 15 Aug 2006 12:28:15 -0500 bcfg2 (0.8.2+svn060801-1) unstable; urgency=low * new packaging code * Use dh_installinit to add init-script handling in post/pre scripts, invoke it after compiling python modules. * revert init scripts to lsb 3.0 * fix bcfg2-server pidfile handling. * fix cron scripts -- Sami Haahtinen Tue, 01 Aug 2006 09:53:19 -0500 bcfg2 (0.8.2+svn060725-1) unstable; urgency=low [ Sami Haahtinen ] * Initial upload to debian. * add cron scripts * Update control.in and copyright files in preparation for debian release -- Sami Haahtinen Tue, 25 Jul 2006 10:58:28 +0300 bcfg2 (0.8.2-1) unstable; urgency=low [ Narayan Desai ] * final release [ Sami Haahtinen ] * add cron scripts -- Sami Haahtinen Sat, 22 Jul 2006 23:16:11 +0300 bcfg2 (0.8.2-0pre12) unstable; urgency=low * new upstream -- desai Thu, 20 Jul 2006 16:53:52 -0500 bcfg2 (0.8.2-0pre11) unstable; urgency=low * Update Debian packaging * Update Init Scripts * Move to Standards-Version 3.7.2 (no changes) * build with cdbs * Use python-central -- Sami Haahtinen Mon, 17 Jul 2006 11:11:49 -0500 bcfg2 (0.8.2-0pre10) unstable; urgency=low * new upstream -- desai Sun, 16 Jul 2006 10:53:52 +0300 bcfg2 (0.8.2-0pre9) unstable; urgency=low * new upstream -- desai Fri, 14 Jul 2006 11:11:49 -0500 bcfg2 (0.8.2-0pre8) unstable; urgency=low * new upstream -- Narayan Desai Wed, 12 Jul 2006 14:36:26 -0500 bcfg2 (0.8.2-0pre7) unstable; urgency=low * new upstream -- Narayan Desai Fri, 7 Jul 2006 14:59:30 -0500 bcfg2 (0.8.2-0pre6) unstable; urgency=low * new upstream -- Narayan Desai Mon, 12 Jun 2006 11:49:35 -0500 bcfg2 (0.8.2-0pre5) unstable; urgency=low * new upstream -- Narayan Desai Mon, 12 Jun 2006 13:59:18 -0500 bcfg2 (0.8.2-0pre4) unstable; urgency=low * new upstream -- Narayan Desai Wed, 26 Apr 2006 13:59:18 -0500 bcfg2 (0.8.2-0pre2) unstable; urgency=low * new upstream -- Narayan Desai Wed, 29 Mar 2006 15:41:29 -0600 bcfg2 (0.8.2-0pre1) unstable; urgency=low * new release -- Narayan Desai Thu, 23 Mar 2006 14:10:00 -0600 bcfg2 (0.8.1-1) unstable; urgency=low * new release -- Narayan Desai Wed, 1 Mar 2006 15:11:25 -0600 bcfg2 (0.8.1pre9-1) unstable; urgency=low * new upstream -- Narayan Desai Wed, 1 Mar 2006 13:40:27 -0600 bcfg2 (0.8.1pre8-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 28 Feb 2006 10:42:11 -0600 bcfg2 (0.8.1pre7-1) unstable; urgency=low * new upstream -- Narayan Desai Fri, 24 Feb 2006 10:38:52 -0600 bcfg2 (0.8.1pre6-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 21 Feb 2006 10:43:23 -0600 bcfg2 (0.8.1pre5-2) unstable; urgency=low * Fix a few client side bugs -- Narayan Desai Mon, 20 Feb 2006 16:35:50 -0600 bcfg2 (0.8.1pre4-1) unstable; urgency=low * new upstream -- Narayan Desai Fri, 17 Feb 2006 13:02:37 -0600 bcfg2 (0.8.1pre3-1) unstable; urgency=low * packaging updates -- Narayan Desai Wed, 15 Feb 2006 09:35:40 -0600 bcfg2 (0.8.1pre1-1) unstable; urgency=low * new upstream prerelease -- Narayan Desai Wed, 25 Jan 2006 09:59:54 -0600 bcfg2 (0.8.0-1) unstable; urgency=low * New upstream release -- Narayan Desai Fri, 20 Jan 2006 15:53:30 -0600 bcfg2 (0.8.0pre5-1) unstable; urgency=low * New upstream release -- Narayan Desai Mon, 16 Jan 2006 12:11:09 -0600 bcfg2 (0.8.0pre4-1) unstable; urgency=low * New upstream release -- Narayan Desai Wed, 11 Jan 2006 16:04:53 -0600 bcfg2 (0.8.0pre3-1) unstable; urgency=low * New upstream release -- Narayan Desai Tue, 10 Jan 2006 15:05:48 -0600 bcfg2 (0.8.0pre2-1) unstable; urgency=low * New upstream release -- Narayan Desai Fri, 6 Jan 2006 14:18:36 -0600 bcfg2 (0.8.0pre1-1) unstable; urgency=low * new upstream * major repository format change -- Narayan Desai Thu, 5 Jan 2006 10:47:04 -0600 bcfg2 (0.7.4pre3-1) unstable; urgency=low * new upstream -- Joey Hagedorn Thu, 22 Dec 2005 11:08:35 -0600 bcfg2 (0.7.4pre2-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 13 Dec 2005 15:35:35 -0600 bcfg2 (0.7.4pre1-1) unstable; urgency=low * new upstream -- Narayan Desai Thu, 8 Dec 2005 22:32:22 -0600 bcfg2 (0.7.3-1) unstable; urgency=low * finally a release -- Narayan Desai Fri, 2 Dec 2005 13:02:37 -0600 bcfg2 (0.7.3pre6-1) unstable; urgency=low * new upstream -- Narayan Desai Thu, 1 Dec 2005 16:16:08 -0600 bcfg2 (0.7.3pre5-1) unstable; urgency=low * new upstream -- Narayan Desai Thu, 1 Dec 2005 11:29:52 -0600 bcfg2 (0.7.3pre4-1) unstable; urgency=low * new upstream -- Narayan Desai Wed, 30 Nov 2005 16:38:35 -0600 bcfg2 (0.7.3pre3-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 29 Nov 2005 15:57:57 -0600 bcfg2 (0.7.3pre2-1) unstable; urgency=low * new upstream -- Narayan Desai Mon, 28 Nov 2005 15:47:12 -0600 bcfg2 (0.7.3pre1-2) unstable; urgency=low * package rework -- Narayan Desai Sat, 12 Nov 2005 15:16:34 -0600 bcfg2 (0.7.3pre1-1) unstable; urgency=low * new upstream * now using lxml instead of elementtree -- Narayan Desai Sat, 12 Nov 2005 12:43:44 -0600 bcfg2 (0.7.2-1) unstable; urgency=low * new upstream -- Narayan Desai Wed, 9 Nov 2005 14:47:38 -0600 bcfg2 (0.7.1-1) unstable; urgency=low * release -- Narayan Desai Mon, 7 Nov 2005 16:26:30 -0600 bcfg2 (0.7.1pre6-1) unstable; urgency=low * new upstream (getting close to release) -- Narayan Desai Thu, 3 Nov 2005 15:46:14 -0600 bcfg2 (0.7.1pre5-1) unstable; urgency=low * new upstream (now with timing results) -- Narayan Desai Fri, 28 Oct 2005 11:21:28 -0500 bcfg2 (0.7.1pre4-1) unstable; urgency=low * new upstream (nearing final release) -- Narayan Desai Tue, 25 Oct 2005 01:32:40 -0500 bcfg2 (0.7.1pre3-1) unstable; urgency=low * new upstream -- Narayan Desai Mon, 24 Oct 2005 04:07:45 -0500 bcfg2 (0.7.1pre2-1) unstable; urgency=low * new upstream -- Narayan Desai Mon, 17 Oct 2005 23:56:02 -0500 bcfg2 (0.7.1pre1-1) unstable; urgency=low * new dev version -- Narayan Desai Thu, 13 Oct 2005 13:54:24 -0500 bcfg2 (0.7-1) unstable; urgency=low * final release -- Narayan Desai Wed, 5 Oct 2005 15:38:50 -0500 bcfg2 (0.7rc2-1) unstable; urgency=low * new upstream -- Narayan Desai Sat, 1 Oct 2005 23:12:49 -0500 bcfg2 (0.7pre1-1) unstable; urgency=low * jump to 0.7 -- Narayan Desai Thu, 29 Sep 2005 13:45:48 -0500 bcfg2 (0.6.11pre9-1) unstable; urgency=low * new upstream -- Narayan Desai Wed, 28 Sep 2005 15:22:03 -0500 bcfg2 (0.6.11pre8-2) unstable; urgency=low * new upstream -- Narayan Desai Wed, 28 Sep 2005 11:11:39 -0500 bcfg2 (0.6.11pre7-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 27 Sep 2005 15:10:24 -0500 bcfg2 (0.6.11pre6-1) unstable; urgency=low * new upstream -- Narayan Desai Fri, 23 Sep 2005 16:35:49 -0500 bcfg2 (0.6.11pre5-1) unstable; urgency=low * new upstream -- Narayan Desai Wed, 21 Sep 2005 04:34:06 -0500 bcfg2 (0.6.11pre4-1) unstable; urgency=low * new upstream -- Narayan Desai Wed, 14 Sep 2005 13:20:26 -0500 bcfg2 (0.6.11pre3-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 13 Sep 2005 10:47:52 -0500 bcfg2 (0.6.11pre2-1) unstable; urgency=low * new upstream -- Narayan Desai Thu, 8 Sep 2005 15:59:32 -0500 bcfg2 (0.6.11pre1-2) unstable; urgency=low * new upstream -- Narayan Desai Wed, 7 Sep 2005 14:15:58 -0500 bcfg2 (0.6.10-1) unstable; urgency=low * new release -- Narayan Desai Fri, 2 Sep 2005 12:42:03 -0500 bcfg2 (0.6.10rc1-1) unstable; urgency=low * new upstream, now with report support -- Narayan Desai Fri, 2 Sep 2005 10:57:27 -0500 bcfg2 (0.6.9.3pre3-1) unstable; urgency=low * new upstream -- Narayan Desai Wed, 17 Aug 2005 15:59:13 -0500 bcfg2 (0.6.9.3pre2-1) unstable; urgency=low * new upstream -- Narayan Desai Wed, 3 Aug 2005 10:20:16 -0500 bcfg2 (0.6.9.3pre1-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 2 Aug 2005 16:24:49 -0500 bcfg2 (0.6.9.2-1) unstable; urgency=low * Here goes one more shot -- Narayan Desai Thu, 14 Jul 2005 10:01:46 -0500 bcfg2 (0.6.9.1-1) unstable; urgency=low * minor bugfix -- Narayan Desai Thu, 14 Jul 2005 09:27:04 -0500 bcfg2 (0.6.9-1) unstable; urgency=low * release -- Narayan Desai Wed, 13 Jul 2005 15:07:58 -0500 bcfg2 (0.6.9pre3-1) unstable; urgency=low * new version -- Narayan Desai Wed, 29 Jun 2005 16:40:22 -0500 bcfg2 (0.6.9pre2-1) unstable; urgency=low * new upstream -- Narayan Desai Wed, 29 Jun 2005 13:12:07 -0500 bcfg2 (0.6.9pre1-1) unstable; urgency=low * new version -- Narayan Desai Tue, 28 Jun 2005 16:17:27 -0500 bcfg2 (0.6.8-2) unstable; urgency=low * minor bugfixes -- Narayan Desai Fri, 13 May 2005 15:18:39 -0500 bcfg2 (0.6.8-1) unstable; urgency=low * New upstream -- Narayan Desai Thu, 12 May 2005 13:01:58 -0500 bcfg2 (0.6.7-2) unstable; urgency=low * bug fix -- Narayan Desai Mon, 11 Apr 2005 13:52:07 -0500 bcfg2 (0.6.7-1) unstable; urgency=low * client refactor -- Narayan Desai Mon, 11 Apr 2005 12:54:59 -0500 bcfg2 (0.6.6-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 29 Mar 2005 10:23:11 -0600 bcfg2 (0.6.5-1) unstable; urgency=low * new upstream -- Narayan Desai Wed, 16 Feb 2005 15:50:43 -0600 bcfg2 (0.6.4-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 1 Feb 2005 16:31:00 -0600 bcfg2 (0.6.4pre1-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 1 Feb 2005 10:59:24 -0600 bcfg2 (0.6.3-4) unstable; urgency=low * More bugfixes -- Narayan Desai Thu, 27 Jan 2005 13:20:10 -0600 bcfg2 (0.6.3-3) unstable; urgency=low * Fixes for TG -- Narayan Desai Thu, 20 Jan 2005 16:45:20 -0600 bcfg2 (0.6.3-2) unstable; urgency=low * Fixed some stats counting and format issues -- Brian Pellin Tue, 11 Jan 2005 15:15:59 -0600 bcfg2 (0.6.3-1) unstable; urgency=low * New Upstream version -- Brian Pellin Tue, 11 Jan 2005 13:53:57 -0600 bcfg2 (0.6.2-2) unstable; urgency=low * Several bug fixes over last release -- Brian Pellin Mon, 10 Jan 2005 12:24:37 -0600 bcfg2 (0.6.2-1) unstable; urgency=low * New upstream release -- Brian Pellin Fri, 7 Jan 2005 11:02:47 -0600 bcfg2 (0.6.1-1) unstable; urgency=low * actual release -- Narayan Desai Thu, 16 Dec 2004 14:33:22 -0600 bcfg2 (0.6.1-0) unstable; urgency=low * new upstream -- Narayan Desai Tue, 14 Dec 2004 18:43:08 -0600 bcfg2 (0.6-1) unstable; urgency=low * final release -- Narayan Desai Fri, 12 Nov 2004 19:56:55 -0600 bcfg2 (0.6pre8-1) unstable; urgency=low * new upstream -- Narayan Desai Thu, 11 Nov 2004 09:48:37 -0600 bcfg2 (0.6pre7-1) unstable; urgency=low * version bump -- Narayan Desai Wed, 10 Nov 2004 10:57:22 -0600 bcfg2 (0.6pre6-3) unstable; urgency=low * debian bugfixes -- Narayan Desai Thu, 4 Nov 2004 10:57:22 -0600 bcfg2 (0.6pre6-2) unstable; urgency=low * new upstream -- Narayan Desai Thu, 4 Nov 2004 09:47:02 -0600 bcfg2 (0.6pre6-1) unstable; urgency=low * new upstream -- Narayan Desai Mon, 1 Nov 2004 13:38:54 -0600 bcfg2 (0.6pre4-2) unstable; urgency=low * new upstream * lintian fixes -- Narayan Desai Fri, 29 Oct 2004 22:30:28 -0500 bcfg2 (0.6pre4-1) unstable; urgency=low * add schema validation * new upstream -- Narayan Desai Fri, 29 Oct 2004 22:30:14 -0500 bcfg2 (0.6pre3-1) unstable; urgency=low * new upstream -- Narayan Desai Wed, 27 Oct 2004 14:40:49 -0500 bcfg2 (0.6pre1-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 26 Oct 2004 11:07:11 -0500 bcfg2 (0.5.2-1) unstable; urgency=low * new upstream -- Narayan Desai Fri, 22 Oct 2004 13:19:35 -0500 bcfg2 (0.5.1-1) unstable; urgency=low * new upstream -- Narayan Desai Tue, 19 Oct 2004 14:12:00 -0500 bcfg2 (0.5-2c) unstable; urgency=low * new upstream -- Narayan Desai Tue, 12 Oct 2004 09:32:37 -0500 bcfg2 (0.4-1) unstable; urgency=low * new upstream -- Narayan Desai Wed, 6 Oct 2004 16:15:21 -0500 bcfg2 (0.3.2-1) unstable; urgency=low * new upstream -- Narayan Desai Thu, 2 Sep 2004 10:41:48 -0500 bcfg2 (0.3-1) unstable; urgency=low * New upstream -- Narayan Desai Tue, 31 Aug 2004 09:39:28 -0500 bcfg2 (0.2-1) unstable; urgency=low * Initial alpha -- Narayan Desai Wed, 11 Aug 2004 09:57:37 -0500 bcfg2-1.3.3/debian/compat000066400000000000000000000000021223671746500151360ustar00rootroot000000000000007 bcfg2-1.3.3/debian/control000066400000000000000000000050201223671746500153400ustar00rootroot00000000000000Source: bcfg2 Section: admin Priority: optional Maintainer: Arto Jantunen Uploaders: Sami Haahtinen Build-Depends: debhelper (>= 7.0.50~), python (>= 2.6), python-setuptools, python-sphinx (>= 1.0.7+dfsg) | python3-sphinx, python-lxml, python-daemon, python-boto, python-cherrypy3, python-gamin, python-genshi, python-pyinotify, python-m2crypto, python-doc, python-mock, python-mock-doc Build-Depends-Indep: python-support (>= 0.5.3) Standards-Version: 3.8.0.0 Homepage: http://bcfg2.org/ Package: bcfg2 Architecture: all Depends: ${python:Depends}, ${misc:Depends}, debsums, python-apt, ucf, lsb-base (>= 3.1-9), python (>= 2.6) Description: Configuration management client Bcfg2 is a configuration management system that generates configuration sets for clients bound by client profiles. bcfg2 is the client portion of bcfg2 system which installs configuration images provided by bcfg2-server Package: bcfg2-server Architecture: all Depends: ${python:Depends}, ${misc:Depends}, python-lxml (>= 0.9), libxml2-utils (>= 2.6.23), lsb-base (>= 3.1-9), ucf, bcfg2 (= ${binary:Version}), openssl, python (>= 2.6), python-pyinotify | python-gamin, python-daemon Recommends: graphviz, patch Suggests: python-cheetah, python-genshi (>= 0.4.4), python-profiler, python-sqlalchemy (>= 0.5.0), python-django, mail-transport-agent, bcfg2-doc (= ${binary:Version}) Description: Configuration management server Bcfg2 is a configuration management system that generates configuration sets for clients bound by client profiles. bcfg2-server is the server for bcfg2 clients, which generates configuration sets and stores statistics of client system states. Package: bcfg2-web Architecture: all Depends: ${python:Depends}, ${misc:Depends}, bcfg2-server (= ${binary:Version}), python-django, python-django-south (>= 0.7.5) Suggests: python-mysqldb, python-psycopg2, python-sqlite, libapache2-mod-wsgi Description: Configuration management web interface Bcfg2 is a configuration management system that generates configuration sets for clients bound by client profiles. bcfg2-web is the reporting server for bcfg2. Package: bcfg2-doc Section: doc Architecture: all Depends: ${sphinxdoc:Depends}, ${misc:Depends} Description: Configuration management system documentation Bcfg2 is a configuration management system that generates configuration sets for clients bound by client profiles. bcfg2-doc is the documentation for bcfg2. bcfg2-1.3.3/debian/copyright000066400000000000000000000051261223671746500156770ustar00rootroot00000000000000This package was debianized by Sami Haahtinen It was downloaded from http://trac.mcs.anl.gov/projects/bcfg2/ Upstream Author: Narayan Desai Copyright: 2004 - 2013: University of Chicago License: Unless otherwise specified, files are copyright by the following: Copyright (c) 2004, University of Chicago. See the COPYRIGHT file at the top-level directory of this distribution and at https://github.com/Bcfg2/bcfg2/blob/master/COPYRIGHT. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. GOVERNMENT LICENSE Portions of this material resulted from work developed under a U.S. Government Contract and are subject to the following license: the Government is granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable worldwide license in this computer software to reproduce, prepare derivative works, and perform publicly and display publicly. DISCLAIMER This computer code material was prepared, in part, as an account of work sponsored by an agency of the United States Government. Neither the United States, nor the University of Chicago, nor any of their employees, makes any warranty express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately owned rights. bcfg2-1.3.3/debian/rules000077500000000000000000000015431223671746500150230ustar00rootroot00000000000000#!/usr/bin/make -f # Lucid does not have dh_python2, but we would like to be able to use # this rules file to build on lucid as well. WITH_PYTHON2 = $(shell test -f /usr/bin/dh_python2 && echo "--with python2") WITH_SPHINXDOC = $(shell test -f /usr/bin/dh_sphinxdoc && echo "--with sphinxdoc") %: dh $@ ${WITH_PYTHON2} ${WITH_SPHINXDOC} override_dh_installinit: # Install bcfg2 initscript without starting it on postinst dh_installinit --package=bcfg2 --no-start # Install bcfg2-server initscript without starting it on postinst dh_installinit --package=bcfg2-server --no-start # Install bcfg2-report-collector initscript without starting it on postinst dh_installinit --package=bcfg2-server --name=bcfg2-report-collector --no-start override_dh_auto_build: dh_auto_build python setup.py build_sphinx override_dh_auto_clean: dh_auto_clean rm -rf build bcfg2-1.3.3/debian/source/000077500000000000000000000000001223671746500152405ustar00rootroot00000000000000bcfg2-1.3.3/debian/source/format000066400000000000000000000000041223671746500164450ustar00rootroot000000000000001.0 bcfg2-1.3.3/debian/watch000066400000000000000000000001751223671746500147740ustar00rootroot00000000000000# format version number, currently 3; this line is compulsory! version=3 ftp://ftp.mcs.anl.gov/pub/bcfg/bcfg2-(.*)\.tar\.gz bcfg2-1.3.3/doc/000077500000000000000000000000001223671746500132635ustar00rootroot00000000000000bcfg2-1.3.3/doc/_static/000077500000000000000000000000001223671746500147115ustar00rootroot00000000000000bcfg2-1.3.3/doc/_static/bcfg2.css000066400000000000000000000011511223671746500164040ustar00rootroot00000000000000/* -- tables ---------------------------------------------------------------- */ table.docutils { border: 0 solid #dce; border-collapse: collapse; } table.docutils td, table.docutils th { padding: 2px 5px 2px 5px; border-left: 0; background-color: #bebebe; } table.field-list td, table.field-list th { border: 0 !important; } table.footnote td, table.footnote th { border: 0 !important; } table.docutils th { border-top: 1px solid #2f4f4f; background-color: #708090; } th { text-align: left; padding-right: 5px; } th.head { text-align: center; } bcfg2-1.3.3/doc/_static/bcfg2_logo.png000066400000000000000000000647451223671746500174420ustar00rootroot00000000000000PNG  IHDR,s"?tEXtSoftwareAdobe ImageReadyqe<iIDATx}鞴9%眃A0xAQkDEEQ1(0AATI%g؜wBwsfj{g6)ofzgϩ0 \%\+\%\€..p p VKK%\%\€..a p p VKK%\% X..a p plX56lsӠW.`Ȏ.aR kƒwL'G?/V}.>kX5X!.`8<*;]Q*}AAԚмq=CKh٬1$TͰe0h4YVU$R9h͆)I2u{-<}u{}[X zwk].U\A~a!lxYͬ fu@& kkupð+* m36\t!YFV,Z ibfɢhx.X36M=;x9|3orxA6CKo~߰n^TDůG;acy3.T_x|>` zr*}W&+y pE^Ĩ$6|鳡S{aƭK?v bOY` h-9>L ˫i0or06u87wHHp +l#1Š`Kob,JQQq ر@6#*TAUUDprB 2H8!ļ^~vlyM Jm(aTK+h!l"`6??knX'栨q`'\l"a t81[ŠlղWߝ /?>p VEJwu)RH lTIƀp*$hjuׁ:]0gꍒ㷬j={ͅ ±c#1\񒜜 7pCb v\38p"Zmp@ePL0b\8p@˥+:o> }o6/?%{i)W맄VWKӦMAӀEnF='* :+bW\4jlԏxIvκ=p 'U*,%2BzB infx!pm|n8.."BxCjLF1Ρ(T?r  0 c뭷ނq@dT€Ib7Ql4(vաSڬ6  ʉ@ EC.)O"89y ]Qry<ؽ7s{?xʌhUU 5iW^ KՃԊ?هemEVM-15Dh{W blǒ+ً?)&hsṔ: YPlL4\ջ3[׭ ёLwH'nn|v/@Fr"NIZ5<^8>idٹIUk#p9B5o(ףxY^ 4 ]?:lڥ*Y'!M@>^5Uh6TLxDmC,+i,o 5-8 mꅬpZ2ю1+ɬtS{Z3(*BP)v#R@Aeft}gY ޣ[RB, DuOgȴlPTl*=!J>)ŰW;d`9u@}8yuH"mf&(Ȉ "޷^i{T}VamwNi+XirKKF(",uMܼ?汀E.=W[ xn2 Xw#H NW]~*)oj -' ^$V))jA3{MT;/ʄ+>"?H/- bj:ՓkN-㠩ˬ CfH+6q`FǕ˰QTTs%^  `2P9 t<{{ٲsMիW7555!***Zyr?~j۶m֭[k 6i|jW蚑qv=L@-7 <כD4R,@%ŏE֮];O>-.-ZhS&FcǎݻkŊ=z1C'볬hYYCzNK#Ptp8Ȑ @ŸU=eRWX )XMSRfPP+9>#Hx +1'{Law"Nء`S>(x0Io*"E> kTw][Y7gmTUٔ7Bq8TN*NV+9 rNG*NOO/J۽{-[C@!-SdD00*}mڴiv:th&M4c΃%''pt7Ð!C1K.];k֬Ybg҄$0` 7nʕ5hРZ}XV߂X.>7x$f$+.n#FAy8inn.|K/ロk5k6W?5، , H<P# hݯ_;oZWRbN璞Uxٴ;w}͚3gΜIU)ˆD:oc r`EJv]eJaBvM)#l|Ƈ+[*(~EC2YQYYCPiy}€dZcb\ aj ȶ.nr#U>-z*dXN˕Xz4aZU+226l@K.1i`룏>>[)jό `eXM>bLdM~雮+bqիa֭pA"V~ "k9rpÐmzg/[li5hKM\zrtXuDBh>ǍwSfZ8"'N`N`EZ:Ì[N:%]ve>;L2{̙K%)DD#X>3!!㇎9bHFξE+"ر])c-.ٳ,\跷yg>[ MA,g'1 "]EA꧚Ip4"O@ a*dDW XeePܧdC}Sns ;a#04v%" %% &aH5n15|Og{衇>>ۓϯ 90`EnuyHI<|Pj3>#07pi+| TlPEj28\"!F 4ʅD?hR5vNS\"\01R|o-EE_7qTUCտC5C%8dQՓ&GFF6moMÇu]7WMet`E0 WT8wСR,O!P} `v ]GR$O@@NZn|׌Y舘_'IN:~g oZ⢱&ĦD'*?~pxd|s&pfD9A*e3POݺ><ڿ[ѣoիg1cnl{S^˪4`֨и#x[ΊDp9a@D,V) ^PHGd"/ 4 1E9VR$6 $,!8A`"UobݩpkGBs㲧&1WD]7NKZ5[j3@ oI"*A?ZEL1Vuq :: ^xhonݺ~C}. n1P jp—۴iӎG_Q UeQ \ vn"y q<Ų)[Q]>dUlcLVZ E5Aij>;{xݖ~I5bbG!"WEe]׭v#}tO iz3=_zntOTHf>Azxo㱟EpLvVz>|xG;g/(s['E۬AF(ݰůoPzZ:!ܔ*a "x 6:run@) SkgpuA@X_lcqMq8Tr8U4E@;QL8)l\qzEMgjiI-m{Y{%Xtة_ BuyeDV9 viN {RDZ\r;"Fw˖-$}sڴiwBJ?`. *v)ԩSB\]CRғag#lOVI!=ܳwߝ%-ThR5܁_W ѢEFSN}F eu:vv_ %+v^ hgNC iN'sH"!3_ktCi 6i8Pjj2LjNK9XHA ,.z4 s-\g}ϯ/Uo k+?! )݇lRFб^* m1@S#0w01@~(  -b ]C> e," P!3P@d+^tcC;4VޗnjieIe2`5=́~UH+VL+184ݐZd?!&14 @vӣG|uKc[hWWo6񃝏&2ҏ!kc7n<7n\iӦ 4\}lw3Xg(bO?}Ǘ_~9Sұ) W^ } v>'l/qr KMu{dΞ={b۶m'̳/udQWuw^6y"m0p'Lİ3gv`x 6l-XbY6"y:cx=ኼKK3HBf-!ła-#1{,Z+'[LcTN$rV6kƫ, N(Դ[3;)CeBAHE' ETvThh\ ؞|\ 4H:KQk#V|$6 YdOtU=.EMjH~>4zٵ9` wG&ubkOAN>}^7߬ݽ{).kB)4E>wyu?f"(WXs} :rʐԇ]v)ʩm… !,_" 'h;J?c*mt知; 裏k7Cwy}po[f]n>|tis-[KqZbqk{z uVK_// G4X \Ѯ}{/tF3k{Gݎ i #X%0rrwZyBQ$B%m1X(+/4Le@}%~"ţg,KsTw"mO$.oV! 6 j+7Z-6-Lz!xJ馛A,ƺ*3$F(bE `4[oENdnsNJvܸjCFh_ #ΣOZ2'N>X>`܋D{/ޕ0~MXs}d\p"g'b]C"Ќ='ʸgn=zwyg. .v~}RSSX7gVlV[%٥wpW P 5hРd|az3>ϟxwp f9eQ߲hXtƱ֢xv&?w 68o2@"FEE)(T&cR)5 G6z-2IGa& T'4thP7"5xW\#;`3/gurԅpbY2)/G.ҁǟ+ɮ]طjR||P1YӬ^YRLj% 2777#0J}Wm ;^:A;:"ohw(¾qر(jv m&$!adݼ=y`/Yot~rK g2Ͼ?czYyR_kw_~/s]DnA:ub3=W#E=CI樋.֍cg9+F5ua9ے-=/E8#{vb,SQCVꤦYf rw)%NkabhIlc59[CE/ܑp0 nbz;u+{~+jV^}<)cunx?#\qee 2 ="fU=ɴ`eNBM o *ٹm,mqV+9Bhg W,3 %eCRL!X )W_HzˁWW>.HGP&5m2|#3E],c@DtA+R=7mڴ~=+b@fΜ9_rx?I}%WH>}Wb_xB"WRkݺuCɬAдqz3{r JG;=pEݧg6HmBH2ϺuV/ X/مE]sF^9f2 Vd|ҿFL0laNAB,"hQ-q“? )͓qQUt'1Ly;Dc批j4eEBxXU b;]טق+Kn{Wh\.Mgl2hȾvmjWC^AC^ﭴG0aIb&GLxݻ|jժAaЫzA+~>Я_n%])K,Ƹ[q#K2)ՙP-> ɓ'ӧO<; ֕vBIe Jgl2,-l^]yh@\/n"ۥ$`H Th }ʿ@qo޽{`߷#K u֭vⳢٚ4i\-O2I]]p,wW22ή]2񷴓g0}[CXP:PQ˓j'ֽ1ȍL/MQ85ZAqHbt`ؘjW1l*̧0egFtSգ" 1Rܘ16H\9 = >*n\EȾX$S͇7pz}Nvt4싏Fbg*m>YO'=bús,n)n6QDWj1?dCe[#;ݻ ,]Q:tP`B6D3g\,eXߖvm۶q2\} N`.((tN@_ P~y(:= qк0-+Vl@`ӢofW_,T4PR!hdSc2Ћ"-=(v" G8#J1,j͔HSԂ>xǎg`[=iJ&[=$'ֆ8)c ?kLd+ظHa@e]1T`*}X !Z1W2# @Ϙ_E A/2Y$c@]w5￁9 1EM"R۷o7u@ v%&_bdKtĉc˖-m36P|ۚQ' @viqK,a-u .rQCXGv횓ޔCIY `(_H ٪ѱh&MKvIl^2 5pvQ 8Ə#B&{,}dݻ v1ӧ(Cq[%EʱP2/8|M[4 `8]fQ́, ' {}cG*3Qְ1&$'e38|b`=De&MÄŌ#RmܞXEA`ɘDHU3MёΙL]*csNFQsۜȺ,6:j|W?7mTa_gӦMO2Xׯ_4&E4V!#<6^#elmڴiȍQV#Q}ʕ+w$VA1˰0#33OIJ#sJ0,ncފLXHam@C؁i k"&e #OMz' rm)[|CMMo֚ZY=P2ҫƅhW> ˎ,7VOdع{~ dX4xAɰ;~ﶁ8cF$QAA%>.'}x6) _l3Cĺ]QB⢘B8A4ƍX^=|{mSɯ״LrcuzR.ZV0X<QSHJ.9CĤGe%6Y~?ФIڦk~P"=e֭t6ҤKbo BSk`4D݋u+G Ai}nI'1 փXJeaPVݛMfAؠ|&Oa5k'NRA(޾]~8B9PD;c&}5"kd7KmRU4xm!F]3ıw"vT.@N\ 3F16`E]3]u@ Xf:zs LBΔ/Lo:6Shv&VdaE0k8̂`Ht^ Nm>^WF<Õ{D(3[0v@eܹ)I%f$O$"sz衇f(O|i9 jaeϣbIiE%B")YŰHF8 ,xk/ iԨQ } 03@E~۷oO>̓"3ӃG}ښ5kB$EOU^-SPɳe˖g^E%ӑ1c<82$%WbO QӧOg.]0~Wi_D#(vi/.-NesVNςx}Q04\.^f\d_Ih%_aWKDN.fwe@~$Js[(nIA~H6HF0"3b҃g 1(}6#;V@$7 3&LxQiΏiGplUədxᦛSLRY<+:\V9k&b1!QO];hbg]G$Eq("TP\2%m`y `.Xjvvv$bSb7MƎ0 ŏX+%ݖ!5fѿ VK=z]'ź"u.oO#; 7$5,+,EDW e{Y>wgh儁׶k [Âa*0-([Eh wPکCMO^?#E>؊m?vqi 9y-+([ک ȃ*%#l_.0a`HPhs-_zIAEtXUUdHH| }?o8^?yddҢə,R8'w p rх0(PbTTYRDsXxz.!"x;Y yAsSO]TAHTᅒLmVbឧ={^i8gd"Ŧ=f #;4az26mB~L86]GOm)̫8)[%&*GUPB c4F+pe]KNHemc38eVz^I! @&^pED2 e|\XFbs!`e=(lǐV.&菢RjeBjep0niVE |WIbuD+<b'/"ʹ0,!7pE7yʟ4|q 0ϲe˪'NT d@Ų8: An60-W!ȓ +Ī.)I9nȌO_cnI=p(e"Y(V~hPL{ۓ r7e*X;> ]F׈fnMͶ:&P +h^_eN/*%ahۆд~}Ը4]{} ;*8n@l!]M~: D1 g>ZI'9V? ["a+]de[5n ~ɉb^h|8*.8Xy8UV~Pfxl p:T8VG@IY/P=9ϒ.V׮][}Wa';#ΛHOv>ksa^Veb*ZNT(?J'$:t7߼W^=o?vӎ̪ղۮxeժU%3 V zi)lLEb+`.vRïՍGf bRnkiU9bhxOAm/,,=QkZFMa+\gG:K0ˌ;\ЯK{hT4G MUk>RD_Ы %G>rŶ`kϞ=)N޲e?4iS һw=)S,¾PwXFr~Ԛ5kcdVTҩ}(Ηaq@ Int@xv{=% D^qeǴbeU1a<݌UjZQhr!Cc_!1ҥ?sʗUVmח4f/,ɪs& +'6 UիSHϤ5Jn EF"d6 Tgfra&=͠)bȴ@衸jz3̲ߩp0[]u8܉{3ziZiP5P=rȉG}K"ħ(nݺʚbpB05']B~p7oޜMtgdd@_ŽJ+ui'Q|G;ڦMcI0UKOOgYZPB=C>)S(7aaH oC0EŮh0Vw7hРȚxoQYt(y1<<x!2 %[&/ʢs(i LQ$IqDQ nHk@MWw[ RйA-8|Ё{ҢErf˕wU> ]Y;)֍AmCZfAQaWx0sG]\W;)v,X|j( PJ;MFz WJ߾}/"'e0m5S,)W\G e[2 ưhҥ : Yj*Oц@23a"47"L\s;ӽ{{LE&|ĶMNݻwo YlFc/.wPxZtkc)B;v4ޠ6_gU/]ԃNd|=;)O[U^a\+dUۀ̦F+ڈ"2%}<>Cr8 XbW3f 18C  59D.[@Fx,@ljɃ-@B P$pp:?\L@@ηXзk׮> 6v*UVZh`Ϟ=90FȽ[f͚51A GIGPL!A5O]KG%!!AYQ_BFIT?G:uTh( .!`-@aUB Y{ۚ G{t$>?ܛ5eR, xX6Oիәp 6q}͝"ti \:֫ Ǐ=ĉoˠds/π=PRyd l7M5q7E`z xeJ(C7~EwRnC%$E&>?;< 5y#* #"[ZLydžl:XVBH(:DrI2s f=nt„ 8[סXjџ(:D˖-E:-V%psa;:|pu0X;eeV?/Mi751=m$š_"X8r#馛&s"V%XL1cBlF)djCrSyl}l@^i>@OJbh\\Q|u Xhy+T)vUZV&U q;]^!]s/<}RdrB*aVTMLȒ8#K i@Z\"gY1t$f|0db9C9eʔ[ƝP2hS(wuuFb)\@;T2%Cݛ }WAIftҝh_^G&8;2͛7o'NxkT$?^ CE bPr_WA~^ ~\mfбՅnMCH8hZ7;w:E^UVŮq\JV!wɰnH Ċg4X0}DT,@>8]5Z8s)ȋiUŅ=]ΥT1L=\sNARY0,Su."aY}b)m?GIQҫȌ'sm;r_FsY_ 7GDIJ΍,k}7Q]bvFJ;RѼ}(onCPw׮]l78Xy'+}[`&iba"٨ S-2 I3gjsÆ $K`_CLľ},**KS舠gwq'9X ت]ŒpӦMM_'LeXn!X=ԫ-|U=\wuuJhy.I-*X㻑%J".MZC/ Z"VN>K+Nj0)8Urm tR^.L^sªlr2MgV$Z? <)VrJ^$eGs4 N_Mn(sLt/so}y'0S@M5W:jѢE~x8 L"!#j_b/r!g)%P?]ؖ2u8G6$I&ڝ'nK/Y ZS_Er}?8{LU\3_^"1& x(Q&U =}?x!>ς@ +5ii7Gn8f< OBav@;:eh&$bQ1},`\^w2#0' ?fîg[-m_Y##3435aDK}>Vv?9ubiWMN9/c=j̘7HռN˖-[O8+WZ,E`Y4C UW_ޮ];t`,VZ_|%K8`M5M6mQxg} B7=+JE}GWT+KUEE6d:GO#&JJ$ˢdݺunݺnIJJjD%!9s&!PYs\m\8>؄3 aUf+XbM.#]rɥ3-Z7<">^Z-㲚`JQ˪.a4b61|)aL=V&PDk  [l>N1cnD#Cq/FlS9PˡA'k"#`ZN- -ƼQ^BlX^?a?r+@:/%ϤIf80Zyf R X,% &矿`PzH<$!e}zj4jЋYmFЮ R3$6VO!666F>c? S E,$ ؤF'sG9Su qAtoMÿFM$i=DF,"VbgYfǺfl) Z p~Jvr )l993(^q[/db҄0tfO X"R+ - A>NE"(ߒ4ʰtdk&31֊}#hjz 5tI!S՟C/Cя-|╸F5AU8KVbPEqgDҞx i)? .NdKcttnEOb lV-&^IEJ`nȨ=H]zMH^^TWeNspĐ`F%Clhҥ~6?m8~ {/!/e^Ľ +zk ,_-PR}=zȊ,^87,bs;Șj -Tɬ`ޏ0?"'_G]Jv>c]=zԺBkI+3Õyȑ#'CĞ^e~nG1|n,Ts-£UV"믿~bvv ɈSu t%%쪫^~sMP*mō#e-"G^b"У%!?n6me}l zfU,B X*@_ի%=?hn(:lXUDl1PRX&h;ԔK$O61f/3#,q,;c@)TT{ d-G_6ô'#UJ8c)\ᯛ)x UADـ>s7dXs `,WMoqp~fmQ2a翸% \HXvKOO?.R'-̟9s3x Mf޽Ak&$"7|;$#EݧD"DUŲ@ } 2f͚v-$NεdAz=den\8Ix6թ?-:_(9Rnӎ-4PZ*%RGQmfCAp9 ?c镲=,̦*,U*m VOeW9.ͪR!LB,Δ\C)AD^(rL`* F4B9xZThfd"GATd/T&'Ue bC?0agx,T"l,K,YۣGg̘q?2!ғ2#m"%G%&B *-ZӧO`'Rș}fs t6l3όEXvcI.%O⾄/%-6G?jӦMC@OfiQ'f舉9vm0E¹tYWTek4O'm܋G DC""&&M6j㻈1puR,ލ?eyh8(R?` ɎK顚GAH'.f#DBMURe)SXh{ֽ{?}j'=T$O-{a5&*ӝ={\[o۱c^>*V=IbQF-?~-۷@`CL,1ϞjF,r#:th)S>>}BIL'슘_a$VL$⛫xM7c JZÓQwM4&dWm6NXLj'zFGTm۶xoժU{ڕю/97ٲf2LW*ƹ׹^M"=&͊‹g&[;k-ڣHwΦ4tp}Ȳ\YJ8ע]L<'r{bPKu*kӎ@L>ti naH'vLc,}\\*2;H:''A19(ܼy+WnAֲES|x$'XY)۵ٳg/ĺvѽPҽ{. H,BS [n_,D淔Vz!DvUd IktaKV'W ϒmD(dnj{0Lz5$_+W!c߿ߧ~:"O~ZPP`$ҸYE\b醧ID[bUd,>,|v?3>TȺi Qrva*I$mWRPBNcQ(f  =(LCBl SgR7ʆo H UN3v|w,)8X>l}c[ DJ QKƁԷo8%̄K`n?`A]x̙r$ݔd P5h r^QkzqڵSEs&1pRRjB28px;.]y͚5nX> ~q1k珧-s;J/F*_Β6 4Y(3D2pV^ziN:@vTv|Ȩ8@QY6mھp?,X3$u\r%bq9o-[m]vYC ĿgV.ٲeq1ʖU3f0瞪@9jY&"XMQn+&L"1xj;0$5~L6? #I 2TSGÃ99 ;I.1) & ¤ VZٟ,R-@@kf*D 8)V&!xP,eBӰ^˫fF')MJ, q96:555Y,Q\$dzyLX[ڡB!9iHRzt-R{#?N[cQ,eJVVƼlRL$^@J]9U`Ȏ*osKb?d2XD‡^`NDyq|6nIT(%a?Emr8h0xٹ:m]2rnS&`'QO[I"|3Vy#B:Ą񎨪c\dC!"YL$3XkS9\Jr FŢ/n(۰r7Lx.2OE瑨5XBSY Z'HlNdrƉU Vֱ"GРQ^v? >Yke),/=U^FrQ_dr*k#M89h hOՒ 61m r ȴ@gP!Z' E=a mK 琙υeY&?ݪF9חYR~8W^-G r0TB\?* X~3@N] ny}6) Q+Cq]0Z~N`d)Jdy_}ţȦ1 AiIIl^NyCL<޴BCP+g$ 9uyMkt&p76D@>!?o@s rdSSZܗZ SG~r-AO'{W!=ϟ=[9I0⁕Yf 0'=g5a#H 'I[0ADC/}dB0VNNv3?Ūeoc5ߧ+rҨQʼQv[t¯!-S~Jߣv ۆD76;Un}Vv.LqCǠcee#Y-d@ " b[ir]bVn?L$"X<\- JUO‰~,zҙuw43Bnx(}# 4Wzԡf: #O ax6Sxss08|}VAѯV׃3\%\v  pWioq{bbX<] 3-ǡ0*zAβ}1%8C@YB(Z#H;B"dX&`q]2`l(!E'0`7᥸Rr7XG'<2w1RW;sge]ZQj+ "k"DTZ4Fm 4UAFђ "%`j1 $ƦG+*ݲ 2׽;󛻳.9f}wL׻_qH% aE$9v lwZrR *#ь#5v8`ii!q ng[WrX-t;ި\9CI")/6n܈ 6N4m_2 Z[SÇ͔W"QB>j0K6UqPsd!?:B n"*%4H~m,O.Vz+2Ēb*-H1SinI+R85G{R_dhxiP.}Ԕ{1wdVnpA0]SzEB%R"y|smw 5wQlvꧠ^DXN*Lѓ:A4'-bP%m>g&HbIb6Cf27I7]dN6 MMM &ӫ"}단q*1 [F_Qƀb㨝UѯϋuFQika2Lֈ%mx^q)CSz-*WGm8JFPq4ougqpzMxlGEC 9uQ( |og5D?UwՂa<_Q+Ux HG.y/Ly׵Nc2!RJ C*S‚tk/=- ])ۤBʨ*?^{X}:k/>Tt2X-@v̯_c? |ܩ2pwɿA(1ܾ)~0fMԷ4ŗ^&];|q[j/Y!NC%Fq_SSt{Ou1z |3`A2]؇xxVϩ;=;%?SO0wu1Z7u,+~J#EC&1H'E(-/mPezW&%6r.XUfR!UB.G[3eY'/8.zۜ%MeM)50 Y s6'>kS`u95h "u 9tmہe bo9ݾ *$XYcknr$RNI;Ǘð`1 ð`1 Â0 Â0 Â0 0 0 0,X 0,X 0,X ð`1 ð`1 ð`1 Â0 Â0 S 0Dd'IENDB`bcfg2-1.3.3/doc/_static/favicon.ico000066400000000000000000000124661223671746500170430ustar00rootroot00000000000000 h&  (  ]LABpp^LAA$%8^>+X5X5^>*8%"$%B%a@$aF`Ba@$B%%"0a@#~W4|W}Z~W4a@#5F2(AV6 bChtbbsibCW7!H4)>qR0ûvuûR0pq[:![;!pC/$@\?)īɳƫʫɲĪ]?)K6,?+ Ųʳ+ 'U6$÷öT4!#):{x:&O4+CqpU=.B( @ $^KBm)/2 2 /)ZG=l$ # t2 T<-D'I,L.L.I,D'R9*2 # s+) ,< I,kQ=\<#`@&eH/eH/`@&\<#iN9I,< , ) .A%Q3^>%~bKoM0tQ3tQ3oM0{_G^>%Q3A%. $-A%T5bB(oM0pV~Z:ii~Z:mRoM0bB(T5A%,+# t< Q3bB(qO1|X8{_kIkIy\|X8qO1bB(Q3< $ r"2 I,^>%oM0~Z:jIms̼ͽͽ̼sjjI~Z:oM0^>%I,1 $WD:mQ8(hM8z]FpVko}xxzzyx|okpV{_GiN9Q8)ZG=l)D'\<#qP3qU~`xhiooih~x~`pTqO2\<#D').I,`@&eKpùźpeJ`@&I,/2 L.iK3Ⱥé}|¨ǹiK3L.2 2 L.sWAµƭŪsWAL.2 .I,sV?qɼȰǮɼqsV?I,/(D'pT>w~įʴçīīçɲį~wpT>D')VC:nQ8(t[HŰ͸̶͸ϸϹϹ̷͸űv]KT;,]JAn"2 I,wʷŴ¹¹ŴȵvI,2 "$ w< qYGɸ®®ȷpWE< # t -B&|mǸƷ|mB&- & .D)u÷ötD).) ' ,=!wbRv`P=!,)  % v2 W>/dLo)/2 2 /(WD:m$????bcfg2-1.3.3/doc/_templates/000077500000000000000000000000001223671746500154205ustar00rootroot00000000000000bcfg2-1.3.3/doc/_templates/indexsidebar.html000066400000000000000000000011531223671746500207470ustar00rootroot00000000000000

Docs for other versions

bcfg2-1.3.3/doc/_templates/layout.html000066400000000000000000000012721223671746500176250ustar00rootroot00000000000000{% extends "!layout.html" %} {% block extrahead %} {{ super() }} {% endblock %} {% block rootrellink %}
  • home
  • help
  • documentation »
  • {% endblock %} {% block relbar1 %}
    sampledoc
    {{ super() }} {% endblock %} bcfg2-1.3.3/doc/appendix/000077500000000000000000000000001223671746500150735ustar00rootroot00000000000000bcfg2-1.3.3/doc/appendix/articles.txt000066400000000000000000000020121223671746500174350ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-articles: ======== Articles ======== * Configuration and change management with Bcfg2: "The Dean" - The powerful Bcfg2 provides a sophisticated environment for centralized configuration management. * Marko Jung, Nils Magnus * In the english 'Linux Magazine', 04/09, pages 30-35, April 2009 * The `Bcfg2 code listings `_ for the article are public. * `Konfigurations- und Change-Management in Bcfg2 `_ * Marko Jung, Nils Magnus * In the german 'Linux Magazin', 10/08, pages 76-80, September 2008 * The `code listings `_ for the article are public. * `System Management Methodologies with Bcfg2 `_ * Narayan Desai, Rick Bradshaw and Joey Hagedorn * In ;login: Magazine, Volume 31, #1, pages 11-18, February 2006 bcfg2-1.3.3/doc/appendix/books.txt000066400000000000000000000002651223671746500167540ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-books: ===== Books ===== * `Configuration Management with Bcfg2 `_ * Narayan Desai and Cory Lueninghoener bcfg2-1.3.3/doc/appendix/configuration.txt000066400000000000000000000003621223671746500205040ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-configuration: ===================== Example configuration ===================== This section contains useful configuration of additional tools. .. toctree:: :maxdepth: 1 :glob: configuration/* bcfg2-1.3.3/doc/appendix/configuration/000077500000000000000000000000001223671746500177425ustar00rootroot00000000000000bcfg2-1.3.3/doc/appendix/configuration/mrepo.txt000066400000000000000000000055031223671746500216300ustar00rootroot00000000000000.. -*- mode: rst -*- .. _mrepo: http://dag.wieers.com/home-made/mrepo/ .. _appendix-configuration-mrepo: mrepo ===== .. This section describes how to setup an `mrepo`_ mirror. `mrepo`_ builds a local APT/Yum RPM repository from local ISO files, downloaded updates, and extra packages from 3rd party repositories. It takes care of setting up the ISO files, downloading the RPMs, configuring HTTP access and providing PXE/TFTP resources for remote network installations. Sample mrepo configuration -------------------------- :: ### Configuration file for mrepo ### The [main] section allows to override mrepo's default settings ### The mrepo-example.conf gives an overview of all the possible settings [main] srcdir = /var/mrepo/src wwwdir = /var/www/mrepo confdir = /etc/mrepo.conf.d arch = x86_64 mailto = smtp-server = localhost hardlink = yes shareiso = yes rsync-timeout = 3600 [centos5] name = CentOS Server $release ($arch) release = 5 arch = x86_64 metadata = yum repomd # ISO images iso = centos-$release-server-$arch-DVD.iso #addons = rsync://mirrors.kernel.org/centos/$release/addons/$arch/RPMS centosplus = rsync://mirrors.kernel.org/centos/$release/centosplus/$arch/RPMS extras = rsync://mirrors.kernel.org/centos/$release/extras/$arch/RPMS #fasttrack = rsync://mirrors.kernel.org/centos/$release/fasttrack/$arch/RPMS os = rsync://mirrors.kernel.org/centos/$release/os/$arch/CentOS updates = rsync://mirrors.kernel.org/centos/$release/updates/$arch/RPMS dag = http://apt.sw.be/redhat/el$release/en/$arch/RPMS.dag dries = http://apt.sw.be/redhat/el$release/en/$arch/RPMS.dries rpmforge = http://apt.sw.be/redhat/el$release/en/$arch/RPMS.rpmforge ### Any other section is considered a definition for a distribution ### You can put distribution sections in /etc/mrepo.conf.d/ ### Examples can be found in the documentation at: ### /usr/share/doc/mrepo-0.8.6/dists/. Update the repositories ----------------------- :: mrepo -ug Example sources.xml file ------------------------ .. code-block:: xml centos-5.4 http://mrepo/centos5-x86_64/RPMS.os x86_64 centos-5.4 http://mrepo/centos5-x86_64/RPMS.updates x86_64 centos-5.4 http://mrepo/centos5-x86_64/RPMS.extras x86_64 bcfg2-1.3.3/doc/appendix/files.txt000066400000000000000000000003461223671746500167410ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-files: ============= Example files ============= In this section are some examples for getting started with a more in-depth usage of Bcfg2. .. toctree:: :maxdepth: 1 :glob: files/* bcfg2-1.3.3/doc/appendix/files/000077500000000000000000000000001223671746500161755ustar00rootroot00000000000000bcfg2-1.3.3/doc/appendix/files/mysql.txt000066400000000000000000000033351223671746500201070ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst .. _appendix-files-mysql: .. Author: Patrick Ruckstuhl MySQL example ============= I had some time ago to continue with putting my configuration into Bcfg2 and maybe this helps someone else. I added a new bundle: .. code-block:: xml The ``users.sh`` script looks like this: .. code-block:: sh #!/bin/sh mysql --defaults-extra-file=/etc/mysql/debian.cnf mysql \ < /root/bcfg2-install/mysql/users.sql On debian there is a user account in ``/etc/mysql/debian.cnf`` automatically created, but you could also (manually) create a user in the database that has enough permissions and add the login information in a file yourself. This file looks like this:: [client] host = localhost user = debian-sys-maint password = XXXXXXXXXX The ``users.sql`` looks like this:: DELETE FROM db; INSERT INTO db VALUES ('localhost', 'phpmyadmin', 'pma', 'Y', 'Y', 'Y', 'Y', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N'); DELETE FROM user WHERE User <> 'debian-sys-maint'; INSERT INTO user VALUES ('localhost', 'root', 'XXXXXXXXXXX', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', '', '', '', '', 0, 0, 0); INSERT INTO user VALUES ('localhost', 'pma', '', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', '', '', '', '', 0, 0, 0); FLUSH PRIVILEGES; bcfg2-1.3.3/doc/appendix/files/ntp.txt000066400000000000000000000071771223671746500175530ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-files-ntp: .. Author: Jason Pepas ntp example =========== Here is a series of example configurations for Bcfg2, each introducing another layer of functionality. * After each change, run ``bcfg-repo-validate -v`` * Run the server with ``bcfg2-server -v`` * Update the client with ``bcfg2 -v -d -n`` (will not actually make client changes) Package only ------------ Our example starts with the bare minimum configuration setup. We have a client, a profile group, a list of packages, and an NTP bundle. ``Metadata/clients.xml``: .. code-block:: xml ``Metadata/groups.xml``: .. code-block:: xml ``Bundler/ntp.xml``: .. code-block:: xml ``Pkgmgr/packages.xml``: .. code-block:: xml (This can also be performed more elegantly with the :ref:`server-plugins-generators-packages` plugin.) Add service ----------- Configure the service, and add it to Rules. ``Rules/services.xml``: .. code-block:: xml ``Bundler/ntp.xml``: .. code-block:: xml Add config file --------------- Setup an ``etc/`` directory structure, and add it to the base:: # cat Cfg/etc/ntp.conf/ntp.conf server ntp1.utexas.edu ``Base/base.xml``: ``Bundler/ntp.xml``: .. code-block:: xml Create a bundle --------------- Bundles allow the grouping of related configuration entries that are used to provide a single service. This is done for several reasons: * Grouping related things in one place makes it easier to add those entries for multiple groups of clients * Grouping entries into bundles makes their validation occur collectively. This means that config files can override the contents of packages. Also, config files are rechecked after packages are upgraded, so that they can be repaired if the package install clobbered them. * Services associated with a bundle get restarted whenever any entity in that bundle is modified. This ensures that new configuration files and software are used after installation. The config file, package, and service are really all related components describing the idea of an ntp client, so they should be logically grouped together. We use a bundle to accomplish this. ``Bundler/ntp.xml``: .. code-block:: xml After this bundle is created, it must be associated with a group (or groups). Add a bundle child element to the group(s) which should install this bundle. ``Metadata/groups.xml``: .. code-block:: xml ... ... Once this bundle is created, a client reconfigure will install these entries. If any are modified, then the *ntpd* service will be restarted. If you only want ntp configurations to be updated (and nothing else), the bcfg2 client can be run with a ``-b `` option that will only update entries in the specified bundle. bcfg2-1.3.3/doc/appendix/guides.txt000066400000000000000000000003121223671746500171100ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-guides: ====== Guides ====== This section contains platform-specific quickstart guides and howtos around Bcfg2. .. toctree:: :maxdepth: 1 :glob: guides/* bcfg2-1.3.3/doc/appendix/guides/000077500000000000000000000000001223671746500163535ustar00rootroot00000000000000bcfg2-1.3.3/doc/appendix/guides/authentication.txt000066400000000000000000000124011223671746500221310ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-guides-authentication: ============== Authentication ============== Scenarios ========= 1. Cluster nodes that are frequently rebuilt Default settings work well; machines do not float, and a per-client password is not required. 2. :ref:`appendix-guides-nat_howto` * Build client records in advance with ``bcfg2-admin``, setting a uuid for each new client. * Set the address attribute for each to the address of the NAT. * Optionally, set a per-client password for each, and set into secure mode. .. note:: This will require the use of the uuid and password from each client, and will require that they come through the NAT address. Building bcfg2.conf automatically ================================= This is a :ref:`Cheetah template ` that automatically constructs per-client bcfg2.conf from the per-client metadata:: [communication] protocol = xmlrpc/ssl #if $self.metadata.uuid != None user = $self.metadata.uuid #end if #if $self.metadata.password != None password = $self.metadata.password #else password = my-password-foobat #end if [components] bcfg2 = https://localhost:6789 In this setup, this will cause any clients that have uuids established to be set to use them in ``bcfg2.conf``. It will also cause any clients with passwords set to use them instead of the global password. How Authentication Works ======================== #. First, the client is associated with a client record. If the client specifies a uuid, it uses this instead of the results of a dns or address lookup. #. Next, the ip address is verified against the client record. If the address doesn't match, then the client must be set to floating='true' #. Finally, the password is verified. If the client is set to secure mode, the only its per-client password is accepted. If it is not set to secure mode, then either the global password or per-client password will be accepted Failure during any of these stages results in authentication failure. Note that clients set into secure mode that do not have per-client passwords set will not be able to connect. SSL Cert-based client authentication ==================================== SSL-based client authentication is supported. This requires several things: #. Certificate Authority (to sign all keys) #. Server key and cert signed by the CA #. Client key and cert signed by the CA A variety of CAs can be used, but these keys can be simply generated using the following set of steps: #. Setup a CA http://www.flatmtn.com/article/setting-openssl-create-certificates #. Create keys for each client and server, signing them with the CA signing cert http://www.flatmtn.com/article/setting-ssl-certificates-apache .. note:: The client CN must be the FQDN of the client (as returned by a reverse DNS lookup of the ip address. Otherwise, you will end up with an error message on the client that looks like:: Server failure: Protocol Error: 401 Unauthorized Failed to download probes from bcfg2 Server Failure You will also see an error message on the server that looks something like:: cmssrv01 bcfg2-server[9785]: Got request for cmssrv115 from incorrect address 131.225.206.122 cmssrv01 bcfg2-server[9785]: Resolved to cmssrv115.fnal.gov #. Distribute the keys and certs to the appropriate locations #. Copy the ca cert to clients, so that the server can be authenticated Clients authenticating themselves with a certificate will be authenticated that way first; clients can be setup to either authenticate solely with certs, use certs with a fallback to password, or password only. Also a bootstrap mode will be added shortly; this will allow a client to authenticate with a password its first time, requiring a certificate all subsequent times. This behavior can be controlled through the use of the auth attribute in ``Metadata/clients.xml``:: Allowed values are: +-------------------+------------------------------------------+ | Auth Type | Meaning | +===================+==========================================+ | ``cert`` | Certificates must be used | +-------------------+------------------------------------------+ | ``cert+password`` | Certificate or password may be used. If | | | a certificate is used, the password must | | | also be used. | +-------------------+------------------------------------------+ | ``bootstrap`` | Password can be used for one client run, | | | after that only certificate is allowed | +-------------------+------------------------------------------+ ``cert+password`` is the default. This can be changed by setting the ``authentication`` parameter in the ``[communcation]`` section of ``bcfg2.conf``. For instance, to set ``bootstrap`` mode as the global default, you would add the following to ``bcfg2.conf``:: [communication] authentication = bootstrap ``bootstrap`` mode is currently incompatible with the :ref:`server-plugins-grouping-metadata-clients-database`. bcfg2-1.3.3/doc/appendix/guides/bootstrap.txt000066400000000000000000000024501223671746500211320ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-guides-bootstrap: ========= Bootstrap ========= Once you have your bcfg2 server setup and working, a common next step to take is automating the addition of new clients. The method for bootstrapping your clients will vary depending on your needs. The simplest way to go about this is to create a public default group in ``Metadata/groups.xml``. Example: .. code-block:: xml This allows clients to freely associate themselves with this group so that you will not be required to manually add them to ``Metadata/clients.xml`` prior to running the client. .. note:: Reverse DNS will need to work in order to automate the process of bootstrapping clients without first adding them to ``Metadata/clients.xml``. There are command line options available on the client which allow you to specify the options that are normally found in the client's ``/etc/bcfg2.conf``:: bcfg2 -x password -p basic -S https://bcfg2-server:6789 The above command will add the client to ``Metadata/clients.xml`` with the profile *basic*. Generally, you should include ``/etc/bcfg2.conf`` in the configuration given to the client by the bcfg2 server in this initial run to avoid specifying these options on future runs. bcfg2-1.3.3/doc/appendix/guides/centos.txt000066400000000000000000000452501223671746500204150ustar00rootroot00000000000000.. -*- mode: rst -*- .. _EPEL: http://fedoraproject.org/wiki/EPEL .. _appendix-guides-centos: ===================== Quickstart for CentOS ===================== This is a complete getting started guide for CentOS. With this document you should be able to install a Bcfg2 server and a Bcfg2 client. Install Bcfg2 ============= The fastest way to get Bcfg2 onto your system is to use Yum or your preferred package management tool. We'll be using the ones that are distributed through EPEL_, but depending on your aversion to risk you could download an RPM from other places as well. See :ref:`getting_started-using_bcfg2-with-centos` for information about building Bcfg2 from source and making your own packages. Using EPEL ---------- Make sure EPEL_ is a valid repository on your server. The `instructions `_ on how to do this basically say:: [root@centos ~]# rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm .. note:: You will have to adjust this command to match your architecture and the current EPEL release. Install the bcfg2-server and bcfg2 RPMs:: [root@centos ~]# yum install bcfg2-server bcfg2 Your system should now have the necessary software to use Bcfg2. The next step is to set up your Bcfg2 :term:`repository`. Initialize your repository ========================== Now that you're done with the install, you need to initialize your repository and setup your ``/etc/bcfg2.conf``. ``bcfg2-admin init`` is a tool which allows you to automate this:: [root@centos ~]# bcfg2-admin init Store bcfg2 configuration in [/etc/bcfg2.conf]: Location of bcfg2 repository [/var/lib/bcfg2]: Input password used for communication verification (without echoing; leave blank for a random): What is the server's hostname: [centos] Input the server location [https://centos:6789]: Input base Operating System for clients: 1: Redhat/Fedora/RHEL/RHAS/Centos 2: SUSE/SLES 3: Mandrake 4: Debian 5: Ubuntu 6: Gentoo 7: FreeBSD : 1 Generating a 2048 bit RSA private key .........................+++ ..................+++ writing new private key to '/etc/bcfg2.key' ----- Signature ok subject=/C=US=ST=Illinois/L=Argonne/CN=centos Getting Private key Repository created successfuly in /var/lib/bcfg2 Change responses as necessary. Start the server ================ You are now ready to start your bcfg2 server for the first time:: [root@centos ~]# /sbin/service bcfg2-server start To verify that everything started ok, look for the running daemon and check the logs:: [root@centos ~]# /etc/init.d/service bcfg2-server status [root@centos ~]# tail /var/log/messages Mar 29 12:42:26 centos bcfg2-server[5093]: service available at https://centos:6789 Mar 29 12:42:26 centos bcfg2-server[5093]: serving bcfg2-server at https://centos:6789 Mar 29 12:42:26 centos bcfg2-server[5093]: serve_forever() [start] Mar 29 12:42:41 centos bcfg2-server[5093]: Handled 16 events in 0.007s Run bcfg2 to be sure you are able to communicate with the server:: [root@centos ~]# bcfg2 -vqn No ca is specified. Cannot authenticate the server with SSL. No ca is specified. Cannot authenticate the server with SSL. Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile Excluding Packages in global exclude list Finished Loaded tool drivers: Action Chkconfig POSIX YUMng Phase: initial Correct entries: 0 Incorrect entries: 0 Total managed entries: 0 Unmanaged entries: 208 Phase: final Correct entries: 0 Incorrect entries: 0 Total managed entries: 0 Unmanaged entries: 208 No ca is specified. Cannot authenticate the server with SSL. The ca message is just a warning, meaning that the client does not have sufficient information to verify that it is talking to the correct server. This can be fixed by distributing the ca certificate from the server to all clients. By default, this file is available in ``/etc/bcfg2.crt`` on the server. Copy this file to the client (with a bundle) and add the ca option to ``bcfg2.conf`` pointing at the file, and the client will be able to verify it is talking to the correct server upon connection:: [root@centos ~]# cat /etc/bcfg2.conf [communication] protocol = xmlrpc/ssl password = N41lMNeW ca = /etc/bcfg2.crt [components] bcfg2 = https://centos:6789 Now if you run the client, no more warning:: [root@centos ~]# bcfg2 -vqn Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile Excluding Packages in global exclude list Finished Loaded tool drivers: Action Chkconfig POSIX YUMng Phase: initial Correct entries: 0 Incorrect entries: 0 Total managed entries: 0 Unmanaged entries: 208 Phase: final Correct entries: 0 Incorrect entries: 0 Total managed entries: 0 Unmanaged entries: 208 Bring your first machine under Bcfg2 control ============================================ Now it is time to get your first machine's configuration into your Bcfg2 :term:`repository`. Let's start with the server itself. Setup the :ref:`server-plugins-generators-packages` plugin ---------------------------------------------------------- First, replace **Pkgmgr** with **Packages** in the plugins line of ``bcfg2.conf``. Then create Packages layout (as per :ref:`packages-exampleusage`) in ``/var/lib/bcfg2`` .. note:: I am using the RawURL syntax here since we are using `mrepo`_ to manage our yum mirrors. .. _mrepo: http://dag.wieers.com/home-made/mrepo/ .. code-block:: xml x86_64 x86_64 x86_64 Due to the :ref:`server-plugins-generators-packages-magic-groups`, we need to modify our Metadata. Let's add a **centos5.4** group which inherits a **centos** group (this should replace the existing **redhat** group) present in ``/var/lib/bcfg2/Metadata/groups.xml``. The resulting file should look something like this .. note:: The reason we are creating a release-specific group in this case is that the YUMSource above is specific to the 5.4 release of centos. That is, it should not apply to other releases (5.1, 5.3, etc). .. code-block:: xml .. note:: When editing your xml files by hand, it is useful to occasionally run `bcfg2-lint` to ensure that your xml validates properly. The final thing we need is for the client to have the proper arch group membership. For this, we will make use of the :ref:`unsorted-dynamic_groups` capabilities of the Probes plugin. Add Probes to your plugins line in ``bcfg2.conf`` and create the Probe.:: [root@centos ~]# grep plugins /etc/bcfg2.conf plugins = Base,Bundler,Cfg,...,Probes [root@centos ~]# mkdir /var/lib/bcfg2/Probes [root@centos ~]# cat /var/lib/bcfg2/Probes/groups #!/bin/sh echo "group:`uname -m`" Now we restart the bcfg2-server:: [root@centos ~]# /etc/init.d/bcfg2-server restart If you now ``tail -f /var/log/messages``, you will see the Packages plugin in action, updating the cache. Start managing packages ----------------------- Add a base-packages bundle. Let's see what happens when we just populate it with the *yum* package. .. code-block:: xml [root@centos ~]# cat /var/lib/bcfg2/Bundler/base-packages.xml You need to reference the bundle from your Metadata. The resulting profile group might look something like this .. code-block:: xml Now if we run the client, we can see what this has done for us.:: [root@centos ~]# bcfg2 -vqn Running probe groups Probe groups has result: x86_64 Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile Excluding Packages in global exclude list Finished Loaded tool drivers: Action Chkconfig POSIX YUMng Package pam failed verification. Phase: initial Correct entries: 94 Incorrect entries: 1 Total managed entries: 95 Unmanaged entries: 113 In dryrun mode: suppressing entry installation for: Package:pam Phase: final Correct entries: 94 Incorrect entries: 1 Package:pam Total managed entries: 95 Unmanaged entries: 113 Interesting, our **pam** package failed verification. What does this mean? Let's have a look:: [root@centos ~]# rpm --verify pam ....L... c /etc/pam.d/system-auth Sigh, it looks like the default RPM install for pam fails to verify using its own verification process (trust me, it's not the only one). At any rate, I was able to get rid of this particular issue by removing the symlink and running ``yum reinstall pam``. As you can see, the Packages plugin has generated the dependencies required for the yum package automatically. The ultimate goal should be to move all the packages from the **Unmanaged** entries section to the **Managed** entries section. So, what exactly *are* those Unmanaged entries?:: [root@centos ~]# bcfg2 -veqn Running probe groups Probe groups has result: x86_64 Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile Excluding Packages in global exclude list Finished Loaded tool drivers: Action Chkconfig POSIX YUMng Extra Package openssh-clients 4.3p2-36.el5_4.4.x86_64. Extra Package libuser 0.54.7-2.1el5_4.1.x86_64. ... Phase: initial Correct entries: 95 Incorrect entries: 0 Total managed entries: 95 Unmanaged entries: 113 Phase: final Correct entries: 95 Incorrect entries: 0 Total managed entries: 95 Unmanaged entries: 113 Package:at Package:avahi Package:avahi-compat-libdns_sd ... Now you can go through these and continue adding the packages you want to your Bundle. After a while, I ended up with a minimal bundle that looks like this .. code-block:: xml Now when I run the client, you can see I have only one unmanaged package:: [root@centos ~]# bcfg2 -veqn Running probe groups Probe groups has result: x86_64 Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile Excluding Packages in global exclude list Finished Loaded tool drivers: Action Chkconfig POSIX YUMng Extra Package gpg-pubkey e8562897-459f07a4.None. Extra Package gpg-pubkey 217521f6-45e8a532.None. Phase: initial Correct entries: 187 Incorrect entries: 0 Total managed entries: 187 Unmanaged entries: 16 Phase: final Correct entries: 187 Incorrect entries: 0 Total managed entries: 187 Unmanaged entries: 16 Package:gpg-pubkey Service:atd Service:avahi-daemon Service:bcfg2-server ... The gpg-pubkey packages are special in that they are not really packages. Currently, the way to manage them is using :ref:`BoundEntries `. So, after adding them, our Bundle now looks like this .. note:: This does not actually control the contents of the files, you will need to do this part separately (see below). .. code-block:: xml .. note:: version="foo" is just a dummy attribute for the gpg-pubkey Package To actually push the gpg keys out via Bcfg2, you will need to manage the files as well. This can be done by adding Path entries for each of the gpg keys you want to manage .. code-block:: xml Then add the files to Cfg:: mkdir -p Cfg/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5 cp /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5 !$/RPM-GPG-KEY-CentOS-5 mkdir -p Cfg/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL cp /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL !$/RPM-GPG-KEY-EPEL You will also want to add an *important* attribute to these files so that they are installed on the client prior to any attempts to install the **gpg-pubkey** rpm packages. This is especially important during the bootstrapping phase and can be accomplished using an :ref:`server-info` file that looks like the following: .. code-block:: xml Now, running the client shows only unmanaged Service entries. Woohoo! Manage services --------------- Now let's clear up the unmanaged service entries by adding the following entries to our bundle. .. code-block:: xml ...and bind them in Rules .. code-block:: xml [root@centos ~]# cat /var/lib/bcfg2/Rules/services.xml Now we run the client and see there are no more unmanaged entries!:: [root@centos ~]# bcfg2 -veqn Running probe groups Probe groups has result: x86_64 Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile Excluding Packages in global exclude list Finished Loaded tool drivers: Action Chkconfig POSIX YUMng Phase: initial Correct entries: 205 Incorrect entries: 0 Total managed entries: 205 Unmanaged entries: 0 Phase: final Correct entries: 205 Incorrect entries: 0 Total managed entries: 205 Unmanaged entries: 0 .. warning:: This basic bundle is created mainly for the purposes of getting you to a completely managed client. It is recommended that you create bundles for appropriate services due to the way bundle updates are managed. Please see :ref:`unsorted-writing_specification` for more details. Dynamic (web) reports ===================== See installation instructions at :ref:`reports-dynamic` Next Steps ========== :ref:`getting_started-index-next-steps` bcfg2-1.3.3/doc/appendix/guides/converging_rhel5.txt000066400000000000000000000071701223671746500223610ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-guides-converging_rhel5: ====================================== Converging on Verification with RHEL 5 ====================================== Running verification ==================== To get complete verification status, run:: bcfg2 -vqned Unmanaged entries ================= * Package (top-level) #. Enable the "Packages" plugin in ``/etc/bcfg2.conf``, and configure the Yum repositories in ``/var/lib/bcfg2/Packages/sources.xml``. #. If a package is unwanted, remove it:: sudo yum remove PACKAGE #. Otherwise, add ```` to the Base or Bundler configuration. * Package (dependency) #. Ensure the Yum repository sources configured in ``/var/lib/bcfg2/Packages/sources.xml`` are correct. #. Ensure the Yum repositories themselves are up-to-date with the main package and dependencies. #. Rebuild the Packages plugin cache:: bcfg2-admin xcmd Packages.Refresh * Service #. Add ```` to the Base or Bundler configuration. #. Add ```` to ``/var/lib/bcfg2/Rules/services.xml``. Incorrect entries ================= For a "Package" --------------- * Failed RPM verification #. Run ``rpm -V PACKAGE`` #. Add configuration files (the ones with "c" next to them in the verification output) to ``/var/lib/bcfg2/Cfg/``. * For example, ``/etc/motd`` to ``/var/lib/bcfg2/Cfg/etc/motd/motd``. Yes, there is an extra directory level named after the file. #. Specify configuration files as ```` in the Base or Bundler configuration. #. Add directories to ``/var/lib/bcfg2/Rules/directories.xml``. For example: .. code-block:: xml * Multiple instances * Option A: Explicitly list the instances #. Drop the ```` from the Base or Bundler configuration. #. Add an explicit ```` and ```` configuration to a new Bundle, like the following: .. code-block:: xml #. Add the bundle to the applicable groups in ``/var/lib/bcfg2/Metadata/groups.xml``. * Option B: Disable verification of the package #. Add ``pkg_checks="false"`` to the ```` tag. For a "Path" ------------------- * Unclear verification problem (no details from Bcfg2) 1. Run ``bcfg2 -vqI`` to see detailed verification issues (but deny any suggested actions). * Permissions mismatch 1. Create an ``info.xml`` file in the same directory as the configuration file. Example: .. code-block:: xml Other troubleshooting tools =========================== * Generate the physical configuration from the server side:: bcfg2-info buildfile /test test.example.com * Generate the physical configuration from the client side:: bcfg2 -vqn -c/root/bcfg2-physical.xml bcfg2-1.3.3/doc/appendix/guides/fedora.txt000066400000000000000000000363571223671746500203720ustar00rootroot00000000000000.. -*- mode: rst -*- .. This guide is based on the Centos guide. .. _guide-fedora: ====== Fedora ====== This guide is work in progess. This is a complete getting started guide for Fedora. With this document you should be able to install a Bcfg2 server, a Bcfg2 client, and change the ``/etc/motd`` file on the client. Prerequisites ============= To setup a configuration management system based on Bcfg2 only a few prerequisites need to be fullfilled. * A server machine that can host the Bcfg2 * Internet access for the installation process * A working network with DNS Install Bcfg2 From RPM ====================== The fastest way to get Bcfg2 onto your system is to use ``yum`` or PackageKit. ``yum`` will pull all dependencies of Bcfg2 automatically in. :: $ su -c 'yum install bcfg2-server bcfg2' Your system should now have the necessary software to use Bcfg2. The next step is to set up your Bcfg2 :term:`repository`. Initialize your repository ========================== Now that you're done with the install, you need to initialize your repository and setup your ``/etc/bcfg2.conf``. ``bcfg2-admin init`` is a tool which allows you to automate this: .. code-block:: sh # bcfg2-admin init Store bcfg2 configuration in [/etc/bcfg2.conf]: Location of bcfg2 repository [/var/lib/bcfg2]: Directory /var/lib/bcfg2 exists. Overwrite? [y/N]:y Input password used for communication verification (without echoing; leave blank for a random): What is the server's hostname: [config01.local.net] Input the server location [https://config01.local.net:6789]: Input base Operating System for clients: 1: Red Hat/Fedora/RHEL/RHAS/Centos 2: SUSE/SLES 3: Mandrake 4: Debian 5: Ubuntu 6: Gentoo 7: FreeBSD : 1 Generating a 1024 bit RSA private key .......................................................++++++ .....++++++ writing new private key to '/etc/bcfg2.key' ----- Signature ok subject=/C=US/ST=Illinois/L=Argonne/CN=config01.local.net Getting Private key Repository created successfuly in /var/lib/bcfg2 Change responses as necessary. Start the server ================ You are now ready to start your Bcfg2 server for the first time:: $ su -c '/etc/init.d/bcfg2-server start' Starting Configuration Management Server: bcfg2-server [ OK ] To verify that everything started ok, look for the running daemon and check the logs: .. code-block:: sh $ su -c 'tail /var/log/messages' May 16 14:14:57 config01 bcfg2-server[2746]: service available at https://config01.local.net:6789 May 16 14:14:57 config01 bcfg2-server[2746]: serving bcfg2-server at https://config01.local.net:6789 May 16 14:14:57 config01 bcfg2-server[2746]: serve_forever() [start] May 16 14:14:57 config01 bcfg2-server[2746]: Handled 16 events in 0.009s Run ``bcfg2`` to be sure you are able to communicate with the server: .. code-block:: sh $ su -c 'bcfg2 -vqne' /usr/lib/python2.6/site-packages/Bcfg2/Client/Tools/rpmtools.py:23: DeprecationWarning: the md5 module is deprecated; use hashlib instead import md5 Loaded plugins: presto, refresh-packagekit Loaded tool drivers: Action Chkconfig POSIX YUMng Extra Package imsettings-libs 0.108.0-2.fc13.i686. Extra Package PackageKit-device-rebind 0.6.4-1.fc13.i686. ... Extra Package newt-python 0.52.11-2.fc13.i686. Extra Package pulseaudio-gdm-hooks 0.9.21-6.fc13.i686. Phase: initial Correct entries: 0 Incorrect entries: 0 Total managed entries: 0 Unmanaged entries: 1314 Phase: final Correct entries: 0 Incorrect entries: 0 Total managed entries: 0 Unmanaged entries: 1314 Package:ConsoleKit Package:jasper-libs Package:pcsc-lite-libs Package:ConsoleKit-libs Package:java-1.5.0-gcj Package:perf ... Package:iw Package:pcre Service:sshd Package:jack-audio-connection-kit Package:pcsc-lite Service:udev-post The ``bcfg2.conf`` file contains only standard plugins so far. .. code-block:: sh $ su -c 'cat /etc/bcfg2.conf' [server] repository = /var/lib/bcfg2 plugins = SSHbase,Cfg,Pkgmgr,Rules,Metadata,Base,Bundler [statistics] sendmailpath = /usr/lib/sendmail [database] engine = sqlite3 # 'postgresql', 'mysql', 'mysql_old', 'sqlite3' or 'ado_mssql'. name = # Or path to database file if using sqlite3. #/etc/brpt.sqlite is default path if left empty user = # Not used with sqlite3. password = # Not used with sqlite3. host = # Not used with sqlite3. port = [communication] protocol = xmlrpc/ssl password = test1234 certificate = /etc/bcfg2.crt key = /etc/bcfg2.key ca = /etc/bcfg2.crt [components] bcfg2 = https://config01.local.net:6789 Add the machines to Bcfg2 ------------------------- ``bcfg2-admin`` can be used to add a machine to Bcfg2 easily. You need to know the Fully Qualified Domain Name (FQDN) of ever system you want to control through Bcfg2. :: bcfg2-admin client add Bring your first machine under Bcfg2 control -------------------------------------------- Now it is time to get the first machine's configuration into the Bcfg2 repository. The server will be the first machine. It's already in the ``Metadata/client.xml``. Setup the :ref:`server-plugins-generators-packages` plugin ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ First, replace **Pkgmgr** with **Packages** in the plugins line of ``bcfg2.conf``. Then create a `Packages/` directory in ``/var/lib/bcfg2`` :: $ su -c 'mkdir /var/lib/bcfg2/Packages' Create a ``packages.conf`` in the ``/var/lib/bcfg2/Packages`` directory with the following contents:: [global] Create a ``sources.xml`` file for the packages in ``/var/lib/bcfg2/Packages`` with the following content. Choose a mirror near your location according the `Mirror list`_ . .. _Mirror list: http://mirrors.fedoraproject.org/publiclist/ .. code-block:: xml Fedora i386 x86_64 Due to the :ref:`server-plugins-generators-packages-magic-groups`, we need to modify our Metadata. Let's add a **fedora13** group which inherits a **fedora** group (this should replace the existing **redhat** group) present in ``/var/lib/bcfg2/Metadata/groups.xml``. The resulting file should look something like this .. note:: The reason we are creating a release-specific group in this case is that the YUMSource above is specific to the 13th release of fedora. That is, it should not apply to other releases (14, 15, etc). .. code-block:: xml .. note:: When editing your xml files by hand, it is useful to occasionally run ``bcfg2-lint`` to ensure that your xml validates properly. Add a probe +++++++++++ The next step for the client will be to have the proper arch group membership. For this, we will make use of the :ref:`unsorted-dynamic_groups` capabilities of the Probes plugin. Add **Probes** to your plugins line in ``bcfg2.conf`` and create the Probe: .. code-block:: sh $ su -c 'mkdir /var/lib/bcfg2/Probes' $ su -c 'cat /var/lib/bcfg2/Probes/groups' #!/bin/sh echo "group:`uname -m`" Now a restart of ``bcfg2-server`` is needed:: $ su -c '/etc/init.d/bcfg2-server restart' To test the Probe just run ``bcfg2 -vqn``. .. code-block:: xml $ su -c 'bcfg2 -vqn' Running probe group Probe group has result: group:i686 ... Start managing packages +++++++++++++++++++++++ Add a base-packages bundle. Let's see what happens when we just populate it with the *yum* package. Create the ``base-packages.xml`` in your ``Bundler/`` directory with a entry for ``yum``. .. code-block:: xml $ cat /var/lib/bcfg2/Bundler/base-packages.xml You need to reference the bundle from your ``group.xml``. The resulting profile group might look something like this .. code-block:: xml Now if we run the client, we can see what this has done for us.:: output As you can see, the Packages plugin has generated the dependencies required for the yum package automatically. The ultimate goal should be to move all the packages from the **Unmanaged** entries section to the **Managed** entries section. So, what exactly *are* those Unmanaged entries?:: output Now you can go through these and continue adding the packages you want to your Bundle. After a while, I ended up with a minimal bundle that looks like this .. code-block:: xml Now when I run the client, you can see I have only one unmanaged package:: outout The gpg-pubkey packages are special in that they are not really packages. Currently, the way to manage them is using :ref:`BoundEntries `. So, after adding them, our Bundle now looks like this .. note:: This does not actually control the contents of the files, you will need to do this part separately (see below). .. code-block:: xml .. note:: version="foo" is just a dummy attribute for the gpg-pubkey Package To actually push the gpg keys out via Bcfg2, you will need to manage the files as well. This can be done by adding Path entries for each of the gpg keys you want to manage .. code-block:: xml Then add the files to Cfg:: mkdir -p Cfg/etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5 cp /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5 !$/RPM-GPG-KEY-CentOS-5 mkdir -p Cfg/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL cp /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL !$/RPM-GPG-KEY-EPEL Now, running the client shows only unmanaged Service entries. Woohoo! Manage services +++++++++++++++ Now let's clear up the unmanaged service entries by adding the following entries to our bundle... .. code-block:: xml ...and bind them in Rules .. code-block:: xml [root@centos ~]# cat /var/lib/bcfg2/Rules/services.xml Now we run the client and see there are no more unmanaged entries! :: $ su -c 'bcfg2 -veqn' Adding Plugins ++++++++++++++ Git --- .. _Git tutorial: http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html Adding the :ref:`server-plugins-version-git` plugins can preserve versioning information. The first step is to add *Git* to your plugin line:: plugins = Base,Bundler,Cfg,...,Git For tracking the configuration files in the ``/var/lib/bcfg2`` directory a git repository need to be established:: git init For more detail about the setup of git please refer to a `git tutorial`_. The first commit can be the empty or the allready populated directory:: git add . && git commit -a While running ``bcfg2-info`` the following line will show up:: Initialized git plugin with git directory = /var/lib/bcfg2/.git bcfg2-1.3.3/doc/appendix/guides/gentoo.txt000066400000000000000000000115701223671746500204130ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-guides-gentoo: ====== Gentoo ====== This document tries to lay out anything Gentoo-specific that you need to know in order to use Bcfg2. Mostly that has to do with getting it to cooperate with the various pieces of Portage. Services, all things POSIX, and just about anything else that Bcfg2 does will work the same on Gentoo as on any other distribution. Bcfg2 is new on Gentoo; please let the list know if you find errors or omissions. Installing Bcfg2 ================ Early in July 2008, Bcfg2 was added to the Gentoo portage tree. If you don't use portage to install Bcfg2, you'll want to make sure you have all the prerequisites installed first. For a server, you'll need: * ``dev-libs/libgamin[python]`` * ``dev-python/lxml`` Clients will need at least: * ``app-portage/gentoolkit`` Portage installs from source ============================ .. versionadded:: 1.3.0 By default the client will run with the ``--gitbinpkgonly`` option. If you want your client to install packages from source (rather than having a binary build host as seen below), you can set the following in ``/etc/bcfg2.conf``.:: [Portage] binpkgonly = false Package Repository ================== .. note: This is only necessary for using binary packages. You’ll need (to make) at least one archive of binary packages. The Portage driver calls ``emerge`` with the ``--getbinpkgonly`` option. See :manpage:`make.conf(5)` and :manpage:`emerge(1)` manpages, specifically the :envvar:`PORTAGE_BINHOST` environment variable. Time Saver: quickpkg -------------------- If you have a standing Gentoo machine that you want to preserve or propagate, you can generate a complete package archive based on the present state of the system by using the quickpkg utility. For example: .. code-block:: sh for pkg in `equery -q l` ; do quickpkg "=$pkg" ; done ...will leave you with a complete archive of all the packages on your system in ``/usr/portage/packages/All``, which you can then move to your ftp server. Cataloging Packages In Your Repository -------------------------------------- Once you have a set of packages, you will need to create a catalog for them in ``/var/lib/bcfg2/Pkgmgr``. Here's a template: .. code-block:: xml ...and a partially filled-out example, for our local Gentoo/VMware build: .. code-block:: xml [...] The `` name (in our example, "gentoo-200701-vmware") should be included by any host which will draw its packages from this list. Our collection of packages for this class of machines is at the listed URI, and we only have one collection of packages for this batch of machines so in our case the `priority` doesn’t really matter, we've set it to `0`. Notice that package name fields are in `CAT/TITLE` format. Here is a hack which will generate a list of Package lines from a system's database of installed packages, especially useful in conjunction with the ``quickpkg`` example above: .. code-block:: sh #!/bin/bash for pkg in `equery -q l` ; do title=`echo $pkg | sed -e 's/\(.*\)-\([0-9].*\)/\1/'` version=`echo $pkg | sed -e 's/\(.*\)-\([0-9].*\)/\2/'` echo " " done Configuring Client Machines =========================== Set up ``/etc/bcfg2.conf`` the way you would for any other Bcfg2 client. In ``make.conf``, set *PORTAGE_BINHOST* to point to the URI of your package repository. You may want to create versions of ``make.conf`` for each package repository you maintain, with appropriate *PORTAGE_BINHOST* URI's in each, and associated with that package archive's group under ``Cfg`` -- for example, we have ``Cfg/etc/make.conf/make.conf.G99_gentoo-200701-vmware``. If a client host switches groups, and the new group needs a different set of packages, everything should just fall into place. Pitfalls ======== /boot ----- Gentoo as well as some other distros recommend leaving ``/boot`` unmounted during normal runtime. This can lead to trouble during verification and package installation, for example when ``/boot/grub/grub.conf`` turns up missing. The simplest way around this might just be to ensure that ``/boot`` is mounted whenever you run Bcfg2, possibly wrapping Bcfg2 in a script for the purpose. I've also thought about adding *Action* clauses to bundles for grub and our kernel packages, which would mount ``/boot`` before the bundle installs and unmount it afterward, but this doesn't get around the problem of those packages flunking verification. bcfg2-1.3.3/doc/appendix/guides/import-existing-ssh-keys.txt000066400000000000000000000073071223671746500240310ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-guides-import-existing-ssh-keys: ======================== Import existing ssh keys ======================== .. note:: In order for the instructions in this guide to work, you will need to first setup the :ref:`reporting system ` so that the server has the information needed to create the existing entries. This guide details the process for importing existing ssh keys into your server repository. Add a bundle for ssh ==================== After verifying that SSHbase is listed on the plugins line in ``/etc/bcfg2.conf``, you need to create a bundle containing the appropriate entries.:: cat > /tmp/ssh.xml << EOF :: mv /tmp/ssh.xml /var/lib/bcfg2/Bundle Next, you need to add the ssh bundle to the client's metadata in groups.xml. Validate your repository ======================== Validation can be performed using the following command:: bcfg2-lint Run the bcfg2 client ==================== :: bcfg2 -vqn You will see the incorrect entries for the ssh files:: Phase: initial Correct entries: 0 Incorrect entries: 7 Total managed entries: 7 Unmanaged entries: 649 In dryrun mode: suppressing entry installation for: Path:/etc/ssh/ssh_host_dsa_key Path:/etc/ssh/ssh_host_rsa_key Path:/etc/ssh/ssh_host_dsa_key.pub Path:/etc/ssh/ssh_host_rsa_key.pub Path:/etc/ssh/ssh_host_key Path:/etc/ssh/ssh_known_hosts Path:/etc/ssh/ssh_host_key.pub Phase: final Correct entries: 0 Incorrect entries: 7 Path:/etc/ssh/ssh_host_dsa_key Path:/etc/ssh/ssh_host_rsa_key Path:/etc/ssh/ssh_host_dsa_key.pub Path:/etc/ssh/ssh_host_rsa_key.pub Path:/etc/ssh/ssh_host_key Path:/etc/ssh/ssh_known_hosts Path:/etc/ssh/ssh_host_key.pub Total managed entries: 7 Unmanaged entries: 649 Install the client's ssh keys into the Bcfg2 repository ======================================================= Now, we pull the ssh host key data for the client out of the uploaded stats and insert it as host-specific copies of these files in ``/var/lib/bcfg2/SSHBase``.:: for key in ssh_host_rsa_key ssh_host_dsa_key ssh_host_key; do sudo bcfg2-admin pull Path /etc/ssh/$key sudo bcfg2-admin pull Path /etc/ssh/$key.pub done This for loop pulls data that was collected by the bcfg2 client out of the statistics file and installs it into the repository. This means that the client will keep the same ssh keys and the bcfg2 server can start generating a correct ssh_known_hosts file for the client. Run the bcfg2 client (again) ============================ :: bcfg2 -vqn This time, we will only see 1 incorrect entry.:: Phase: initial Correct entries: 6 Incorrect entries: 1 Total managed entries: 7 Unmanaged entries: 649 In dryrun mode: suppressing entry installation for: Path:/etc/ssh/ssh_known_hosts Phase: final Correct entries: 6 Incorrect entries: 1 Path:/etc/ssh/ssh_known_hosts Total managed entries: 7 Unmanaged entries: 649 Now, the only wrong entry is the ssh_known_hosts file, so go ahead and install it:: bcfg2 -vqI After answering 'y' to the interactive prompt, the client will install the known_hosts file successfully. bcfg2-1.3.3/doc/appendix/guides/nat_howto.txt000066400000000000000000000063141223671746500211220ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-guides-nat_howto: ========= NAT HOWTO ========= This page describes how to setup Bcfg2 to properly function with a collection of clients behind NAT. It describes the issues, how the underlying portions of the Bcfg2 system function, and how to correctly setup client metadata to cope with this environment. Issues ====== Bcfg2, by default, uses ip address lookup to determine the identity of a client that has connected. This process doesn't work properly in the case of NAT'ed hosts, because all requests from these clients come from the same external address when connecting to the server. These client identification issues will manifest themselves in a number of ways: * Inability to setup discrete clients with different profiles * Incorrect sharing of probe results across clients in the same NAT pool * Inability to bootstrap clients properly when client data is not predefined Architectural Issues ==================== Client identification is performed at the beginning of each client/server interaction. As of 0.9.3, client identification via IP address can be completely short-circuited through the use of a client uuid. Setup of client uuids requires actions of both the client and server. On the server side, the client uuid must be added to the client record in ``Metadata/clients.xml``. This attribute allows the server to use the user part of the authentication to resolve the client's metadata. Also, either the location attribute should be set to floating, or the IP address of the NAT must be reflected in the address attribute. Once added, the Client entry in clients.xml will look something like this: .. code-block:: xml Alternatively, the Client entry can be setup like this: .. code-block:: xml The difference between these definitions is explained in detail in the :ref:`appendix-guides-authentication` section, but in short, the second form requires that the client access the server from the NAT address, while the first form allows it to connect from any address provided it uses the proper uuid. (Client identification is orthogonal to the use of per-client passwords; this can be set in addition to the attributes above.) Once this setup is done, each client must be configured to use the proper uuid upon any server interaction. This can be done using either the command line argument -u, or the setting "user" in the "communication" section of ``/etc/bcfg2.conf``. UUID Choice =========== When determining client UUIDs, one must take care to ensure that UUIDs are unique to the client. Any hardware-specific attribute, like a hash of a mac address would be sufficient. Alternatively, if a local hostname is unique, it may be used as well. Automated Client Bootstrapping ============================== Automated setup of new clients from behind NAT works by using the common password. For example:: /usr/sbin/bcfg2 -u ubik3 -p desktop -x It is not possible at this time to do automated setup without setting up a pre-shared per-client key. bcfg2-1.3.3/doc/appendix/guides/ubuntu.txt000066400000000000000000001074251223671746500204470ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst .. _appendix-guides-ubuntu: ====== Ubuntu ====== .. note:: This particular how to was done on saucy, but should apply to any other `stable`__ version of Ubuntu. __ ubuntu-releases_ .. _ubuntu-releases: https://wiki.ubuntu.com/Releases Install Bcfg2 ============= We first need to install the server. For this example, we will use the bcfg2 server package from the bcfg2 `PPA`_ (note that there is also a version available in the ubuntu archives, but it is not as up to date). .. _PPA: https://launchpad.net/~bcfg2/+archive/ppa Install bcfg2-server -------------------- :: aptitude install bcfg2-server Remove the default configuration preseeded by the ubuntu package:: root@saucy:~# rm -rf /etc/bcfg2* /etc/ssl/bcfg2* /var/lib/bcfg2 Initialize your repository ========================== Now that you're done with the install, you need to intialize your repository and setup your bcfg2.conf. bcfg2-admin init is a tool which allows you to automate this process.:: root@saucy:~# bcfg2-admin init Store Bcfg2 configuration in [/etc/bcfg2.conf]: Location of Bcfg2 repository [/var/lib/bcfg2]: Input password used for communication verification (without echoing; leave blank for a random): What is the server's hostname: [saucy] Input the server location (the server listens on a single interface by default) [https://saucy:6789]: Input base Operating System for clients: 1: Redhat/Fedora/RHEL/RHAS/CentOS 2: SUSE/SLES 3: Mandrake 4: Debian 5: Ubuntu 6: Gentoo 7: FreeBSD 8: Arch : 5 Path where Bcfg2 server private key will be created [/etc/ssl/bcfg2.key]: Path where Bcfg2 server cert will be created [/etc/ssl/bcfg2.crt]: The following questions affect SSL certificate generation. If no data is provided, the default values are used. Country name (2 letter code) for certificate: US State or Province Name (full name) for certificate: Illinois Locality Name (eg, city) for certificate: Argonne Repository created successfuly in /var/lib/bcfg2 Generating a 2048 bit RSA private key ....................................................................................................................+++ ..............................+++ writing new private key to '/etc/ssl/bcfg2.key' ----- Signature ok subject=/C=US/ST=Illinois/L=Argonne/CN=saucy Getting Private key Of course, change responses as necessary. Start the server ================ Before you start the server, you need to fix your network resolution for this host. The short and easy way is to remove the 127.0.1.1 line in ``/etc/hosts`` and move your hostname to the 127.0.0.1 line. :: 127.0.0.1 saucy localhost # The following lines are desirable for IPv6 capable hosts ... .. _Debian Manual: http://www.debian.org/doc/manuals/debian-reference/ch05.en.html#_the_hostname_resolution .. note:: This configuration is not recommended except as a quick hack to get you through this guide. Ideally you'd add a line containing the host's actual IP address. More information on why this is broken can be found in the `Debian Manual`_. You are now ready to start your bcfg2 server for the first time.:: root@saucy:~# /etc/init.d/bcfg2-server start Starting Configuration Management Server: * bcfg2-server root@saucy:~# tail /var/log/syslog Jul 18 17:50:48 saucy bcfg2-server[5872]: Reconnected to syslog Jul 18 17:50:48 saucy bcfg2-server[5872]: bcfg2-server daemonized Jul 18 17:50:48 saucy bcfg2-server[5872]: service available at https://saucy:6789 Jul 18 17:50:48 saucy bcfg2-server[5872]: serving bcfg2-server at https://saucy:6789 Jul 18 17:50:48 saucy bcfg2-server[5872]: serve_forever() [start] Jul 18 17:50:48 saucy bcfg2-server[5872]: Handled 13 events in 0.006s Run bcfg2 to be sure you are able to communicate with the server:: root@saucy:~# bcfg2 -vqn Starting Bcfg2 client run at 1374188552.53 Loaded tool drivers: APT Action DebInit POSIX POSIXUsers Upstart VCS Loaded experimental tool drivers: POSIXUsers Phase: initial Correct entries: 0 Incorrect entries: 0 Total managed entries: 0 Unmanaged entries: 590 Phase: final Correct entries: 0 Incorrect entries: 0 Total managed entries: 0 Unmanaged entries: 590 Finished Bcfg2 client run at 1374188563.26 Bring your first machine under Bcfg2 control ============================================ Now it is time to get your first machine's configuration into your Bcfg2 repository. Let's start with the server itself. Setup the :ref:`server-plugins-generators-packages` plugin ---------------------------------------------------------- Replace Pkgmgr with Packages in the plugins line of ``bcfg2.conf``:: root@saucy:~# cat /etc/bcfg2.conf [server] repository = /var/lib/bcfg2 plugins = Bundler,Cfg,Metadata,Packages,Rules,SSHbase # Uncomment the following to listen on all interfaces #listen_all = true [statistics] sendmailpath = /usr/lib/sendmail #web_debug = False #time_zone = [database] #engine = sqlite3 # 'postgresql', 'mysql', 'mysql_old', 'sqlite3' or 'ado_mssql'. #name = # Or path to database file if using sqlite3. #/bcfg2.sqlite is default path if left empty #user = # Not used with sqlite3. #password = # Not used with sqlite3. #host = # Not used with sqlite3. #port = [reporting] transport = LocalFilesystem [communication] protocol = xmlrpc/ssl password = secret certificate = /etc/ssl/bcfg2.crt key = /etc/ssl/bcfg2.key ca = /etc/ssl/bcfg2.crt [components] bcfg2 = https://saucy:6789 Create Packages layout (as per :ref:`packages-exampleusage`) in ``/var/lib/bcfg2`` .. code-block:: xml root@saucy:~# mkdir /var/lib/bcfg2/Packages root@saucy:~# cat /var/lib/bcfg2/Packages/packages.conf [global] root@saucy:~# cat /var/lib/bcfg2/Packages/sources.xml main multiverse restricted universe amd64 bcfg2 bcfg2-server main multiverse restricted universe amd64 bcfg2 bcfg2-server main multiverse restricted universe amd64 bcfg2 bcfg2-server main amd64 Above, we have grouped our package sources under **ubuntu-saucy**. We need to add this group to our ``/var/lib/bcfg2/Metadata/groups.xml`` so that our client is able to obtain these sources. .. code-block:: xml .. note:: When editing your xml files by hand, it is useful to occasionally run ``bcfg2-lint -v`` to ensure that your xml validates properly. The last thing we need is for the client to have the proper arch group membership. For this, we will make use of the :ref:`unsorted-dynamic_groups` capabilities of the Probes plugin. Add Probes to your plugins line in ``bcfg2.conf`` and create the Probe. .. code-block:: sh root@saucy:~# grep plugins /etc/bcfg2.conf plugins = Bundler,Cfg,Metadata,...,Probes root@saucy:~# mkdir /var/lib/bcfg2/Probes root@saucy:~# cat /var/lib/bcfg2/Probes/groups #!/bin/sh ARCH=$(uname -m) case "$ARCH" in "x86_64") echo "group:amd64" ;; "i686") echo "group:i386" ;; esac Now we restart the bcfg2-server:: root@saucy:~# /etc/init.d/bcfg2-server restart Stopping Configuration Management Server: * bcfg2-server Starting Configuration Management Server: * bcfg2-server root@saucy:~# tail /var/log/syslog Jul 18 18:43:22 saucy bcfg2-server[6215]: Reconnected to syslog Jul 18 18:43:22 saucy bcfg2-server[6215]: bcfg2-server daemonized Jul 18 18:43:22 saucy bcfg2-server[6215]: service available at https://saucy:6789 Jul 18 18:43:22 saucy bcfg2-server[6215]: Failed to read file probed.xml: Error reading file '/var/lib/bcfg2/Probes/probed.xml': failed to load external entity "/var/lib/bcfg2/Probes/probed.xml" Jul 18 18:43:22 saucy bcfg2-server[6215]: serving bcfg2-server at https://saucy:6789 Jul 18 18:43:22 saucy bcfg2-server[6215]: serve_forever() [start] Jul 18 18:43:22 saucy bcfg2-server[6215]: Reloading Packages plugin Jul 18 18:43:22 saucy bcfg2-server[6215]: Handled 15 events in 0.205s .. note:: The error regarding *probed.xml* is non-fatal and just telling you that the file doesn't yet exist. It will be populated once you have run a client with the Probes plugin enabled. Start managing packages ----------------------- Add a base-saucy (or whatever release you happen to be using) bundle. Let's see what happens when we just populate it with the ubuntu-standard package. .. code-block:: xml root@saucy:~# cat /var/lib/bcfg2/Bundler/base-saucy.xml You need to reference the bundle from your Metadata. The resulting profile group might look something like this .. code-block:: xml Now if we run the client in debug mode (-d), we can see what this has done for us.:: root@saucy:/var/lib/bcfg2# bcfg2 -vqdn Configured logging: DEBUG to console; DEBUG to syslog {'help': False, 'extra': False, 'ppath': '/var/cache/bcfg2', 'ca': '/etc/ssl/bcfg2.crt', 'rpm_version_fail_action': 'upgrade', 'yum_version_fail_action': 'upgrade', 'retry_delay': '1', 'posix_uid_whitelist': [], 'rpm_erase_flags': ['allmatches'], 'verbose': True, 'certificate': '/etc/ssl/bcfg2.crt', 'paranoid': False, 'rpm_installonly': ['kernel', 'kernel-bigmem', 'kernel-enterprise', 'kernel-smp', 'kernel-modules', 'kernel-debug', 'kernel-unsupported', 'kernel-devel', 'kernel-source', 'kernel-default', 'kernel-largesmp-devel', 'kernel-largesmp', 'kernel-xen', 'gpg-pubkey'], 'cache': None, 'yum24_autodep': True, 'yum_pkg_verify': True, 'probe_timeout': None, 'yum_installed_action': 'install', 'rpm_verify_fail_action': 'reinstall', 'dryrun': True, 'retries': '3', 'apt_install_path': '/usr', 'quick': True, 'password': 'secret', 'yum24_installed_action': 'install', 'kevlar': False, 'max_copies': 1, 'syslog': True, 'decision_list': False, 'configfile': '/etc/bcfg2.conf', 'remove': None, 'server': 'https://saucy:6789', 'encoding': 'UTF-8', 'timeout': 90, 'debug': True, 'yum24_installonly': ['kernel', 'kernel-bigmem', 'kernel-enterprise', 'kernel-smp', 'kernel-modules', 'kernel-debug', 'kernel-unsupported', 'kernel-devel', 'kernel-source', 'kernel-default', 'kernel-largesmp-devel', 'kernel-largesmp', 'kernel-xen', 'gpg-pubkey'], 'yum24_erase_flags': ['allmatches'], 'yum24_pkg_checks': True, 'interactive': False, 'apt_etc_path': '/etc', 'rpm_installed_action': 'install', 'yum24_verify_fail_action': 'reinstall', 'omit_lock_check': False, 'yum24_pkg_verify': True, 'serverCN': None, 'file': None, 'apt_var_path': '/var', 'posix_gid_whitelist': [], 'posix_gid_blacklist': [], 'indep': False, 'decision': 'none', 'servicemode': 'default', 'version': False, 'rpm_pkg_checks': True, 'profile': None, 'yum_pkg_checks': True, 'args': [], 'bundle': [], 'posix_uid_blacklist': [], 'user': 'root', 'key': '/etc/ssl/bcfg2.key', 'command_timeout': None, 'probe_exit': True, 'lockfile': '/var/lock/bcfg2.run', 'yum_verify_fail_action': 'reinstall', 'yum24_version_fail_action': 'upgrade', 'yum_verify_flags': [], 'logging': None, 'rpm_pkg_verify': True, 'bundle_quick': False, 'rpm_verify_flags': [], 'yum24_verify_flags': [], 'skipindep': False, 'skipbundle': [], 'portage_binpkgonly': False, 'drivers': ['APK', 'APT', 'Action', 'Blast', 'Chkconfig', 'DebInit', 'Encap', 'FreeBSDInit', 'FreeBSDPackage', 'IPS', 'MacPorts', 'OpenCSW', 'POSIX', 'POSIXUsers', 'Pacman', 'Portage', 'RPM', 'RPMng', 'RcUpdate', 'SELinux', 'SMF', 'SYSV', 'Systemd', 'Upstart', 'VCS', 'YUM', 'YUM24', 'YUMng', 'launchd']} Starting Bcfg2 client run at 1374191628.88 Running probe groups Running: /tmp/tmpEtgdwo < group:amd64 Probe groups has result: group:amd64 POSIX: Handlers loaded: nonexistent, directory, hardlink, symlink, file, device, permissions Loaded tool drivers: APT Action DebInit POSIX POSIXUsers Upstart VCS Loaded experimental tool drivers: POSIXUsers The following packages are specified in bcfg2: ubuntu-standard The following packages are prereqs added by Packages: accountsservice libdrm2 libusb-1.0-0 adduser libedit2 libustr-1.0-1 apparmor libelf1 libuuid1 apt libexpat1 libwind0-heimdal apt-transport-https libffi6 libx11-6 apt-utils libfribidi0 libx11-data base-files libfuse2 libxau6 base-passwd libgcc1 libxcb1 bash libgck-1-0 libxdmcp6 bash-completion libgcr-3-common libxext6 bsdmainutils libgcr-base-3-1 libxml2 bsdutils libgcrypt11 libxmuu1 busybox-initramfs libgdbm3 libxtables10 busybox-static libgeoip1 locales ca-certificates libglib2.0-0 login command-not-found libglib2.0-data logrotate command-not-found-data libgnutls26 lsb-base coreutils libgpg-error0 lsb-release cpio libgpm2 lshw cron libgssapi-krb5-2 lsof dash libgssapi3-heimdal ltrace dbus libhcrypto4-heimdal makedev debconf libheimbase1-heimdal man-db debconf-i18n libheimntlm0-heimdal manpages debianutils libhx509-5-heimdal memtest86+ diffutils libidn11 mime-support dmidecode libisc92 mlocate dmsetup libisccc90 module-init-tools dnsutils libisccfg90 mount dosfstools libjson-c2 mountall dpkg libjson0 mtr-tiny e2fslibs libk5crypto3 multiarch-support e2fsprogs libkeyutils1 nano ed libklibc ncurses-base file libkmod2 ncurses-bin findutils libkrb5-26-heimdal netbase friendly-recovery libkrb5-3 ntfs-3g ftp libkrb5support0 openssh-client fuse libldap-2.4-2 openssl gcc-4.8-base liblocale-gettext-perl parted geoip-database liblwres90 passwd gettext-base liblzma5 pciutils gnupg libmagic1 perl-base gpgv libmount1 plymouth grep libncurses5 plymouth-theme-ubuntu-text groff-base libncursesw5 popularity-contest gzip libnewt0.52 powermgmt-base hdparm libnfnetlink0 ppp hostname libnih-dbus1 pppconfig ifupdown libnih1 pppoeconf info libnuma1 procps initramfs-tools libp11-kit0 psmisc initramfs-tools-bin libpam-modules python-apt-common initscripts libpam-modules-bin python3 insserv libpam-runtime python3-apt install-info libpam-systemd python3-commandnotfound iproute libpam0g python3-dbus iproute2 libparted0debian1 python3-distupgrade iptables libpcap0.8 python3-gdbm iputils-tracepath libpci3 python3-minimal irqbalance libpcre3 python3-update-manager iso-codes libpipeline1 python3.3 klibc-utils libplymouth2 python3.3-minimal kmod libpng12-0 readline-common krb5-locales libpolkit-gobject-1-0 rsync language-selector-common libpopt0 sed libaccountsservice0 libprocps0 sensible-utils libacl1 libpython3-stdlib sgml-base libapparmor-perl libpython3.3-minimal shared-mime-info libapparmor1 libpython3.3-stdlib strace libapt-inst1.5 libreadline6 systemd-services libapt-pkg4.12 libroken18-heimdal sysv-rc libasn1-8-heimdal librtmp0 sysvinit-utils libasprintf0c2 libsasl2-2 tar libatm1 libsasl2-modules tcpdump libattr1 libselinux1 telnet libaudit-common libsemanage-common time libaudit1 libsemanage1 tzdata libbind9-90 libsepol1 ubuntu-keyring libblkid1 libslang2 ubuntu-release-upgrader-core libbsd0 libsqlite3-0 ucf libbz2-1.0 libss2 udev libc-bin libssl1.0.0 ufw libc6 libstdc++6 update-manager-core libcap-ng0 libsystemd-daemon0 upstart libcap2 libsystemd-login0 usbutils libcomerr2 libtasn1-3 util-linux libcurl3-gnutls libtext-charwidth-perl uuid-runtime libdb5.1 libtext-iconv-perl wget libdbus-1-3 libtext-wrapi18n-perl whiptail libdbus-glib-1-2 libtinfo5 xauth libdevmapper1.02.1 libudev1 xml-core libdns95 libusb-0.1-4 zlib1g Phase: initial Correct entries: 280 Incorrect entries: 0 Total managed entries: 280 Unmanaged entries: 313 Installing entries in the following bundle(s): base-saucy Bundle base-saucy was not modified Phase: final Correct entries: 280 Incorrect entries: 0 Total managed entries: 280 Unmanaged entries: 313 Finished Bcfg2 client run at 1374191642.69 As you can see, the Packages plugin has generated the dependencies required for the ubuntu-standard package for us automatically. The ultimate goal should be to move all the packages from the **Unmanaged** entries section to the **Managed** entries section. So, what exactly *are* those Unmanaged entries? :: Starting Bcfg2 client run at 1374192077.76 Running probe groups Probe groups has result: group:amd64 Loaded tool drivers: APT Action DebInit POSIX POSIXUsers Upstart VCS Loaded experimental tool drivers: POSIXUsers Phase: initial Correct entries: 280 Incorrect entries: 0 Total managed entries: 280 Unmanaged entries: 313 Phase: final Correct entries: 280 Incorrect entries: 0 Total managed entries: 280 Unmanaged entries: 313 POSIXGroup:adm POSIXGroup:audio POSIXGroup:backup ... Package:deb:apt-xapian-index Package:deb:aptitude Package:deb:aptitude-common ... Now you can go through these and continue adding the packages you want to your Bundle. Note that ``aptitude why`` is useful when trying to figure out the reason for a package being installed. Also, ``deborphan`` is helpful for removing leftover dependencies which are no longer needed. After a while, I ended up with a minimal bundle that looks like this: .. code-block:: xml Once your ``bcfg2 -vqen`` output no longer shows Package entries, you can move on to the next section. Manage users ------------ The default setting in ``login.defs`` is for system accounts to be UIDs < 1000. We will ignore those accounts for now (you can manage them if you like at a later time). To ignore system UID/GIDs, add the following lines to ``bcfg2.conf`` (we will also ignore the nobody uid and nogroup gid--65534). :: [POSIXUsers] uid_blacklist = 0-999,65534 gid_blacklist = 0-999,65534 If you run the client again with ``bcfg2 -vqen``, you should now see a :ref:`POSIXUser ` entry and :ref:`POSIXGroup ` entry for your user account (assuming this is a fresh install with a regular user). You can manage this user by adding the following to your bundle. .. code-block:: xml adm cdrom dip lpadmin plugdev sambashare sudo Manage services --------------- To clear up the unmanaged service entries, you will need to add the entries to your bundle. Here's an example of what that might look like. .. code-block:: xml Add the literal entries in Rules to bind the Service entries from above. .. code-block:: xml root@saucy:~# cat /var/lib/bcfg2/Rules/services.xml Now we run the client and see there are no more unmanaged entries! :: root@saucy:~# bcfg2 -vqn Starting Bcfg2 client run at 1374271524.83 Running probe groups Probe groups has result: group:amd64 Loaded tool drivers: APT Action DebInit POSIX POSIXUsers Upstart VCS Loaded experimental tool drivers: POSIXUsers Phase: initial Correct entries: 519 Incorrect entries: 0 Total managed entries: 519 Unmanaged entries: 0 Phase: final Correct entries: 519 Incorrect entries: 0 Total managed entries: 519 Unmanaged entries: 0 All entries correct. Finished Bcfg2 client run at 1374271541.56 .. warning:: This basic bundle is created mainly for the purposes of getting you to a completely managed client. It is recommended that you create bundles for appropriate services due to the way bundle updates are managed. Please see :ref:`unsorted-writing_specification` for more details. Upstart ^^^^^^^ Upstart services are defined like this: .. code-block:: xml Some Upstart services require additional parameters, like network-interface and bridge-network-interface: .. code-block:: xml Dynamic (web) reports ===================== See installation instructions at :ref:`appendix-guides-web-reports-install` Next Steps ========== :ref:`getting_started-index-next-steps` bcfg2-1.3.3/doc/appendix/guides/using-bcfg2-with-centos.txt000066400000000000000000000047111223671746500234670ustar00rootroot00000000000000.. -*- mode: rst -*- .. _EPEL: http://fedoraproject.org/wiki/EPEL .. _RPMForge: https://rpmrepo.org/RPMforge .. _getting_started-using_bcfg2-with-centos: ======================= Using Bcfg2 With CentOS ======================= This section covers specific topics for using Bcfg2 with CentOS. Most likely the tips on this page also apply to other members of the Red Hat family of Linux operating systems. From Source +++++++++++ Install Prerequisities ###################### While you can go about building all these things from source, this how to will try and meet the dependencies using packages from EPEL_ or RPMforge_. The *el5* package should be compatible with CentOS 5.x. EPEL_ for 5.x :: [root@centos ~]# rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm EPEL_ for 6.x :: [root@centos ~]# rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-5.noarch.rpm RPMforge_ for 5.x :: [root@centos ~]# rpm -Uvh http://dag.wieers.com/rpm/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.x86_64.rpm .. note:: Be careful with `mixing package repositories `_. Now you can install the rest of the prerequisites:: [root@centos ~]# yum install python-genshi python-cheetah python-lxml Build Packages from source ########################## * After installing git, clone the master branch:: [root@centos redhat]# git clone git://git.mcs.anl.gov/bcfg2.git * Install the ``fedora-packager`` package :: [root@centos ~]# yum install fedora-packager * A directory structure for the RPM build process has to be established. :: [you@centos ~]$ rpmdev-setuptree * Change to the *redhat* directory of the checked out Bcfg2 source:: [you@centos ~]$ cd bcfg2/redhat/ * In the particular directory is a Makefile which will do the job of building the RPM packages. You can do this as root, but it's not recommended:: [you@centos redhat]$ make * Now the new RPM package can be installed. Please adjust the path to your RPM package:: [root@centos ~]# rpm -ihv /home/YOU/rpmbuild/RPMS/noarch/bcfg2-server-1.0.0-0.2r5835.noarch.rpm Install Packages from Package repository ######################################## To install the bcfg2-server and bcfg2 from a package repository, just use Yum to do it:: [root@centos ~]# yum install bcfg2-server bcfg2 .. toctree:: :hidden: bcfg2-1.3.3/doc/appendix/guides/vcs.txt000066400000000000000000000051771223671746500177210ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-guides-vcs: ======================= Version control systems ======================= The sections in this guide only cover the basics steps in the setup of the different version control systems for usage with the Bcfg2. Git === .. _Git tutorial: http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html Adding the :ref:`server-plugins-version-git` plugin will allow you to store version information in the statistics database. For tracking the configuration files in the ``/var/lib/bcfg2`` directory a git repository needs to be established:: git init For more detail about the setup of git please refer to a `git tutorial`_. The first commit can be the empty or the already populated directory:: git add . && git commit -a While running ``bcfg2-info`` the following line will show up:: Initialized git plugin with git directory = /var/lib/bcfg2/.git Mercurial ========= The :ref:`server-plugins-version-hg` plugin also allows you to store version information in the statistics database. To use mercurial to track your configuration files, the repository must be initialized:: hg init Mercurial will not commit the files to the repository until a user name is defined in ``/var/lib/bcfg2/.hg/`` .. code-block:: sh cat <> /var/lib/bcfg2/.hg/hgrc [ui] username = Yor name END_ENTRY Now you are able to make submissions to the repository:: hg commit While running ``bcfg2-info`` the following line will show up:: Initialized hg plugin with hg directory = /var/lib/bcfg2/.hg Darcs ===== The :ref:`server-plugins-version-darcs` plugin also allows you to store version information in the statistics database. To use darcs to track your configuration files, the repository must be initialized:: darcs initialize To commit to the darcs repository an author must be added to the ``_darcs/prefs/author`` file. If the ``author`` file is missing, darcs will ask you to enter your e-mail address. .. code-block:: sh cat <> /var/lib/bcfg2/_darcs/prefs/author you@example.com END_ENTRY All files in the ``/var/lib/bcfg2`` directory should be added to darcs now:: darcs add * After that you can submit them to the repository:: darcs record While running ``bcfg2-info`` the following line will show up:: Initialized Darcs plugin with darcs directory = /var/lib/bcfg2/_darcs Cvs === The :ref:`server-plugins-version-cvs` plugin also allows you to store version information in the statistics database. plugins = Base,Bundler,Cfg,...,Cvs The CVS repository must be initialized:: cvs -d /var/lib/bcfg2 init bcfg2-1.3.3/doc/appendix/guides/web-reports-install.txt000066400000000000000000000037121223671746500230340ustar00rootroot00000000000000.. -*- mode: rst -*- .. _EPEL: http://fedoraproject.org/wiki/EPEL .. This is combination of the Ubuntu guide and the Centos guide for installing the web reports. .. _appendix-guides-web-reports-install: ======================== Web Reporting Quickstart ======================== You need to install the bcfg2-web package that is available for your particular distribution. All packages for Fedora are in the Fedora Package Collection. You can find packages for CentOS and RHEL in EPEL_:: [root@system01 ~]# yum -y install bcfg2-web The same packages are needed for Debian/Ubuntu systems:: [root@system01 ~]# aptitude install bcfg2-web Add Reporting to the plugins line of ``bcfg2.conf``. The resulting **[server]** section should look something like this:: [server] repository = /var/lib/bcfg2 plugins = Base,Bundler,Cfg,...,Reporting [reporting] transport = LocalFilesystem You then need to initialize the reporting database:: [root@system01 ~]# bcfg2-admin reports init Start/restart the Bcfg2 server:: [root@system01 ~]# /etc/init.d/bcfg2-server restart Start the Bcfg2 report collector:: [root@system01 ~]# /etc/init.d/bcfg2-report-collector start Run the Bcfg2 client in order to populate the statistics database. Copy server/statistics sections of ``bcfg2.conf`` to ``/etc/bcfg2-web.conf`` (make sure it is world-readable). You should then have something like this:: [server] repository = /var/lib/bcfg2 plugins = Base,Bundler,Cfg,...,Reporting [database] engine = sqlite3 # 'postgresql', 'mysql', 'mysql_old', 'sqlite3' or 'ado_mssql'. name = user = # Not used with sqlite3. password = # Not used with sqlite3. host = # Not used with sqlite3. port = [reporting] transport = LocalFilesystem Restart apache and point a browser to your Bcfg2 server. If using sqlite be sure the sql database file and directory containing the database are writable to apache. bcfg2-1.3.3/doc/appendix/index.txt000066400000000000000000000002611223671746500167420ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-index: ======== Appendix ======== .. toctree:: :maxdepth: 2 files configuration books papers articles guides tools bcfg2-1.3.3/doc/appendix/papers.txt000066400000000000000000000041331223671746500171270ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-papers: ====== Papers ====== * Configuration Life-Cycle Management on the TeraGrid. * Ti Leggett, Cory Lueninghoener, and Narayan Desai * In Proceedings of TeraGrid '07 Conference, June 2007 * `A Scalable Approach To Deploying And Managing Appliances `_ * Rick Bradshaw, Narayan Desai, Tim Freeman, and Kate Keahey * In Proceedings of the TeraGrid '07 Conference, June 2007 * `Bcfg2 - Konfigurationsmanagement Für Heterogene Umgebungen `_ * Marko Jung, Robert Gogolok * In Proceedings of German Unix User Group's Frühjahrsfachgespräch 2007, March 2007. * `Directing Change Using Bcfg2 `_ * Narayan Desai, Rick Bradshaw, Joey Hagedorn, and Cory Lueninghoener * In Proceedings of the Twentieth Large Install System Administration Conference (LISA XX), December 2-9, 2006, Washington D.C., USA, 2006. * `A Case Study in Configuration Management Tool Deployment `_ * Narayan Desai, Rick Bradshaw, Scott Matott, Sandra Bittner, Susan Coghlan, Remy Evard, Cory Leunighhoener, Ti Leggett, J.P. Navarro, Gene Rackow, Craig Stacey, and Tisha Stacey * In Proceedings of the Nineteenth Large Install System Administration Conference (LISA XIX), December 4-9, 2005, San Diego, CA, USA, 2005. * `Bcfg2: A Pay As You Go Approach to Configuration Complexity `_ * Narayan Desai * In Proceedings of the 2005 Australian Unix Users Group (AUUG2005), October 16-21, 2005, Sydney, Australia, 2005. * `Bcfg: A Configuration Management Tool for Heterogenous Environments `_ * Narayan Desai, Andrew Lusk, Rick Bradshaw, and Remy Evard * In Proceedings of the 5th IEEE International Conference on Cluster Computing (CLUSTER03), pages 500-503. IEEE Computer Society, 2003. bcfg2-1.3.3/doc/appendix/tools.txt000066400000000000000000000004461223671746500170000ustar00rootroot00000000000000.. -*- mode: rst -*- .. _appendix-tools: ===== Tools ===== In the ``tools/`` directory are several tools collected. Those tools can help you to maintain your Bcfg2 configuration, to make the initial setup easier, or to do some other tasks. https://github.com/Bcfg2/bcfg2/tree/maint/tools bcfg2-1.3.3/doc/architecture/000077500000000000000000000000001223671746500157455ustar00rootroot00000000000000bcfg2-1.3.3/doc/architecture/client.txt000066400000000000000000000127721223671746500177750ustar00rootroot00000000000000.. -*- mode: rst -*- .. _architecture-client: The Bcfg2 Client ================ The Bcfg2 client performs all client configuration or reconfiguration operations. It renders a declarative configuration specification, provided by the Bcfg2 server, into a set of configuration operations which will, if executed, attempt to change the client's state into that described by the configuration specification. Conceptually, the Bcfg2 client serves to isolate the Bcfg2 server and specification from the imperative operations required to implement configuration changes. This isolation allows declarative specifications to be manipulated symbolically on the server, without needing to understand the properties of the underlying system tools. In this way, the Bcfg2 client acts as a sort of expert system that *knows* how to implement declarative configuration changes. The operation of the Bcfg2 client is intended to be as simple as possible. The normal configuration process consists of four main steps: * **Probe Execution** During the probe execution stage, the client connects to the server and downloads a series of probes to execute. These probes reveal local facts to the Bcfg2 server. For example, a probe could discover the type of video card in a system. The Bcfg2 client returns this data to the server, where it can influence the client configuration generation process. * **Configuration Download and Inventory** The Bcfg2 client now downloads a configuration specification from the Bcfg2 server. The configuration describes the complete target state of the machine. That is, all aspects of client configuration should be represented in this specification. For example, all software packages and services should be represented in the configuration specification. The client now performs a local system inventory. This process consists of verifying each entry present in the configuration specification. After this check is completed, heuristic checks are executed for configuration not included in the configuration specification. We refer to this inventory process as 2-way validation, as first we verify that the client contains all configuration that is included in the specification, then we check if the client has any extra configuration that isn't present. This provides a fairly rigorous notion of client configuration congruence. Once the 2-way verification process has been performed, the client has built a list of all configuration entries that are out of spec. This list has two parts: specified configuration that is incorrect (or missing) and unspecified configuration that should be removed. * **Configuration Update** The client now attempts to update its configuration to match the specification. Depending on options, changes may not (or only partially) be performed. First, if extra configuration correction is enabled, extra configuration can be removed. Then the remaining changes are processed. The Bcfg2 client loops while progress is made in the correction of these incorrect configuration entries. This loop results in the client being able to accomplish all it will be able to during one execution. Once all entries are fixed, or no progress is being made, the loop terminates. Once all configuration changes that can be performed have been, bundle dependencies are handled. Bundle groupings result in two different behaviors. Contained entries are assumed to be inter-dependent. To address this, the client re-verifies each entry in any bundle containing an updates configuration entry. Also, services contained in modified bundles are restarted. * **Statistics Upload** Once the reconfiguration process has concluded, the client reports information back to the server about the actions it performed during the reconfiguration process. Statistics function as a detailed return code from the client. The server stores statistics information. Information included in this statistics update includes (but is not limited to): * Overall client status (clean/dirty) * List of modified configuration entries * List of uncorrectable configuration entries * List of unmanaged configuration entries Architecture Abstraction ------------------------ The Bcfg2 client internally supports the administrative tools available on different architectures. For example, ``rpm`` and ``apt-get`` are both supported, allowing operation of Debian, Redhat, SUSE, and Mandriva systems. The client toolset is determined based on the availability of client tools. The client includes a series of libraries which describe how to interact with the system tools on a particular platform. Three of the libraries exist. There is a base set of functions, which contain definitions describing how to perform POSIX operations. Support for configuration files, directories, symlinks, hardlinks, etc., are included here. Two other libraries subclass this one, providing support for Debian and rpm-based systems. The Debian toolset includes support for apt-get and update-rc.d. These tools provide the ability to install and remove packages, and to install and remove services. The Redhat toolset includes support for rpm and chkconfig. Any other platform that uses these tools can also use this toolset. Hence, all of the other familiar rpm-based distributions can use this toolset without issue. Other platforms can easily use the POSIX toolset, ignoring support for packages or services. Alternatively, adding support for new toolsets isn't difficult. Each toolset consists of about 125 lines of python code. bcfg2-1.3.3/doc/architecture/config-spec.txt000066400000000000000000000054621223671746500207120ustar00rootroot00000000000000.. -*- mode: rst -*- .. _architecture-config-spec: The Literal Configuration Specification ======================================= Literal configuration specifications are served to clients by the Bcfg2 server. This is a differentiating factor for Bcfg2; all other major configuration management systems use a non-literal configuration specification. That is, the clients receive a symbolic configuration that they process to implement target states. We took the literal approach for a few reasons: * A small list of configuration element types can be defined, each of which can have a set of defined semantics. This allows the server to have a well-formed model of client-side operations. Without a static lexicon with defined semantics, this isn't possible. This allows the server, for example, to record the update of a package as a coherent event. * Literal configurations do not require client-side processing. Removing client-side processing reduces the critical footprint of the tool. That is, the Bcfg2 client (and the tools it calls) need to be functional, but the rest of the system can be in any state. Yet, the client will receive a correct configuration. * Having static, defined element semantics also requires that all operations be defined and implemented in advance. The implementation can maximize reliability and robustness. In more ad-hoc setups, these operations aren't necessarily safely implemented. The Structure of Specifications ------------------------------- Configuration specifications contain some number of clauses. Two types of clauses exist. Bundles are groups of inter-dependent configuration entities. The purpose of bundles is to encode installation-time dependencies such that all new configuration is properly activated during reconfiguration operations. That is, if a daemon configuration file is changed, its daemon should be restarted. Another example of bundle usage is the reconfiguration of a software package. If a package contains a default configuration file, but it gets overwritten by an environment-specific one, then that updated configuration file should survive package upgrade. The purpose of bundles is to describe services, or reconfigured software packages. Independent clauses contain groups of configuration entities that aren't related in any way. This provides a convenient mechanism that can be used for bulk installations of software. Each of these clauses contains some number of configuration entities. A number of configuration entities exist including Path, Package, Service, etc. Each of these correspond to the obvious system item. Configuration specifications can get quite large; many systems have specifications that top one megabyte in size. An example of one is included in an appendix. These configurations can be written by hand, or generated by the server. bcfg2-1.3.3/doc/architecture/design.txt000066400000000000000000000067031223671746500177650ustar00rootroot00000000000000.. -*- mode: rst -*- .. _architecture-design: Design Considerations ===================== This section will discuss several aspects of the design of Bcfg2, and the particular use cases that motivated them. Initially, this will consist of a discussion of the system metadata, and the intended usage model for package indices as well. System Metadata --------------- Bcfg2 system metadata describes the underlying patterns in system configurations. It describes commonalities and differences between these specifications in a rigorous way. The groups used by Bcfg2's metadata are responsible for differentiating clients from one another, and building collections of allocatable configuration. The Bcfg2 metadata system has been designed with several high-level goals in mind. Flexibility and precision are paramount concerns; no configuration should be undescribable using the constructs present in the Bcfg2 repository. We have found (generally the hard way) that any assumptions about the inherent simplicity of configuration patterns tend to be wrong, so obscenely complex configurations must be representable, even if these requirements seem illogical during the implementation. In particular, we wanted to streamline several operations that commonly occurred in our environment. * Copying one node's profile to another node. In many environments, many nodes are instances of a common configuration specification. They all have similar roles and software. In our environment, desktop machines were the best example of this. Other than strictly per-host configuration like SSH keys, all desktop machines use a common configuration specification. This trivializes the process of creating a new desktop machine. * Creating a specialized version of an existing profile. In environments with highly varied configurations, departmental infrastructure being a good example, "another machine like X but with extra software" is a common requirement. For this reason, it must be trivially possible to inherit most of a configuration specification from some more generic source, while being able to describe overriding aspects in a convenient fashion. * Compose several pre-existing configuration aspects to create a new profile. The ability to compose configuration aspects allows the easy creation of new profiles based on a series of predefined set of configuration specification fragments. The end result is more agility in environments where change is the norm. In order for a classing system to be comprehensive, it must be usable in complex ways. The Bcfg2 metadata system has constructs that map cleanly to first-order logic. This implies that any complex configuration pattern can be represented (at all) by the metadata, as first-order logic is provably comprehensive. (There is a discussion later in the document describing the metadata system in detail, and showing how it corresponds to first-order logic) These use cases motivate several of the design decisions that we made. There must be a many to one correspondence between clients and groups. Membership in a given profile group must imbue a client with all of its configuration properties. Package Management ------------------ The interface provided in the Bcfg2 repository for package specification was designed with automation in mind. The goal was to support an append only interface to the repository, so that users do not need to continuously re-write already existing bits of specification. bcfg2-1.3.3/doc/architecture/goals.txt000066400000000000000000000042661223671746500176230ustar00rootroot00000000000000.. -*- mode: rst -*- .. _architecture-goals: Goals ===== * **Model configurations using declarative semantics.** Declarative semantics maximize the utility of configuration management tools; they provide the most flexibility for the tool to determine the right course of action in any given situation. This means that users can focus on the task of describing the desired configuration, while leaving the task of transitioning clients states to the tool. * **Configuration descriptions should be comprehensive.** This means that configurations served to the client should be sufficient to reproduce all desired functionality. This assumption allows the use of heuristics to detect extra configuration, aiding in reliable, comprehensive configuration definitions. * **Provide a flexible approach to user interactions.** Most configuration management systems take a rigid approach to user interactions; that is, either the client system is always correct, or the central system is. This means that users are forced into an overly proscribed model where the system asserts where correct data is. Configuration data modification is frequently undertaken on both the configuration server and clients. Hence, the existence of a single canonical data location can easily pose a problem during normal tool use. Bcfg2 takes a different approach. The default assumption is that data on the server is correct, however, the client has the option to run in another mode where local changes are catalogued for server-side integration. If the Bcfg2 client is run in dry run mode, it can help to reconcile differences between current client state and the configuration described on the server. The Bcfg2 client also searches for extra configuration; that is, configuration that is not specified by the configuration description. When extra configuration is found, either configuration has been removed from the configuration description on the server, or manual configuration has occurred on the client. Options related to two-way verification and removal are useful for configuration reconciliation when interactive access is used. * Plugins and administrative applications. * Incremental operations. bcfg2-1.3.3/doc/architecture/index.txt000066400000000000000000000011471223671746500176200ustar00rootroot00000000000000.. -*- mode: rst -*- .. _architecture-index: =========================== Detailed Bcfg2 Architecture =========================== Bcfg2 is based on a client-server architecture. The client is responsible for interpreting (but not processing) the configuration served by the server. This configuration is literal, so no local process is required. After completion of the configuration process, the client uploads a set of statistics to the server. This section will describe the goals and then the architecture motivated by it. .. toctree:: :maxdepth: 1 goals client server config-spec design bcfg2-1.3.3/doc/architecture/server.txt000066400000000000000000000071031223671746500200150ustar00rootroot00000000000000.. -*- mode: rst -*- .. _architecture-server: The Bcfg2 Server ================ The Bcfg2 server is responsible for taking a network description and turning it into a series of configuration specifications for particular clients. It also manages probed data and tracks statistics for clients. The Bcfg2 server takes information from two sources when generating client configuration specifications. The first is a pool of metadata that describes clients as members of an aspect-based classing system. That is, clients are defined in terms of aspects of their behavior. The other is a file system repository that contains mappings from metadata to literal configuration. These are combined to form the literal configuration specifications for clients. The Configuration Specification Construction Process ---------------------------------------------------- As we described in the previous section, the client connects to the server to request a configuration specification. The server uses the client's metadata and the file system repository to build a specification that is tailored for the client. This process consists of the following steps: * **Metadata Lookup** The server uses the client's IP address to initiate the metadata lookup. This initial metadata consists of a (profile, image) tuple. If the client already has metadata registered, then it is used. If not, then default values are used and stored for future use. This metadata tuple is expanded using some profile and class definitions also included in the metadata. The end result of this process is metadata consisting of hostname, profile, image, a list of classes, a list of attributes and a list of bundles. * **Abstract Configuration Construction** Once the server has the client metadata, it is used to create an abstract configuration. An abstract configuration contains all of the configuration elements that will exist in the final specification **without** any specifics. All entries will be typed (i.e. the tagname will be one of Package, Path, Action, etc) and will include a name. These configuration entries are grouped into bundles, which document installation time interdependencies. Here is an example of an abstract configuration entry: .. code-block:: xml * **Configuration Binding** The abstract configuration determines the structure of the client configuration, however, it doesn't yet contain literal configuration information. After the abstract configuration is created, each configuration entry must be bound to a client-specific value. The Bcfg2 server uses plugins to provide these client-specific bindings. The Bcfg2 server core contains a dispatch table that describes which plugins can handle requests of a particular type. The responsible plugin is located for each entry. It is called, passing in the configuration entry and the client's metadata. The behavior of plugins is explicitly undefined, so as to allow maximum flexibility. The behaviours of the stock plugins are documented elsewhere in this manual. Once this binding process is completed, the server has a literal, client-specific configuration specification. This specification is complete and comprehensive; the client doesn't need to process it at all in order to use it. It also represents the totality of the configuration specified for the client. Here is the entry from above once it has been bound to its literal specification (In this case, using the Packages plugin). .. code-block:: xml bcfg2-1.3.3/doc/client/000077500000000000000000000000001223671746500145415ustar00rootroot00000000000000bcfg2-1.3.3/doc/client/agent.txt000066400000000000000000000042531223671746500164040ustar00rootroot00000000000000.. -*- mode: rst -*- .. _client-agent: ============================= Agent Functionality using SSH ============================= The Bcfg2 agent code provides the ability to trigger a client update from the server using a secure mechanism that is restricted to running the Bcfg2 client with the options the agent was started with. This same capability is provided by SSH keypairs, if properly configured. Setup is pretty easy: #. Create an ssh keypair that is to be used solely for triggering Bcfg2 client runs. This key may or may not have a password associated with it; a keyphrase will make things more secure, but will require a person to enter the key passphrase, so it will not be usable automatically.:: $ ssh-keygen -t dsa -b 1024 -f /path/to/key -N "" Generating public/private dsa key pair. Your identification has been saved in /path/to/key. Your public key has been saved in /path/to/key.pub. The key fingerprint is: aa:25:9b:a7:10:60:f3:eb:2b:ae:4b:1a:42:1b:63:5d desai@ubik #. Add this public key to root's authorized_keys file, with several commands prepended to it:: command="/usr/sbin/bcfg2 -q ",no-port-forwarding,no-X11-forwarding,no-pty,no-agent-forwarding,from="" This key is now only useful to call the Bcfg2 client, from the Bcfg2 server's ip address. If PermitRootLogin was set to no in sshd_config, you will need to set it to forced-commands-only. Adding a & to the end of the command will cause the command to immediately return. #. Now, to cause a client to reconfigure, call:: $ ssh -i /path/to/key root@client /usr/sbin/bcfg2 Note that you will not be able to alter the command line options from the ones specified in authorized_keys in any way. Also, it is not needed that the invocation of Bcfg2 in the ssh command match. The following will have the same result.:: $ ssh -i /path/to/key root@client /bin/true If a passphrase was used to create the keypair, then it will need to be entered here. See Also ======== `SSH "triggers" `_ (from Ganneff's Little Blog) bcfg2-1.3.3/doc/client/debugging.txt000066400000000000000000000026151223671746500172410ustar00rootroot00000000000000.. -*- mode: rst -*- .. _client-debugging: ================ Client Debugging ================ When working on the Bcfg2 client, it is helpful to employ a few specific techniques to isolate and remedy problems. First, running the client with the -f flag allows configuration from a local file, rather than querying the server. This helps rule out server configuration problems, and allows for rapid development. For example: ``bcfg2 -f test-config.conf`` with the following test-config.conf: .. code-block:: rst Next, it is important to look at the interactive mode. This is similar to the interactive mode on the server and provides an interactive Python interpreter with which one may manipulate all the objects in the client. It will setup all the infrastructure so you will have the appropriate objects to play with. It will run the client through once, then present you with an interpreter. Try it out with: ``python -i /usr/bin/bcfg2`` or, for more fun, a local config file and also enable Debugging and Verbose output with `-d` and `-v`, yielding ``python -i /usr/bin/bcfg2 -d -v -f test-config.conf``. Now we just explore; use ``dir()`` to examine different objects in the client, or run a reconfiguration again by calling `client.run()` bcfg2-1.3.3/doc/client/index.txt000066400000000000000000000022021223671746500164050ustar00rootroot00000000000000.. -*- mode: rst -*- .. _client-index: The Bcfg2 Client ================ The Bcfg2 client attempts to reconcile the current configuration state with the configuration passed down from the server using various client tools. It does not perform any processing of the target configuration description. We chose this architecture, as opposed to one with a smarter client, for a few reasons: * Client failure forces administrators to perform an O(n) reconfiguration operation. Simpler code is easier to debug and maintain. * Minimize the bootstrap size; a complicated client can require more aspects of the system to function in order for reconfiguration to work. * Isolate configuration generation functionality on the server, where it can be readily observed. This is the most complicated task that Bcfg2 performs. * The results of the configuration process fit a fairly simple model. We wanted to validate it. The result is that Bcfg2 has a programmable deployment engine that can be driven by anything that writes a compatible configuration description. .. toctree:: :maxdepth: 2 modes tools metadata agent debugging bcfg2-1.3.3/doc/client/metadata.txt000066400000000000000000000116311223671746500170640ustar00rootroot00000000000000.. -*- mode: rst -*- .. _client-metadata: =============== Client Metadata =============== This page describes ClientMetadata objects. These are used to describe clients in terms of a variety of parameters, group memberships, and so forth. Construction ============ ClientMetadata instances are constructed whenever the server needs to recognize a client. This occurs in every aspect of client server interaction: * Probing * Configuration Generation * Statistics Upload This construction process spans several server plugins. The :ref:`server-plugins-grouping-metadata` is responsible for initial instance creation, including the client hostname, profile, and basic group memberships. After this initial creation, Connector plugins (such as :ref:`server-plugins-probes-index` or :ref:`server-plugins-connectors-properties`) can add additional group memberships for clients. These memberships are merged into the instance; that is, the new group memberships are treated as if they were included in groups.xml. If any of these groups are defined in groups.xml, then groups included there are included in the ClientMetadata instance group list. At the end of this process, the ClientMetadata instance has its complete set of group memberships. At this point, each connector plugin has the opportunity to return an additional object which will be placed in an attribute corresponding to the Connector name. For example, the Probes plugin returns a dictionary of probe name to probe result mappings for the client. This dictionary is available as the "Probes" attribute. With this, ClientMetadata resolution is complete, and the ClientMetadata instance can be used by the rest of the system. Contents ======== ClientMetadata instances contain all of the information needed to differentiate clients from one another. This data includes: * hostname * groups * profile group * address information (if specified) ClientMetadata instances also contain a query object. This can be used to query the metadata of other clients. Currently, several methods are supported. In this table, we refer to the instance as meta. Each of these is a function that must be called. +------------------------------------------+-------------------+----------------+ | Name | Description | Return Type | +==========================================+===================+================+ | meta.query.names_by_groups([group list]) | Returns names of | List of | | | clients which are | client names | | | members of all | | | | groups | | +------------------------------------------+-------------------+----------------+ | meta.query.names_by_profile(profile) | Returns names of | List of | | | clients which use | client names | | | profile group | | +------------------------------------------+-------------------+----------------+ | meta.query.all_clients() | Returns names of | List of | | | all clients | client names | +------------------------------------------+-------------------+----------------+ | meta.query.all_groups() | Returns names of | List of | | | all groups | group names | +------------------------------------------+-------------------+----------------+ | meta.query.all() | Returns metadata | List of | | | for all clients | ClientMetadata | | | | instances | +------------------------------------------+-------------------+----------------+ | meta.query.by_name(name) | Returns metadata | ClientMetadata | | | for named client | instance | +------------------------------------------+-------------------+----------------+ | meta.query.by_groups([group list]) | Returns metadata | List of | | | for all members | ClientMetadata | | | of all groups | instances | +------------------------------------------+-------------------+----------------+ | meta.query.by_profile(profile) | Returns metadata | List of | | | for all profile | ClientMetadata | | | havers | instances | +------------------------------------------+-------------------+----------------+ In general, there is no substantial benefit to using name returning versions of the query functions; metadata resolution is (in general) fast. bcfg2-1.3.3/doc/client/modes.txt000066400000000000000000000050031223671746500164070ustar00rootroot00000000000000.. -*- mode: rst -*- .. _client-modes: ============ Client modes ============ Dryrun mode =========== Dryrun mode (-n) prevents the client from making changes, but gives you some insight into the state of the machine. This mode is also useful if you simply want to gather data from the client into the reporting system. Interactive mode ================ The client can be run interactively (-I) so that you are able to step through each operation in order to see what the client is doing. .. _client-modes-paranoid: Paranoid mode ============= Paranoid mode creates a backup of a local configuration file before Bcfg2 replaces the file. This allows for easier recovery by the local administrator. How do I use it? ---------------- #. In the Bcfg2 repository, put `paranoid='true'` in the ``info.xml`` file (this is the default setting). #. On the client, create ``/var/cache/bcfg2`` (or specify an alternate path in the [paranoid] section of ``/etc/bcfg2.conf``). #. On the client, run `bcfg2` with the `-P` option (alternatively, you can set *paranoid* to *true* in the **[client]** section of ``bcfg2.conf``). This will save a copy of the replaced file in ``/var/cache/bcfg2``, but it'll be named as the path to the file with /'s replaced by _'s. For example, the old ``/etc/hosts`` will be named ``/var/cache/bcfg2/etc_hosts``. Extra configuration ------------------- .. versionadded:: 1.0.0 Here is an example of how to use some of the extra paranoid features available. For the following section in ``bcfg2.conf`` (client-side):: [paranoid] path = /my/custom/backup/path max_copies = 5 You will have the file backups store in ``/my/custom/backup/path``. This will also keep the five most recent backups of files. Altering the global metadata to enable paranoid mode for all files ------------------------------------------------------------------ You may also want to just globally enable the *paranoid* attribute for all files distributed to clients from your Bcfg2 server. You can accomplish this by adding a global metadata override in your ``bcfg2.conf`` (server-side) with the following syntax:: [mdata] paranoid=true .. note:: This is the default setting. Overall client service mode =========================== .. versionadded:: 1.0.0 Overall client service mode. Specified on the client using ``-s ``. * default * perform all service manipulations * disabled * perform no service manipulations * build * attempt to stop all services started * deprecates/replaces -B bcfg2-1.3.3/doc/client/tools.txt000066400000000000000000000102241223671746500164410ustar00rootroot00000000000000.. -*- mode: rst -*- .. _client-tools: Client Tool Drivers =================== Client tool drivers allow Bcfg2 to execute configuration operations by interfacing with platform and distribution specific tools. Tool drivers handle any reconfiguration or verification operation. So far we have tools that primarily deal with packaging systems and service management. The POSIX tool also handles file system and permissions/groups operations. To write your own tool driver, to handle a new packaging format, or new service architecture see :ref:`development-client-driver` When the Bcfg2 client is run, it attempts to instantiate each of these drivers. The succeeding list of drivers are printed as a debug message after this process has completed. Drivers can supercede one another, for example, the Yum driver conflicts (and unloads) the RPM driver. This behavior can be overridden by running the Bcfg2 client with the ``-D`` flag. This flag takes a colon delimited list of drivers to use on the system. Currently these are the tool drivers that are distributed with Bcfg2: .. toctree:: :maxdepth: 2 :glob: tools/* Action ------ Pre and post-install tests and actions. This driver executes commands and supplies status information to the Bcfg2 server via the statistics mechanism. It can also be used to prevent bundle installation when pre-conditions are not met. See the UsingActions page for more details. APK --- This tool driver is used to handle packages on apk based systems like Alpine Linux and employs the "apk" executable. Extra information can be found at `apk-tools`_. .. _apk-tools: http://apk-tools.sourceforge.net/ APT --- Debian Packages. This tool driver is used to handle packages on dpkg based systems and employs the "apt" executable. Extra information can be found at :ref:`client-tools-apt`. Blast ----- Blastwave Packages. This tool driver is for blastwave packages on solaris Chkconfig --------- Tool to manage services (primarily on Redhat based distros). .. note:: Start and stop are standard arguments, but the one for reload isn't consistent across services. You can specify which argument to use with the ``target`` attribute in Service tags. Example: .. code-block:: xml DebInit ------- Debian Service Support; exec's update-rc.d to configure services. Encap ----- `Encap `_ Packages. FreeBSDInit ----------- FreeBSD Service Support. Only bundle updates will work. FreeBSDPackage -------------- FreeBSD Packages. Verifies packages and their version numbers but can't install packages. launchd ------- Mac OS X Services. To use this tool, you must maintain a standard launch daemon .plist file in ``/Library/LaunchDaemons/`` (example ssh.plist) and setup an entry in your config to load or unload the service. .. code-block:: xml Note the name is the *Label* specified inside of the .plist file Portage ------- Support for Gentoo Packages. POSIX ----- Files and Permissions are handled by the POSIX driver. Usage well documented other places. RcUpdate -------- Uses the rc-update executable to manage services on distributions such as Gentoo. RPM --- Executes RPM to manage packages on Redhat-based and similar systems. Consider using the :ref:`YUM ` tool instead if possible. Formerly called ``RPMng``, but was renamed for the 1.3 release. SMF --- Solaris Service Support. Example legacy run service (lrc): .. code-block:: xml Systemd ------- Systemd service support. SYSV ---- Handles System V Packaging format that is available on Solaris. Upstart ------- Upstart service support. Uses `Upstart`_ to configure services. .. _Upstart: http://upstart.ubuntu.com/ YUM --- Handles RPMs using the YUM package manager. Renamed from ``YUMng`` for the 1.3 release. See :ref:`client-tools-yum` for more details. YUM24 ----- .. warning:: Deprecated in favor of :ref:`YUM ` Handles RPMs using older versions of the YUM package manager. bcfg2-1.3.3/doc/client/tools/000077500000000000000000000000001223671746500157015ustar00rootroot00000000000000bcfg2-1.3.3/doc/client/tools/actions.txt000066400000000000000000000056271223671746500201140ustar00rootroot00000000000000.. -*- mode: rst -*- .. _client-tools-actions: ======= Actions ======= This page describes use of the Action configuration entry. Action entries are commands that are executed either before bundle installation, after bundle installation or both. If exit status is observed, a failing pre-action will cause no modification of the enclosing bundle to be performed; all entries in included in that bundle will not be modified. Failing actions are reported through Bcfg2's reporting system, so they can be centrally observed. Actions look like: .. code-block:: xml .. xml:type:: ActionType Note that the status attribute tells the bcfg2 client to ignore return status, causing failures to still not be centrally reported. If central reporting of action failure is desired, set this attribute to 'check'. Also note that Action entries included in Base will not be executed. Actions may be completely defined inside of a bundle with the use of :ref:`server-configurationentries`, much like Packages, Services or Paths. The Rules plugin can also bind these entries. For example to include the above action in a bundle, first the Action entry must be included in the bundle: .. code-block:: xml ... Then a corresponding entry must be included in the Rules directory, like: .. code-block:: xml This allows different clients to get different actions as a part of the same bundle based on group membership. Example Action (add APT keys) ============================= This example will add the '0C5A2783' for aptitude. It is useful to run this during the client bootstrap process so that the proper keys are installed prior to the bcfg2 client trying to install a package which requires this key. .. code-block:: xml Example BoundAction (add RPM GPG keys) ====================================== This example will add the RPM-GPG-KEY-redhat-release key to the RPM GPG keyring **before** Package entries are handled on the client run. .. code-block:: xml bcfg2-1.3.3/doc/client/tools/apt.txt000066400000000000000000000005701223671746500172300ustar00rootroot00000000000000.. -*- mode: rst -*- .. _client-tools-apt: =============== APT Client Tool =============== The APT tool allows you to configure custom options in ``bcfg2.conf`` for systems where the tools reside in non-standard locations. The available options (and their corresponding default values) are:: [APT] install_path = '/usr' var_path = '/var' etc_path = '/etc' bcfg2-1.3.3/doc/client/tools/posixusers.txt000066400000000000000000000077501223671746500206770ustar00rootroot00000000000000.. -*- mode: rst -*- .. _client-tools-posixusers: ========== POSIXUsers ========== .. versionadded:: 1.3.0 The POSIXUsers tool handles the creation of users and groups as defined by ``POSIXUser`` and ``POSIXGroup`` entries. For a full description of those tags, see :ref:`server-plugins-generators-rules`. The POSIXUsers tool relies on the ``useradd``, ``usermod``, ``userdel``, ``groupadd``, ``groupmod``, and ``groupdel`` tools, since there is no Python library to manage users and groups. It expects those tools to be in ``/usr/sbin``. Primary group creation ====================== Each user must have a primary group, which can be specified with the ``group`` attribute of the ``POSIXUser`` tag. (If the ``group`` attribute is not specified, then a group with the same name as the user will be used.) If that group does not exist, the POSIXUsers tool will create it automatically. It does this by adding a ``POSIXGroup`` entry on the fly; this has a few repercussions: * When run in interactive mode (``-I``), Bcfg2 will prompt for installation of the group separately from the user. * The ``POSIXGroup`` entry is added to the same bundle as the ``POSIXUser`` entry, so if the group is created, the bundle is considered to have been modified and consequently Actions will be run and Services will be restarted. This should never be a concern, since the group can only be created, not modified (it has no attributes other than its name), and if the group is being created then the user will certainly be created or modified as well. * The group is created with no specified GID number. If you need to specify a particular GID number, you must explicitly define a ``POSIXGroup`` entry for the group. Managed UID/GID Ranges ====================== In many cases, there will be users on a system that you do not want to manage with Bcfg2, nor do you want them to be flagged as extra entries. For example, users from an LDAP directory. In this case, you may want to manage the local users on a machine with Bcfg2, while leaving the LDAP users to be managed by the LDAP directory. To do this, you can configure the UID and GID ranges that are to be managed by Bcfg2 by setting the following options in the ``[POSIXUsers]`` section of ``bcfg2.conf`` on the *client*: * ``uid_whitelist`` * ``uid_blacklist`` * ``gid_whitelist`` * ``gid_blacklist`` Each option takes a comma-delimited list of numeric ranges, inclusive at both bounds, one of which may be open-ended on the upper bound, e.g.:: [POSIXUsers] uid_blacklist=1000- gid_whitelist=0-500,700-999 This would tell Bcfg2 to manage all users whose uid numbers were *not* greater than or equal to 1000, and all groups whose gid numbers were 0 <= ``gid`` <= 500 or 700 <= ``gid`` <= 999. If a whitelist is provided, it will be used; otherwise, the blacklist will be used. (I.e., if you provide both, the blacklist will be ignored.) If a user or group is added to the specification with a uid or gid in an unmanaged range, it will produce an error. .. note:: If you specify POSIXUser or POSIXGroup tags without an explicit uid or gid, this will **not** prevent the users/groups from being created with a uid/gid in an unmanaged range. If you want that to happen, you will need to configure your ``useradd``/``groupadd`` defaults appropriately. Note also, however, that this will not cause Bcfg2 errors; it is only an error if a POSIXUser or POSIXGroup has an *explicit* uid/gid in an unmanaged range. Creating a baseline configuration ================================= The majority of users on many systems are created by the packages that are installed, but currently Bcfg2 cannot query the package database to determine these users. (In some cases, this is a limitation of the packaging system.) The often-tedious task of creating a baseline that defines all users and groups can be simplified by use of the ``tools/posixusers_baseline.py`` script, which outputs a bundle containing all users and groups on the machine it's run on. bcfg2-1.3.3/doc/client/tools/vcs.txt000066400000000000000000000006201223671746500172330ustar00rootroot00000000000000.. -*- mode: rst -*- .. _client-tools-vcs: =============== VCS Client Tool =============== .. warning: This tool is currently under development. .. note: Currently, the only supported VCS is git. The VCS tool allows you to checkout particular revisions from a VCS repository on the client to a specified path. The tool requires the appropriate python libraries for the VCS used to be installed. bcfg2-1.3.3/doc/client/tools/yum.txt000066400000000000000000000261701223671746500172620ustar00rootroot00000000000000.. -*- mode: rst -*- .. _client-tools-yum: ============================ Bcfg2 RPM/YUM Client Drivers ============================ The RPM and YUM client drivers provide client support for RPMs (installed directly from URLs) and Yum repositories. These drivers were formerly called ``RPMng`` and ``YUMng``, respectively, but were renamed for Bcfg2 1.3.0. Features ======== * Full RPM package identification using epoch, version, release and arch. * Support for multiple instances of packages with the Instance tag. * Better control of the RPM verification using the pkg_checks, pkg_verify and verify_flags attributes. * Support for install only packages such as the kernel packages. * Support for per instance ignoring of individual files for the RPM verification with the Ignore tag. * Multiple package Instances with full version information listed in interactive mode. * Support for installation and removal of gpg-pubkey packages. * Support for controlling what action is taken on package verification failure with the install_action, version_fail_action and verify_fail_action attributes. Installation ============ isprelink --------- ``isprelink`` is a Python module that can greatly improve the performance of the ``RPM`` driver. It should be installed on any system that has prelink installed and will be using the ``RPM`` driver. Source can be found at ftp://ftp.mcs.anl.gov/pub/bcfg/isprelink-0.1.2.tar.gz To compile and install prelink, execute:: python setup.py install in the rpmtools directory. The elfutils-libelf-devel package is required for the compilation. There may also be RPMs available in the repositories for your distro. Configuration and Usage ======================= Loading of RPM -------------- The RPM driver can be loaded by command line options, client configuration file options or as the default driver for RPM packages. From the command line:: bcfg2 -n -v -d -D Action,POSIX,Chkconfig,RPM This produces quite a bit of output so you may want to redirect the output to a file for review. In the ``bcfg2.conf`` file:: [client] drivers = Action,Chkconfig,POSIX,RPM Configuration File Options -------------------------- A number of paramters can be set in the client configuration for both the RPM and YUM drivers. Each driver has its own section (``[RPM]`` or ``[YUM]``), and most of the same options are accepted by each driver. An example config might look like this:: [RPM] pkg_checks = true pkg_verify = true erase_flags = allmatches installonlypackages = kernel, kernel-bigmem, kernel-enterprise, kernel-smp, kernel-modules, kernel-debug, kernel-unsupported, kernel-source, kernel-devel, kernel-default, kernel-largesmp-devel, kernel-largesmp, kernel-xen, gpg-pubkey install_action = install version_fail_action = upgrade verify_fail_action = reinstall installonlypackages ^^^^^^^^^^^^^^^^^^^ Install-only packages are packages that should only ever be installed or deleted, not upgraded. It is best practice to only ever install/delete kernel packages, the wisdom being that the package for the currently running kernel should always be installed. Doing an upgrade would delete the running kernel package. ``gpg-pubkey`` will be automatically added to the list of install-only packages. Example:: [RPM] installonlypackages = kernel, kernel-bigmem, kernel-enterprise, kernel-smp, kernel-modules, kernel-debug, kernel-unsupported, kernel-source, kernel-devel, kernel-default, kernel-largesmp-devel, kernel-largesmp, kernel-xen, gpg-pubkey This option is not honored by the ``YUM`` driver. erase_flags ^^^^^^^^^^^ erase_flags are rpm options used by 'rpm -erase' in the client ``Remove()`` method. The RPM erase is written using rpm-python and does not use the rpm command. The erase flags are specified in the client configuration file as a comma separated list and apply to all RPM erase operations. The following rpm erase options are supported. See the rpm man page for details:: noscripts notriggers repackage allmatches nodeps This option is not honored by the ``YUM`` driver. pkg_checks ^^^^^^^^^^ The RPM/YUM drivers do the following three checks/status: #. Installed #. Version #. rpm verify Setting pkg_checks = true (the default) in the client configuration file means that all three checks will be done for all packages. Setting pkg_checks = false in the client configuration file means that only the Installed check will be done for all packages. The true/false value can be any combination of upper and lower case. .. note:: #. pkg_checks must evaluate true for both the client (this option) and the package (see the Package Tag pkg_checks attribute below) for the action to take place. #. If pkg_checks = false then the Pkgmgr entries do not need the version information. See the examples towards the bottom of the page. pkg_verify ^^^^^^^^^^ The RPM/YUM drivers do the following three checks/status: #. Installed #. Version #. rpm verify Setting pkg_verify = true (the default) in the client configuration file means that all three checks will be done for all packages as long as pkg_checks = true. Setting pkg_verify = false in the client configuration file means that the rpm verify wil not be done for all packages on the client. The true/false value can be any combination of upper and lower case. .. note:: #. pkg_verify must evaluate true for both the client (this option) and the package instance (see the Instance Tag pkg_verify attribute below) for the action to take place. install_action ^^^^^^^^^^^^^^ ``install_action`` controls whether or not a package instance will be installed if the package instance isn't installed. If install_action = install then the package instance is installed. If install_action = none then the package instance is not installed. .. note:: #. install_action must evaluate true for both the client (this option) and the package instance (see the Instance Tag install_action attribute below) for the action to take place. version_fail_action ^^^^^^^^^^^^^^^^^^^ ``version_fail_action`` controls whether or not a package instance will be updated if the installed package instance isn't the same version as specified in the configuration. If version_fail_action = upgrade then the package instance is upgraded (or downgraded). If version_fail_action = none then the package instance is not upgraded (or downgraded). .. note:: #. verion_fail_action must evaluate true for both the client (this option) and the package instance (see the Instance Tag version_fail_action attribute below) for the action to take place. verify_fail_action ^^^^^^^^^^^^^^^^^^ ``verify_fail_action`` controls whether or not a package instance will be reinstalled if the installed package instance fails the Yum or RPM verify. If verify_fail_action = reinstall then the package instance is reinstalled. If verify_fail_action = none then the package instance is not reinstalled. .. note:: #. verify_fail_action must evaluate true for both the client (this option) and the package instance (see the Instance Tag verify_fail_action attribute below) for the action to take place. #. The driver will not attempt to reinstall a package instance if the only failure is a configuration file. Interactive Mode ---------------- Running the client in interactive mode (-I) prompts for the actions to be taken as before. Prompts are per package and may apply to multiple instances of that package. Each per package prompt will contain a list of actions per instance. In the RPM driver, actions are encoded as: * D - Delete * I - Install * R - Reinstall * U - Upgrade/Downgrade An example follows:: Install/Upgrade/delete Package aaa_base instance(s) - R(*:10.2-38.*) (y/N) Install/Upgrade/delete Package evms instance(s) - R(*:2.5.5-67.*) (y/N) Install/Upgrade/delete Package gpg-pubkey instance(s) - D(*:9c800aca-40d8063e.*) D(*:0dfb3188-41ed929b.*) D(*:7e2e3b05-44748aba.*) D(*:a1912208-446a0899.*) D(*:9c777da4-4515b5fd.*) D(*:307e3d54-44201d5d.*) (y/N) Install/Upgrade/delete Package module-init-tools instance(s) - R(*:3.2.2-62.*) (y/N) Install/Upgrade/delete Package multipath-tools instance(s) - R(*:0.4.7-29.*) (y/N) Install/Upgrade/delete Package pam instance(s) - R(*:0.99.6.3-29.1.*) (y/N) Install/Upgrade/delete Package perl-AppConfig instance(s) - U(None:1.52-4.noarch -> *:1.63-17.*) (y/N) Install/Upgrade/delete Package postfix instance(s) - R(*:2.3.2-28.*) (y/N) Install/Upgrade/delete Package sysconfig instance(s) - R(*:0.60.4-3.*) (y/N) Install/Upgrade/delete Package udev instance(s) - R(*:103-12.*) (y/N) GPG Keys -------- GPG is used by RPM to 'sign' packages. All vendor packages are signed with the vendors GPG key. Additional signatures maybe added to the rpm file at the users discretion. It is normal to have multiple GPG keys installed. For example, SLES10 out of the box has six GPG keys installed. To the RPM database all GPG 'packages' have the name 'gpg-pubkey', which may be nothing like the name of the file specified in the rpm -import command. For example on Centos 4 the file name is RPM-GPG-KEY-centos4. For SLES10 this means that there are six packages with the name 'gpg-pubkey' installed. RPM does not check GPG keys at package installation, while YUM does. RPM uses the rpm command for installation and does not therefore check GPG signatures at package install time. RPM uses rpm-python for verification and does by default do signature checks as part of the client Inventory process. To do the signature check the appropriate GPG keys must be installed. rpm-python is not very friendly if the required key(s) is not installed (it crashes the client). The RPM driver detects, on a per package instance basis, if the appropriate key is installed. If it is not, a warning message is printed and the signature check is disabled for that package instance, for that client run only. GPG keys can be installed and removed by the RPM driver. To install a GPG key configure it in Pkgmgr/Rules as a package and add gpg-pubkey to the clients abstract configuration. The gpg-pubkey package/instance is treated as an install only package. gpg-pubkey packages are installed by the RPM driver with the rpm -import command. gpg-pubkey packages will be removed by ``bcfg2 -r packages`` if they are not in the clients configuration. Ignoring Files during Verification ---------------------------------- The :ref:`path-ignore` Path tag is used to exempt individual files from the RPM verification. This is done by comparing the verification failure results with the ignore Path. If there is a match, that entry is not used by the client to determine if a package has failed verification. Path ignore entries can be specified at both the Package level, in which case they apply to all Instances, and/or at the Instance level, in which case they only apply to that instance. See :ref:`path-ignore` for more details. Example: .. code-block:: xml bcfg2-1.3.3/doc/composable-metadata000066400000000000000000000036251223671746500171160ustar00rootroot00000000000000This documents the redesign of the Metadata subsystem. Goals * Separate core metadata (groups, etc) functionality from augmentors * Enable metadata integration with external data sources * Make metadata features (group inclusion, categories) usable from external datasources The basic idea of this redesign is to split Metadata functions into two major components. One master MetadataPlugin instance handles client identification/authentication, profile assertion, group categories, and metadata instance construction. Multiple MetadataConnectorPlugin instances each contribute additional group memberships and a set of per-instance key/value pairs. This data is merged into the client metadata instance by the master MetadataPlugin. Use Cases * Mapping external data into client metadata instances ** Probes ** Properties ** External network management ** Monitoring ** LDAP data API * Bcfg2.Server.Core.Core ** build_metadata(client_name) * Bcfg2.Server.Plugin.MetadataPlugin ** get_initial_metadata(client_name) ** merge_additional_metadata(metadata, source, group, data_dict) * Bcfg2.Server.Plugin.MetadataConnectorPlugin ** get_additional_metadata(client_metadata) Metadata Resolution Control Flow * B.S.P.MP.resolve_client() -> canonical client name * B.S.C.C.build_metadata() ** B.S.P.MP.get_initial_metadata() -> partial ClientMetadata inst ** [B.S.P.MCP.get_additional_metadata()] -> [([group list], {data dictionary})] ** [B.S.P.MP.merge_additional_metadata()] Implementation Plan (done) * Define new plugin classes * Split Probe code out to discrete plugin * Implement connector support in Core * switch callers to Core.build_metadata * Implement group inheritance/category safety for Connector groups Next Steps * Figure out new version of properties * ICE integration? * zultron's host properties * other external data sources bcfg2-1.3.3/doc/conf.py000066400000000000000000000276601223671746500145750ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Bcfg2 documentation build configuration file, created by # sphinx-quickstart on Sun Dec 13 12:10:30 2009. # # 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. import os import re import sys import time # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../src/lib')) sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('exts')) # -- 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 = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', 'xmlschema'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # Path to XML schemas xmlschema_path = "../schemas" # The suffix of source filenames. source_suffix = '.txt' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. #master_doc = 'contents' master_doc = 'index' # General information about the project. # py3k compatibility if sys.hexversion >= 0x03000000: project = 'Bcfg2' copyright = '2009-%s, Narayan Desai' % time.strftime('%Y') else: project = u'Bcfg2' copyright = u'2009-%s, Narayan Desai' % time.strftime('%Y') # 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 = '1.3' # The full version, including alpha/beta/rc tags. release = '1.3.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- 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 = 'default' # 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 = { "collapsiblesidebar": "true" } # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. html_favicon = 'favicon.ico' # 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'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. html_sidebars = { 'index': 'indexsidebar.html' } # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Bcfg2doc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). # py3k compatibility if sys.hexversion >= 0x03000000: latex_documents = [ ('index', 'Bcfg2.tex', 'Bcfg2 Documentation', 'Narayan Desai et al.', 'manual'), ] else: latex_documents = [ ('index', 'Bcfg2.tex', u'Bcfg2 Documentation', u'Narayan Desai et al.', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('man/bcfg2', 'bcfg2', 'Bcfg2 client tool', [], 1), ('man/bcfg2-admin', 'bcfg2-admin', 'Perform repository administration tasks', [], 8), ('man/bcfg2-build-reports', 'bcfg2-build-reports', 'Generate state reports for Bcfg2 clients', [], 8), ('man/bcfg2.conf', 'bcfg2.conf', 'Configuration parameters for Bcfg2', [], 5), ('man/bcfg2-crypt', 'bcfg2-crypt', 'Bcfg2 encryption and decryption utility', [], 8), ('man/bcfg2-info', 'bcfg2-info', 'Creates a local version of the Bcfg2 server core for state observation', [], 8), ('man/bcfg2-lint', 'bcfg2-lint', 'Check Bcfg2 specification for validity, common mistakes, and style', [], 8), ('man/bcfg2-lint.conf', 'bcfg2-lint.conf', 'Configuration parameters for bcfg2-lint', [], 5), ('man/bcfg2-report-collector', 'bcfg2-report-collector', 'Reports collection daemon', [], 8), ('man/bcfg2-reports', 'bcfg2-reports', 'Query reporting system for client status', [], 8), ('man/bcfg2-server', 'bcfg2-server', 'Server for client configuration specifications', [], 8), ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Bcfg2', u'Bcfg2 Documentation', u'Narayan Desai', 'Bcfg2', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # autodoc settings autodoc_default_flags = ['members', 'show-inheritance'] autoclass_content = "both" private_re = re.compile(r'^\s*\.\.\s*private-include:\s*(.+)$') private_include = [] def skip_member_from_docstring(app, what, name, obj, skip, options): """ since sphinx 1.0 autodoc doesn't support the :private-members: directive, this function allows you to specify ``.. private-include: [,, )`` tuples. This determines which entries the Tool module can be used on. In this case, we set ``__handles__ = [('Package', 'rpm')]``. #. Add verification support by defining a method named ``Verify``. See :func:`Bcfg2.Client.Tools.Tool.Inventory` for details. This method should return True/False depending on current entry installation status. In the failure path, the current state of failing entry attributes should be set in the entry, to aid in auditing. (For example, if a file should be mode 644, and is currently mode 600, then set attribute current_mode='600' in the input entry) #. Add installation support by defining a method named ``Install`_ based core. This page documents the server core interface so that other cores can be written to take advantage of other technologies, e.g., `Tornado `_ or `Twisted `_. A core implementation needs to: * Override :func:`Bcfg2.Server.Core.BaseCore._daemonize` to handle daemonization, writing the PID file, and dropping privileges. * Override :func:`Bcfg2.Server.Core.BaseCore._run` to handle server startup. * Override :func:`Bcfg2.Server.Core.BaseCore._block` to run the blocking server loop. * Call :func:`Bcfg2.Server.Core.BaseCore.shutdown` on orderly shutdown. Nearly all XML-RPC handling is delegated entirely to the core implementation. It needs to: * Call :func:`Bcfg2.Server.Core.BaseCore.authenticate` to authenticate clients. * Handle :exc:`xmlrpclib.Fault` exceptions raised by the exposed XML-RPC methods as appropriate. * Dispatch XML-RPC method invocations to the appropriate method, including Plugin RMI. The client address pair (a tuple of remote IP address and remote hostname) must be prepended to the argument list passed to built-in methods (i.e., not to plugin RMI). Additionally, running and configuring the server is delegated to the core. It needs to honor the configuration options that influence how and where the server runs, including the server location (host and port), listening interfaces, and SSL certificate and key. Base Core ========= .. automodule:: Bcfg2.Server.Core Core Implementations ==================== Builtin Core ------------ The builtin server core consists of the core implementation (:class:`Bcfg2.Server.BuiltinCore.Core`) and the XML-RPC server implementation (:mod:`Bcfg2.SSLServer`). Core ~~~~ .. automodule:: Bcfg2.Server.BuiltinCore XML-RPC Server ~~~~~~~~~~~~~~ .. automodule:: Bcfg2.SSLServer Multiprocessing Core -------------------- .. automodule:: Bcfg2.Server.MultiprocessingCore CherryPy Core ------------- .. automodule:: Bcfg2.Server.CherryPyCore bcfg2-1.3.3/doc/development/documentation.txt000066400000000000000000000065051223671746500212250ustar00rootroot00000000000000.. -*- mode: rst -*- .. _development-documentation: =============== Documentation =============== There are two parts of documentation in the Bcfg2 project: * The Wiki_ * The Manual_ The wiki ======== .. _Wiki: http://bcfg2.org .. _Manual: http://docs.bcfg2.org .. _Trac: http://trac.edgewall.org/ .. _OpenID: https://openid.org/ .. _MCS: http://www.mcs.anl.gov/ .. _Argonne National Laboratory: http://www.anl.gov/ A python-based Trac_ instance is used for the Bcfg2 development website. The Wiki_ part of the website can be edited after you have successfully logged in. In order to login, a vaild OpenID provider is needed. Please request your access to the Wiki_ on the :ref:`help-mailinglist` or in the :ref:`help-irc`. The manual ========== .. _rst: http://en.wikipedia.org/wiki/ReStructuredText .. _Sphinx: http://sphinx.pocoo.org .. _Docutils: http://docutils.sourceforge.net The source for the Manual_ is located in the ``doc/`` directory in the git repository or in the source tarball. All files are written in rst_ (ReStructuredText) format. Sphinx_ is used to build the documentation from the restructured text sources. Building the Manual ------------------- * Install the prerequisites. Docutils_ and Sphinx_ are needed to build. * For Debian (Lenny) the tools are available in the `backports `_ repository; installation can be done with the following:: apt-get -t lenny-backports install python-sphinx * The tools for Fedora based systems are in the `Fedora Package Collection `_; installation can be done easily with Yum:: yum -y install python-sphinx python-docutils * The tools for RHEL6-based systems are in the base distribution; you can install them with Yum:: yum -y install python-sphinx python-docutils * The tools for RHEL5-based systems are in the `Extra Packages for Enterprise Linux(EPEL) `_ repository; if your system is configured for EPEL, you can install them with Yum:: yum -y install python-sphinx python-docutils * Additionally, to build the PDF version: * LaTeX * pdftex * Download the source. Please refer to :ref:`source` for more details. * Build the HTML version by running the following command in the top level of the source directory. The output will appear in ``build/sphinx/html``:: python setup.py build_sphinx * Building the PDF version :: python setup.py build_sphinx --builder=latex cd build/sphinx/latex make .. _doc-styleguide: Documentation Style Guide for Bcfg2 =================================== This is a style guide to use when creating documentation for Bcfg2. It is meant to be helpful, not a hindrance. Basics ------ **Bcfg2** When referring to project, Bcfg2 is the preferred use of case. **Monospace fonts** When referring to commands written on the command line use ``monospace`` fonts. **Repository** When used alone this refers to a Bcfg2 :term:`repository`. When there is a chance for confusion, for instance in documents that also discuss :term:`VCS`, be sure to use the longer phrase "Bcfg2 :term:`repository`". Sections -------- Unless necessary, all the documentation follows the sections header rules available at http://docs.python.org/devguide/documenting.html#sections bcfg2-1.3.3/doc/development/fam.txt000066400000000000000000000047701223671746500171210ustar00rootroot00000000000000.. -*- mode: rst -*- .. _development-fam: ========================== File Monitor Development ========================== Bcfg2 depends heavily on file activity monitoring (FAM) to reload data from disk when it changes. A number of FAM backends are supported (documented thoroughly below), but you may wish to develop additional backends. For instance, the current best FAM backend on Linux is INotify, but if you are running a non-Linux system that lacks INotify support you may wish to write a backend for your OS (e.g., a kqueue backend for BSD-based Bcfg2 servers). This page documents the FAM API and the existing FAM backends. .. _development-fam-event-codes: Event Codes =========== Five event codes are generally understood: +----------+-----------------------------------------------------------+ | Event | Description | +==========+===========================================================+ | exists | Produced when a monitor is added to a file or directory | | | that exists, and produced for all files or directories | | | inside a directory that is monitored (non-recursively). | +----------+-----------------------------------------------------------+ | endExist | Produced immediately after ``exists``. No plugins should | | | process this event meaningfully, so FAM backends do not | | | need to produce it. | +----------+-----------------------------------------------------------+ | created | Produced when a file is created inside a monitored | | | directory. | +----------+-----------------------------------------------------------+ | changed | Produced when a monitored file, or a file inside a | | | monitored directory, is changed. | +----------+-----------------------------------------------------------+ | deleted | Produced when a monitored file, or a file inside a | | | monitored directory, is deleted. | +----------+-----------------------------------------------------------+ Basics ====== .. automodule:: Bcfg2.Server.FileMonitor Existing FAM Backends ===================== Pseudo ------ .. automodule:: Bcfg2.Server.FileMonitor.Pseudo Fam --- .. automodule:: Bcfg2.Server.FileMonitor.Fam Gamin ----- .. automodule:: Bcfg2.Server.FileMonitor.Gamin Inotify ------- .. automodule:: Bcfg2.Server.FileMonitor.Inotify bcfg2-1.3.3/doc/development/index.txt000066400000000000000000000011131223671746500174510ustar00rootroot00000000000000.. -*- mode: rst -*- .. _development-index: ================= Bcfg2 Development ================= There are many ways to get involved in Bcfg2 development. Here we will outline some things that can help you get familiar with the various areas of the Bcfg2 code. The easiest way to submit a patch is to submit a pull request on Github. You can fork and clone the source tree at https://github.com/bcfg2/Bcfg2 Users wishing to contribute on a regular basis can apply for direct git access. Mail the :ref:`help-mailinglist` for details. .. toctree:: :maxdepth: 1 :glob: * bcfg2-1.3.3/doc/development/lint.txt000066400000000000000000000112601223671746500173140ustar00rootroot00000000000000.. -*- mode: rst -*- .. _development-lint: =============================== bcfg2-lint Plugin Development =============================== ``bcfg2-lint``, like most parts of Bcfg2, has a pluggable backend that lets you easily write your own plugins to verify various parts of your Bcfg2 specification. Plugins are loaded in one of two ways: * They may be included in a module of the same name as the plugin class in :mod:`Bcfg2.Server.Lint`, e.g., :mod:`Bcfg2.Server.Lint.Validate`. * They may be included directly in a Bcfg2 server plugin, called "Lint", e.g., :class:`Bcfg2.Server.Plugins.Metadata.MetadataLint`. Plugin Types ============ There are two types of ``bcfg2-lint`` plugins: Serverless plugins ------------------ Serverless plugins are run before ``bcfg2-lint`` starts up a local Bcfg2 server, so the amount of introspection they can do is fairly limited. They can directly examine the Bcfg2 specification, of course, but they can't examine the entries handled by a given plugin or anything that requires a running server. If a serverless plugin raises a lint error, however, the server will not be started and no `Server plugins`_ will be run. This makes them useful to check for the sorts of errors that might prevent the Bcfg2 server from starting properly. Serverless plugins must subclass :class:`Bcfg2.Server.Lint.ServerlessPlugin`. :mod:`Bcfg2.Server.Lint.Validate` is an example of a serverless plugin. Server plugins -------------- Server plugins are run after a local Bcfg2 server has been started, and have full access to all of the parsed data and so on. Because of this, they tend to be easier to use than `Serverless plugins`_, and thus are more common. Server plugins are only run if all `Serverless plugins`_ run successfully (i.e., raise no errors). Server plugins must subclass :class:`Bcfg2.Server.Lint.ServerPlugin`. :mod:`Bcfg2.Server.Lint.Genshi` is an example of a server plugin. Error Handling ============== The job of a ``bcfg2-lint`` plugin is to find errors. Each error that a plugin may produce must have a name, a short string that briefly describes the error and will be used to configure error levels in ``bcfg2.conf``. It must also have a default reporting level. Possible reporting levels are "error", "warning", or "silent". All of the errors that may be produced by a plugin must be returned as a dict by :func:`Bcfg2.Server.Lint.Plugin.Errors`. For instance, consider :func:`Bcfg2.Server.Lint.InfoXML.InfoXML.Errors`: .. code-block:: python @classmethod def Errors(cls): return {"no-infoxml": "warning", "deprecated-info-file": "warning", "paranoid-false": "warning", "required-infoxml-attrs-missing": "error"} This means that the :class:`Bcfg2.Server.Lint.InfoXML.InfoXML` lint plugin can produce five lint errors, although four of them are just warnings by default. The errors returned by each plugin's ``Errors()`` method will be passed to :func:`Bcfg2.Server.Lint.ErrorHandler.RegisterErrors`, which will use that information and the information in the config file to determine how to display (or not display) each error to the end user. Errors are produced in a plugin with :func:`Bcfg2.Server.Lint.Plugin.LintError`, which takes two arguments: the name of the error, which must correspond to a key in the dict returned by :func:`Bcfg2.Server.Lint.Plugin.Errors`, and a freeform string that will be displayed to the end user. Note that the error name and its display are thus only tied together when the error is produced; that is, a single error (by name) can have two completely different outputs. Basics ====== .. automodule:: Bcfg2.Server.Lint Existing ``bcfg2-lint`` Plugins =============================== AWSTagsLint ----------- .. autoclass:: Bcfg2.Server.Plugins.AWSTags.AWSTagsLint BundlerLint ----------- .. autoclass:: Bcfg2.Server.Plugins.Bundler.BundlerLint Comments -------- .. automodule:: Bcfg2.Server.Lint.Comments Genshi ------ .. automodule:: Bcfg2.Server.Lint.Genshi GroupNames ---------- .. automodule:: Bcfg2.Server.Lint.GroupNames GroupPatternsLint ----------------- .. autoclass:: Bcfg2.Server.Plugins.GroupPatterns.GroupPatternsLint InfoXML ------- .. automodule:: Bcfg2.Server.Lint.InfoXML MergeFiles ---------- .. automodule:: Bcfg2.Server.Lint.MergeFiles MetadataLint ------------ .. autoclass:: Bcfg2.Server.Plugins.Metadata.MetadataLint PkgmgrLint ---------- .. autoclass:: Bcfg2.Server.Plugins.Pkgmgr.PkgmgrLint RequiredAttrs ------------- .. automodule:: Bcfg2.Server.Lint.RequiredAttrs TemplateHelperLint ------------------ .. autoclass:: Bcfg2.Server.Plugins.TemplateHelper.TemplateHelperLint Validate -------- .. automodule:: Bcfg2.Server.Lint.Validate bcfg2-1.3.3/doc/development/packages.txt000066400000000000000000000030151223671746500201230ustar00rootroot00000000000000.. -*- mode: rst -*- .. _development-packages: ======================= Developing for Packages ======================= The :ref:`server-plugins-generators-packages` plugin offers multiple backends to support different types of software repositories. New backends can be written to handle new types of software repositories. Each new Packages backend must be contained in its own module in ``Bcfg2.Server.Plugins.Packages``. Each module must implement two classes: A :class:`Bcfg2.Server.Plugins.Packages.Collection.Collection` subclass called ``Collection``, and a :class:`Bcfg2.Server.Plugins.Packages.Source.Source` subclass called ``Source``. E.g., the :mod:`Bcfg2.Server.Plugins.Packages.Yum` backend has :class:`Bcfg2.Server.Plugins.Packages.Yum.YumCollection` and :class:`Bcfg2.Server.Plugins.Packages.Yum.YumSource` objects. These interfaces are explained in detail below. The Collection Object ===================== .. automodule:: Bcfg2.Server.Plugins.Packages.Collection The Source Object ================= .. automodule:: Bcfg2.Server.Plugins.Packages.Source The Packages Module =================== .. automodule:: Bcfg2.Server.Plugins.Packages Packages Source Description =========================== .. automodule:: Bcfg2.Server.Plugins.Packages.PackagesSources Existing Packages Backends ========================== Yum --- .. automodule:: Bcfg2.Server.Plugins.Packages.Yum APT --- .. automodule:: Bcfg2.Server.Plugins.Packages.Apt Pacman ------ .. automodule:: Bcfg2.Server.Plugins.Packages.Pac bcfg2-1.3.3/doc/development/plugins.txt000066400000000000000000000163351223671746500200370ustar00rootroot00000000000000.. -*- mode: rst -*- .. _development-plugins: Bcfg2 Plugin development ======================== While the Bcfg2 server provides a good interface for representing general system configurations, its plugin interface offers the ability to implement configuration interfaces and representation tailored to problems encountered by a particular site. This chapter describes what plugins are good for, what they can do, and how to implement them. Several plugins themselves have pluggable backends, and for narrow cases you may want to develop a backend for an existing plugin rather than an entirely new plugin. See the following pages for more information: .. toctree:: :maxdepth: 1 cfg packages Bcfg2 Plugins ------------- Bcfg2 plugins are loadable python modules that the Bcfg2 server loads at initialization time. These plugins can contribute to the functions already offered by the Bcfg2 server or can extend its functionality. In general, plugins will provide some portion of the configuration for clients, with a data representation that is tuned for a set of common tasks. Much of the core functionality of Bcfg2 is implemented by several plugins, however, they are not special in any way; new plugins could easily supplant one or all of them. .. automodule:: Bcfg2.Server.Plugin :no-members: Server Plugin Types ------------------- A plugin must implement at least one of the interfaces described below. Each interface is available as a class in :mod:`Bcfg2.Server.Plugin`. In most cases, a plugin must also inherit from :class:`Bcfg2.Server.Plugin.base.Plugin`, which is the base Plugin object (described below). Some of the interfaces listed below are themselves Plugin objects, so your custom plugin would only need to inherit from the plugin type. Plugin ^^^^^^ .. autoclass:: Bcfg2.Server.Plugin.base.Plugin :members: name, __author__, experimental, deprecated, conflicts, sort_order, __rmi__, init_repo, shutdown :inherited-members: :show-inheritance: With the exceptions of :class:`Bcfg2.Server.Plugin.interfaces.Statistics` and :class:`Bcfg2.Server.Plugin.interfaces.ThreadedStatistics`, the plugin interfaces listed below do **not** inherit from Plugin; they simply provide interfaces that a given plugin may or must implement. Interfaces ^^^^^^^^^^ .. class:: Bcfg2.Server.Plugin.interfaces .. automodule:: Bcfg2.Server.Plugin.interfaces Exposing XML-RPC Functions -------------------------- Plugins can expose XML-RPC functions that can then be called with :ref:`bcfg2-admin xcmd `. Note that there is absolutely no access control beyond the initial authentication, so take care to not expose any data or behavior via XML-RPC that you would not want all of your clients to be able to see or use. To expose a function, simply add its name to the ``__rmi__`` class attribute. (RMI stands for "Remote Method Invocation.") Consider this example from the :ref:`server-plugins-generators-packages` plugin: .. code-block:: python class Packages(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.StructureValidator, Bcfg2.Server.Plugin.Generator, Bcfg2.Server.Plugin.Connector, Bcfg2.Server.Plugin.ClientRunHooks): name = 'Packages' conflicts = ['Pkgmgr'] __rmi__ = Bcfg2.Server.Plugin.Plugin.__rmi__ + ['Refresh', 'Reload'] def Refresh(self): self._load_config(force_update=True) return True def Reload(self): self._load_config() return True This exposes two functions, ``Refresh`` and ``Reload``, in addition to any default methods that are already exposed. To call one of these functions, you could run:: bcfg2-admin xcmd Packages.Refresh Invalidating Caches ------------------- .. versionadded:: 1.3.0 In Bcfg2 1.3.0, some limited :ref:`server-caching` was introduced. If you are writing a :class:`Bcfg2.Server.Plugin.interfaces.Connector` plugin that implements :func:`Bcfg2.Server.Plugin.interfaces.Connector.get_additional_groups`, then you need to be able to invalidate the server metadata cache in order to be compatible with the ``cautious`` or ``aggressive`` caching modes. The two attributes you need to know about are: * :attr:`Bcfg2.Server.Core.metadata_cache_mode`: A string description of the caching mode. See :ref:`server-caching` for a description of each mode. * :attr:`Bcfg2.Server.Core.metadata_cache`: A dict-like :class:`Bcfg2.Cache.Cache` object that stores the cached data. :class:`Bcfg2.Server.Plugin.base.Plugin` objects have access to the :class:`Bcfg2.Server.Core` object as ``self.core``. In general, you'll be interested in the :func:`Bcfg2.Cache.Cache.expire` method; if called with no arguments, it expires all cached data; if called with one string argument, it expires cached data for the named client. It's important, therefore, that your Connector plugin can either track when changes are made to the group membership it reports, and expire cached data appropriately when in ``cautious`` or ``aggressive`` mode; or prudently flag an incompatibility with those two modes. For examples, see: * :func:`Bcfg2.Server.Plugins.Probes.ReceiveData` takes a copy of the groups that have been assigned to a client by :ref:`server-plugins-probes-index`, and if that data changes when new probe data is received, it invalidates the cache for that client. * :func:`Bcfg2.Server.Plugins.GroupPatterns.Index` expires the entire cache whenever a FAM event is received for the :ref:`server-plugins-grouping-grouppatterns` config file. * :func:`Bcfg2.Server.Plugins.PuppetENC.end_client_run` expires the entire cache at the end of every client run and produces a message at the warning level that the :ref:`server-plugins-connectors-puppetenc` plugin is incompatible with aggressive caching. Tracking Execution Time ----------------------- .. versionadded:: 1.3.0 Statistics can and should track execution time statistics using :mod:`Bcfg2.Statistics`. This module tracks execution time for the server core and for plugins, and exposes that data via ``bcfg2-admin perf``. This data can be invaluable for locating bottlenecks or other performance issues. The simplest way to track statistics is to use the :func:`Bcfg2.Server.Plugin.helpers.track_statistics` decorator to decorate functions that you would like to track execution times for: .. code-block:: python from Bcfg2.Server.Plugin import track_statistics @track_statistics() def do_something(self, ...): ... This will track the execution time of ``do_something``. More granular usage is possible by using :func:`time.time` to manually determine the execution time of a given event and calling :func:`Bcfg2.Statistics.Statistics.add_value` with an appropriate statistic name. Bcfg2.Statistics ^^^^^^^^^^^^^^^^ .. automodule:: Bcfg2.Statistics Plugin Helper Classes --------------------- .. automodule:: Bcfg2.Server.Plugin.helpers :inherited-members: .. Debuggable is in base to avoid circular imports, but it's a helper .. and should be listed here in the docs .. autoclass:: Bcfg2.Server.Plugin.base.Debuggable :inherited-members: Plugin Exceptions ----------------- .. automodule:: Bcfg2.Server.Plugin.exceptions See Also -------- * :ref:`development-compat` * :ref:`development-utils` bcfg2-1.3.3/doc/development/setup.txt000066400000000000000000000062401223671746500175100ustar00rootroot00000000000000.. -*- mode: rst -*- .. _development-setup: Environment setup for development ================================= Checking Out a Copy of the Code ------------------------------- * Check out a copy of the code:: git clone https://github.com/Bcfg2/bcfg2.git * Add :file:`bcfg2/src/sbin` to your :envvar:`PATH` environment variable * Add :file:`bcfg2/src/lib` to your :envvar:`PYTHONPATH` environment variable Using a Virtual Environment for Development ------------------------------------------- Bcfg2 is a pure Python program, and Python makes available certain tools that simplify creating isolated environments. Such environments are useful for running code under development, running code that needs to be installed without actually installing it in system locations, or running parallel, independent installations of the same packages. One popular tool for doing this is `virtualenv `_. The following commands will bootstrap an isolated environment where the Bcfg2 server can run. They assume you are starting from an empty directory, on a Posix-like system that has Python and the ``virtualenv`` package installed (e.g., on Debian it is available as ``python-virtualenv``): .. code-block:: sh # Work in a scratch directory mkdir test_env cd test_env # This creates the environment virtualenv . # "Activate" the environment. From this point forward, Python # and its libraries will first be searched for in test_env and # its subdirectories. When you begin a new session that should # use this environment, re-execute this command. . bin/activate # The pip command is useful for installing python code from a # variety of locations, including directly from git repositories easy_install pip # Install Bcfg2 from git. The -e puts the source in an editable # git clone under the "src" dir. pip install -e git://git.mcs.anl.gov/bcfg2.git#egg=Bcfg2 # Install a newer version of the Cheetah library, for example pip install --upgrade cheetah # If you want to run IPython from within the virtual # environment, it will need to be installed locally, even if it # is already available on the system, or else it won't find . pip install --upgrade ipython # Note, if you install IPython, deactivate and reactivate the # virtualenv before attempting to use it. deactivate . bin/activate .. note:: One caveat about this environment is that it assumes you have already installed Bcfg2's dependencies on the system itself. Pip is capable of building packages such as ``lxml`` that include native code, but you will need to be sure its build-time prerequisites are available. Consider using the above commands to create an isolated Bcfg2 environment in the same directory as your Bcfg2 :term:`repository`. Copy your :file:`/etc/bcfg2.conf` file into a local :file:`etc` directory, tweak the paths as needed and you can run an independent Bcfg2 server as a non-root user. This is useful for confirming a new release of Bcfg2 and all its tools works against your current :term:`repository` before upgrading. bcfg2-1.3.3/doc/development/specification_overview.png000066400000000000000000000170151223671746500230650ustar00rootroot00000000000000PNG  IHDRJ8PLTE̙f::WWuu3J` w(/7>FMUl"Dfƪ3"J1`@wO^n}Ҍ"Df׈33GG\\pp)Rz"31J@`Ow^n}"Df3M"f+3<DMUh|:Wu3Pm33MMff3Mf"+3<DMUh:|Wu3"J1`@wO^n}Ҍ"Df̈ת33MMff:Wu3(P7mETbq3Mf3Uw--DD[[qq3&M3f@MYfs:WuȒׯ33MMff:Wu3M&f3@MYfs:WubKGDf |d pHYsodIDATx[R:[)z<a杹,`>>8k%pDw#BTUq&?M6vǛM=t?N_&1tѱܫ`_& &YƿlOk1L(e^'EB`n_;cl0sF l>szn|` AX! 3v(CQcӁ `Xt($`(C%F"}mlz8a Fa[sΕnYw3>q5}c8DUzś%/! щUen$WhX <0سĻjyDH';/fd 72;hOwk,NDc nT;`Ƥ.5H.lAwc_j  !أP( AѰ?''ueT ؜ػ_{nl0C0! 0! `0l0! `0! `CVؔ vh[00000000000000000000lsXNE0GL, c1%0[`ctĢ0C{F4o^?Oz#zzϟz^|j,q>0N4)ؗ񓧴C-+M_|<2N0;|(ZYV&񞞑^tQpJuO씑_Hyi]U]\:%ej~l . #Q߳ng%yCUΫcSN͉2.Wy;.uYJ2Ko|[:79@q>P<])>7I٧ҷڀRetok]V5j}ПM~Ik1 {g&ǔ95A٫O|%;Z-ͬwYJ gҨu(攛\2[>Sэji;vfnl p V/-{nfўq{o/[(7K+QV]|.uYJ2%+>2?*kL}'S}̄ozQh8])}}UjSQ  ¿ZWU׺N%Yo-52u͹ddk`yBygr/WI!vaYEC0! 0dSJٱ|]8%zԓ<#C;60on <<`""j>?VH#|cV p8HX=6L 7^hcrxCtbz{g+2}C4`>"m 7N ^9E)w@LFލotF/@ Q`FB7&|fƪFglB=9n I."љvQ-=\ I7bPbE `C! `C`C0`C0ĦT#DDo ncwR `VMc0pĄ1`0 N8^ 5(eG3;]~~Ui髋`^Egˎ\@|K.)?k"r[e~/<ηϛi_WEkWM{Ч '\V-(nK*H_ 7k0T][mš2:#8tU.U~G}~;xw5et<97.y'p]ym +v\xEwʨk'Y)KV}ogrWNm%?O>GhuU/LlS(JV.ecp}^dZn̲s62nlZ>e˖FFǗj9΄r/kTˤnE1u/8k" 2oU@ܪd$%}y0|:؋na9kK֭"[Xދ0! `C! `C`Cbp70 kpG`6TL@_h] "E-<# P.i,<3#UfX+0_ii8Rr+̭$V5W%ܓhuvYWYm59 ˚1/`0ڳeQ%q L2x[GS&!-֛sI'J/r|K7>3$$h`$r00n`$BvHGWlu$w_>АJQ@ 6'lUJ I#EJS*F`ўU)6*+hNsasR`IAs TX%(S#lUJ: ţ&'/3ʵ(K7zeiLIuߞr5X4 Ԑ+]) ָ0u<}5<v롼69)TG\L|벬 Z>;0d p?| # p0iNjP7F1>/ino"w2,RkjS ɼAaЦ `jϴ`JvpIc0W&1ǚ+7J4ָ|k6w4y̋n .-7E4"0swܗ,{'oBwh^ 'F3݊&!'ttğ;((pپC*|vntn:|&w/UIeI+=86 S ]  BE;ctla:,_1hCQ+m8ʎ Hڊd vI$oٱlR+0!㏣}DU* a=Iz@h 0 ъyBM-3iuBgF ZcHe?)G?:Ѯ̙~Z 9Nq|b,O~*b"T^T| EE*2+l'v)'5<$,YepcRx3`02`ٕiuf葾mw܋_DXٵs{*N%M cSI MQSI. 7TR0.Jjd^~TRa 1:;ed FTR_qwvƪ ΀X>`NC[fM; 64l=Hظ07iC)q;UT՗sNJ.E>|9Ng^frXQR;inUUSyw[q4k>}1fr2PN=5%Zx՜_ӬY;˫눨b?@&%Sn"'߶>pDowsvȱ3LSﺩ풛?|:sKc$+/Ol4<5הΕ4%2/7:hhLo-z?9^V·Mh|0%G<pI^5utJK:8@To?2d<t/V {`OUvxl04̸QN8 up4%z%zv2_@hAXi,yICJQi i0%3@8p>MӺ(;,I@p뾌x} F'N%줦T4)̀526QA^}ID)I4CpHf?TmïS/zryj3.3f~Mr Ґa"}Hß7 `N`C0x~9_'A~! ,HDCYQ$X%DY{ w Adn/|,}" Xz`VX> `hD #lAक(H}$)6 MX ;)iу>1.7(b p(%'g`lpYeW'&YpFGˤ;n`HhKq 0`0`0v60F  `9"(fb0(Y@hsF(C̀OW7W?Ua#u`0<2F x dd>p v1#Y0! `0! `C! `C!6 + 0!){IENDB`bcfg2-1.3.3/doc/development/testing.txt000066400000000000000000000054301223671746500200250ustar00rootroot00000000000000.. -*- mode: rst -*- .. _development-testing: Testing ======= Testing Prereleases ------------------- Before each release, several prereleases will be tagged. It is helpful to have users test these releases (when feasible) because it is hard to replicate the full range of potential reconfiguration situations; between different operating systems, system management tools, and configuration specification variation, there can be large differences between sites. For more details please visit `Tracking Development Releases of Bcfg2 `_ . Upgrade Testing --------------- This section describes upgrade procedures to completely test the client and server. These procedures can be used for either pre-release testing, or for confidence building in a new release. Server Testing ^^^^^^^^^^^^^^ 1. Ensure that the server produces the same configurations for clients * Before the upgrade, generate all client configurations using the buildall subcommand of bcfg2-info. This subcommand takes a directory argument; it will generate one client configuration in each file, naming each according to the client name. .. code-block:: sh mgt1:~/bcfg# bcfg2-info Filesystem check 1 of 25 ... > buildall /path/to/cf-old Generated config for fs2.bgl.mcs.anl.gov in 1.97310400009 seconds Generated config for fs13.bgl.mcs.anl.gov in 1.47958016396 seconds ... Take notice of any messages produced during configuration generation. These generally reflect minor issues in the configuration specification. Ideally, they should be fixed. * Upgrade the server software * Generate all client configurations in a second location using the new software. Any tracebacks reflect bugs, and should be filed in the ticketing system. Any new messages should be carefully examined. * Compare each file in the old directory to those in the new directory using ``bcfg2-admin compare -r /old/directory /new/directory`` .. code-block:: sh mgt1:~/bcfg# bcfg2-admin compare -r cf-old/ cf-new/ Entry: fs2.bgl.mcs.anl.gov.xml Entry: fs2.bgl.mcs.anl.gov.xml good Entry: fs13.bgl.mcs.anl.gov.xml Entry: fs13.bgl.mcs.anl.gov.xml good Entry: login1.bgl.mcs.anl.gov.xml ConfigFile /bin/whatami contents differ ConfigFile /bin/whatami differs (in bundle softenv) Entry: login1.bgl.mcs.anl.gov.xml bad This can be used to compare configurations for single clients, or different clients. 2. Compare old and new group diagrams (using ``bcfg2-admin viz``) Client Testing ^^^^^^^^^^^^^^ Run the client in dry-run and non-dry-run mode; ensure that multiple runs produce consistent results. bcfg2-1.3.3/doc/development/tips.txt000066400000000000000000000025321223671746500173270ustar00rootroot00000000000000.. -*- mode: rst -*- .. _development-tips: Tips for Bcfg2 Development -------------------------- #. Focus on either the client or server code. This focuses the development process down to the precise pieces of code that matter for the task at hand. * If you are developing a client driver, then write up a small configuration specification that includes the needed characteristics. * If you are working on the server, run ``bcfg2-info`` and use to assess the code. #. Use the python interpreter. One of python's most appealing features is interactive use of the interpreter. * If you are developing for the client-side, run ``python -i /usr/sbin/bcfg2`` with the appropriate bcfg2 options. This will cause the python interpreter to continue running, leaving all variables intact. This can be used to examine data state in a convenient fashion. * If you are developing for the server side, use ``bcfg2-info`` and the "debug" option. This will leave you at a python interpreter prompt, with the server core loaded in the variable "bcore". #. Use ``pylint`` obsessively. It raises a lot of style-related warnings which can be ignored, but most all of the errors are legitimate. #. If you are doing anything with Regular Expressions, `Kodos`_ and `re-try`_ are your friends. .. _Kodos: http://kodos.sourceforge.net .. _re-try: http://re-try.appspot.com bcfg2-1.3.3/doc/development/unit-testing.txt000066400000000000000000000322701223671746500210040ustar00rootroot00000000000000.. -*- mode: rst -*- .. _development-unit-testing: ================== Bcfg2 unit testing ================== .. _Python Mock Module: http://www.voidspace.org.uk/python/mock .. _Python Nose: http://readthedocs.org/docs/nose/en/latest/ You will first need to install the `Python Mock Module`_ and `Python Nose`_ modules. You can then run the existing tests with the following: .. code-block: bash cd testsuite nosetests You should see output something like the following:: .................................................. ---------------------------------------------------------------------- Ran 50 tests in 0.121s OK Unit tests are also run by Travis-CI, a free continuous integration service, at http://travis-ci.org/#!/Bcfg2/bcfg2/ Testing in a virtualenv ======================= Travis-CI runs the unit tests in a virtual environment, so to emulate that testing environment as closely as possible you can also use a virtual environment. To do so, you must have `virtualenv `_ installed. There are two ways to test: Either with just the bare essential packages installed, or with optional packages installed as well. (Optional packages are things like Genshi; you can run Bcfg2 with them or without them.) For completeness, the tests should be run in both manners. (On Python 3, almost none of the optional packages are available, so it can only be run with just the required packages.) To install the optional packages, set: .. code-block:: bash export WITH_OPTIONAL_DEPS=yes This flag tells the install script to install optional dependencies as well as requirements. This assumes that you will create a virtual environment in ``~/venvs/``, and that the Bcfg2 source tree is cloned into ``~/bcfg2``. First, create a new virtual environment and activate it: .. code-block:: bash cd ~/venvs virtualenv travis source travis/bin/activate Get the test suite from bcfg2: .. code-block:: bash cp -R ~/bcfg2/* ~/venvs/travis/ Next, you must install prerequisite packages that are required to build some of the required Python packages, and some optional packages that are much easier to install from binary (rather than from source). If you are running on Ubuntu (the platform Travis-CI runs on) and have sudo, you can simply run: .. code-block:: bash testsuite/before_install.sh If not, you will need to examine ``testsuite/before_install.sh`` and install the packages manually. The equivalent for Fedora, for instance, would be: .. code-block:: bash sudo yum -y update sudo yum -y install swig pylint libxml2 if [[ "$WITH_OPTIONAL_DEPS" == "yes" ]]; then sudo yum -y install libselinux-python pylibacl python-inotify \ PyYAML fi You could install these requirements using pip, but you'll likely need to install a great many development packages required to compile them. Next, install required Python packages: .. code-block:: bash testsuite/install.sh Install Bcfg2 itself to the virtualenv: .. code-block:: bash pip install -e . Now you can run tests: .. code-block:: bash nosetests testsuite Writing Unit Tests ================== Bcfg2 makes extremely heavy use of object inheritance, which can make it challenging at times to write reusable tests. For instance, when writing tests for the base :class:`Bcfg2.Server.Plugin.base.Plugin` class, which all Bcfg2 :ref:`server-plugins-index` inherit from via the :mod:`Plugin interfaces `, yielding several levels of often-multiple inheritance. To make this easier, our unit tests adhere to several design considerations: Inherit Tests ------------- Our test objects should have inheritance trees that mirror the inheritance trees of their tested objects. For instance, the :class:`Bcfg2.Server.Plugins.Metadata.Metadata` class definition is: .. code-block:: python class Metadata(Bcfg2.Server.Plugin.Metadata, Bcfg2.Server.Plugin.Statistics, Bcfg2.Server.Plugin.DatabaseBacked): Consequently, the ``TestMetadata`` class definition is: .. code-block:: python class TestMetadata(TestPlugin.TestMetadata, TestPlugin.TestStatistics, TestPlugin.TestDatabaseBacked): .. note:: The test object names are abbreviated because of the system of relative imports in the ``testsuite`` tree, described below. This gives us a large number of tests basically "for free": all core :class:`Bcfg2.Server.Plugin.interfaces.Metadata`, :class:`Bcfg2.Server.Plugin.interfaces.Statistics`, and :class:`Bcfg2.Server.Plugin.helpers.DatabaseBacked` functionality is automatically tested on the ``Metadata`` class, which gives the test writer a lot of free functionality and also an easy list of which tests must be overridden to provide tests appropriate for the ``Metadata`` class implementation. Additionally, a test class should have a class variable that describes the class that is being tested, and tests in that class should use that class variable to instantate the tested object. For instance, the test for :class:`Bcfg2.Server.Plugin.helpers.DirectoryBacked` looks like this: .. code-block:: python class TestDirectoryBacked(Bcfg2TestCase): test_obj = DirectoryBacked ... def test_child_interface(self): """ ensure that the child object has the correct interface """ self.assertTrue(hasattr(self.test_obj.__child__, "HandleEvent")) Then test objects that inherit from ``TestDirectoryBacked`` can override that object, and the ``test_child_interface`` test (e.g.) will still work. For example: .. code-block:: python class TestPropDirectoryBacked(TestDirectoryBacked): test_obj = PropDirectoryBacked Finally, each test class must also provide a ``get_obj`` method that takes no required arguments and produces an instance of ``test_obj``. All test methods must use ``self.get_obj()`` to instantiate an object to be tested. An object that does not inherit from any other tested Bcfg2 objects should inherit from :class:`testsuite.common.Bcfg2TestCase`, described below. .. _development-unit-testing-relative-imports: Relative Imports ---------------- In order to reuse test code and allow for test inheritance, each test module should add all parent module paths to its ``sys.path``. For instance, assuming a test in ``testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestMetadata.py``, the following paths should be added to ``sys.path``:: testsuite testsuite/Testsrc testsuite/Testsrc/Testlib testsuite/Testsrc/Testlib/TestServer testsuite/Testsrc/Testlib/TestServer/TestPlugins This must be done because Python 2.4, one of our target platforms, does not support relative imports. An easy way to do this is to add the following snippet to the top of each test file: .. code-block:: python import os import sys # add all parent testsuite directories to sys.path to allow (most) # relative imports in python 2.4 path = os.path.dirname(__file__) while path != "/": if os.path.basename(path).lower().startswith("test"): sys.path.append(path) if os.path.basename(path) == "testsuite": break path = os.path.dirname(path) In addition, each new directory created in ``testsuite`` must contain an empty ``__init__.py``. This will allow you, within ``TestMetadata.py``, to import common test code and the parent objects the ``TestMetadata`` class will inherit from: .. code-block:: python from common import inPy3k, call, builtins, u, can_skip, \ skip, skipIf, skipUnless, Bcfg2TestCase, DBModelTestCase, syncdb, \ patchIf, datastore from TestPlugin import TestXMLFileBacked, TestMetadata as _TestMetadata, \ TestStatistics, TestDatabaseBacked Avoid Patching Where Possible ----------------------------- The `Python Mock Module`_ provides a ``patch`` decorator that can be used to replace tested objects with ``Mock`` objects. This is wonderful and necessary, but due to differences in the way various versions of Python and Python Mock handle object scope, it's not always reliable when combined with our system of test object inheritance. Consequently, you should follow these rules when considering whether to use ``patch``: * If you need to mock an object that is not part of Bcfg2 (e.g., a builtin or an object in another Python library), use ``patch``. * If you need to patch an object being tested in order to instantiate it, use ``patch``, but see below. * If you need to patch a function (not a method) that is part of Bcfg2, use ``patch``. * If you need to mock an object that is part of the object being tested, do not use ``patch``. As an example of the last rule, assume you are writing tests for :class:`Bcfg2.Server.Plugin.helpers.FileBacked`. :func:`Bcfg2.Server.Plugin.helpers.FileBacked.HandleEvent` calls :func:`Bcfg2.Server.Plugin.helpers.FileBacked.Index`, so we need to mock the ``Index`` function. This is the **wrong** way to do that: .. code-block:: python class TestFileBacked(Bcfg2TestCase): @patch("%s.open" % builtins) @patch("Bcfg2.Server.Plugin.helpers.FileBacked.Index") def test_HandleEvent(self, mock_Index, mock_open): ... Tests that inherit from ``TestFileBacked`` will not reliably patch the correct ``Index`` function. Instead, assign the object to be mocked directly: .. code-block:: python class TestFileBacked(Bcfg2TestCase): @patch("%s.open" % builtins) def test_HandleEvent(self, mock_open): fb = self.get_obj() fb.Index = Mock() .. note:: ``@patch`` decorations are evaluated at compile-time, so a workaround like this does **not** work: .. code-block:: python class TestFileBacked(Bcfg2TestCase): @patch("%s.open" % builtins) @patch("%s.%s.Index" % (self.test_obj.__module__, self.test_obj.__name)) def test_HandleEvent(self, mock_Index, mock_open): ... But see below about patching objects before instantiation. In some cases, you will need to patch an object in order to instantiate it. For instance, consider :class:`Bcfg2.Server.Plugin.helpers.DirectoryBacked`, which attempts to set a file access monitor watch when it is instantiated. This won't work during unit testing, so we have to patch :func:`Bcfg2.Server.Plugin.helpers.DirectoryBacked.add_directory_monitor` in order to successfully instantiate a ``DirectoryBacked`` object. In order to do that, we need to patch the object being tested, which is a variable, but we need to evaluate the patch at run-time, not at compile time, in order to deal with inheritance. This can be done with a ``@patch`` decorator on an inner function, e.g.: .. code-block:: python class TestDirectoryBacked(Bcfg2TestCase): test_obj = DirectoryBacked def test__init(self): @patch("%s.%s.add_directory_monitor" % (self.test_obj.__module__, self.test_obj.__name__)) def inner(mock_add_monitor): db = self.test_obj(datastore, Mock()) mock_add_monitor.assert_called_with('') inner() ``inner()`` is patched when ``test__init()`` is called, and so ``@patch()`` is called with the module and the name of the object being tested as defined by the test object (i.e., not as defined by the parent object). If this is not done, then the patch will be applied at compile-time and ``add_directory_monitor`` will be patched on the ``DirectoryBacked`` class instead of on the class to be tested. Some of our older unit tests do not follow these rules religiously, so as more tests are written that inherit from larger portions of the ``testsuite`` tree they may need to be refactored. Naming ------ In order to make the system of inheritance we implement possible, we must follow these naming conventions fairly religiously. * Test classes are given the name of the object to be tested with ``Test`` prepended. E.g., the test for the :class:`Bcfg2.Server.Plugins.Metadata.Metadata` is named ``TestMetadata``. * Test classes that test miscellaneous functions in a module are named ``TestFunctions``. * Test modules are given the name of the module to be tested with ``Test`` prepended. Tests for ``__init__.py`` are named ``Test_init.py`` (one underscore). * Tests for methods or functions are given the name of the method or function to be tested with ``test_`` prepended. E.g., the test for :class:`Bcfg2.Server.Plugin.helpers.StructFile.Match` is called ``test_Match``; the test for :class:`Bcfg2.Server.Plugin.helpers.StructFile._match` is called ``test__match``. * Tests for magic methods -- those that start and end with double underscores -- are named ``test__``, where name is the name of the magic method without underscores. E.g., a test for ``__init__`` is called ``test__init``, and a test for ``__getitem__`` is called ``test__getitem``. If this causes a collision with a non-magic function (e.g., if a class also has a function called ``_getitem()``, the test for which would also be called ``test__getitem``, seriously consider refactoring the code for the class. Common Test Code ---------------- .. automodule:: testsuite.common bcfg2-1.3.3/doc/development/utils.txt000066400000000000000000000006301223671746500175050ustar00rootroot00000000000000.. -*- mode: rst -*- .. _development-utils: ================ Common Utilities ================ Some helper functions, classes, etc., are useful to both the client and server. Some of these are used to maintain :ref:`development-compat`, and should go in ``Bcfg2.Compat``. Those that aren't strictly for Python compatibility go in ``Bcfg2.Utils``, which is documented below. .. automodule:: Bcfg2.Utils bcfg2-1.3.3/doc/development/versioning.txt000066400000000000000000000010701223671746500205270ustar00rootroot00000000000000.. -*- mode: rst -*- .. _development-versioning: Versioning Bcfg2 ---------------- #. These are the conventions Bcfg2 has adopted for `versioning`_: * The version number will be broken down into Major.Minor.MicroBuild. * Major and Minor are ever increasing integers. * Micro is a single digit integer. This is because of limits in some of the package systems (specifically Mac OS X). * Build is either missing or refers to release candidates: e.g. "pre3". .. _versioning: https://secure.wikimedia.org/wikipedia/en/wiki/Software_versioning bcfg2-1.3.3/doc/exts/000077500000000000000000000000001223671746500142465ustar00rootroot00000000000000bcfg2-1.3.3/doc/exts/xmlschema.py000066400000000000000000001007751223671746500166130ustar00rootroot00000000000000""" Sphinx extension to generate documention from XML schemas. Known to be woefully imcomplete, probably buggy, terrible error handling, but it *works* for the subset of XML schema we use in Bcfg2. Provides the following directives: * ``.. xml:schema:: ``: Document an XML schema * ``.. xml:type:: ``: Document a complexType or simpleType * ``.. xml:group:: ``: Document an element group * ``.. xml:attributegroup:: ``: Document an attributeGroup * ``.. xml:element:: ``: Document an XML element Each directive supports the following options: * ``:namespace: ``: Specify the namespace of the given entity * ``:nochildren:``: Do not generate documentation for child entities * ``:noattributegroups:``: Do not generate documentation about attribute groups * ``:nodoc:``: Do not include the documentation included in the entity annotation * ``:notext:``: Do not generate documentation about the text content of the entity * ``:onlyattrs: ,``: Only generate documentation about the comma-separated list of attributes given * ``:requiredattrs: ,attr>``: Claim that the attributes named in the given comma-separated list are required, even if they are not flagged as such in the schema. * ``:linktotype: [,]``: If used as a flag, link to documentation on all child types and elements. If a list is given, only link to those types given. (The default is to generate full inline docs for those types.) * ``:noautodep: [,]``: Do not automatically generate docs for any dependent entities. * ``:inlinetypes: ,``: Override a default ``:linktotype:`` setting for the given types. Provides the following roles to link to the objects documented above: * ``:xml:schema:````: Link to an XML schema * ``:xml:type:````: Link to a complexType or simpleType * ``:xml:group:````: Link to an element group * ``:xml:attributegroup:````: Link to an attributeGroup * ``:xml:element:````: Link to an element * ``:xml:attribute:`:```: Link to the attribute in the given context. The context is the name of the containing object, e.g., the parent attributeGroup, element, or complexType. * ``:xml:datatype:````: Link to a built-in XML data type. Note that the entity being linked to does not need to have been explicitly documented with a directive; e.g., if you document a schema that contains a complexType, you can link to that type without having used the ``xml:type::`` directive. Note also that it's far more reliable to link to a complexType than an element, since element name collisions are fairly common. You should avoid type name collisions whenever possible to maximize usability of this extension. There are two configuration items that may be added to conf.py: * ``xmlschema_path`` gives the base path to all XML schemas. * ``xmlschema_datatype_url`` gives a string pattern that will be used to generate links to built-in XML types. It must contain a single ``%s``, which will be replaced by the name of the type. """ import os import operator import lxml.etree from docutils import nodes from sphinx import addnodes, roles from docutils.statemachine import ViewList from docutils.parsers.rst import directives from sphinx.util.nodes import make_refnode, split_explicit_title, \ nested_parse_with_titles from sphinx.util.compat import Directive from sphinx.domains import ObjType, Domain try: from new import classobj except ImportError: classobj = type XS = "http://www.w3.org/2001/XMLSchema" XS_NS = "{%s}" % XS NSMAP = dict(xs=XS) def comma_split(opt): return opt.split(",") def flag_or_split(opt): try: return opt.split(",") except AttributeError: return True class _XMLDirective(Directive): """ Superclass for the other XML schema directives. """ required_arguments = 1 option_spec = dict(namespace=directives.unchanged, nochildren=directives.flag, noattributegroups=directives.flag, nodoc=directives.flag, notext=directives.flag, onlyattrs=comma_split, requiredattrs=comma_split, linktotype=flag_or_split, noautodep=flag_or_split, inlinetypes=comma_split) types = [] def run(self): name = self.arguments[0] env = self.state.document.settings.env reporter = self.state.memo.reporter ns_name = self.options.get('namespace') try: ns_uri = env.xmlschema_namespaces[ns_name] except KeyError: # URI given as namespace ns_uri = ns_name etype = None for etype in self.types: try: entity = env.xmlschema_entities[ns_uri][etype][name] break except KeyError: pass else: reporter.error("No XML %s %s found" % (" or ".join(self.types), name)) return [] documentor = XMLDocumentor(entity, env, self.state, name=name, ns_uri=ns_uri, include=self.process_include(), options=self.process_options()) return documentor.document() def process_include(self): return dict(children='nochildren' not in self.options, attributegroups='noattributegroups' not in self.options, doc='nodoc' not in self.options, text='notext' not in self.options) def process_options(self): return dict(onlyattrs=self.options.get('onlyattrs'), requiredattrs=self.options.get('requiredattrs', []), linktotype=self.options.get('linktotype', []), noautodep=self.options.get('noautodep', False), inlinetypes=self.options.get('inlinetypes', [])) def XMLDirective(types): class cls(_XMLDirective): pass cls.__name__ = 'XML%sDirective' % types[0] cls.types = types return cls class XMLDocumentor(object): def __init__(self, entity, environment, state, name=None, ns_uri=None, parent=None, include=None, options=None): self.entity = entity self.env = environment self.entities = self.env.xmlschema_entities self.namespaces = self.env.xmlschema_namespaces self.namespaces_by_uri = self.env.xmlschema_namespaces_by_uri self.state = state self.include = include self.options = options self.app = self.env.app self.reporter = self.state.memo.reporter if name is None: self.ns_uri = ns_uri self.fqname = self.entity.get("name") self.ns_name, self.name = self.split_ns(self.fqname) if self.ns_uri is None and self.ns_name is not None: self.ns_uri = self.namespaces[self.ns_name] else: self.ns_uri = ns_uri self.ns_name = self.namespaces_by_uri[self.ns_uri] self.name = name if self.ns_name: self.fqname = "%s:%s" % (self.ns_name, self.name) else: self.fqname = name self.tname = nodes.strong(self.fqname, self.fqname) self.tag = self.entity.tag[len(XS_NS):] self.type = tag2type(self.tag) self.parent = parent if self.parent is None: self.dependencies = [] self.documented = [] else: self.dependencies = self.parent.dependencies self.documented = self.parent.documented def document(self): eid = (self.tag, self.fqname) if eid in self.documented: return [build_paragraph(get_xref(self.tag, eid[1]))] else: self.documented.append(eid) rv = [self.target_node(self.tag, self.ns_name, self.name)] data = addnodes.desc(objtype=self.tag) targetid = get_target_id(self.tag, self.ns_name, self.name) header = addnodes.desc_signature('', '', first=True, ids=[targetid]) if self.include['doc']: header.extend([nodes.emphasis(self.tag, self.tag), text(" "), self.tname]) data.append(header) contents = nodes.definition() if self.include['doc']: contents.append(self.get_doc(self.entity)) contents.extend(getattr(self, "document_%s" % self.tag)()) data.append(contents) rv.append(data) if self.parent is None: # avoid adding duplicate dependencies added = [(self.type, self.name)] for typ, name, entity in self.dependencies: if not name: name = entity.get('name') if (typ, name) in added: continue ns_name, name = self.split_ns(name) ns_uri = self.namespaces[ns_name] if not entity: try: entity = self.entities[ns_uri][typ][name] except KeyError: self.app.warn("Dependency %s not found in schemas" % get_target_id(typ, ns_name, name)) continue doc = self.get_documentor(entity, name=name, ns_uri=ns_uri) rv.extend(doc.document()) added.append((typ, name)) return rv def document_schema(self): try: element = self.entity.xpath("xs:element", namespaces=NSMAP)[0] ns, name = self.split_ns(element.get("name")) doc = self.get_documentor(element, name=name, ns_uri=self.namespaces[ns]) return doc.document() except IndexError: # no top-level element or group -- just a list of # (abstract) complexTypes? rv = [] for ctype in self.entity.xpath("xs:complexType", namespaces=NSMAP): ns, name = self.split_ns(ctype.get("name")) doc = self.get_documentor(ctype, name=name, ns_uri=self.namespaces[ns]) rv.extend(doc.document()) return rv def document_group(self): rv = nodes.definition_list() try: (children, groups) = \ self.get_child_elements(self.entity, nodeclass=nodes.paragraph) except TypeError: return [build_paragraph(nodes.strong("Any", "Any"), " arbitrary element allowed")] append_node(rv, nodes.term, text("Elements:")) append_node(rv, nodes.definition, *children) if len(groups): append_node(rv, nodes.term, text("Element groups:")) append_node(rv, nodes.definition, *groups) return rv def document_element(self): fqtype = self.entity.get("type") if fqtype: (etype_ns, etype) = self.split_ns(fqtype) ns_uri = self.get_namespace_uri(etype_ns) values = self.get_values_from_type() if values != "Any": return [build_paragraph( self.tname, " takes only text content, which may be the ", "following values: ", values)] elif etype in self.entities[ns_uri]["complexType"]: if ((self.options['linktotype'] is True or self.name in self.options['linktotype'] or etype in self.options['linktotype'] or fqtype in self.options['linktotype']) and self.name not in self.options['inlinetypes'] and etype not in self.options['inlinetypes']): self.add_dep('complexType', fqtype, None) return [build_paragraph("Type: ", get_xref("type", fqtype))] typespec = self.entities[ns_uri]["complexType"][etype] doc = self.get_documentor(typespec, name=self.entity.get("name")) rv = [self.target_node("complexType", etype_ns, etype)] if self.include['doc'] and not self.get_doc(self.entity): rv.append(self.get_doc(typespec)) rv.extend(doc.document_complexType()) return rv else: self.reporter.error("Unknown element type %s" % fqtype) return [] else: rv = [] typespec = self.entity.xpath("xs:complexType", namespaces=NSMAP)[0] if self.include['doc'] and not self.get_doc(self.entity): rv.append(self.get_doc(typespec)) if typespec is not None: rv = [self.target_node("complexType", self.ns_name, self.name)] doc = self.get_documentor(typespec) rv.extend(doc.document_complexType()) return rv def document_complexType(self): rv = nodes.definition_list() try: content = self.entity.xpath("xs:simpleContent", namespaces=NSMAP)[0] base = content.xpath("xs:extension|xs:restriction", namespaces=NSMAP)[0] attr_container = base except IndexError: base = None attr_container = self.entity ##### ATTRIBUTES ##### table, tbody = self.get_attr_table() attrs = self.get_attrs(attr_container) if attrs: tbody.extend(attrs) foreign_attr_groups = nodes.bullet_list() for agroup in attr_container.xpath("xs:attributeGroup", namespaces=NSMAP): # if the attribute group is in another namespace, just # link to it ns, name = self.split_ns(agroup.get('ref')) if ns != self.ns_name: append_node( foreign_attr_groups, nodes.list_item, build_paragraph(get_xref(tag2type("attributeGroup"), ":".join([ns, name])))) else: tbody.extend(self.get_attrs( self.entities['attributeGroup'][name])) if len(tbody): append_node(rv, nodes.term, text("Attributes:")) append_node(rv, nodes.definition, table) if self.include['attributegroups'] and len(foreign_attr_groups): append_node(rv, nodes.term, text("Attribute groups:")) append_node(rv, nodes.definition, foreign_attr_groups) ##### ELEMENTS ##### if self.include['children']: # todo: distinguish between elements that may occur and # elements that must occur try: (children, groups) = self.get_child_elements(self.entity) except TypeError: children = None groups = None rv.append(build_paragraph(nodes.strong("Any", "Any"), " arbitrary child elements allowed")) if children: append_node(rv, nodes.term, text("Child elements:")) append_node(rv, nodes.definition, build_node(nodes.bullet_list, *children)) if groups: append_node(rv, nodes.term, text("Element groups:")) append_node(rv, nodes.definition, *groups) ##### TEXT CONTENT ##### if self.include['text']: if self.entity.get("mixed", "false").lower() == "true": append_node(rv, nodes.term, text("Text content:")) append_node(rv, nodes.definition, build_paragraph(self.get_values_from_simpletype())) elif base is not None: append_node(rv, nodes.term, text("Text content:")) append_node( rv, nodes.definition, build_paragraph(self.get_values_from_simpletype(content))) return [rv] def document_attributeGroup(self): attrs = self.get_attrs(self.entity) if attrs: table, tbody = self.get_attr_table() tbody.extend(attrs) return [table] else: return [] def get_attr_table(self): atable = nodes.table() atgroup = build_node(nodes.tgroup('', cols=5), nodes.colspec(colwidth=10), nodes.colspec(colwidth=50), nodes.colspec(colwidth=20), nodes.colspec(colwidth=10), nodes.colspec(colwidth=10), nodes.thead('', build_table_row("Name", "Description", "Values", "Required", "Default"))) atable.append(atgroup) atable_body = nodes.tbody() atgroup.append(atable_body) return (atable, atable_body) def get_child_elements(self, el, nodeclass=None): """ returns a tuple of (child element nodes, element group nodes). HOWEVER, if _any_ child is allowed, returns True. """ children = [] groups = [] if nodeclass is None: nodeclass = nodes.list_item if el.xpath("xs:any", namespaces=NSMAP): return True for child in el.xpath("xs:element", namespaces=NSMAP): node = nodeclass() if child.get('ref'): node.append(build_paragraph(get_xref('element', child.get('ref')))) else: # child element given inline doc = self.get_documentor(child, name=child.get('name')) node.extend(doc.document()) children.append(node) for group in el.xpath("xs:group", namespaces=NSMAP): if group.get('ref'): name = group.get('ref') node = nodeclass() node.append(build_paragraph(get_xref('group', name))) self.add_dep('group', name, None) groups.append(node) else: rv = self.get_child_elements(group, nodeclass=nodeclass) try: children.extend(rv[0]) groups.extend(rv[1]) except TypeError: return rv for container in el.xpath("xs:all|xs:choice|xs:sequence", namespaces=NSMAP): rv = self.get_child_elements(container, nodeclass=nodeclass) try: children.extend(rv[0]) groups.extend(rv[1]) except TypeError: return rv return (children, groups) def get_documentor(self, entity, name=None, ns_uri=None): if name is None: name = self.name if ns_uri is None: ns_uri = self.ns_uri return XMLDocumentor(entity, self.env, self.state, name=name, ns_uri=ns_uri, parent=self, options=self.options, include=self.include) def get_attrs(self, el, context=None): cnode = el while context is None and cnode is not None: context = cnode.get('name') cnode = cnode.getparent() rows = [] for attr in el.xpath("xs:attribute[@name]", namespaces=NSMAP): name = attr.get("name") if self.ns_name: fqname = "%s:%s" % (self.ns_name, name) else: fqname = name if (self.options['onlyattrs'] and name not in self.options['onlyattrs'] and fqname not in self.options['onlyattrs']): continue tag = attr.tag[len(XS_NS):] row = [build_paragraph(self.target_node(tag, self.ns_name, context, name), nodes.literal(fqname, fqname))] row.append(self.get_doc(attr)) if attr.get("type") is not None: row.append(build_paragraph( self.get_values_from_type(entity=attr))) else: try: atype = attr.xpath("xs:simpleType", namespaces=NSMAP)[0] row.append(self.get_values_from_simpletype(atype)) except IndexError: # todo: warn about no type found pass reqd = 0 if (name in self.options['requiredattrs'] or attr.get("use", "optional") == "required"): row.append("Yes") reqd = 1 else: row.append("No") default = attr.get("default") if default is None: row.append("None") else: row.append(nodes.literal(default, default)) # we record name and required separately to make sorting # easier rows.append((name, reqd, build_table_row(*row))) rows.sort(key=operator.itemgetter(0)) rows.sort(key=operator.itemgetter(1), reverse=True) if not self.options['onlyattrs'] or '*' in self.options['onlyattrs']: try: anyattr = el.xpath("xs:anyAttribute", namespaces=NSMAP)[0] rows.append((None, None, build_table_row('*', self.get_doc(anyattr), "Any", "No", "None"))) except IndexError: pass return [r[2] for r in rows] def get_values_from_type(self, entity=None, typeattr='type'): if entity is None: entity = self.entity ns_name, name = self.split_ns(entity.get(typeattr)) ns_uri = self.get_namespace_uri(ns_name, entity=entity) if ns_uri == XS: return self.get_builtin_type(name) elif name in self.entities[ns_uri]['simpleType']: return self.get_values_from_simpletype( self.entities[ns_uri]['simpleType'][name]) else: return "Any" def get_builtin_type(self, vtype): if vtype == "boolean": return get_value_list(["true", "false"]) else: return get_datatype_ref(vtype, vtype, self.app.config.xmlschema_datatype_url) def get_doc(self, el): try: return self.parse(el.xpath("xs:annotation/xs:documentation", namespaces=NSMAP)[0].text) except IndexError: return build_paragraph('') def parse(self, text): node = nodes.paragraph() vl = ViewList() for line in text.splitlines(): vl.append(line, '') nested_parse_with_titles(self.state, vl, node) try: return node[0] except IndexError: return build_paragraph(text) def split_ns(self, name): try: (ns, name) = name.split(":") except ValueError: ns = self.ns_name return (ns, name) def get_values_from_simpletype(self, entity=None): if entity is None: entity = self.entity # todo: xs:union, xs:list try: restriction = entity.xpath("xs:restriction|xs:extension", namespaces=NSMAP)[0] except IndexError: return "Any" doc = self.get_doc(restriction) if len(doc) == 1 and len(doc[0]) == 0: # if get_doc returns a paragraph node with an empty Text # node enum = [e.get("value") for e in restriction.xpath("xs:enumeration", namespaces=NSMAP)] if len(enum): return get_value_list(enum) else: return self.get_values_from_type(entity=restriction, typeattr='base') else: return doc def add_dep(self, typ, name, entity): try: if name in self.options['noautodep']: return except TypeError: if self.options['noautodep']: return self.dependencies.append((typ, name, entity)) def target_node(self, tag, ns, *extra): targetid = get_target_id(tag, ns, *extra) fqname = targetid[len(tag) + 1:] rv = nodes.target('', '', ids=[targetid]) self.add_domain_data(tag2type(tag), fqname, (self.env.docname, targetid)) return rv def add_domain_data(self, typ, key, data): if key not in self.env.domaindata['xml'][typ]: self.env.domaindata['xml'][typ][key] = data def get_namespace_uri(self, ns_name, entity=None): if entity is None: entity = self.entity xs_ns = get_xs_ns(entity) if ns_name == xs_ns: return XS else: return self.namespaces[ns_name] def tag2type(tag): if tag in ['complexType', 'simpleType']: return 'type' elif tag == 'attributeGroup': return 'attributegroup' return tag def text(txt): return nodes.Text(txt, txt) def append_node(parent, cls_or_node, *contents): parent.append(build_node(cls_or_node, *contents)) def build_node(cls_or_node, *contents): if isinstance(cls_or_node, (type, classobj)): rv = cls_or_node() else: rv = cls_or_node rv.extend(contents) return rv def get_xref(typ, target, title=None): if title is None: title = target ref = addnodes.pending_xref(title, reftype=typ, refdomain="xml", reftarget=target) ref.append(nodes.literal(title, title)) return ref def build_table_row(*vals): rv = nodes.row('') for val in vals: if isinstance(val, nodes.Node): node = val else: node = nodes.paragraph(val, val) rv.append(nodes.entry(node, node)) return rv def build_paragraph(*args): """ convenience method to build a paragraph node """ rv = nodes.paragraph() for content in args: if isinstance(content, nodes.Node): rv.append(content) else: rv.append(text(content)) return rv def get_target_id(etype, ns_name, *extra): if ns_name: return ":".join([etype, ns_name] + list(extra)) else: return ":".join([etype] + list(extra)) def get_value_list(vals): rv = nodes.paragraph() if vals: rv.append(nodes.literal(vals[0], vals[0])) for i in range(1, len(vals)): rv.append(text(" | ")) rv.append(nodes.literal(vals[i], vals[i])) return rv def get_xs_ns(el): return get_namespace_name(el, XS) def get_namespace_name(el, ns_uri): for name, ns in el.nsmap.items(): if ns == ns_uri: return name return None def get_datatype_ref(title, target, baseurl): return build_node(nodes.reference('', '', refuri=baseurl % target), nodes.literal(title, title)) class XMLDatatypeRole(object): def __init__(self, baseurl): self.baseurl = baseurl def __call__(self, name, rawtext, text, lineno, inliner, options={}, content=[]): has_explicit_title, title, target = split_explicit_title(text) return [get_datatype_ref(title, target, self.baseurl)], [] class XMLXRefRole(roles.XRefRole): def __init__(self, typ, **kwargs): roles.XRefRole.__init__(self, **kwargs) self.type = typ def process_link(self, env, refnode, has_explicit_title, title, target): if (self.type == 'attribute' and not has_explicit_title and ':' in title): title = title.split(':')[-1] return roles.XRefRole.process_link(self, env, refnode, has_explicit_title, title, target) class XMLDomain(Domain): name = "xml" label = "XML" types = dict(schema=['schema'], type=['complexType', 'simpleType'], group=['group'], attributegroup=['attributeGroup'], element=['element'], attribute=None) object_types = dict([(t, ObjType("XML %s" % t.title(), t)) for t in types.keys()]) directives = dict([(t, XMLDirective(h)) for t, h in types.items() if h is not None]) roles = dict([(t, XMLXRefRole(t)) for t in types.keys()]) dangling_warnings = dict([(t, "unknown XML %s: %%(target)s" % t) for t in types.keys()]) initial_data = dict([(t, dict()) for t in types.keys()]) data_version = 3 def clear_doc(self, docname): to_del = [] for dtype in self.types.keys(): for key, (doc, _) in self.data[dtype].iteritems(): if doc == docname: to_del.append((dtype, key)) for dtype, key in to_del: del self.data[dtype][key] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): if typ in ['complexType', 'simpleType']: typ = 'type' if target in self.data[typ]: docname, labelid = self.data[typ][target] else: return None return make_refnode(builder, fromdocname, docname, labelid, contnode) def get_objects(self): for dtype in self.types.keys(): for name, (docname, tgtid) in self.data[dtype].iteritems(): yield (name, name, dtype, docname, tgtid, self.object_types[dtype].attrs['searchprio']) def setup(app): app.add_config_value('xmlschema_path', '.', False) app.add_config_value('xmlschema_datatype_url', 'http://www.w3.org/TR/xmlschema-2/#%s', False) app.add_domain(XMLDomain) app.connect('builder-inited', load_xml_schemas) app.connect('builder-inited', add_xml_datatype_role) def add_xml_datatype_role(app): app.add_role_to_domain('xml', 'datatype', XMLDatatypeRole(app.config.xmlschema_datatype_url)) def load_xml_schemas(app): entities = dict() entities[None] = dict(schema=dict(), group=dict(), attributeGroup=dict(), element=dict(), simpleType=dict(), complexType=dict()) namespaces = dict() namespaces_by_uri = dict() schemapath = os.path.abspath(os.path.join(app.builder.env.srcdir, app.config.xmlschema_path)) for root, _, files in os.walk(schemapath): for fname in files: if not fname.endswith(".xsd"): continue path = os.path.join(root, fname) relpath = path[len(schemapath):].strip("/") schema = lxml.etree.parse(path).getroot() ns = schema.get("targetNamespace") ns_name = get_namespace_name(schema, ns) if ns_name not in namespaces: namespaces[ns_name] = ns if ns not in namespaces_by_uri: namespaces_by_uri[ns] = ns_name if ns not in entities: entities[ns] = dict(schema=dict(), group=dict(), attributeGroup=dict(), element=dict(), simpleType=dict(), complexType=dict()) # schemas don't require namespaces to be identified # uniquely, but we let the user identify them either with # or without the namespace entities[None]['schema'][relpath] = schema entities[ns]['schema'][relpath] = schema for entity in schema.xpath("//xs:*[@name]", namespaces=NSMAP): tag = entity.tag[len(XS_NS):] if tag in entities[ns]: entities[ns][tag][entity.get("name")] = entity app.builder.env.xmlschema_namespaces = namespaces app.builder.env.xmlschema_namespaces_by_uri = namespaces_by_uri app.builder.env.xmlschema_entities = entities bcfg2-1.3.3/doc/getting_started/000077500000000000000000000000001223671746500164525ustar00rootroot00000000000000bcfg2-1.3.3/doc/getting_started/index.txt000066400000000000000000000214531223671746500203270ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst .. _getting_started-index: =============== Getting started =============== The steps below should get you from just thinking about a configuration management system to an operational installation of Bcfg2. If you get stuck, be sure to check the :ref:`help-mailinglist` or to drop in on our :ref:`help-irc`. See the `Platform-specific Quickstart Notes`_ at the end of this page if you happen to be using one of the more common operating systems. Get and Install Bcfg2 Server ============================ We recommend running the server on a Linux machine for ease of deployment due to the availability of packages for the dependencies. First, you need to download and install Bcfg2. The section :ref:`installation-index` in this manual describes the steps to take. To start, you will need to install the server on one machine and the client on one or more machines. Yes, your server can also be a client (and should be by the time your environment is fully managed). .. _Bcfg2 download page: http://trac.mcs.anl.gov/projects/bcfg2/wiki/Download Set up Repository ================= The next step after installing the Bcfg2 packages is to configure the server. You can easily set up a personalized default configuration by running, on the server, :: bcfg2-admin init You will be presented with a series of questions that will build a Bcfg2 configuration file in ``/etc/bcfg2.conf``, set up a skeleton repository (in ``/var/lib/bcfg2`` by default), help you create ssl certificates, and do any other similar tasks needed to get you started. Once this process is done, you can start the Bcfg2 server:: /etc/init.d/bcfg2-server start You can try it out by running the Bcfg2 client on the same machine, acting like it is your first client. .. note:: The following command will tell the client to run in no-op mode, meaning it will only check the client against the repository and report any differences it sees. It won't make any changes (partially because you haven't populated the repository with any yet). However, nobody is perfect. You can make a typo, our software can have bugs, monkeys can break in and hit enter before you are done. Don't run this command on a production system if you don't know what it does and aren't prepared for the consequences. We don't know of anybody having problems with it before, but it is better to be safe than sorry. And now for the command:: bcfg2 -q -v -n That can be translated as "bcfg2 quick verbose no-op". The output should be something similar to:: Loaded tool drivers: Chkconfig POSIX YUMng Phase: initial Correct entries: 0 Incorrect entries: 0 Total managed entries: 0 Unmanaged entries: 242 Phase: final Correct entries: 0 Incorrect entries: 0 Total managed entries: 0 Unmanaged entries: 242 Perfect! We have started out with an empty configuration, and none of our configuration elements are correct. It doesn't get much cleaner than that. But what about those unmanaged entries? Those are the extra configuration elements (probably all packages and services at the moment) that still aren't managed, but have been detected by the client tools. Your goal now is to migrate each of those plus any it can't see up to the "Correct entries" line. Populate Repository =================== Finally, you need to populate your repository. Unfortunately, from here on out we can't write up a simple recipe for you to follow to get this done. It is very dependent on your local configuration, your configuration management goals, the politics surrounding your particular machines, and many other similar details. We can, however, give you guidance. After the above steps, you should have a toplevel repository structure that looks like:: bcfg-server:~ # ls /var/lib/bcfg2 Base/ Bundler/ Cfg/ Metadata/ Pkgmgr/ Rules/ SSHbase/ etc/ The place to start is the Metadata directory, which contains two files: ``clients.xml`` and ``groups.xml``. Your current ``clients.xml`` will look pretty close to: .. code-block:: xml The ``clients.xml`` file is just a series of ```` tags, each of which describe one host you manage. Right now we only manage one host, the server machine we just created. This machine is bound to the ``basic`` profile, is pingable, has a pingtime of ``0``, and has the name ``bcfg-server.example.com``. The two "ping" parameters don't matter to us at the moment, but the other two do. The name parameter is the fully qualified domain name of your host, and the profile parameter maps that host into the ``groups.xml`` file. Our simple ``groups.xml`` file looks like: .. code-block:: xml There are two types of groups in Bcfg: profile groups (``profile='true'``) and non-profile groups (``profile='false'``). Profile groups can act as top-level groups to which clients can bind, while non-profile groups only exist as members of other groups. In our simple starter case, we have a profile group named ``basic``, and that is the group that our first client bound to. Our first client is a SuSE machine, so it contains the ``suse`` group. Of course, ``bcfg2-admin`` isn't smart enough to fill out the rest of your config, so the ``suse`` group further down is empty. Let's say the first thing we want to set up on our machine is the message of the day. To do this, we simply need to create a Bundle and add that Bundle to an appropriate group. In this simple example, we start out by adding .. code-block:: xml to the ``basic`` group. Next, we create a motd.xml file in the Bundler directory: .. code-block:: xml Now when we run the client, we get slightly different output:: Loaded tool drivers: Chkconfig POSIX YUM Incomplete information for entry Path:/etc/motd; cannot verify Phase: initial Correct entries: 0 Incorrect entries: 1 Total managed entries: 1 Unmanaged entries: 242 In dryrun mode: suppressing entry installation for: Path:/etc/motd Phase: final Correct entries: 0 Incorrect entries: 1 Total managed entries: 1 Unmanaged entries: 242 We now have an extra unmanaged entry, bringing our total number of managed entries up to one. To manage it we need to copy ``/etc/motd`` to ``/var/lib/bcfg2/Cfg/etc/motd/``. Note the layout of that path: all plain-text config files live in the Cfg directory. The directory structure under that directory directly mimics your real filesystem layout, making it easy to find and add new files. The last directory is the name of the file itself, so in this case the full path to the motd file would be ``/var/lib/bcfg2/Cfg/etc/motd/motd``. Copy your real ``/etc/motd`` file to that location, run the client again, and you will find that we now have a correct entry:: Loaded tool drivers: Chkconfig POSIX YUM Phase: initial Correct entries: 1 Incorrect entries: 0 Total managed entries: 1 Unmanaged entries: 242 Phase: final Correct entries: 1 Incorrect entries: 0 Total managed entries: 1 Unmanaged entries: 242 Done! Now we just have 242 (or more) entries to take care of! :ref:`server-plugins-structures-bundler-index` is a relatively easy directory to populate. You can find many samples of Bundles in the :ref:`Bundler Example Repository `, many of which can be used without editing. .. _getting_started-index-next-steps: Next Steps ========== Several other utilities can help from this point on: :ref:`bcfg2-info ` is a utility that instantiates a copy of the Bcfg2 server core (minus the networking code) for examination. From this, you can directly query: * Client Metadata * Which entries are provided by particular plugins * Client Configurations Run ``bcfg2-info``, and type help and the prompt when it comes up. ``bcfg2-admin`` can perform a variety of repository maintenance tasks. Run ``bcfg2-admin`` help for details. Once you have the server setup, you may be interested in :ref:`bootstrapping ` additional clients. Platform-specific Quickstart Notes ================================== * :ref:`appendix-guides-centos` * :ref:`appendix-guides-ubuntu` * :ref:`getting_started-macosx-notes` bcfg2-1.3.3/doc/getting_started/macosx/000077500000000000000000000000001223671746500177445ustar00rootroot00000000000000bcfg2-1.3.3/doc/getting_started/macosx/notes.txt000066400000000000000000000060341223671746500216400ustar00rootroot00000000000000.. -*- mode: rst -*- .. _getting_started-macosx-notes: =============================== Setting up Bcfg2 From Scratch =============================== Ala `Managing /etc/motd with Bcfg2 Starting From an Empty VM `_, I'll be setting up a fresh OS X 10.6 machine to be managed by Bcfg2. Get OS X 10.6 Running ===================== Use your favorite provisioning method to get your operating system running and fully patched. For this hands on, I'm running OS X 10.6.8 (Build 10K540) with the supplied python 2.6.1. I've also turned on Remote Login (i.e. ssh) so I can use my client to write this document going through the steps; having ssh on is not a requirement for this howto. Get bcfg2-server Working ======================== Get bcfg2 package ----------------- You might be able to get a package already built for you, but it is not hard to build it from the source. You'll need git (via `git-osx-installer `_ or `homebrew `_; the former is easier, the later more developer friendly) and Apple's `XCode `_. The first step is to clone the bcfg2 repository into a working directory: .. code-block: bash cd ~/Developer git clone https://github.com/Bcfg2/bcfg2.git cd bcfg2 At this point you will probably want to checkout a release tag (`git tag -l` to see a list of them). This test is using v1.2.0pre4. Once you've done that you can build the server. .. code-block: bash git checkout v1.2.0pre4 cd osx make server The server package contains both the client and the server. The package is located at ``./osx/bcfg2-VERSION.pkg``. Copy it to the machine you want to set up from scratch and install it. THIS NEEDS TO VERIFIED Some of the differences between bcfg2 on Mac OS X and Debian is that the libraries are stored at `/Library/Frameworks/Python.framework/Versions/Current/share/bcfg2/` `/Library/Python/site-packages/Bcfg2/` instead of `/usr/lib/pymodules/` and `/usr/share/pyshare/Bcfg2. Also, instead of cron and init.d, `/Library/LaunchDaemons/gov.anl.mcs.bcfg2-daily.plist` controls peridic runs and starts and stops. The runtime files are stored in `/usr/local/bin` under Mac OS X instead of /usr/sbin/ for Debian. VERIFY:: 10.6_client :~ user$ sudo /usr/local/bin/bcfg2-admin init Failed to import lxml dependency. Shutting down server. Try: sudo easy_install lxml. If you don't have gcc-4.2 installed, you'll need to install it on a machine that does. Then move `/Library/Python/2.6/sites-packages/lxml-2.3-py2.6-macosx-10.6-universal.egg` to the client and add the line "./lxml-2.3-py2.6-macosx-10.6-universal.egg" to `/Library/Python/2.6/site-packages/easy-install.pth`. Getting a new error:: $ sudo /usr/local/bin/bcfg2-admin init Interactively initialize a new repository. bcfg2-admin init $ So what is lxml easy_install fully installing? Need to make a package (Lettuce to the rescue!) bcfg2-1.3.3/doc/glossary.txt000066400000000000000000000026231223671746500156720ustar00rootroot00000000000000.. -*- mode: rst -*- .. _glossary: ======== Glossary ======== .. glossary:: :sorted: client A system that runs the :command:`bcfg2` command. Typically, this is to receive a configuration from a Bcfg2 server. generator A type of plugin which provides file contents. For example :ref:`server-plugins-generators-cfg` or :ref:`server-plugins-generators-sshbase`. Genshi A Python-based templating engine. `Genshi Homepage`_. group A "tag" assigned to a client through a probe or other plugin. irc channel #bcfg2 on freenode probe A script that executes on a client machine and sets client metadata such as group membership. profile A special type of group that a client is explicitly assigned to. repository A collection of folders and files that together define the configurations that Bcfg2 applies to clients. The repository is located at :file:`/var/lib/bcfg2` by default. This is not to be confused with a :term:`VCS` repository, which is an excellent place to pull your Bcfg2 repository from to manage changes. When used alone, :term:`repository` refers to a Bcfg2 repository. VCS Stands for `Version Control System `_. .. _Genshi Homepage: http://genshi.edgewall.org/ bcfg2-1.3.3/doc/help/000077500000000000000000000000001223671746500142135ustar00rootroot00000000000000bcfg2-1.3.3/doc/help/faq/000077500000000000000000000000001223671746500147625ustar00rootroot00000000000000bcfg2-1.3.3/doc/help/faq/client.txt000066400000000000000000000013271223671746500170040ustar00rootroot00000000000000.. -*- mode: rst -*- .. _faq-client: FAQ: Client =========== **No ca is specified. Cannot authenticate the server with SSL.** This means that the client does not have a copy of the CA for the server, so it can't verify that it is talking to the right server. To fix this issue, copy ``/etc/bcfg2.crt`` from the server to ``/etc/bcfg2.ca`` on the client. Then add the following on the client.:: [communication] ca = /etc/bcfg2.ca .. FIXME: What is the solution for this? .. **Server failure** .. .. On Fedora 14 and above it can happen that no connection is possible. .. .. # bcfg2 -vqne .. Server failure: Protocol Error: 401 Unauthorized .. Failed to download probes from bcfg2 .. Server Failure bcfg2-1.3.3/doc/help/faq/general.txt000066400000000000000000000037061223671746500171460ustar00rootroot00000000000000.. -*- mode: rst -*- .. _faq-general: FAQ: General ============ **What does Bcfg2 stand for?** Initially, Bcfg stood for the bundle configuration system. Bcfg2 is the second major revision. At this point, the acronym is meaningless, but the name has stuck. Luckily, Bcfg2 googles better than Bcfg does. No, seriously. Try it. All I know is that I have no interest in a billion cubic feet of gas. **What architectures does Bcfg2 support?** Bcfg2 should run on any POSIX compatible operating system, however direct support for an operating system's package and service formats are limited by the currently available :ref:`client-tools` (although new client tools are pretty easy to add). The following is an incomplete but more exact list of platforms on which Bcfg2 works. * GNU/Linux deb based distros * GNU/Linux rpm based distros * Solaris pkg based * Gentoo portage based * OSX (POSIX/launchd support) **What pre-requisites are needed to run Bcfg2?** Please visit the :ref:`installation-prerequisites` section in the manual. **Why won't bcfg2-server start?** If your server doesn't seem to be starting and you see no error messages in your server logs, try running it in the foreground to see why. **Why am I getting a traceback?** If you get a traceback, please let us know. You can file a `ticket `_, send the traceback to the :ref:`help-mailinglist`, or hop on the :ref:`help-irc` and let us know. **Where are the server log messages?** The bcfg2-server process logs to syslog facility LOG_DAEMON. The server produces a series of messages upon a variety of events and errors. **Is there a way to check if all repository XML files conform to schemas?** Bcfg2 comes with XML schemas describing all of the XML formats used in the server repository. A validation command ``bcfg2-lint`` is included with the source distribution and all packages. ``bcfg2-lint`` can also performs lots of other checks for common mistakes. bcfg2-1.3.3/doc/help/faq/index.txt000066400000000000000000000004131223671746500166300ustar00rootroot00000000000000.. -*- mode: rst -*- .. _faq-index: === FAQ === The Frequently Asked Questions (FAQ) answers the most common questions about Bcfg2. At the moment the FAQ is splitted into a general and a client specfic section. .. toctree:: :maxdepth: 2 general client bcfg2-1.3.3/doc/help/index.txt000066400000000000000000000023021223671746500160600ustar00rootroot00000000000000.. -*- mode: rst -*- .. _help-index: ======================= Getting Help with Bcfg2 ======================= Having trouble? We'd like to help! There are several ways to get in touch with the Bcfg2 community. * Try the :ref:`FAQ ` -- it's got answers to many common questions. * Check the :ref:`help-troubleshooting` page to see if you can narrow down the cause of your issue. * Looking for specific information? Try the :ref:`genindex`, :ref:`modindex`, or the :ref:`detailed table of contents `. * Search for information in the :ref:`help-mailinglist`. * Visit our :ref:`help-irc`, or search the `IRC logs`_. Note that the IRC channel tends to be much busier than the mailing list; use whichever seems most appropriate for your query, but don't let the lack of mailing list activity make you think the project isn't active. .. _IRC logs: http://colabti.org/irclogger/irclogger_logs/bcfg2 .. _Bcfg2 mailing list archives: http://trac.mcs.anl.gov/projects/bcfg2/wiki/MailingList .. _Trac ticket tracker: http://bcfg2.org Report A Bug ============ Report bugs with Bcfg2 on the `Trac ticket tracker`_. .. toctree:: :hidden: mailinglist irc faq/index troubleshooting bcfg2-1.3.3/doc/help/irc.txt000066400000000000000000000032771223671746500155420ustar00rootroot00000000000000.. -*- mode: rst -*- .. _Freenode: http://chat.freenode.net .. _#bcfg2: irc://chat.freenode.net/bcfg2 .. _help-irc: =========== IRC Channel =========== The Bcfg2 IRC channel is `#bcfg2`_ on `Freenode`_. It is home to both support and development discussions. If you have a question, suggestion, or just want to know about Bcfg2, please drop in and say hi. Archives are available at: http://colabti.org/irclogger/irclogger_logs/bcfg2 .. raw:: html

    in the timespan (yyyymmdd-yyyymmdd)

    Options:


    Administrative Note =================== If the IRC logging stops working for a while, coordinate on #bcfg2 and then bug **feb** on #irclogger (freenode), and stick around on that channel until you get an answer (**feb** is great, but busy and in a non-US time zone). Actually as long as **ilogger2** is logged in you should be okay (**feb** looks at the logs). If you have private logs for the period of time **ilogger2** was off the channel, you can convert them to the will incorporate them into the online logs. Be sure to strip out any private messages in your logs first :-) .. _format shown here: http://colabti.org/irclogger/irclogger_log/bcfg2?date=2008-03-21,Fri;raw=on bcfg2-1.3.3/doc/help/mailinglist.txt000066400000000000000000000013201223671746500172640ustar00rootroot00000000000000.. -*- mode: rst -*- .. _help-mailinglist: ============ Mailing List ============ To subscribe to the mailing list for Bcfg2 please visit the `bcfg-dev mailman page`_ `Searchable archives`_ are available from Gmane. You can also read the mailing list from any NNTP client via Gmane. .. _bcfg-dev mailman page: https://lists.mcs.anl.gov/mailman/listinfo/bcfg-dev .. _Searchable archives: http://dir.gmane.org/gmane.comp.sysutils.bcfg2.devel .. raw:: html
    bcfg2-1.3.3/doc/help/troubleshooting.txt000066400000000000000000000341231223671746500202060ustar00rootroot00000000000000.. -*- mode: rst -*- .. _help-troubleshooting: =============== Troubleshooting =============== From time to time, Bcfg2 produces results that the user finds surprising. This can happen either due to bugs or user error. This page describes several techniques to gain visibility into the bcfg2 client and server and understand what is going on. Figure out if error is client or server side ============================================ * Cache a copy of the client configuration using ``bcfg2 -qnc /tmp/config.xml`` * Look in the file and search for the entry of interest * If it looks correct, then there is a client issue * If not, it is time to inspect things on the server This file contains all aspects of client configuration. It is structured as a series of bundles and base entries. .. note:: Most often the entry is not correct and the issue lies in the specification. Review server log messages ========================== The bcfg2-server process logs to syslog facility LOG_DAEMON. The server produces a series of messages upon a variety of events and errors. The server also supports two XML-RPC methods that can be used to turn up the debug level in a live server: * ``toggle_debug``: Turn debug on or off, depending on the current setting. * ``set_debug``: Turn debug explicitly on or off. These can be called with :ref:`bcfg2-admin xcmd `, e.g.:: bcfg2-admin xcmd toggle_debug bcfg2-admin xcmd set_debug true Each plugin also supports these two methods, which can be used to set the debug level individually on a given plugin, e.g.:: bcfg2-admin xcmd Packages.set_debug true bcfg2-admin xcmd Probes.toggle_debug Finally, the File Activity Monitor has its own analogue to these two methods, for setting the debug level of the FAM:: bcfg2-admin xcmd Inotify.toggle_debug bcfg2-admin xcmd Inotify.set_debug false Check if all repository XML files conform to schemas ==================================================== Bcfg2 comes with XML schemas describing all of the XML formats used in the server repository. A validation command ``bcfg2-lint`` is included with the source distribution and all packages. If the bcfg2 server is not reflecting recent changes, try restarting the bcfg2-server process ============================================================================================= If this fixes the problem, it is either a bug in the underlying file monitoring system (fam or gamin) or a bug in Bcfg2's file monitoring code. In either case, file a `ticket `_ in the tracking system. In the ticket, include: * filesystem monitoring system (fam or gamin) * kernel version (if on linux) * if any messages of the form "Handled N events in M seconds" appeared between the modification event and the client configuration generation request appeared in the server log * which plugin handled the file in the repostiory (Cfg, Rules, Packages, SSHbase, Metadata, etc.) * if a touch of the file after the modification causes the problem to go away bcfg2-info ========== Bcfg2 server operations can be simulated using the ``bcfg2-info`` command. The command is interactive, and has commands to allow several useful operations * clients - Current client metadata (profile and group) settings * groups - Current group metadata values * mappings - Configuration entries provided by plugins * buildfile [--altsrc=] - Build a config file for a client * buildbundle - Render a templated bundle for a client * showentries - Build the abstract configuration (list of entries) for a client * build - Build the complete configuration for a client Type `help` in bcfg2-info for more information. Error Messages ============== The tables below describe error messages produced by Bcfg2 and steps that can be taken to remedy them. Client Errors ------------- +------------------------------+----------------------------+--------------+ | Error | Meaning | Repair | +==============================+============================+==============+ | Incomplete information for | The described entry is not | [c1]_ | | entry : | fully specified by the | | | cannot verify | server, so no verification | | | | can be performed. | | +------------------------------+----------------------------+--------------+ | Incomplete information for | The described entry is not | [c1]_ | | entry : | fully specified by the | | | cannot install | server, so no verification | | | | can be performed. | | +------------------------------+----------------------------+--------------+ | The following entries are | The client cannot figure | [c2]_ | | not handled by any tool: | out how to handle this | | | : | entry. | | +------------------------------+----------------------------+--------------+ | No ca is specified. Cannot | The client is unable to | [c3]_ | | authenticate the server with | verify the server. | | | SSL. | | | +------------------------------+----------------------------+--------------+ | GID normalization failed for | The client is unable to | [c4]_ | | FILENAME. Does group GROUP | convert the group GROUP to | | | exist? | a usable GID. | | +------------------------------+----------------------------+--------------+ | UID normalization failed for | The client is unable to | [c5]_ | | FILENAME. Does owner OWNER | convert the owner OWNER to | | | exist? | a usable UID. | | +------------------------------+----------------------------+--------------+ | SSL CA error | The CA certificate | [c6]_ | | | specified in bcfg2.conf is | | | | incorrect | | +------------------------------+----------------------------+--------------+ .. [c1] This entry is not being bound. Ensure that a version of this entry applies to this client. .. [c2] It is possible that the type attribute for this generator entry is undefined. You may need to add the appropriate type attribute in the literal entry specification. .. [c3] Copy the Bcfg2 server's CA certificate to the client and specify it using the **ca** option in the [communication] section of ``bcfg2.conf`` .. [c4] If the group doesn't exist, you need to specify the correct one in an :ref:`info.xml ` file or set the default group appropriately. .. [c5] If the owner doesn't exist, you need to specify the correct one in an :ref:`info.xml ` file or set the default owner appropriately. .. [c6] Check that the CA specified in bcfg2.conf is appropriate for the server you are attempting to access. Server Errors ------------- +------------------------------+---------------------+--------------+ | Error | Meaning | Repair | +==============================+=====================+==============+ | Failed to bind entry: | The server was | [s1]_ | | | unable to find a | | | | suitable version of | | | | entry for client. | | +------------------------------+---------------------+--------------+ | Failed to bind to socket | The server was | [s2]_ | | | unable to bind to | | | | the tcp server | | | | socket. | | +------------------------------+---------------------+--------------+ | Failed to load | The server was | [s3]_ | | ssl key | unable to read and | | | | process the ssl key.| | +------------------------------+---------------------+--------------+ | Failed to read file | The server failed | [s4]_ | | | to read the | | | | specified file | | +------------------------------+---------------------+--------------+ | Failed to parse file | The server failed | [s5]_ | | | to parse the | | | | specified XML file | | +------------------------------+---------------------+--------------+ | Client metadata resolution | The server cannot | [s6]_ | | error for | resolve the client | | | | hostname or the | | | | client is | | | | associated with a | | | | non-profile group. | | +------------------------------+---------------------+--------------+ | Failed to decode | The encoding being | [s7]_ | | Please verify you are using | used is unable to | | | the proper encoding | decode the | | | | character present | | | | in this file. | | +------------------------------+---------------------+--------------+ | Got unknown entries | The Packages plugin | [s8]_ | | [list of unknown entries] | has no knowledge of | | | | the listed entries | | +------------------------------+---------------------+--------------+ | Failed to import lxml | The server cannot | [s9]_ | | dependency. Shutting | import lxml | | | down server. | | | +------------------------------+---------------------+--------------+ | You need to specify base64 | The server cannot | [s10]_ | | encoding for | send the file as | | | | ascii text | | +------------------------------+---------------------+--------------+ | ERROR: Error reading file | The server cannot | [s11]_ | | '/path/to/schema': failed to | find the schema | | | load external entity | file | | | "/path/to/schema" | | | +------------------------------+---------------------+--------------+ | Packages: No matching | None of the sources | [s12]_ | | sources for client | defined in the | | | ; improper group | Package plugin's | | | memberships? | ``sources.xml`` | | | | apply to the client | | +------------------------------+---------------------+--------------+ .. [s1] This entry is not being bound. Ensure that a version of this entry applies to this client. .. [s2] Ensure that another instance of the daemon (or any other process) is not listening on the same port. .. [s3] Ensure that the key is readable by the user running the daemon and that it is well-formed. .. [s4] Ensure that this file still exists; a frequent cause is the deletion of a temp file. .. [s5] Ensure that the file is properly formed XML. .. [s6] Fix hostname resolution for the client or ensure that the profile group is properly setup. .. [s7] Ensure the correct encoding is specified in the [components] section of ``bcfg2.conf``. .. [s8] For packages listed other than **gpg-pubkey**, this error means that the Packages plugin is unable to find the package in any of the sources listed in ``Packages/sources.xml``. The issue often arises when the client is not in one of the groups necessary for the Source listed. In the case of gpg-pubkey, you can safely ignore the message as the Packages plugin has no knowledge of these packages (however, note that this package is most often specified as a BoundPackage entry). .. [s9] Ensure that you have installed all the necessary :ref:`installation-prerequisites`. .. [s10] You likely need to specify a base64 encoding using an :ref:`server-info` file for this entry. .. [s11] Verify that you have the proper prefix set in bcfg2.conf. .. [s12] Ensure that the client is a member of all the appropriate :ref:`server-plugins-generators-packages-magic-groups` as well as any additional groups you may have defined in your :ref:`server-plugins-generators-packages` configuration. FAQs ==== Why won't bcfg2-server start? ----------------------------- If your server doesn't seem to be starting and you see no error messages in your server logs, try running it in the foreground to see why. Why am I getting a traceback? ----------------------------- If you get a traceback, please let us know by reporting it on `Trac ticket tracker`_, via the mailing list, or on IRC. Your best bet to get a quick response will be to jump on IRC during the daytime (CST). .. _Trac ticket tracker: http://bcfg2.org What is the most common cause of "The following entries are not handled by any tool"? ------------------------------------------------------------------------------------- Often it corresponds to entries that aren't bound by the server (for which you'll get error messages on the server). You should try inspecting the logs on the server to see what may be the cause. bcfg2-1.3.3/doc/index.txt000066400000000000000000000031461223671746500151370ustar00rootroot00000000000000.. -*- mode: rst -*- .. _index: ============================= Bcfg2 documentation |release| ============================= What is Bcfg2? ============== Bcfg2 helps system administrators produce a consistent, reproducible, and verifiable description of their environment, and offers visualization and reporting tools to aid in day-to-day administrative tasks. It is the fifth generation of configuration management tools developed in the `Mathematics and Computer Science Division`_ of `Argonne National Laboratory`_. .. _Mathematics and Computer Science Division: http://www.mcs.anl.gov/ .. _Argonne National Laboratory: http://www.anl.gov/ It is based on an operational model in which the specification can be used to validate and optionally change the state of clients, but in a feature unique to Bcfg2 the client's response to the specification can also be used to assess the completeness of the specification. Using this feature, Bcfg2 provides an objective measure of how good a job an administrator has done in specifying the configuration of client systems. Bcfg2 is therefore built to help administrators construct an accurate, comprehensive specification. Bcfg2 has been designed from the ground up to support gentle reconciliation between the specification and current client states. It is designed to gracefully cope with manual system modifications. Finally, due to the rapid pace of updates on modern networks, client systems are constantly changing; if required in your environment, Bcfg2 can enable the construction of complex change management and deployment strategies. .. toctree:: :maxdepth: 2 contents bcfg2-1.3.3/doc/installation/000077500000000000000000000000001223671746500157645ustar00rootroot00000000000000bcfg2-1.3.3/doc/installation/distributions.txt000066400000000000000000000076121223671746500214350ustar00rootroot00000000000000.. -*- mode: rst -*- .. _distributions: =========================== Distribution-specific notes =========================== The installation of Bcfg2 on a specific distribution depends on the package management tool and the availability of the package in the distribution's repository. Alpine Linux ============ Packages for `Alpine Linux`_ are available in the `testing`_ repository. Just use `apk` to perform the installation :: apk add bcfg2 bcfg2-server -U -X http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted .. _Alpine Linux: http://www.alpinelinux.org/ .. _testing: http://git.alpinelinux.org/cgit/aports/tree/testing/bcfg2 ArchLinux ========= Packages for `Arch Linux`_ are available in the Arch User Repository (AUR_). Just use `pacman` to perform the installation :: pacman -S bcfg2 bcfg2-server .. _Arch Linux: http://www.archlinux.org/ .. _AUR: http://aur.archlinux.org/packages.php?ID=20979 Debian ====== Packages of Bcfg2 are available for Debian Lenny, Debian Squeeze, and Debian Sid. The fastest way to get Bcfg2 onto your Debian system is to use ``apt-get`` or ``aptitude``. :: sudo aptitude install bcfg2 bcfg2-server If you want to use unofficial packages from Bcfg2 see the instructions at `CustomDebianRepository`_. .. _CustomDebianRepository: http://trac.mcs.anl.gov/projects/bcfg2/wiki/PrecompiledPackages#UnofficialDebianRepository Fedora ====== The fastest way to get Bcfg2 packages onto your Fedora_ system is to use `yum` or PackageKit. Yum will pull in all dependencies of Bcfg2 automatically. :: su -c 'yum install bcfg2-server bcfg2' Be aware that the latest release of Bcfg2 may only be available for the Development release of Fedora (Rawhide). With the activation of the Rawhide repository of Fedora you will be able to install it. :: su -c 'yum install --enablerepo=rawhide bcfg2-server bcfg2' This way is not recommended on production systems. Only for testing. Gentoo ====== Bcfg2 can be installed via portage. OS X ==== Bcfg2 can be installed either via MacPorts or by creating a native OS X package. MacPorts -------- Once macports is installed:: port install bcfg2 Using native OS X python ------------------------ First, make sure you have Xcode installed as you need ``packagemaker`` which comes bundled in the Developer tools. Clone the git source:: git clone git://git.mcs.anl.gov/bcfg2.git Change to the osx directory and type make. Your new package should be located at ``bcfg2-$VERSION.pkg`` (where ``$VERSION`` is that which is specified in ``setup.py``). RHEL / Centos / Scientific Linux ================================ While you can go about building all these things from source, this section will try and meet the dependencies using packages from EPEL_ [#f1]_. The *el5* and the *el6* package should be compatible with `CentOS`_ 5.x/6.x and `Scientific Linux`_. EPEL_ for 5.x :: [root@centos ~]# rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm EPEL_ for 6.x :: [root@centos ~]# rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-5.noarch.rpm Install the bcfg2-server and bcfg2 RPMs:: [root@centos ~]# yum install bcfg2-server bcfg2 .. note:: The latest package for *el5* is only available in the testing repository. .. [#f1] For more details check the EPEL_ `instructions `_ .. _CentOS: http://www.centos.org/ .. _Scientific Linux: http://www.scientificlinux.org/ .. _EPEL: http://fedoraproject.org/wiki/EPEL Ubuntu ====== We highly recommend following the instructions at `ubuntu-installation`_ in order to install a recent version of Bcfg2 on your system. However, if you would like to install the older package, you can use the following command:: sudo aptitude install bcfg2 bcfg2-server .. _ubuntu-installation: http://trac.mcs.anl.gov/projects/bcfg2/wiki/PrecompiledPackages#UbuntuLaunchpadBcfg2PPA bcfg2-1.3.3/doc/installation/index.txt000066400000000000000000000012231223671746500176320ustar00rootroot00000000000000.. -*- mode: rst -*- .. _installation-index: ============ Installation ============ Before installing, you will need to choose a machine to be the Bcfg2 server. We recommend a Linux-based machine for this purpose, but the server will work on any supported operating system. Note that you may eventually want to run a web server on this machine for reporting and serving up package repositories. The server package only needs to be installed on your designated Bcfg2 server machine. The clients package needs to be installed on any machine you plan to manage by Bcfg2. .. toctree:: :maxdepth: 2 prerequisites source packages distributions bcfg2-1.3.3/doc/installation/packages.txt000066400000000000000000000045611223671746500203110ustar00rootroot00000000000000.. -*- mode: rst -*- .. _packages: .. _CentOS: http://www.centos.org/ .. _Red Hat/RHEL: http://www.redhat.com/rhel/ .. _Scientific Linux: http://www.scientificlinux.org/ .. _EPEL: http://fedoraproject.org/wiki/EPEL .. _RPMForge: https://rpmrepo.org/RPMforge Building RPM packages from source ================================= The Bcfg2 distribution contains two different spec files. Building from Tarball --------------------- * Copy the tarball to ``/usr/src/packages/SOURCES/`` * Extract another copy of it somewhere else (eg: ``/tmp``) and retrieve the ``misc/bcfg2.spec`` file * Run :: rpmbuild -ba bcfg2.spec * The resulting RPMs will be in ``/usr/src/packages/RPMS/`` and SRPMs in ``/usr/src/packages/SRPMS`` Building from an GIT Checkout ----------------------------- * Change to the ``redhat/`` directory in the working copy * Run :: make * The resulting RPMs will be in ``/usr/src/redhat/RPMS/`` and SRPMs in ``/usr/src/redhat/SRPMS`` and will have the SVN revision appended Building RPM packages with ``rpmbuild`` --------------------------------------- While you can go about building all these things from source, this how to will try and meet the dependencies using packages from EPEL_. The *el5* and the *el6* package should be compatible with CentOS 5.x. * Installation of the EPEL_ repository package :: [root@centos ~]# rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-6.noarch.rpm * Now you can install the rest of the prerequisites :: [root@centos ~]# yum install python-genshi python-cheetah python-lxml * After installing git, check out the master branch :: [root@centos redhat]# git clone git://git.mcs.anl.gov/bcfg2.git * Install the ``fedora-packager`` package :: [root@centos ~]# yum install fedora-packager * A directory structure for the RPM build process has to be established. :: [you@centos ~]$ rpmdev-setuptree * Change to the *redhat* directory of the checked out Bcfg2 source:: [you@centos ~]$ cd bcfg2/redhat/ * In the particular directory is a ``Makefile`` which will do the job of building the RPM packages. You can do this as root, but it's not recommended:: [you@centos redhat]$ make * Now the new RPM package can be installed. Please adjust the path to your RPM package :: [root@centos ~]# rpm -ihv /home/YOU/rpmbuild/RPMS/noarch/bcfg2-server-1.0.0-0.2r5835.noarch.rpm bcfg2-1.3.3/doc/installation/prerequisites.txt000066400000000000000000000067741223671746500214470ustar00rootroot00000000000000.. -*- mode: rst -*- .. _installation-prerequisites: Prerequisites ============= Bcfg2 has several server side prerequisites and a minimal set of client side requirements. This page describes the prerequisite software situation on all supported platforms. The table describes what software is needed on the client and server side. Bcfg2 Client ------------ +----------------------------+------------------------+--------------------------------+ | Software | Version | Requires | +============================+========================+================================+ | libxml2 (if lxml is used) | Any | | +----------------------------+------------------------+--------------------------------+ | libxslt (if lxml is used) | Any | libxml2 | +----------------------------+------------------------+--------------------------------+ | python | 2.4 and greater [#f1] | | +----------------------------+------------------------+--------------------------------+ | lxml or elementtree [#f2]_ | Any | lxml: libxml2, libxslt, python | +----------------------------+------------------------+--------------------------------+ | python-apt [#f3]_ | Any | python | +----------------------------+------------------------+--------------------------------+ | debsums (if APT tool | Any | | | driver is used) | | | +----------------------------+------------------------+--------------------------------+ .. [#f1] python 2.5 and later works with elementtree. .. [#f2] elementtree is included in python 2.5 and later. .. [#f3] python-apt is only required on platforms that use apt, such as Debian and Ubuntu. Bcfg2 Server ------------ +-------------------------------+----------+--------------------------------+ | Software | Version | Requires | +===============================+==========+================================+ | libxml2 | 2.6.24+ | | +-------------------------------+----------+--------------------------------+ | libxslt | Any | libxml2 | +-------------------------------+----------+--------------------------------+ | python | 2.2-2.7 | | +-------------------------------+----------+--------------------------------+ | lxml | 0.9+ | lxml: libxml2, libxslt, python | +-------------------------------+----------+--------------------------------+ | gamin or fam | Any | | +-------------------------------+----------+--------------------------------+ | python-gamin or python-fam | Any | gamin or fam, python | +-------------------------------+----------+--------------------------------+ | M2crypto or python-ssl (note | Any | python, openssl | | that the ssl module is | | | | included in python versions | | | | 2.6 and later | | | +-------------------------------+----------+--------------------------------+ bcfg2-1.3.3/doc/installation/source.txt000066400000000000000000000023361223671746500200310ustar00rootroot00000000000000.. -*- mode: rst -*- .. _GPG1: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x75BF2C177F7D197E .. _GPG2: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x80B8492FA88FFF4B .. _Download: http://trac.mcs.anl.gov/projects/bcfg2/wiki/Download .. _source: Installation from source ======================== Download -------- Tarball ^^^^^^^ The Bcfg2 source tarball can be grabbed from the `Download`_ page. All tarballs are signed with GPG keys `7F7D197E `_ or `A88FFF4B `_. You can verify your download by importing the keys and running :: gpg --recv-keys 0x75bf2c177f7d197e 0x80B8492FA88FFF4B gpg --verify bcfg2-.tar.gz.gpg bcfg2-.tar.gz Git checkout ^^^^^^^^^^^^ You can also get the latest (possibly broken) code via git :: git clone git://git.mcs.anl.gov/bcfg2.git Install ------- If you are working with the release tarball of Bcfg2 you need to untar it before you can go on with the installation :: tar -xzf bcfg2-.tar.gz Now you can build Bcfg2 with. If you are working from a git clone no need to be specified. :: cd bcfg2- python setup.py install --prefix=/install/prefix This will install both the client and server on that machine. bcfg2-1.3.3/doc/intersphinx/000077500000000000000000000000001223671746500156365ustar00rootroot00000000000000bcfg2-1.3.3/doc/intersphinx/cherrypy/000077500000000000000000000000001223671746500175035ustar00rootroot00000000000000bcfg2-1.3.3/doc/intersphinx/cherrypy/objects.inv000066400000000000000000001426161223671746500216640ustar00rootroot00000000000000# Sphinx inventory version 1 # Project: CherryPy # Version: 3.2.0 cherrypy.wsgiserver.ssl_pyopenssl mod refman/wsgiserver/ssl_pyopenssl.html cherrypy mod refman/cherrypy.html cherrypy.lib.encoding mod refman/lib/encoding.html cherrypy.wsgiserver mod refman/wsgiserver/init.html cherrypy.lib.cptools mod refman/lib/cptools.html cherrypy._cpconfig mod refman/_cpconfig.html cherrypy.lib.jsontools mod refman/lib/jsontools.html cherrypy.lib.xmlrpc mod refman/lib/xmlrpc.html cherrypy._cpreqbody mod refman/_cpreqbody.html cherrypy._cptools mod refman/_cptools.html cherrypy.lib.profiler mod refman/lib/profiler.html cherrypy.lib.caching mod refman/lib/caching.html cherrypy._cprequest mod refman/_cprequest.html cherrypy.process.servers mod refman/process/servers.html cherrypy.process.plugins mod refman/process/plugins/index.html cherrypy.lib.sessions mod refman/lib/sessions.html cherrypy.process.wspbus mod refman/process/wspbus.html cherrypy.lib.httpauth mod refman/lib/httpauth.html cherrypy.lib.auth_digest mod refman/lib/auth_digest.html cherrypy.lib.auth mod refman/lib/auth.html cherrypy.wsgiserver.ssl_builtin mod refman/wsgiserver/ssl_builtin.html cherrypy._cpdispatch mod refman/_cpdispatch.html cherrypy._cpchecker mod refman/_cpchecker.html cherrypy.lib.reprconf mod refman/lib/reprconf.html cherrypy._cpserver mod refman/_cpserver.html cherrypy.lib.auth_basic mod refman/lib/auth_basic.html cherrypy._cpwsgi mod refman/_cpwsgi.html cherrypy._cptree mod refman/_cptree.html cherrypy.lib.static mod refman/lib/static.html cherrypy._cplogging mod refman/_cplogging.html cherrypy.lib.covercp mod refman/lib/covercp.html cherrypy.lib.httputil mod refman/lib/httputil.html cherrypy._cperror mod refman/_cperror.html cherrypy.url function refman/cherrypy.html cherrypy._cplogging.LogManager.error method refman/_cplogging.html cherrypy._cptree.Application.release_serving method refman/_cptree.html cherrypy.lib.httputil.header_elements function refman/lib/httputil.html cherrypy.process.plugins.DropPrivileges.umask attribute refman/process/plugins/dropprivileges.html cherrypy._cpreqbody.Entity.fullvalue method refman/_cpreqbody.html cherrypy.lib.sessions.FileSession.setup classmethod refman/lib/sessions.html cherrypy.process.plugins.Monitor.start method refman/process/plugins/index.html cherrypy.lib.cptools.SessionAuth.do_check method refman/lib/cptools.html cherrypy._cpreqbody.Entity.attempt_charsets attribute refman/_cpreqbody.html cherrypy.lib.sessions.MemcachedSession.acquire_lock method refman/lib/sessions.html cherrypy.wsgiserver.HTTPRequest.ready attribute refman/wsgiserver/init.html cherrypy.lib.sessions.MemcachedSession.has_key method refman/lib/sessions.html cherrypy.lib.httputil.AcceptElement.qvalue attribute refman/lib/httputil.html cherrypy._cprequest.Request.method attribute refman/_cprequest.html cherrypy.wsgiserver.WSGIGateway_u0.get_environ method refman/wsgiserver/init.html cherrypy.HTTPError.set_response method refman/cherrypy.html cherrypy._cpserver.Server.base method refman/_cpserver.html cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter.get_environ method refman/wsgiserver/ssl_builtin.html cherrypy.lib.httputil.protocol_from_http function refman/lib/httputil.html cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter.wrap method refman/wsgiserver/ssl_pyopenssl.html cherrypy.process.servers.FlupFCGIServer class refman/process/servers.html cherrypy.lib.sessions.PostgresqlSession.values method refman/lib/sessions.html cherrypy.process.plugins.Autoreloader class refman/process/plugins/index.html cherrypy.lib.reprconf.Config.reset method refman/lib/reprconf.html cherrypy.lib.sessions.RamSession.acquire_lock method refman/lib/sessions.html cherrypy._cpserver.Server.ssl_context attribute refman/_cpserver.html cherrypy.process.servers.client_host function refman/process/servers.html cherrypy.lib.sessions.MemcachedSession.items method refman/lib/sessions.html cherrypy.lib.sessions.MemcachedSession.pop method refman/lib/sessions.html cherrypy.lib.sessions.FileSession.clean_up method refman/lib/sessions.html cherrypy.lib.sessions.MemcachedSession.setdefault method refman/lib/sessions.html cherrypy._cprequest.Response class refman/_cprequest.html cherrypy.wsgiserver.HTTPRequest.outheaders attribute refman/wsgiserver/init.html cherrypy.process.plugins.Monitor class refman/process/plugins/index.html cherrypy.lib.sessions.Session.timeout attribute refman/lib/sessions.html cherrypy._cpreqbody.Entity.headers attribute refman/_cpreqbody.html cherrypy._cpchecker.Checker.check_compatibility method refman/_cpchecker.html cherrypy.wsgiserver.HTTPServer.interrupt attribute refman/wsgiserver/init.html cherrypy.lib.sessions.FileSession.generate_id method refman/lib/sessions.html cherrypy._cpconfig.Config.reset method refman/_cpconfig.html cherrypy._cprequest.Request.error_response attribute refman/_cprequest.html cherrypy.lib.sessions.MemcachedSession.values method refman/lib/sessions.html cherrypy.lib.sessions.FileSession.acquire_lock method refman/lib/sessions.html cherrypy.lib.jsontools.json_out function refman/lib/jsontools.html cherrypy.lib.sessions.MemcachedSession.clean_up method refman/lib/sessions.html cherrypy.lib.reprconf.unrepr function refman/lib/reprconf.html cherrypy._cprequest.Request.run method refman/_cprequest.html cherrypy.lib.cptools.SessionAuth.do_logout method refman/lib/cptools.html cherrypy.wsgiserver.FatalSSLAlert class refman/wsgiserver/init.html cherrypy.lib.static.serve_download function refman/lib/static.html cherrypy.lib.sessions.close function refman/lib/sessions.html cherrypy._cprequest.Request.header_list attribute refman/_cprequest.html cherrypy.wsgiserver.HTTPRequest.chunked_write attribute refman/wsgiserver/init.html cherrypy._cpreqbody.Part.process method refman/_cpreqbody.html cherrypy.lib.auth_digest.digest_auth function refman/lib/auth_digest.html cherrypy._cplogging.LogManager.error_file attribute refman/_cplogging.html cherrypy.process.wspbus.Bus.stop method refman/process/wspbus.html cherrypy.lib.jsontools.json_processor function refman/lib/jsontools.html cherrypy.wsgiserver.HTTPServer.max_request_header_size attribute refman/wsgiserver/init.html cherrypy.process.wspbus.ChannelFailures class refman/process/wspbus.html cherrypy.lib.encoding.ResponseEncoder.encode_stream method refman/lib/encoding.html cherrypy.lib.caching.MemoryCache.maxsize attribute refman/lib/caching.html cherrypy._cpconfig.Config.iterkeys attribute refman/_cpconfig.html cherrypy.wsgiserver.HTTPConnection.close method refman/wsgiserver/init.html cherrypy.lib.httpauth.parseAuthorization function refman/lib/httpauth.html cherrypy._cptree.Application.request_class attribute refman/_cptree.html cherrypy.wsgiserver.HTTPServer.nodelay attribute refman/wsgiserver/init.html cherrypy.wsgiserver.HTTPServer.timeout attribute refman/wsgiserver/init.html cherrypy.lib.sessions.MemcachedSession.load method refman/lib/sessions.html cherrypy._cpconfig.Config.iteritems attribute refman/_cpconfig.html cherrypy._cprequest.Request.dispatch attribute refman/_cprequest.html cherrypy.lib.caching.Cache class refman/lib/caching.html cherrypy.process.wspbus.ChannelFailures.get_instances method refman/process/wspbus.html cherrypy.lib.profiler.Profiler class refman/lib/profiler.html cherrypy._cprequest.Response.body attribute refman/_cprequest.html cherrypy._cprequest.Request.config attribute refman/_cprequest.html cherrypy.wsgiserver.HTTPRequest.inheaders attribute refman/wsgiserver/init.html cherrypy._cplogging.LogManager.error_log attribute refman/_cplogging.html cherrypy.process.servers.check_port function refman/process/servers.html cherrypy.wsgiserver.HTTPServer.bind method refman/wsgiserver/init.html cherrypy._cpdispatch.RoutesDispatcher.find_handler method refman/_cpdispatch.html cherrypy.lib.sessions.FileSession.regenerate method refman/lib/sessions.html cherrypy._cprequest.Request class refman/_cprequest.html cherrypy.process.plugins.ThreadManager class refman/process/plugins/index.html cherrypy.lib.sessions.Session.originalid attribute refman/lib/sessions.html cherrypy._cprequest.Request.login attribute refman/_cprequest.html cherrypy.process.plugins.BackgroundTask class refman/process/plugins/index.html cherrypy._cpreqbody.SizedReader.read method refman/_cpreqbody.html cherrypy._cpreqbody.Entity class refman/_cpreqbody.html cherrypy._cpserver.Server.shutdown_timeout attribute refman/_cpserver.html cherrypy._cplogging.LogManager.wsgi attribute refman/_cplogging.html cherrypy.wsgiserver.HTTPConnection class refman/wsgiserver/init.html cherrypy.lib.sessions.MemcachedSession.get method refman/lib/sessions.html cherrypy.process.servers.FlupSCGIServer class refman/process/servers.html cherrypy.process.servers.ServerAdapter.start method refman/process/servers.html cherrypy._cpreqbody.Part.read_lines_to_boundary method refman/_cpreqbody.html cherrypy.process.plugins.Monitor.stop method refman/process/plugins/index.html cherrypy._cprequest.Request.app attribute refman/_cprequest.html cherrypy._cprequest.Response.collapse_body method refman/_cprequest.html cherrypy._cpwsgi.AppResponse class refman/_cpwsgi.html cherrypy.wsgiserver.HTTPServer.version attribute refman/wsgiserver/init.html cherrypy.lib.sessions.MemcachedSession class refman/lib/sessions.html cherrypy.process.wspbus.Bus.exit method refman/process/wspbus.html cherrypy.HTTPError.reason attribute refman/cherrypy.html cherrypy._cpserver.Server.nodelay attribute refman/_cpserver.html cherrypy._cprequest.Request.methods_with_bodies attribute refman/_cprequest.html cherrypy.wsgiserver.HTTPRequest.parse_request method refman/wsgiserver/init.html cherrypy.lib.caching.MemoryCache class refman/lib/caching.html cherrypy.lib.encoding.gzip function refman/lib/encoding.html cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter.get_context method refman/wsgiserver/ssl_pyopenssl.html cherrypy.lib.reprconf.Config.update method refman/lib/reprconf.html cherrypy._cprequest.Hook.priority attribute refman/_cprequest.html cherrypy._cptools.CachingTool class refman/_cptools.html cherrypy._cpserver.Server.socket_port attribute refman/_cpserver.html cherrypy.lib.profiler.ProfileAggregator class refman/lib/profiler.html cherrypy.lib.httputil.HeaderElement.parse staticmethod refman/lib/httputil.html cherrypy.lib.auth_digest.HttpDigestAuthorization.is_nonce_stale method refman/lib/auth_digest.html cherrypy.lib.sessions.FileSession.save method refman/lib/sessions.html cherrypy.lib.auth_digest.HttpDigestAuthorization.HA2 method refman/lib/auth_digest.html cherrypy._cprequest.Response.timeout attribute refman/_cprequest.html cherrypy.lib.sessions.Session.regenerate method refman/lib/sessions.html cherrypy.lib.static.serve_file function refman/lib/static.html cherrypy.lib.sessions.RamSession.generate_id method refman/lib/sessions.html cherrypy.process.plugins.Daemonizer class refman/process/plugins/daemonizer.html cherrypy.lib.sessions.Session.update method refman/lib/sessions.html cherrypy.lib.sessions.PostgresqlSession class refman/lib/sessions.html cherrypy.lib.caching.MemoryCache.delay attribute refman/lib/caching.html cherrypy._cpreqbody.Entity.default_content_type attribute refman/_cpreqbody.html cherrypy._cprequest.Request.local attribute refman/_cprequest.html cherrypy.HTTPError.code attribute refman/cherrypy.html cherrypy._cpreqbody.Entity.filename attribute refman/_cpreqbody.html cherrypy.process.wspbus.Bus.log method refman/process/wspbus.html cherrypy.HTTPRedirect.set_response method refman/cherrypy.html cherrypy.wsgiserver.WSGIGateway class refman/wsgiserver/init.html cherrypy._cpreqbody.Part.attempt_charsets attribute refman/_cpreqbody.html cherrypy._cprequest.Request.error_page attribute refman/_cprequest.html cherrypy._cpconfig.Config.clear attribute refman/_cpconfig.html cherrypy.lib.caching.Cache.get method refman/lib/caching.html cherrypy._cpreqbody.Entity.length attribute refman/_cpreqbody.html cherrypy.wsgiserver.HTTPRequest.server attribute refman/wsgiserver/init.html cherrypy.lib.caching.expires function refman/lib/caching.html cherrypy.wsgiserver.WorkerThread.server attribute refman/wsgiserver/init.html cherrypy._cpchecker.Checker.on attribute refman/_cpchecker.html cherrypy._cpserver.Server.instance attribute refman/_cpserver.html cherrypy._cpwsgi.CPWSGIApp.response_class attribute refman/_cpwsgi.html cherrypy.lib.profiler.make_app class refman/lib/profiler.html cherrypy._cprequest.Request.stage attribute refman/_cprequest.html cherrypy.process.plugins.Autoreloader.files attribute refman/process/plugins/index.html cherrypy.wsgiserver.WorkerThread.conn attribute refman/wsgiserver/init.html cherrypy._cpconfig.merge function refman/_cpconfig.html cherrypy._cptools.Toolbox class refman/_cptools.html cherrypy._cprequest.Response.time attribute refman/_cprequest.html cherrypy._cpreqbody.RequestBody.type attribute refman/_cpreqbody.html cherrypy._cperror.NotFound class refman/_cperror.html cherrypy.lib.caching.tee_output function refman/lib/caching.html cherrypy.lib.cptools.session_auth function refman/lib/cptools.html cherrypy._cpreqbody.Part.part_class attribute refman/_cpreqbody.html cherrypy.lib.auth_digest.HttpDigestAuthorization.validate_nonce method refman/lib/auth_digest.html cherrypy.process.plugins.DropPrivileges.gid attribute refman/process/plugins/dropprivileges.html cherrypy.wsgiserver.HTTPRequest.write method refman/wsgiserver/init.html cherrypy.lib.caching.get function refman/lib/caching.html cherrypy._cpreqbody.Part.fullvalue method refman/_cpreqbody.html cherrypy.wsgiserver.HTTPServer.gateway attribute refman/wsgiserver/init.html cherrypy._cprequest.Request.params attribute refman/_cprequest.html cherrypy.lib.httputil.valid_status function refman/lib/httputil.html cherrypy._cprequest.Response.check_timeout method refman/_cprequest.html cherrypy.lib.sessions.PostgresqlSession.pop method refman/lib/sessions.html cherrypy._cplogging.LogManager.time method refman/_cplogging.html cherrypy.lib.sessions.MemcachedSession.clear method refman/lib/sessions.html cherrypy.lib.sessions.RamSession.id attribute refman/lib/sessions.html cherrypy._cpwsgi.VirtualHost.use_x_forwarded_host attribute refman/_cpwsgi.html cherrypy.lib.sessions.RamSession.release_lock method refman/lib/sessions.html cherrypy.process.servers.ServerAdapter.wait method refman/process/servers.html cherrypy.lib.sessions.RamSession.pop method refman/lib/sessions.html cherrypy._cpreqbody.RequestBody class refman/_cpreqbody.html cherrypy.lib.sessions.MemcachedSession.setup classmethod refman/lib/sessions.html cherrypy._cpreqbody.Part.default_proc method refman/_cpreqbody.html cherrypy.lib.httputil.CaseInsensitiveDict class refman/lib/httputil.html cherrypy.process.plugins.ThreadManager.acquire_thread method refman/process/plugins/index.html cherrypy.lib.sessions.FileSession.load method refman/lib/sessions.html cherrypy._cpdispatch.VirtualHost class refman/_cpdispatch.html cherrypy.process.plugins.Autoreloader.stop method refman/process/plugins/index.html cherrypy.lib.sessions.FileSession.id attribute refman/lib/sessions.html cherrypy._cpreqbody.SizedReader.readline method refman/_cpreqbody.html cherrypy.wsgiserver.WSGIGateway_u0 class refman/wsgiserver/init.html cherrypy.wsgiserver.KnownLengthRFile class refman/wsgiserver/init.html cherrypy.lib.sessions.PostgresqlSession.acquire_lock method refman/lib/sessions.html cherrypy.process.plugins.DropPrivileges.uid attribute refman/process/plugins/dropprivileges.html cherrypy.lib.encoding.compress function refman/lib/encoding.html cherrypy._cprequest.Response.stream attribute refman/_cprequest.html cherrypy._cpconfig.Config.popitem attribute refman/_cpconfig.html cherrypy._cprequest.Request.script_name attribute refman/_cprequest.html cherrypy._cplogging.LogManager.logger_root attribute refman/_cplogging.html cherrypy.lib.sessions.Session.has_key method refman/lib/sessions.html cherrypy._cprequest.Request.headers attribute refman/_cprequest.html cherrypy._cpreqbody.RequestBody.part_class attribute refman/_cpreqbody.html cherrypy._cpwsgi.VirtualHost.default attribute refman/_cpwsgi.html cherrypy._cplogging.WSGIErrorHandler.emit method refman/_cplogging.html cherrypy.lib.sessions.Session class refman/lib/sessions.html cherrypy.lib.auth_basic.checkpassword_dict function refman/lib/auth_basic.html cherrypy._cpreqbody.Entity.processors attribute refman/_cpreqbody.html cherrypy.lib.httpauth.basicAuth function refman/lib/httpauth.html cherrypy.lib.xmlrpc.on_error function refman/lib/xmlrpc.html cherrypy.wsgiserver.HTTPServer.tick method refman/wsgiserver/init.html cherrypy._cpserver.Server.protocol_version attribute refman/_cpserver.html cherrypy.lib.sessions.MemcachedSession.regenerate method refman/lib/sessions.html cherrypy._cprequest.Request.body attribute refman/_cprequest.html cherrypy.process.servers.FlupFCGIServer.start method refman/process/servers.html cherrypy._cpwsgi.CPWSGIApp.head attribute refman/_cpwsgi.html cherrypy.lib.sessions.PostgresqlSession.release_lock method refman/lib/sessions.html cherrypy.process.servers.FlupSCGIServer.start method refman/process/servers.html cherrypy._cpconfig.Config.setdefault attribute refman/_cpconfig.html cherrypy._cpreqbody.RequestBody.default_proc method refman/_cpreqbody.html cherrypy._cprequest.Request.is_index attribute refman/_cprequest.html cherrypy.lib.sessions.FileSession.get method refman/lib/sessions.html cherrypy._cprequest.Request.close method refman/_cprequest.html cherrypy._cptools.SessionTool.regenerate method refman/_cptools.html cherrypy._cpserver.Server class refman/_cpserver.html cherrypy.process.plugins.DropPrivileges class refman/process/plugins/dropprivileges.html cherrypy.wsgiserver.HTTPRequest.respond method refman/wsgiserver/init.html cherrypy.process.wspbus.Bus.start_with_callback method refman/process/wspbus.html cherrypy.lib.caching.MemoryCache.maxobjects attribute refman/lib/caching.html cherrypy._cpserver.Server.socket_host attribute refman/_cpserver.html cherrypy.process.servers.wait_for_free_port function refman/process/servers.html cherrypy.lib.auth_basic.basic_auth function refman/lib/auth_basic.html cherrypy._cprequest.HookMap.attach method refman/_cprequest.html cherrypy._cpreqbody.RequestBody.make_file method refman/_cpreqbody.html cherrypy.lib.sessions.init function refman/lib/sessions.html cherrypy.lib.auth_digest.synthesize_nonce function refman/lib/auth_digest.html cherrypy.lib.httputil.Host class refman/lib/httputil.html cherrypy._cpdispatch.Dispatcher class refman/_cpdispatch.html cherrypy._cptree.Application.response_class attribute refman/_cptree.html cherrypy.process.plugins.Autoreloader.sysfiles method refman/process/plugins/index.html cherrypy._cptree.Application.get_serving method refman/_cptree.html cherrypy.lib.sessions.RamSession.delete method refman/lib/sessions.html cherrypy._cpreqbody.Entity.parts attribute refman/_cpreqbody.html cherrypy.lib.httputil.HeaderElement class refman/lib/httputil.html cherrypy._cpchecker.Checker.check_config_namespaces method refman/_cpchecker.html cherrypy._cptools.SessionTool class refman/_cptools.html cherrypy.lib.cptools.accept function refman/lib/cptools.html cherrypy._cprequest.Request.protocol attribute refman/_cprequest.html cherrypy.lib.sessions.Session.setdefault method refman/lib/sessions.html cherrypy._cprequest.Request.show_tracebacks attribute refman/_cprequest.html cherrypy.lib.sessions.FileSession.clear method refman/lib/sessions.html cherrypy.lib.sessions.RamSession.clean_up method refman/lib/sessions.html cherrypy.lib.sessions.FileSession.values method refman/lib/sessions.html cherrypy.lib.sessions.PostgresqlSession.get method refman/lib/sessions.html cherrypy.lib.jsontools.json_handler function refman/lib/jsontools.html cherrypy.lib.cptools.validate_since function refman/lib/cptools.html cherrypy.lib.httputil.urljoin function refman/lib/httputil.html cherrypy._cprequest.Request.remote attribute refman/_cprequest.html cherrypy.process.wspbus.Bus.restart method refman/process/wspbus.html cherrypy.process.wspbus.Bus.publish method refman/process/wspbus.html cherrypy._cprequest.Request.closed attribute refman/_cprequest.html cherrypy._cprequest.Request.process_query_string method refman/_cprequest.html cherrypy._cptools.XMLRPCController class refman/_cptools.html cherrypy.process.wspbus.Bus.block method refman/process/wspbus.html cherrypy.wsgiserver.ssl_pyopenssl.SSLConnection class refman/wsgiserver/ssl_pyopenssl.html cherrypy.wsgiserver.WSGIGateway.get_environ method refman/wsgiserver/init.html cherrypy._cprequest.Request.hooks attribute refman/_cprequest.html cherrypy._cplogging.LogManager.screen attribute refman/_cplogging.html cherrypy.lib.cptools.log_traceback function refman/lib/cptools.html cherrypy.HTTPError.status attribute refman/cherrypy.html cherrypy._cpdispatch.XMLRPCDispatcher class refman/_cpdispatch.html cherrypy.lib.sessions.RamSession.clear method refman/lib/sessions.html cherrypy.lib.sessions.Session.get method refman/lib/sessions.html cherrypy.lib.sessions.Session.clean_freq attribute refman/lib/sessions.html cherrypy._cpwsgi.InternalRedirector class refman/_cpwsgi.html cherrypy._cptree.Application.script_name attribute refman/_cptree.html cherrypy.wsgiserver.HTTPRequest.read_request_headers method refman/wsgiserver/init.html cherrypy.lib.sessions.Session.missing attribute refman/lib/sessions.html cherrypy._cptools.HandlerWrapperTool class refman/_cptools.html cherrypy.process.plugins.ThreadManager.graceful method refman/process/plugins/index.html cherrypy._cptree.Tree.mount method refman/_cptree.html cherrypy._cprequest.Request.rfile attribute refman/_cprequest.html cherrypy.wsgiserver.WSGIGateway_10.get_environ method refman/wsgiserver/init.html cherrypy.lib.covercp.CoverStats class refman/lib/covercp.html cherrypy._cpchecker.Checker.check_localhost method refman/_cpchecker.html cherrypy._cprequest.Hook.failsafe attribute refman/_cprequest.html cherrypy.lib.sessions.expire function refman/lib/sessions.html cherrypy.process.plugins.Monitor.thread attribute refman/process/plugins/index.html cherrypy.lib.xmlrpc.process_body function refman/lib/xmlrpc.html cherrypy._cperror.HTTPError.reason attribute refman/_cperror.html cherrypy._cpdispatch.Dispatcher.find_handler method refman/_cpdispatch.html cherrypy.process.plugins.Monitor.frequency attribute refman/process/plugins/index.html cherrypy.lib.cptools.MonitoredHeaderMap class refman/lib/cptools.html cherrypy.quickstart function refman/cherrypy.html cherrypy.process.plugins.Autoreloader.unsubscribe method refman/process/plugins/index.html cherrypy.lib.httpauth.calculateNonce function refman/lib/httpauth.html cherrypy._cpreqbody.process_urlencoded function refman/_cpreqbody.html cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter.private_key attribute refman/wsgiserver/ssl_pyopenssl.html cherrypy.process.plugins.PerpetualTimer class refman/process/plugins/index.html cherrypy._cpreqbody.Part.maxrambytes attribute refman/_cpreqbody.html cherrypy._cprequest.Response.timed_out attribute refman/_cprequest.html cherrypy._cpreqbody.Entity.name attribute refman/_cpreqbody.html cherrypy._cpconfig.Config.pop attribute refman/_cpconfig.html cherrypy.lib.caching.MemoryCache.maxobj_size attribute refman/lib/caching.html cherrypy.lib.sessions.PostgresqlSession.generate_id method refman/lib/sessions.html cherrypy._cptree.Tree.apps attribute refman/_cptree.html cherrypy._cptree.Application.config attribute refman/_cptree.html cherrypy.lib.sessions.PostgresqlSession.save method refman/lib/sessions.html cherrypy._cpchecker.Checker.check_app_config_brackets method refman/_cpchecker.html cherrypy._cpconfig.Config.copy attribute refman/_cpconfig.html cherrypy.HTTPError class refman/cherrypy.html cherrypy.lib.cptools.log_hooks function refman/lib/cptools.html cherrypy.lib.encoding.ResponseEncoder class refman/lib/encoding.html cherrypy._cprequest.Response.cookie attribute refman/_cprequest.html cherrypy.process.wspbus.Bus.start method refman/process/wspbus.html cherrypy.lib.profiler.new_func_strip_path function refman/lib/profiler.html cherrypy.wsgiserver.SizeCheckWrapper class refman/wsgiserver/init.html cherrypy.wsgiserver.HTTPServer.ConnectionClass attribute refman/wsgiserver/init.html cherrypy.wsgiserver.WSGIGateway.start_response method refman/wsgiserver/init.html cherrypy.lib.auth_digest.www_authenticate function refman/lib/auth_digest.html cherrypy.lib.cptools.trailing_slash function refman/lib/cptools.html cherrypy.lib.sessions.FileSession.pop method refman/lib/sessions.html cherrypy.lib.sessions.RamSession.values method refman/lib/sessions.html cherrypy.lib.cptools.log_request_headers function refman/lib/cptools.html cherrypy.lib.sessions.Session.items method refman/lib/sessions.html cherrypy.lib.sessions.Session.values method refman/lib/sessions.html cherrypy.lib.auth_digest.get_ha1_file_htdigest function refman/lib/auth_digest.html cherrypy.lib.sessions.Session.save method refman/lib/sessions.html cherrypy.lib.httputil.HeaderElement.from_str classmethod refman/lib/httputil.html cherrypy.lib.sessions.FileSession.update method refman/lib/sessions.html cherrypy.wsgiserver.HTTPRequest.parse_request_uri method refman/wsgiserver/init.html cherrypy.process.plugins.Autoreloader.start method refman/process/plugins/index.html cherrypy._cpreqbody.Entity.make_file method refman/_cpreqbody.html cherrypy._cpserver.Server.socket_queue_size attribute refman/_cpserver.html cherrypy.lib.sessions.Session.delete method refman/lib/sessions.html cherrypy.process.plugins.ThreadManager.stop method refman/process/plugins/index.html cherrypy.lib.sessions.Session.pop method refman/lib/sessions.html cherrypy.process.servers.FlupFCGIServer.stop method refman/process/servers.html cherrypy.lib.encoding.decompress function refman/lib/encoding.html cherrypy._cperror.HTTPRedirect.encoding attribute refman/_cperror.html cherrypy.wsgiserver.HTTPServer.protocol attribute refman/wsgiserver/init.html cherrypy._cpreqbody.Part.default_content_type attribute refman/_cpreqbody.html cherrypy.lib.sessions.FileSession.keys method refman/lib/sessions.html cherrypy._cpreqbody.process_multipart_form_data function refman/_cpreqbody.html cherrypy.lib.httputil.HeaderMap.encode method refman/lib/httputil.html cherrypy._cpreqbody.Part class refman/_cpreqbody.html cherrypy.lib.auth_digest.HttpDigestAuthorization class refman/lib/auth_digest.html cherrypy.process.servers.FlupSCGIServer.stop method refman/process/servers.html cherrypy.lib.sessions.Session.clear method refman/lib/sessions.html cherrypy.lib.auth.digest_auth function refman/lib/auth.html cherrypy.lib.caching.MemoryCache.antistampede_timeout attribute refman/lib/caching.html cherrypy._cpreqbody.Entity.content_type attribute refman/_cpreqbody.html cherrypy.lib.xmlrpc.respond function refman/lib/xmlrpc.html cherrypy.process.wspbus.Bus.graceful method refman/process/wspbus.html cherrypy.lib.sessions.RamSession.items method refman/lib/sessions.html cherrypy._cpreqbody.Entity.type attribute refman/_cpreqbody.html cherrypy.lib.reprconf.attributes function refman/lib/reprconf.html cherrypy.lib.cptools.redirect function refman/lib/cptools.html cherrypy.wsgiserver.HTTPServer.bind_addr attribute refman/wsgiserver/init.html cherrypy._cpconfig.Config.update method refman/_cpconfig.html cherrypy.lib.cptools.ignore_headers function refman/lib/cptools.html cherrypy._cptools.HandlerTool.handler method refman/_cptools.html cherrypy._cpreqbody.SizedReader.readlines method refman/_cpreqbody.html cherrypy._cprequest.Request.handler attribute refman/_cprequest.html cherrypy._cpwsgi.VirtualHost class refman/_cpwsgi.html cherrypy.lib.cptools.response_headers function refman/lib/cptools.html cherrypy._cperror.HTTPError class refman/_cperror.html cherrypy.process.plugins.Autoreloader.run method refman/process/plugins/index.html cherrypy._cptools.Tool class refman/_cptools.html cherrypy.wsgiserver.HTTPRequest.send_headers method refman/wsgiserver/init.html cherrypy.lib.cptools.flatten function refman/lib/cptools.html cherrypy._cpconfig.Config.has_key attribute refman/_cpconfig.html cherrypy._cplogging.WSGIErrorHandler class refman/_cplogging.html cherrypy.lib.auth.basic_auth function refman/lib/auth.html cherrypy._cperror.TimeoutError class refman/_cperror.html cherrypy.lib.httputil.AcceptElement class refman/lib/httputil.html cherrypy.wsgiserver.HTTPServer.maxthreads attribute refman/wsgiserver/init.html cherrypy.lib.sessions.set_response_cookie function refman/lib/sessions.html cherrypy.lib.auth_digest.get_ha1_dict function refman/lib/auth_digest.html cherrypy.wsgiserver.HTTPServer.minthreads attribute refman/wsgiserver/init.html cherrypy._cpwsgi.CPWSGIApp.namespace_handler method refman/_cpwsgi.html cherrypy.lib.reprconf.NamespaceSet class refman/lib/reprconf.html cherrypy._cpwsgi.AppResponse.close method refman/_cpwsgi.html cherrypy.lib.httputil.HeaderMap.values method refman/lib/httputil.html cherrypy.lib.auth.check_auth function refman/lib/auth.html cherrypy._cpreqbody.Part.type attribute refman/_cpreqbody.html cherrypy.wsgiserver.HTTPServer class refman/wsgiserver/init.html cherrypy.lib.static.staticfile function refman/lib/static.html cherrypy._cpdispatch.PageHandler class refman/_cpdispatch.html cherrypy._cpreqbody.Entity.default_proc method refman/_cpreqbody.html cherrypy.wsgiserver.HTTPServer.stop method refman/wsgiserver/init.html cherrypy._cpreqbody.RequestBody.bufsize attribute refman/_cpreqbody.html cherrypy._cpreqbody.Part.read_into_file method refman/_cpreqbody.html cherrypy.lib.sessions.MemcachedSession.release_lock method refman/lib/sessions.html cherrypy.lib.httputil.get_ranges function refman/lib/httputil.html cherrypy.lib.reprconf.as_dict function refman/lib/reprconf.html cherrypy.lib.httputil.HeaderMap.output method refman/lib/httputil.html cherrypy.process.wspbus.Bus.unsubscribe method refman/process/wspbus.html cherrypy._cprequest.Request.request_line attribute refman/_cprequest.html cherrypy.lib.profiler.serve function refman/lib/profiler.html cherrypy._cptree.Application.log attribute refman/_cptree.html cherrypy.process.servers.ServerAdapter class refman/process/servers.html cherrypy._cplogging.LogManager.reopen_files method refman/_cplogging.html cherrypy._cpreqbody.RequestBody.process method refman/_cpreqbody.html cherrypy._cprequest.Hook.callback attribute refman/_cprequest.html cherrypy.process.plugins.SignalHandler.unsubscribe method refman/process/plugins/signalhandler.html cherrypy._cpserver.Server.max_request_header_size attribute refman/_cpserver.html cherrypy._cprequest.Request.process_request_body attribute refman/_cprequest.html cherrypy._cpreqbody.Entity.charset attribute refman/_cpreqbody.html cherrypy.wsgiserver.ThreadPool.start method refman/wsgiserver/init.html cherrypy._cpchecker.Checker.check_skipped_app_config method refman/_cpchecker.html cherrypy._cptree.Tree.graft method refman/_cptree.html cherrypy._cplogging.LogManager class refman/_cplogging.html cherrypy._cprequest.Request.query_string attribute refman/_cprequest.html cherrypy._cprequest.Request.path_info attribute refman/_cprequest.html cherrypy._cprequest.Request.throw_errors attribute refman/_cprequest.html cherrypy.InternalRedirect class refman/cherrypy.html cherrypy.lib.sessions.RamSession.keys method refman/lib/sessions.html cherrypy._cpdispatch.Dispatcher.dispatch_method_name attribute refman/_cpdispatch.html cherrypy.HTTPRedirect.urls attribute refman/cherrypy.html cherrypy.wsgiserver.CP_fileobject.sendall method refman/wsgiserver/init.html cherrypy.lib.sessions.MemcachedSession.update method refman/lib/sessions.html cherrypy._cpdispatch.MethodDispatcher class refman/_cpdispatch.html cherrypy.CherryPyException class refman/cherrypy.html cherrypy.TimeoutError class refman/cherrypy.html cherrypy.lib.sessions.RamSession.update method refman/lib/sessions.html cherrypy.lib.sessions.PostgresqlSession.regenerate method refman/lib/sessions.html cherrypy.lib.caching.AntiStampedeCache class refman/lib/caching.html cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter.certificate attribute refman/wsgiserver/ssl_pyopenssl.html cherrypy.lib.caching.MemoryCache.clear method refman/lib/caching.html cherrypy.lib.sessions.PostgresqlSession.setup classmethod refman/lib/sessions.html cherrypy.lib.sessions.FileSession.items method refman/lib/sessions.html cherrypy._cplogging.LogManager.access_log attribute refman/_cplogging.html cherrypy.lib.httpauth.md5SessionKey function refman/lib/httpauth.html cherrypy.lib.cptools.SessionAuth.do_login method refman/lib/cptools.html cherrypy.lib.sessions.PostgresqlSession.update method refman/lib/sessions.html cherrypy.lib.static.serve_fileobj function refman/lib/static.html cherrypy.process.servers.ServerAdapter.stop method refman/process/servers.html cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter.context attribute refman/wsgiserver/ssl_pyopenssl.html cherrypy.process.plugins.ThreadManager.threads attribute refman/process/plugins/index.html cherrypy.wsgiserver.CherryPyWSGIServer class refman/wsgiserver/init.html cherrypy.wsgiserver.HTTPServer.shutdown_timeout attribute refman/wsgiserver/init.html cherrypy._cprequest.Request.toolmaps attribute refman/_cprequest.html cherrypy.lib.auth_digest.HttpDigestAuthorization.request_digest method refman/lib/auth_digest.html cherrypy._cprequest.Hook class refman/_cprequest.html cherrypy._cpconfig.Config class refman/_cpconfig.html cherrypy.lib.sessions.PostgresqlSession.setdefault method refman/lib/sessions.html cherrypy.lib.sessions.FileSession.setdefault method refman/lib/sessions.html cherrypy.lib.profiler.Profiler.statfiles method refman/lib/profiler.html cherrypy.wsgiserver.CP_fileobject class refman/wsgiserver/init.html cherrypy._cprequest.Request.base attribute refman/_cprequest.html cherrypy.lib.jsontools.json_in function refman/lib/jsontools.html cherrypy._cpwsgi.CPWSGIApp.pipeline attribute refman/_cpwsgi.html cherrypy._cpreqbody.RequestBody.maxbytes attribute refman/_cpreqbody.html cherrypy.lib.httpauth.doAuth function refman/lib/httpauth.html cherrypy._cpserver.Server.start method refman/_cpserver.html cherrypy._cpserver.Server.thread_pool attribute refman/_cpserver.html cherrypy.process.wspbus.ChannelFailures.handle_exception method refman/process/wspbus.html cherrypy._cperror.HTTPRedirect.status attribute refman/_cperror.html cherrypy._cpchecker.Checker.check_app_config_entries_dont_start_with_script_name method refman/_cpchecker.html cherrypy.process.plugins.PIDFile class refman/process/plugins/pidfile.html cherrypy.lib.sessions.FileSession.delete method refman/lib/sessions.html cherrypy.lib.auth_digest.get_ha1_dict_plain function refman/lib/auth_digest.html cherrypy.process.wspbus.Bus.wait method refman/process/wspbus.html cherrypy.lib.sessions.PostgresqlSession.clear method refman/lib/sessions.html cherrypy._cpreqbody.SizedReader class refman/_cpreqbody.html cherrypy._cperror.InternalRedirect class refman/_cperror.html cherrypy._cpdispatch.RoutesDispatcher class refman/_cpdispatch.html cherrypy._cpreqbody.Entity.fp attribute refman/_cpreqbody.html cherrypy.wsgiserver.HTTPRequest.conn attribute refman/wsgiserver/init.html cherrypy.process.plugins.SimplePlugin.bus attribute refman/process/plugins/index.html cherrypy._cprequest.Response.headers attribute refman/_cprequest.html cherrypy.lib.cptools.SessionAuth.anonymous method refman/lib/cptools.html cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter.private_key attribute refman/wsgiserver/ssl_builtin.html cherrypy.lib.httputil.decode_TEXT function refman/lib/httputil.html cherrypy._cplogging.WSGIErrorHandler.flush method refman/_cplogging.html cherrypy.process.wspbus.Bus class refman/process/wspbus.html cherrypy._cpconfig.Config.itervalues attribute refman/_cpconfig.html cherrypy.lib.sessions.Session.keys method refman/lib/sessions.html cherrypy.wsgiserver.Gateway class refman/wsgiserver/init.html cherrypy.lib.static.staticdir function refman/lib/static.html cherrypy.process.plugins.ThreadManager.release_thread method refman/process/plugins/index.html cherrypy.lib.sessions.PostgresqlSession.clean_up method refman/lib/sessions.html cherrypy.process.plugins.Autoreloader.frequency attribute refman/process/plugins/index.html cherrypy.process.plugins.SignalHandler.handle_SIGHUP method refman/process/plugins/signalhandler.html cherrypy.wsgiserver.HTTPRequest class refman/wsgiserver/init.html cherrypy.wsgiserver.ChunkedRFile class refman/wsgiserver/init.html cherrypy._cpwsgi.CPWSGIApp class refman/_cpwsgi.html cherrypy._cpserver.Server.ssl_certificate_chain attribute refman/_cpserver.html cherrypy._cprequest.Request.scheme attribute refman/_cprequest.html cherrypy._cpreqbody.RequestBody.default_content_type attribute refman/_cpreqbody.html cherrypy.lib.sessions.Session.load method refman/lib/sessions.html cherrypy.lib.sessions.RamSession.setdefault method refman/lib/sessions.html cherrypy._cpchecker.Checker class refman/_cpchecker.html cherrypy.process.plugins.Monitor.graceful method refman/process/plugins/index.html cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter.certificate_chain attribute refman/wsgiserver/ssl_pyopenssl.html cherrypy.lib.profiler.Profiler.stats method refman/lib/profiler.html cherrypy._cperror.HTTPRedirect.urls attribute refman/_cperror.html cherrypy.wsgiserver.WSGIPathInfoDispatcher class refman/wsgiserver/init.html cherrypy.lib.sessions.MemcachedSession.keys method refman/lib/sessions.html cherrypy._cpconfig.Config.items attribute refman/_cpconfig.html cherrypy.lib.caching.Cache.clear method refman/lib/caching.html cherrypy.wsgiserver.HTTPServer.server_name attribute refman/wsgiserver/init.html cherrypy.wsgiserver.HTTPRequest.close_connection attribute refman/wsgiserver/init.html cherrypy.lib.cptools.proxy function refman/lib/cptools.html cherrypy._cpserver.Server.max_request_body_size attribute refman/_cpserver.html cherrypy._cpwsgi.VirtualHost.domains attribute refman/_cpwsgi.html cherrypy.lib.sessions.RamSession.get method refman/lib/sessions.html cherrypy.wsgiserver.ThreadPool.grow method refman/wsgiserver/init.html cherrypy.lib.sessions.Session.generate_id method refman/lib/sessions.html cherrypy.lib.sessions.MemcachedSession.id attribute refman/lib/sessions.html cherrypy.lib.sessions.RamSession.save method refman/lib/sessions.html cherrypy.lib.httpauth.checkResponse function refman/lib/httpauth.html cherrypy.wsgiserver.HTTPRequest.simple_response method refman/wsgiserver/init.html cherrypy.process.wspbus.Bus.subscribe method refman/process/wspbus.html cherrypy.process.plugins.Autoreloader.subscribe method refman/process/plugins/index.html cherrypy._cprequest.Request.cookie attribute refman/_cprequest.html cherrypy._cptree.Tree class refman/_cptree.html cherrypy.lib.encoding.ResponseEncoder.encode_string method refman/lib/encoding.html cherrypy._cpreqbody.process_multipart function refman/_cpreqbody.html cherrypy._cprequest.Request.respond method refman/_cprequest.html cherrypy.wsgiserver.WorkerThread.ready attribute refman/wsgiserver/init.html cherrypy.wsgiserver.ssl_pyopenssl.SSL_fileobject class refman/wsgiserver/ssl_pyopenssl.html cherrypy.expose function refman/cherrypy.html cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter.bind method refman/wsgiserver/ssl_pyopenssl.html cherrypy.wsgiserver.HTTPServer.ssl_adapter attribute refman/wsgiserver/init.html cherrypy.wsgiserver.SSLAdapter class refman/wsgiserver/init.html cherrypy._cperror.HTTPRedirect.set_response method refman/_cperror.html cherrypy.process.plugins.Monitor.callback attribute refman/process/plugins/index.html cherrypy._cptree.Application.merge method refman/_cptree.html cherrypy.lib.sessions.PostgresqlSession.keys method refman/lib/sessions.html cherrypy.lib.cptools.SessionAuth class refman/lib/cptools.html cherrypy._cpchecker.Checker.check_static_paths method refman/_cpchecker.html cherrypy._cpwsgi.AppResponse.run method refman/_cpwsgi.html cherrypy._cperror.HTTPRedirect class refman/_cperror.html cherrypy.lib.sessions.Session.id attribute refman/lib/sessions.html cherrypy._cpwsgi.CPWSGIApp.tail method refman/_cpwsgi.html cherrypy._cprequest.Response.header_list attribute refman/_cprequest.html cherrypy.lib.sessions.PostgresqlSession.id attribute refman/lib/sessions.html cherrypy.HTTPRedirect class refman/cherrypy.html cherrypy._cprequest.Response.finalize method refman/_cprequest.html cherrypy.lib.caching.MemoryCache.put method refman/lib/caching.html cherrypy.lib.sessions.PostgresqlSession.items method refman/lib/sessions.html cherrypy._cptools.HandlerTool class refman/_cptools.html cherrypy.lib.sessions.Session.regenerated attribute refman/lib/sessions.html cherrypy.HTTPRedirect.encoding attribute refman/cherrypy.html cherrypy.process.servers.ServerAdapter.restart method refman/process/servers.html cherrypy.lib.sessions.FileSession class refman/lib/sessions.html cherrypy._cplogging.LogManager.access_file attribute refman/_cplogging.html cherrypy._cprequest.Request.handle_error method refman/_cprequest.html cherrypy._cpchecker.Checker.check_site_config_entries_in_app_config method refman/_cpchecker.html cherrypy._cpchecker.Checker.formatwarning method refman/_cpchecker.html cherrypy._cpserver.Server.ssl_private_key attribute refman/_cpserver.html cherrypy.process.plugins.SignalHandler.subscribe method refman/process/plugins/signalhandler.html cherrypy.lib.xmlrpc.patched_path function refman/lib/xmlrpc.html cherrypy.lib.sessions.save function refman/lib/sessions.html cherrypy.lib.profiler.Profiler.run method refman/lib/profiler.html cherrypy.wsgiserver.HTTPServer.max_request_body_size attribute refman/wsgiserver/init.html cherrypy.process.plugins.SimplePlugin class refman/process/plugins/index.html cherrypy.process.plugins.Autoreloader.match attribute refman/process/plugins/index.html cherrypy._cpdispatch.LateParamPageHandler class refman/_cpdispatch.html cherrypy.log function refman/cherrypy.html cherrypy._cpreqbody.RequestBody.read_into_file method refman/_cpreqbody.html cherrypy.lib.sessions.RamSession class refman/lib/sessions.html cherrypy.wsgiserver.WSGIGateway.write method refman/wsgiserver/init.html cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter class refman/wsgiserver/ssl_builtin.html cherrypy._cprequest.Request.show_mismatched_params attribute refman/_cprequest.html cherrypy.process.plugins.SignalHandler.signals attribute refman/process/plugins/signalhandler.html cherrypy._cptree.Application.wsgiapp attribute refman/_cptree.html cherrypy.wsgiserver.HTTPConnection.RequestHandlerClass attribute refman/wsgiserver/init.html cherrypy._cpserver.Server.bind_addr attribute refman/_cpserver.html cherrypy._cptree.Application.root attribute refman/_cptree.html cherrypy.wsgiserver.HTTPConnection.communicate method refman/wsgiserver/init.html cherrypy.wsgiserver.HTTPServer.request_queue_size attribute refman/wsgiserver/init.html cherrypy.lib.sessions.MemcachedSession.save method refman/lib/sessions.html cherrypy._cperror.HTTPError.code attribute refman/_cperror.html cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter class refman/wsgiserver/ssl_pyopenssl.html cherrypy.lib.sessions.FileSession.release_lock method refman/lib/sessions.html cherrypy._cpwsgi.AppResponse.translate_headers method refman/_cpwsgi.html cherrypy._cpwsgi.CPWSGIApp.config attribute refman/_cpwsgi.html cherrypy.lib.caching.MemoryCache.delete method refman/lib/caching.html cherrypy.lib.encoding.decode function refman/lib/encoding.html cherrypy.NotFound class refman/cherrypy.html cherrypy._cpserver.Server.socket_timeout attribute refman/_cpserver.html cherrypy._cpwsgi.ExceptionTrapper class refman/_cpwsgi.html cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter.get_environ method refman/wsgiserver/ssl_pyopenssl.html cherrypy.lib.httputil.parse_query_string function refman/lib/httputil.html cherrypy.lib.sessions.RamSession.has_key method refman/lib/sessions.html cherrypy._cprequest.Hook.kwargs attribute refman/_cprequest.html cherrypy.wsgiserver.MaxSizeExceeded class refman/wsgiserver/init.html cherrypy._cpconfig.Config.fromkeys staticmethod refman/_cpconfig.html cherrypy.wsgiserver.WorkerThread class refman/wsgiserver/init.html cherrypy.lib.sessions.Session.clean_thread attribute refman/lib/sessions.html cherrypy.lib.caching.Cache.put method refman/lib/caching.html cherrypy._cpserver.Server.ssl_certificate attribute refman/_cpserver.html cherrypy._cpserver.Server.socket_file attribute refman/_cpserver.html cherrypy.lib.caching.Cache.delete method refman/lib/caching.html cherrypy._cprequest.Request.server_protocol attribute refman/_cprequest.html cherrypy.lib.sessions.PostgresqlSession.load method refman/lib/sessions.html cherrypy._cpchecker.Checker.check_config_types method refman/_cpchecker.html cherrypy._cpreqbody.Part.make_file method refman/_cpreqbody.html cherrypy.lib.sessions.Session.clean_up method refman/lib/sessions.html cherrypy._cpreqbody.Entity.params attribute refman/_cpreqbody.html cherrypy.lib.sessions.PostgresqlSession.delete method refman/lib/sessions.html cherrypy.lib.sessions.Session.loaded attribute refman/lib/sessions.html cherrypy._cpreqbody.Entity.part_class attribute refman/_cpreqbody.html cherrypy.lib.caching.MemoryCache.expire_freq attribute refman/lib/caching.html cherrypy.lib.httputil.HeaderMap class refman/lib/httputil.html cherrypy.lib.caching.MemoryCache.expire_cache method refman/lib/caching.html cherrypy._cpserver.Server.ssl_module attribute refman/_cpserver.html cherrypy.HTTPRedirect.status attribute refman/cherrypy.html cherrypy._cpreqbody.Entity.process method refman/_cpreqbody.html cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter.wrap method refman/wsgiserver/ssl_builtin.html cherrypy._cpconfig.Config.get attribute refman/_cpconfig.html cherrypy.wsgiserver.ThreadPool class refman/wsgiserver/init.html cherrypy._cprequest.Request.process_headers method refman/_cprequest.html cherrypy.lib.sessions.Session.id_observers attribute refman/lib/sessions.html cherrypy.lib.sessions.Session.locked attribute refman/lib/sessions.html cherrypy.lib.reprconf.Config class refman/lib/reprconf.html cherrypy._cprequest.Request.get_resource method refman/_cprequest.html cherrypy._cpreqbody.Entity.read_into_file method refman/_cpreqbody.html cherrypy.wsgiserver.ThreadPool.shrink method refman/wsgiserver/init.html cherrypy.process.plugins.Autoreloader.graceful method refman/process/plugins/index.html cherrypy.wsgiserver.HTTPServer.software attribute refman/wsgiserver/init.html cherrypy._cprequest.Request.throws attribute refman/_cprequest.html cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter.bind method refman/wsgiserver/ssl_builtin.html cherrypy.wsgiserver.ThreadPool.idle attribute refman/wsgiserver/init.html cherrypy._cprequest.Request.query_string_encoding attribute refman/_cprequest.html cherrypy.lib.cptools.referer function refman/lib/cptools.html cherrypy._cprequest.HookMap class refman/_cprequest.html cherrypy._cprequest.Response.status attribute refman/_cprequest.html cherrypy._cprequest.HookMap.run method refman/_cprequest.html cherrypy.lib.reprconf.modules function refman/lib/reprconf.html cherrypy.lib.sessions.RamSession.load method refman/lib/sessions.html cherrypy.process.plugins.SimplePlugin.unsubscribe method refman/process/plugins/index.html cherrypy._cptools.SessionAuthTool class refman/_cptools.html cherrypy.wsgiserver.HTTPServer.ready attribute refman/wsgiserver/init.html cherrypy._cptools.ErrorTool class refman/_cptools.html cherrypy._cpdispatch.LateParamPageHandler.kwargs attribute refman/_cpdispatch.html cherrypy.lib.caching.MemoryCache.get method refman/lib/caching.html cherrypy._cprequest.Request.prev attribute refman/_cprequest.html cherrypy.lib.reprconf.Parser class refman/lib/reprconf.html cherrypy.lib.cptools.autovary function refman/lib/cptools.html cherrypy.lib.cptools.validate_etags function refman/lib/cptools.html cherrypy.wsgiserver.NoSSLError class refman/wsgiserver/init.html cherrypy._cpconfig.Config.keys attribute refman/_cpconfig.html cherrypy._cpreqbody.Part.boundary attribute refman/_cpreqbody.html cherrypy.lib.covercp.get_tree function refman/lib/covercp.html cherrypy.process.servers.wait_for_occupied_port function refman/process/servers.html cherrypy._cplogging.LogManager.access method refman/_cplogging.html cherrypy.wsgiserver.HTTPServer.start method refman/wsgiserver/init.html cherrypy._cptree.Application.find_config method refman/_cptree.html cherrypy.process.plugins.SignalHandler.handlers attribute refman/process/plugins/signalhandler.html cherrypy._cperror.HTTPError.set_response method refman/_cperror.html cherrypy._cpreqbody.RequestBody.fullvalue method refman/_cpreqbody.html cherrypy._cprequest.Request.body_params attribute refman/_cprequest.html cherrypy._cpserver.Server.httpserver_from_self method refman/_cpserver.html cherrypy.lib.caching.AntiStampedeCache.wait method refman/lib/caching.html cherrypy._cperror.CherryPyException class refman/_cperror.html cherrypy.lib.sessions.RamSession.regenerate method refman/lib/sessions.html cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter.certificate attribute refman/wsgiserver/ssl_builtin.html cherrypy.process.plugins.SimplePlugin.subscribe method refman/process/plugins/index.html cherrypy.lib.reprconf.Parser.as_dict method refman/lib/reprconf.html cherrypy.lib.sessions.FileSession.has_key method refman/lib/sessions.html cherrypy.lib.sessions.PostgresqlSession.has_key method refman/lib/sessions.html cherrypy.lib.sessions.MemcachedSession.generate_id method refman/lib/sessions.html cherrypy._cptree.Application class refman/_cptree.html cherrypy.process.plugins.SignalHandler class refman/process/plugins/signalhandler.html cherrypy.process.plugins.SignalHandler.set_handler method refman/process/plugins/signalhandler.html cherrypy._cperror.format_exc function refman/_cperror.html cherrypy._cpserver.Server.wsgi_version attribute refman/_cpserver.html cherrypy._cperror.HTTPError.status attribute refman/_cperror.html cherrypy._cpconfig.Config.values attribute refman/_cpconfig.html cherrypy._cplogging.LogManager.appid attribute refman/_cplogging.html cherrypy.wsgiserver.WSGIGateway_10 class refman/wsgiserver/init.html cherrypy.lib.covercp.serve function refman/lib/covercp.html cherrypy.lib.sessions.MemcachedSession.delete method refman/lib/sessions.html cherrypy._cpserver.Server.thread_pool_max attribute refman/_cpserver.html cherrypy.lib.httputil.HeaderMap.elements method refman/lib/httputil.html cherrypy._cptree.Tree.script_name method refman/_cptree.html cherrypy.lib.httpauth.digestAuth function refman/lib/httpauth.html bcfg2-1.3.3/doc/introduction/000077500000000000000000000000001223671746500160045ustar00rootroot00000000000000bcfg2-1.3.3/doc/introduction/architecture-overview.txt000066400000000000000000000024761223671746500231040ustar00rootroot00000000000000.. -*- mode: rst -*- .. _architecture-overview: Architecture Overview ===================== Bcfg2 provides a declarative interface to system configuration. Its configuration specifications describe a literal configuration goal state for clients. In this architecture, the Bcfg2 client tool is responsible for determining what, if any, configuration operations must occur and then performing those operations. The client also uploads statistics and client configuration state information. The design and implementation of the reporting system is described on a separate :ref:`page `. A comprehensive description of the Bcfg2 Architecture (and the choices behind the design) can be found at :ref:`architecture-index`. Server ------ The role of the Bcfg2 server is rendering a client-specific target configuration description from a global specification. The specification consists of a directory structure containing data for a variety of server plugins. The Bcfg2 server has a plugin interface that can be used to interpret the configuration specification. Read on for more information about :ref:`server-index`. Client ------ The Bcfg2 client is responsible for determining what operations are necessary in order to reach the desired configuration state. Read on for more information about :ref:`client-index`. bcfg2-1.3.3/doc/introduction/index.txt000066400000000000000000000030601223671746500176530ustar00rootroot00000000000000.. -*- mode: rst -*- .. _introduction-index: Introduction ============ Bcfg2 helps system administrators produce a consistent, reproducible, and verifiable description of their environment, and offers visualization and reporting tools to aid in day-to-day administrative tasks. It is the fifth generation of configuration management tools developed in the `Mathematics and Computer Science Division`_ of `Argonne National Laboratory`_. .. _Mathematics and Computer Science Division: http://www.mcs.anl.gov/ .. _Argonne National Laboratory: http://www.anl.gov/ It is based on an operational model in which the specification can be used to validate and optionally change the state of clients, but in a feature unique to Bcfg2 the client's response to the specification can also be used to assess the completeness of the specification. Using this feature, Bcfg2 provides an objective measure of how good a job an administrator has done in specifying the configuration of client systems. Bcfg2 is therefore built to help administrators construct an accurate, comprehensive specification. Bcfg2 has been designed from the ground up to support gentle reconciliation between the specification and current client states. It is designed to gracefully cope with manual system modifications. Finally, due to the rapid pace of updates on modern networks, client systems are constantly changing; if required in your environment, Bcfg2 can enable the construction of complex change management and deployment strategies. .. toctree:: :maxdepth: 2 architecture-overview os-support bcfg2-1.3.3/doc/introduction/os-support.txt000066400000000000000000000032331223671746500207010ustar00rootroot00000000000000.. -*- mode: rst -*- .. _os-support: What Operating Systems Does Bcfg2 Support? ------------------------------------------ Bcfg2 is fairly portable. It has been successfully run on: * `AIX`_, `FreeBSD`_, `OpenBSD`_, `Mac OS X`_, `OpenSolaris`_, `Solaris`_. .. _AIX: http://www.ibm.com/aix .. _FreeBSD: http://www.freebsd.org/ .. _OpenBSD: http://www.openbsd.org/ .. _Mac OS X: http://www.apple.com/macosx/ .. _OpenSolaris: http://opensolaris.org/ .. _Solaris: http://www.sun.com/software/solaris/ * Many `GNU/Linux`_ distributions, including `Archlinux`_, `Blag`_, `CentOS`_, `Debian`_, `Fedora`_, `Gentoo`_, `gNewSense`_, `Mandriva`_, `OpenSUSE`_, `Red Hat/RHEL`_, `Scientific Linux`_, `SuSE/SLES`_, `Trisquel`_, and `Ubuntu`_. .. _GNU/Linux: http://www.gnu.org/gnu/Linux-and-gnu.html .. _Archlinux: http://www.archlinux.org .. _Blag: http://www.blagblagblag.org/ .. _CentOS: http://www.centos.org/ .. _Debian: http://www.debian.org/ .. _Fedora: http://www.fedoraproject.org/ .. _Gentoo: http://www.gentoo.org/ .. _gNewSense: http://www.gnewsense.org/ .. _Mandriva: http://www.mandriva.com/ .. _OpenSUSE: http://opensuse.org/ .. _Red Hat/RHEL: http://www.redhat.com/rhel/ .. _Scientific Linux: http://www.scientificlinux.org/ .. _SuSE/SLES: http://www.novell.com/linux/ .. _Trisquel: http://trisquel.info/ .. _Ubuntu: http://www.ubuntu.com/ Bcfg2 should run on any POSIX compatible operating system, however direct support for an operating system's package and service formats are limited by the currently available :ref:`client-tools` (new client tools are pretty easy to add). Check the :ref:`FAQ ` for a more exact list of platforms on which Bcfg2 works`. bcfg2-1.3.3/doc/man/000077500000000000000000000000001223671746500140365ustar00rootroot00000000000000bcfg2-1.3.3/doc/man/bcfg2-admin.txt000066400000000000000000000106141223671746500166520ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst bcfg2-admin =========== .. program:: bcfg2-admin Synopsis -------- **bcfg2-admin** [-C *configfile*] *mode* [*mode args*] [*mode options*] Description ----------- :program:`bcfg2-admin` is used to perform Bcfg2 repository administration. Options ------- -C configfile Specify alternate bcfg2.conf location. -E encoding Specify the encoding of config files. -Q path Specify the path to the server repository. -S server Manually specify the server location (as opposed to using the value in bcfg2.conf). This should be in the format "https://server:port" -d Enable debugging output. -h Print usage information. -o logfile Writes a log to the specified path. --ssl-key=key Specify the path to the SSL key. -v Enable verbose output. -x password Use 'password' for client communication. Modes ----- backup Create an archive of the entire Bcfg2 repository. bundle *action* Display details about the available bundles (See BUNDLE OPTIONS below). client *action* *client* [attribute=value] Add, edit, or remove clients entries in metadata (See CLIENT OPTIONS below). compare *old* *new* Compare two client configurations. Can be used to verify consistent behavior between releases. Determine differences between files or directories (See COMPARE OPTIONS below). init Initialize a new repository (interactive). minestruct *client* [-f xml-file] [-g groups] Build structure entries based on client statistics extra entries (See MINESTRUCT OPTIONS below). perf Query server for performance data. pull *client* *entry-type* *entry-name* Install configuration information into repo based on client bad entries (See PULL OPTIONS below). reports [init|load_stats|purge|scrub|update] Interact with the dynamic reporting system (See REPORTS OPTIONS below). snapshots [init|dump|query|reports] Interact with the Snapshots database (See SNAPSHOTS OPTIONS below). syncdb Sync the Django ORM with the configured database. tidy Remove unused files from repository. viz [-H] [-b] [-k] [-o png-file] Create a graphviz diagram of client, group and bundle information (See VIZ OPTIONS below). xcmd Provides a XML-RPC Command Interface to the bcfg2-server. BUNDLE OPTIONS ++++++++++++++ mode One of the following. *list-xml* List all available xml bundles *list-genshi* List all available genshi bundles *show* Interactive dialog to get details about the available bundles CLIENT OPTIONS ++++++++++++++ mode One of the following. *add* Add a client *del* Delete a client *list* List all client entries client Specify the client's name. attribute=value Set attribute values when adding a new client. Allowed attributes are 'profile', 'uuid', 'password', 'location', 'secure, and 'address'. COMPARE OPTIONS +++++++++++++++ old Specify the location of the old configuration file. new Specify the location of the new configuration file. MINESTRUCT OPTIONS ++++++++++++++++++ client Client whose metadata is to be searched for extra entries. -g *groups* Hierarchy of groups in which to place the extra entries in. -f *outputfile* Specify the xml file in which to write the extra entries. PULL OPTIONS ++++++++++++ client Specify the name of the client to search for. entry type Specify the type of the entry to pull. entry name Specify the name of the entry to pull. REPORTS OPTIONS +++++++++++++++ load_stats [-s] [-c] [-03] Load statistics data. purge [--client [n]] [--days [n]] [--expired] Purge historic and expired data. scrub Scrub the database for duplicate reasons and orphaned entries. update Apply any updates to the reporting database. SNAPSHOTS OPTIONS +++++++++++++++++ init Initialize the snapshots database. query Query the snapshots database. dump Dump some of the contents of the snapshots database. reports [-a] [-b] [-e] [--date=MM-DD-YYYY] Generate reports for clients in the snapshots database. VIZ OPTIONS +++++++++++ -H Include hosts in diagram. -b Include bundles in diagram. -o Write to outfile file instead of stdout. -k Add a shape/color key. See Also -------- :manpage:`bcfg2-info(8)`, :manpage:`bcfg2-server(8)` bcfg2-1.3.3/doc/man/bcfg2-build-reports.txt000066400000000000000000000013471223671746500203600ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst bcfg2-build-reports =================== .. program:: bcfg2-build-reports Synopsis -------- **bcfg2-build-reports** [*-A*] [*-c*] [*-s*] Description ----------- :program:`bcfg2-build-reports` is used to build all client state reports. See the Bcfg2 manual for report setup information. Options ------- -A Displays all data. -c configfile Specify an alternate report configuration path. The default is ``repo/etc/reports-configuration.xml``. -h Print usage information. -s statsfile Use an alternative path for the statistics file. The default is ``repo/etc/statistics.xml``. See Also -------- :manpage:`bcfg2(1)`, :manpage:`bcfg2-server(8)` bcfg2-1.3.3/doc/man/bcfg2-crypt.txt000066400000000000000000000110271223671746500167220ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst bcfg2-crypt =========== .. program:: bcfg2-crypt Synopsis -------- **bcfg2-crypt** [-C *configfile*] [--decrypt|--encrypt] [--cfg|--properties] [--stdout] [--remove] [--xpath *xpath*] [-p *passphrase-or-name*] [-v] [-I] *filename* [*filename*...] Description ----------- :program:`bcfg2-crypt` performs encryption and decryption of Cfg and Properties files. It's often sufficient to run :program:`bcfg2-crypt` with only the name of the file you wish to encrypt or decrypt; it can usually figure out what to do. Options ------- -C configfile Specify alternate bcfg2.conf location. --decrypt, --encrypt Select encryption or decryption mode for the given file(s). This is usually unnecessary, as :program:`bcfg2-crypt` can often determine which is necessary based on the contents of each file. --cfg An XML file should be encrypted in its entirety rather than element-by-element. This is only necessary if the file is an XML file whose name ends with *.xml* and whose top-level tag is **. See [MODES] below for details. --properties Process a file as an XML Properties file, and encrypt the text of each element separately. This is necessary if, for example, you've used a different top-level tag than *Properties* in your Properties files. See [MODES] below for details. --stdout Print the resulting file to stdout instead of writing it to a file. --remove Remove the plaintext file after it has been encrypted. Only meaningful for Cfg files. --xpath xpath Encrypt the character content of all elements that match the specified XPath expression. The default is *\*[@encrypted]* or *\**; see [MODES] below for more details. Only meaningful for Properties files. -p passphrase Specify the name of a passphrase specified in the *[encryption]* section of *bcfg2.conf*. See [SELECTING PASSPHRASE] below for more details. -v Be verbose. -I When encrypting a Properties file, interactively select the elements whose data should be encrypted. -h Print usage information. Modes ----- :program:`bcfg2-crypt` can encrypt Cfg files or Properties files; they are handled very differently. Cfg When :program:`bcfg2-crypt` is used on a Cfg file, the entire file is encrypted. This is the default behavior on files that are not XML, or that are XML but whose top-level tag is not **. This can be enforced by use of the *--cfg* option. Properties When :program:`bcfg2-crypt` is used on a Properties file, it encrypts the character content of elements matching the XPath expression given by *--xpath*. By default the expression is *\*[@encrypted]*, which matches all elements with an *encrypted* attribute. If you are encrypting a file and that expression doesn't match any elements, then the default is *\**, which matches everything. When :program:`bcfg2-crypt` encrypts the character content of an element, it also adds the *encrypted* attribute, set to the name of the passphrase used to encrypt that element. When it decrypts an element it does not remove *encrypted*, though; this lets you easily and efficiently run :program:`bcfg2-crypt` against a single Properties file to encrypt and decrypt it without needing to specify a long list of options. See the online Bcfg2 docs on Properties files for more information on how this works. Selecting passphrase -------------------- The passphrase used to encrypt or decrypt a file is discovered in the following order. #. The passphrase given on the command line using *-p* is used. #. If exactly one passphrase is specified in *bcfg2.conf*, it will be used. #. If operating in Properties mode, *bcfg2.conf* will attempt to read the name of the passphrase from the encrypted elements. #. If decrypting, all passphrases will be tried sequentially. #. If no passphrase has been determined at this point, an error is produced and the file being encrypted or decrypted is skipped. See Also -------- :manpage:`bcfg2-server(8)` bcfg2-1.3.3/doc/man/bcfg2-info.txt000066400000000000000000000045121223671746500165150ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst bcfg2-info ========== .. program:: bcfg2-info Synopsis -------- **bcfg2-info** [-C *configfile*] [-E *encoding*] [-Q *repository path*] [-h] [-p] [-x *password*] [*mode*] [*mode args*] [*mode options*] Description ----------- :program:`bcfg2-info` instantiates an instance of the Bcfg2 core for data examination and debugging purposes. Options ------- -C configfile Specify alternate bcfg2.conf location. -E encoding Specify the encoding of config files. -Q path Specify the path to the server repository. -d Enable debugging output. -h Print usage information. -p profile Specify a profile. -x password Use 'password' for client communication. Modes ----- build *hostname* *filename* Build config for hostname, writing to filename. buildall *directory* Build configs for all clients in directory. buildallfile *directory* *filename* [*hostnames*] Build config file for all clients in directory. buildbundle *filename* *hostname* Build bundle for hostname (not written to disk). If filename is a bundle template, it is rendered. builddir *hostname* *dirname* Build config for hostname, writing separate files to dirname. buildfile [--altsrc=*altsrc*] *filename* *hostname* Build config file for hostname (not written to disk). bundles Print out group/bundle information. clients Print out client/profile information. config Print out the configuration of the Bcfg2 server. debug Shell out to native python interpreter. event_debug Display filesystem events as they are processed. groups List groups. help Print the list of available commands. mappings [*entry type*] [*entry name*] Print generator mappings for optional type and name. packageresolve *hostname* *package* [*package*...] Resolve the specified set of packages. packagesources *hostname* Show package sources. profile *command* *args* Profile a single bcfg2-info command. quit Exit bcfg2-info command line. showentries *hostname* *type* Show abstract configuration entries for a given host. showclient *client1* *client2* Show metadata for given hosts. update Process pending file events. version Print version of this tool. See Also -------- :manpage:`bcfg2(1)`, :manpage:`bcfg2-server(8)` bcfg2-1.3.3/doc/man/bcfg2-lint.conf.txt000066400000000000000000000071351223671746500174600ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst bcfg2-lint.conf =============== Description ----------- bcfg2-lint.conf includes configuration parameters for bcfg2-lint. File format ----------- The file is INI-style and consists of sections and options. A section begins with the name of the sections in square brackets and continues until the next section begins. Options are specified in the form "name=value". The file is line-based each newline-terminated line represents either a comment, a section name or an option. Any line beginning with a hash (#) is ignored, as are lines containing only whitespace. The file consists of one *[lint]* section, up to one *[errors]* section, and then any number of plugin-specific sections, documented below. (Note that this makes it quite feasible to combine your *bcfg2-lint.conf* into your :manpage:`bcfg2.conf(5)` file, if you so desire). Global options -------------- These options apply to *bcfg2-lint* generally, and must be in the *[lint]* section. plugins A comma-delimited list of plugins to run. By default, all plugins are run. This can be overridden by listing plugins on the command line. See :manpage:`bcfg2-lint(8)` for a list of the available plugins. Error handling -------------- Error handling is configured in the *[errors]* section. Each option should be the name of an error and one of *error*, *warning*, or *silent*, which tells :program:`bcfg2-lint` how to handle the warning. Error names and their defaults can be displayed by running :program:`bcfg2-lint` with the *--list-errors* option. Plugin options -------------- These options apply only to a single plugin. Each option should be in a section named for its plugin; for instance, options for the InfoXML plugin would be in a section called *[InfoXML]*. If a plugin is not listed below, then it has no configuration. In many cases, the behavior of a plugin can be configured by modifying how errors from it are handled. See ERROR HANDLING, above. Comments ++++++++ The *Comments* plugin configuration specifies which VCS keywords and comments are required for which file types. The valid types of file are *global* (all file types), *bundler* (non-templated bundle files), *genshibundler* (templated bundle files), *properties* (property files), *cfg* (non-templated Cfg files), *genshi* or *cheetah* (templated Cfg files), *infoxml* (info.xml files), and *probe* (probe files). The specific types (i.e., types other than "global") all supplement global; they do not override it. The exception is if you specify an empty option, e.g.: cfg_keywords = By default, the *$Id$* keyword is checked for and nothing else. Multiple keywords or comments should be comma-delimited. * *_keywords* Ensure that files of the specified type have the given VCS keyword. Do *not* include the dollar signs. I.e.: infoxml_keywords = Revision *not* infoxml_keywords = $Revision$ * *_comments* Ensure that files of the specified type have a comment containing the given string. In XML files, only comments are checked. In plain text files, all lines are checked since comment characters may vary. InfoXML +++++++ required_attrs A comma-delimited list of attributes to require on ** tags. Default is "owner,group,mode". MergeFiles ++++++++++ threshold The threshold at which MergeFiles will suggest merging config files and probes. Default is 75% similar. Validate ++++++++ schema The full path to the XML Schema files. Default is ``/usr/share/bcfg2/schema``. This can be overridden with the *--schema* command-line option. See Also -------- :manpage:`bcfg2-lint(8)` bcfg2-1.3.3/doc/man/bcfg2-lint.txt000066400000000000000000000102571223671746500165330ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst bcfg2-lint ========== .. program:: bcfg2-lint Synopsis -------- **bcfg2-lint** [*options*] [*plugin* [*plugin*...]] Description ----------- :program:`bcfg2-lint` checks the Bcfg2 specification for schema validity, common mistakes, and other criteria. It can be quite helpful in finding typos or malformed data. :program:`bcfg2-lint` exits with a return value of 2 if errors were found, and 3 if warnings (but no errors) were found. Any other non-0 exit value denotes some failure in the script itself. :program:`bcfg2-lint` is a rewrite of the older bcfg2-repo-validate tool. Options ------- -C configfile Specify alternate bcfg2.conf location. -Q path Specify the path to the server repository. -v Be verbose. --lint-config Specify path to bcfg2-lint.conf (default ``/etc/bcfg2-lint.conf``). --stdin Rather than operating on all files in the Bcfg2 specification, only validate a list of files supplied on stdin. This mode is particularly useful in pre-commit hooks. This makes a few assumptions: Metadata files will only be checked if a valid chain of XIncludes can be followed all the way from clients.xml or groups.xml. Since there are multiple formats of metadata stored in Metadata/ (i.e., clients and groups), there is no way to determine which sort of data a file contains unless there is a valid chain of XIncludes. It may be useful to always specify all metadata files should be checked, even if not all of them have changed. Property files will only be validated if both the property file itself and its matching schema are included on stdin. Plugins ------- In addition to the plugins listed below, Bcfg2 server plugins may have their own *bcfg2-lint* functionality, which is enabled automatically when the server plugin is enabled. See :manpage:`bcfg2-lint.conf(5)` for more information on lint plugin configuration. Comments Check the specification for VCS keywords and any comments that are required. By default, this only checks that the *$Id$* keyword is included and expanded in all files. You may specify VCS keywords to check and comments to be required in the config file. (For instance, you might require that every file have a "Maintainer" comment.) In XML files, only comments are checked for the keywords and comments required. Genshi Ensure that all Genshi templates are valid and compile properly. GroupNames Ensure that all groups called by name in Metadata, Rules, Bundler, GroupPatterns, and Cfg are valid. InfoXML Check that certain attributes are specified in *info.xml* files. By default, requires that *owner*, *group*, and *mode* are specified. Can also require that an *info.xml* exists for all Cfg files, and that paranoid mode be enabled for all files. MergeFiles Suggest that similar probes and config files be merged into single probes or TGenshi templates. RequiredAttrs Check that all entries have the appropriate required attributes, and that the attributes are in a valid format. This goes above and beyond the validation offered by an XML schema. Validate Validate the Bcfg2 specification against the XML schemas. Property files are freeform XML, but if a *.xsd* file with a matching filename is provided, then schema validation will be performed on property files individually as well. For instance, if you have a property file named *ntp.xml* then by placing a schema for that file in *ntp.xsd* schema validation will be performed on *ntp.xml*. Bugs ---- :program:`bcfg2-lint` may not handle some deprecated plugins as well as it handles newer ones. For instance, there may be some places where it expects all of your configuration files to be handled by Cfg rather than by a mix of Cfg and TGenshi or TCheetah. See Also -------- :manpage:`bcfg2(1)`, :manpage:`bcfg2-server(8)`, :manpage:`bcfg2-lint.conf(5)` bcfg2-1.3.3/doc/man/bcfg2-report-collector.txt000066400000000000000000000021311223671746500210540ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst bcfg2-report-collector ====================== .. program:: bcfg2-report-collector Synopsis -------- **bcfg2-report-collector** [*options*] Description ----------- :program:`bcfg2-report-collector` runs a daemon to collect logs from the LocalFilesystem :ref:`Bcfg2 Reports ` transport object and add them to the Reporting storage backend. Options ------- -C configfile Specify alternate bcfg2.conf location. -D pidfile Daemonize, placing the program pid in *pidfile*. -E encoding Specify the encoding of config files. -Q path Specify the path to the server repository. -W configfile Specify the path to the web interface configuration file. -d Enable debugging output. -h Print usage information. -o path Set path of file log -v Run in verbose mode. --version Print the version and exit See Also -------- :manpage:`bcfg2-server(8)`, :manpage:`bcfg2-reports(8)` bcfg2-1.3.3/doc/man/bcfg2-reports.txt000066400000000000000000000062411223671746500172610ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst bcfg2-reports ============= .. program:: bcfg2-reports Synopsis -------- **bcfg2-reports** [-a] [-b *NAME*] [-c] [-d] [-e *NAME*] [-h] [-m *NAME*] [-s *NAME*] [-x *NAME*] [--badentry=\ *KIND,NAME*] [--extraentry=\ *KIND,NAME*] [--fields=\ *ARG1,ARG2,...*] [--modifiedentry=\ *KIND,NAME*] [--sort=\ *ARG1,ARG2,...*] [--stale] [-v] Description ----------- :program:`bcfg2-reports` allows you to retrieve data from the database about clients, and the states of their current interactions. It also allows you to change the expired/unexpired states. The utility runs as a standalone application. It does, however, use the models from ``src/lib/Bcfg2/Reporting/models.py``. Options ------- -h Print usage information. Modes ----- The following are various modes available for :program:`bcfg2-reports`. Single-Host Modes +++++++++++++++++ -b, --bad hostname Shows bad entries from the current interaction of *hostname*. -e, --extra hostname Shows extra entries from the current interaction of *hostname*. -m, --modified hostname Shows modified entries from the current interaction of *hostname*. -s, --show hostname Shows bad, modified, and extra entries from the current interaction of *hostname*. -t, --total hostname Shows total number of managed and good entries from the current interaction of *hostname*. -x, --expire hostname Toggles expired/unexpired state of *hostname*. -a, --all Show all hosts, including expired hosts. Host Selection Modes ++++++++++++++++++++ -a, --all Show all hosts, including expired hosts. -c, --clean Show only clean hosts. -d, --dirty Show only dirty hosts. --stale Show hosts that haven't run in the last 24 hours. Entry Modes +++++++++++ The following mode flags require either a comma-delimited list of any number of *:* arguments describing entries, or the *--file* option. --badentry=entrylist Shows only hosts whose current interaction has bad entries matching the given entry or entries. --extraentry=entrylist Shows only hosts whose current interaction has extra entries matching the given entry or entries. --entrystatus=entry Shows the status of the single entry (given by *:*) on all hosts. --modifiedentry=entrylist Shows only hosts whose current interaction has modified entries matching the given entry or entries. Entry Options ^^^^^^^^^^^^^ The following options can be used with the above Entry Modes. --fields=fields Only display the listed fields. Takes a comma-delimited list of field names --file=file Read *:* pairs from the specified file instead of the command line. See Also -------- :manpage:`bcfg2(1)`, :manpage:`bcfg2-server(8)` bcfg2-1.3.3/doc/man/bcfg2-server.txt000066400000000000000000000021451223671746500170700ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst bcfg2-server ============ .. program:: bcfg2-server Synopsis -------- **bcfg2-server** [-d] [-v] [-C *configfile*] [-D *pidfile*] [-E *encoding*] [-Q *repo path*] [-S *server url*] [-o *logfile*] [-x *password*] [--ssl-key=\ *ssl key*] Description ----------- :program:`bcfg2-server` is the daemon component of Bcfg2 which serves configurations to clients based on the data in its repository. Options ------- -C configfile Specify alternate bcfg2.conf location. -D pidfile Daemonize, placing the program pid in *pidfile*. -E encoding Specify the encoding of config files. -Q path Specify the path to the server repository. -S server Manually specify the server location (as opposed to using the value in bcfg2.conf). This should be in the format "https://server:port" -d Enable debugging output. -v Run in verbose mode. -h Print usage information. --ssl-key=key Specify the path to the SSL key. See Also -------- :manpage:`bcfg2(1)`, :manpage:`bcfg2-lint(8)` bcfg2-1.3.3/doc/man/bcfg2.conf.txt000066400000000000000000000517461223671746500165230ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst bcfg2.conf ========== Description ----------- bcfg2.conf includes configuration parameters for the Bcfg2 server and client. File format ----------- The file is INI-style and consists of sections and options. A section begins with the name of the sections in square brackets and continues until the next section begins. Options are specified in the form "name=value". The file is line-based each newline-terminated line represents either a comment, a section name or an option. Any line beginning with a hash (#) is ignored, as are lines containing only whitespace. Server options -------------- These options are only necessary on the Bcfg2 server. They are specified in the **[server]** section of the configuration file. repository Specifies the path to the Bcfg2 repository containing all of the configuration specifications. The repository should be created using the `bcfg2-admin init` command. filemonitor The file monitor used to watch for changes in the repository. The default is the best available monitor. The following values are valid:: inotify gamin fam pseudo fam_blocking Whether the server should block at startup until the file monitor backend has processed all events. This can cause a slower startup, but ensure that all files are recognized before the first client is handled. ignore_files A comma-separated list of globs that should be ignored by the file monitor. Default values are:: *~ *# #* *.swp *.swpx *.swx SCCS .svn 4913 .gitignore listen_all This setting tells the server to listen on all available interfaces. The default is to only listen on those interfaces specified by the bcfg2 setting in the components section of ``bcfg2.conf``. plugins A comma-delimited list of enabled server plugins. Currently available plugins are:: Account Base Bundler Bzr Cfg Cvs Darcs DBStats Decisions Deps Editor FileProbes Fossil Git GroupPatterns Guppy Hg Hostbase Ldap Metadata NagiosGen Ohai Packages Pkgmgr POSIXCompat Probes Properties PuppetENC Reporting Rules SEModules ServiceCompat Snapshots SSHbase SSLCA Statistics Svn TCheetah TemplateHelper TGenshi Trigger Descriptions of each plugin can be found in their respective sections below. prefix Specifies a prefix if the Bcfg2 installation isn't placed in the default location (e.g. ``/usr/local``). backend Specifies which server core backend to use. Current available options are:: cherrypy builtin best The default is *best*, which is currently an alias for *builtin*. More details on the backends can be found in the official documentation. user The username or UID to run the daemon as. Default is *0*. group The group name or GID to run the daemon as. Default is *0*. vcs_root Specifies the path to the root of the VCS working copy that holds your Bcfg2 specification, if it is different from *repository*. E.g., if the VCS repository does not hold the bcfg2 data at the top level, you may need to set this option. umask The umask to set for the server. Default is *0077*. Server Plugins -------------- This section has a listing of all the plugins currently provided with Bcfg2. Account Plugin ++++++++++++++ The account plugin manages authentication data, including the following. * ``/etc/passwd`` * ``/etc/group`` * ``/etc/security/limits.conf`` * ``/etc/sudoers`` * ``/root/.ssh/authorized_keys`` Base Plugin +++++++++++ The Base plugin is a structure plugin that provides the ability to add lists of unrelated entries into client configuration entry inventories. Base works much like Bundler in its file format. This structure plugin is good for the pile of independent configs needed for most actual systems. Bundler Plugin ++++++++++++++ The Bundler plugin is used to describe groups of inter-dependent configuration entries, such as the combination of packages, configuration files, and service activations that comprise typical Unix daemons. Bundles are used to add groups of configuration entries to the inventory of client configurations, as opposed to describing particular versions of those entries. Bzr Plugin ++++++++++ The Bzr plugin allows you to track changes to your Bcfg2 repository using a GNU Bazaar version control backend. Currently, it enables you to get revision information out of your repository for reporting purposes. Cfg Plugin ++++++++++ The Cfg plugin provides a repository to describe configuration file contents for clients. In its simplest form, the Cfg repository is just a directory tree modeled off of the directory tree on your client machines. Cvs Plugin (experimental) +++++++++++++++++++++++++ The Cvs plugin allows you to track changes to your Bcfg2 repository using a Concurrent version control backend. Currently, it enables you to get revision information out of your repository for reporting purposes. Darcs Plugin (experimental) +++++++++++++++++++++++++++ The Darcs plugin allows you to track changes to your Bcfg2 repository using a Darcs version control backend. Currently, it enables you to get revision information out of your repository for reporting purposes. DBStats Plugin ++++++++++++++ Direct to database statistics plugin. Decisions Plugin ++++++++++++++++ The Decisions plugin has support for a centralized set of per-entry installation decisions. This approach is needed when particular changes are deemed "*high risk*"; this gives the ability to centrally specify these changes, but only install them on clients when administrator supervision is available. Defaults Plugin +++++++++++++++ The Defaults plugin can be used to populate default attributes for entries. Defaults is *not* a Generator plugin, so it does not actually bind an entry; Defaults are applied after an entry has been bound, and only populate attributes that are not yet set. Deps Plugin +++++++++++ The Deps plugin allows you to make a series of assertions like "Package X requires Package Y (and optionally also Package Z etc.)" Editor Plugin +++++++++++++ The Editor plugin attempts to allow you to partially manage configuration for a file. Its use is not recommended and not well documented. FileProbes Plugin +++++++++++++++++ The FileProbes plugin allows you to probe a client for a file, which is then added to the Cfg specification. If the file changes on the client, FileProbes can either update it in the specification or allow Cfg to replace it. Fossil Plugin +++++++++++++ The Fossil plugin allows you to track changes to your Bcfg2 repository using a Fossil SCM version control backend. Currently, it enables you to get revision information out of your repository for reporting purposes. Git Plugin ++++++++++ The Git plugin allows you to track changes to your Bcfg2 repository using a Git version control backend. Currently, it enables you to get revision information out of your repository for reporting purposes. GroupPatterns Plugin ++++++++++++++++++++ The GroupPatterns plugin is a connector that can assign clients group membership based on patterns in client hostnames. Guppy Plugin ++++++++++++ The Guppy plugin is used to trace memory leaks within the bcfg2-server process using Guppy. Hg Plugin (experimental) ++++++++++++++++++++++++ The Hg plugin allows you to track changes to your Bcfg2 repository using a Mercurial version control backend. Currently, it enables you to get revision information out of your repository for reporting purposes. Hostbase Plugin +++++++++++++++ The Hostbase plugin is an IP management system built on top of Bcfg2. Ldap Plugin +++++++++++ The Ldap plugin makes it possible to fetch data from an LDAP directory, process it and attach it to your metadata. Metadata Plugin +++++++++++++++ The Metadata plugin is the primary method of specifying Bcfg2 server metadata. NagiosGen Plugin ++++++++++++++++ The NagiosGen plugin dynamically generates Nagios configuration files based on Bcfg2 data. Ohai Plugin (experimental) ++++++++++++++++++++++++++ The Ohai plugin is used to detect information about the client operating system. The data is reported back to the server using JSON. Packages Plugin +++++++++++++++ The Packages plugin is an alternative to Pkgmgr for specifying package entries for clients. Where Pkgmgr explicitly specifies package entry information, Packages delegates control of package version information to the underlying package manager, installing the latest version available from through those channels. Pkgmgr Plugin +++++++++++++ The Pkgmgr plugin resolves the Abstract Configuration Entity "Package" to a package specification that the client can use to detect, verify and install the specified package. POSIXCompat Plugin ++++++++++++++++++ The POSIXCompat plugin provides a compatibility layer for 1.3 POSIX Entries so that they are compatible with older clients. Probes Plugin +++++++++++++ The Probes plugin gives you the ability to gather information from a client machine before you generate its configuration. This information can be used with the various templating systems to generate configuration based on the results. Properties Plugin +++++++++++++++++ The Properties plugin is a connector plugin that adds information from properties files into client metadata instances. PuppetENC Plugin ++++++++++++++++ The PuppetENC plugin is a connector plugin that adds support for Puppet External Node Classifiers. Reporting Plugin ++++++++++++++++ The Reporting plugin enables the collection of data for use with Bcfg2's dynamic reporting system. Rules Plugin ++++++++++++ The Rules plugin provides literal configuration entries that resolve the abstract configuration entries normally found in the Bundler and Base plugins. The literal entries in Rules are suitable for consumption by the appropriate client drivers. SEModules Plugin ++++++++++++++++ The SEModules plugin provides a way to distribute SELinux modules via Bcfg2. ServiceCompat Plugin ++++++++++++++++++++ The ServiceCompat plugin converts service entries for older clients. Snapshots Plugin ++++++++++++++++ The Snapshots plugin stores various aspects of a client’s state when the client checks in to the server. SSHbase Plugin ++++++++++++++ The SSHbase generator plugin manages ssh host keys (both v1 and v2) for hosts. It also manages the ssh_known_hosts file. It can integrate host keys from other management domains and similarly export its keys. SSLCA Plugin ++++++++++++ The SSLCA plugin is designed to handle creation of SSL privatekeys and certificates on request. Statistics ++++++++++ The Statistics plugin is deprecated (see Reporting). Svn Plugin ++++++++++ The Svn plugin allows you to track changes to your Bcfg2 repository using a Subversion backend. Currently, it enables you to get revision information out of your repository for reporting purposes. TCheetah Plugin +++++++++++++++ The TCheetah plugin allows you to use the cheetah templating system to create files. It also allows you to include the results of probes executed on the client in the created files. TGenshi Plugin ++++++++++++++ The TGenshi plugin allows you to use the Genshi templating system to create files. It also allows you to include the results of probes executed on the client in the created files. Trigger Plugin ++++++++++++++ The Trigger plugin provides a method for calling external scripts when clients are configured. Caching options --------------- These options are specified in the **[caching]** section. client_metadata The following four caching modes are available for client metadata: * off: No caching of client metadata objects is performed. This is the default. * initial: Only initial metadata objects are cached. Initial metadata objects are created only from the data in the Metadata plugin, before additional groups from other plugins are merged in. * cautious: Final metadata objects are cached, but each client’s cache is cleared at the start of each client run, immediately after probe data is received. Cache is also cleared as in aggressive mode. *on* is a synonym for cautious. * aggressive: Final metadata objects are cached. Each plugin is responsible for clearing cache when appropriate. Client options -------------- These options only affect client functionality. They can be specified in the **[client]** section. decision Specify the server decision list mode (whitelist or blacklist). (This settiing will be ignored if the client is called with the -f option). drivers Specify tool driver set to use. This option can be used to explicitly specify the client tool drivers you want to use when the client is run. paranoid Run the client in paranoid mode. profile Assert the given profile for the host. Communication options --------------------- Specified in the **[communication]** section. These options define settings used for client-server communication. ca The path to a file containing the CA certificate. This file is required on the server, and optional on clients. However, if the cacert is not present on clients, the server cannot be verified. certificate The path to a file containing a PEM formatted certificate which signs the key with the ca certificate. This setting is required on the server in all cases, and required on clients if using client certificates. key Specifies the path to a file containing the SSL Key. This is required on the server in all cases, and required on clients if using client certificates. password Required on both the server and clients. On the server, sets the password clients need to use to communicate. On a client, sets the password to use to connect to the server. protocol Communication protocol to use. Defaults to xmlrpc/ssl. retries A client-only option. Number of times to retry network communication. Default is 3 retries. retry_delay A client-only option. Number of seconds to wait in between retrying network communication. Default is 1 second. serverCommonNames A client-only option. A colon-separated list of Common Names the client will accept in the SSL certificate presented by the server. timeout A client-only option. The network communication timeout. user A client-only option. The UUID of the client. Component options ----------------- Specified in the **[components]** section. bcfg2 URL of the server. On the server this specifies which interface and port the server listens on. On the client, this specifies where the client will attempt to contact the server. e.g. *bcfg2 = https://10.3.1.6:6789* encoding Text encoding of configuration files. Defaults to UTF-8. lockfile The path to the client lock file, which is used to ensure that only one Bcfg2 client runs at a time on a single client. Logging options --------------- Specified in the **[logging]** section. These options control the server logging functionality. debug Whether or not to enable debug-level log output. Default is false. path Server log file path. syslog Whether or not to send logging data to syslog. Default is true. verbose Whether or not to enable verbose log output. Default is false. MDATA options ------------- Specified in the **[mdata]** section. These options affect the default metadata settings for Paths with type='file'. owner Global owner for Paths (defaults to root) group Global group for Paths (defaults to root) mode Global permissions for Paths (defaults to 644) secontext Global SELinux context for Path entries (defaults to *__default__*, which restores the expected context) paranoid Global paranoid settings for Paths (defaults to false) sensitive Global sensitive settings for Paths (defaults to false) important Global important settings for Paths. Defaults to false. Packages options ---------------- The following options are specified in the **[packages]** section. resolver Enable dependency resolution. Default is 1 (true). metadata Enable metadata processing. Default is 1 (true). If metadata is disabled, it’s implied that resolver is also disabled. yum_config The path at which to generate Yum configs. No default. apt_config The path at which to generate APT configs. No default. gpg_keypath The path on the client where RPM GPG keys will be copied before they are imported on the client. Default is ``/etc/pki/rpm-gpg``. version Set the version attribute used when binding Packages. Default is auto. The following options are specified in the **[packages:yum]** section. use_yum_libraries By default, Bcfg2 uses an internal implementation of Yum’s dependency resolution and other routines so that the Bcfg2 server can be run on a host that does not support Yum itself. If you run the Bcfg2 server on a machine that does have Yum libraries, however, you can enable use of those native libraries in Bcfg2 by setting this to 1. helper Path to bcfg2-yum-helper. By default, Bcfg2 looks first in $PATH and then in ``/usr/sbin/bcfg2-yum-helper`` for the helper. The following options are specified in the **[packages:pulp]** section. username The username of a Pulp user that will be used to register new clients and bind them to repositories. password The password of a Pulp user that will be used to register new clients and bind them to repositories. All other options in the **[packages:yum]** section will be passed along verbatim to the Yum configuration if you are using the native Yum library support. Paranoid options ---------------- These options allow for finer-grained control of the paranoid mode on the Bcfg2 client. They are specified in the **[paranoid]** section of the configuration file. path Custom path for backups created in paranoid mode. The default is in ``/var/cache/bcfg2``. max_copies Specify a maximum number of copies for the server to keep when running in paranoid mode. Only the most recent versions of these copies will be kept. Snapshots options ----------------- Specified in the **[snapshots]** section. These options control the server snapshots functionality. driver sqlite database The name of the database to use for statistics data. e.g.: ``$REPOSITORY_DIR/etc/bcfg2.sqlite`` SSLCA options ------------- These options are necessary to configure the SSLCA plugin and can be found in the **[sslca_default]** section of the configuration file. config Specifies the location of the openssl configuration file for your CA. passphrase Specifies the passphrase for the CA’s private key (if necessary). If no passphrase exists, it is assumed that the private key is stored unencrypted. chaincert Specifies the location of your ssl chaining certificate. This is used when pre-existing certifcate hostfiles are found, so that they can be validated and only regenerated if they no longer meet the specification. If you’re using a self signing CA this would be the CA cert that you generated. Database options ---------------- Server-only, specified in the **[database]** section. These options control the database connection of the server. engine The database engine used by the statistics module. One of the following:: postgresql mysql sqlite3 ado_mssql name The name of the database to use for statistics data. If 'database_engine' is set to 'sqlite3' this is a file path to the sqlite file and defaults to ``$REPOSITORY_DIR/etc/brpt.sqlite``. user User for database connections. Not used for sqlite3. password Password for database connections. Not used for sqlite3. host Host for database connections. Not used for sqlite3. port Port for database connections. Not used for sqlite3. options Various options for the database connection. The value is expected as multiple key=value pairs, separated with commas. The concrete value depends on the database engine. Reporting options ----------------- config Specifies the location of the reporting configuration (default is /etc/bcfg2-web.conf. time_zone Specifies a time zone other than that used on the system. (Note that this will cause the Bcfg2 server to log messages in this time zone as well). web_debug Turn on Django debugging. See Also -------- :manpage:`bcfg2(1)`, :manpage:`bcfg2-server(8)` bcfg2-1.3.3/doc/man/bcfg2.txt000066400000000000000000000126661223671746500155750ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst bcfg2 ===== .. program:: bcfg2 Synopsis -------- **bcfg2** [*options*] Description ----------- :program:`bcfg2` runs the Bcfg2 configuration process on the current host. This process consists of the following steps. * Fetch and execute probes * Upload probe results * Fetch the client configuration * Check the current client state * Attempt to install the desired configuration * Upload statistics about the Bcfg2 execution and client state Options ------- -B Configure everything except the given bundle(s). -C configfile Specify alternate bcfg2.conf location. -D drivers Specify a comma-delimited set of Bcfg2 tool drivers. *NOTE: only drivers listed will be loaded. (e.g., if you do not include POSIX, you will be unable to verify/install Path entries).* -E encoding Specify the encoding of config files. -I Run bcfg2 in interactive mode. The user will be prompted before each change. -O Omit lock check. -P Run bcfg2 in paranoid mode. Diffs will be logged for configuration files marked as paranoid by the Bcfg2 server. -Q Run bcfg2 in "bundle quick" mode, where only entries in a bundle are verified or installed. This runs much faster than -q, but doesn't provide statistics to the server at all. In order for this option to work, the -b option must also be provided. This option is incompatible with -r. -R retrycount Specify the number of times that the client will attempt to retry network communication. -S server Manually specify the server location (as opposed to using the value in bcfg2.conf). This should be in the format "https://server:port" -Z Do not configure independent entries. -b bundles Run only the specified colon-delimited set of bundles. -c cachefile Cache a copy of the configuration in cachefile. --ca-cert=cacert Specifiy the path to the SSL CA certificate. -d Enable debugging output. -e When in verbose mode, display extra entry information. -f path Configure from a file rather than querying the server. -h Print usage information. -k Run in bulletproof mode. This currently only affects behavior in the debian toolset; it calls apt-get update and clean and dpkg --configure --pending. -l decisionmode Run the client in the specified decision list mode ("whitelist" or "blacklist"), or "none", which can be used in order to override the decision list mode specified in bcfg2.conf). This approach is needed when particular changes are deemed "high risk". It gives the ability tocentrally specify these changes, but only install them on clients when administrator supervision is available. Because collaborative configuration is one of the remaining hard issues in configuration management, these issues typically crop up in environments with several administrators and much configuration variety. (This setting will be ignored if the -f option is also specified). -n Run bcfg2 in dry-run mode. No changes will be made to the system. -o logfile Writes a log to the specified path. -p profile Assert a profile for the current client. -q Run bcfg2 in quick mode. Package checksum verification won't be performed. This mode relaxes the constraints of correctness, and thus should only be used in safe conditions. -r mode Cause bcfg2 to remove extra configuration elements it detects. Mode is one of "all", "Services", "Packages", or "Users". "all" removes all extra entries. "Services", "Packages", and "Users" remove only the extra configuration elements of the respective type. ("Services" actually just disables extra services, since they can't be removed, and "Users" removes extra POSIXUser and POSIXUser entries.) -s servicemode Set bcfg2 interaction level for services. Default behavior is to modify all services affected by reconfiguration. "build" mode attempts to stop all services started. "disabled" suppresses all attempts to modify services. --ssl-cert=cert Specify the path to the SSL certificate. --ssl-cns=CNs Colon-delimited list of acceptable SSL server Common Names. --ssl-key=key Specify the path to the SSL key. -u user Attempt to authenticate as 'user'. -t timeout Set the timeout (in seconds) for client communication. Default is 90 seconds. -v Run bcfg2 in verbose mode. -x password Use 'password' for client communication. -z Only configure independent entries, ignore bundles. See Also -------- :manpage:`bcfg2-server(8)`, :manpage:`bcfg2-info(8)` bcfg2-1.3.3/doc/man/index.txt000066400000000000000000000002041223671746500157020ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst .. _man-index: ========= Man Pages ========= .. toctree:: :maxdepth: 1 :glob: * bcfg2-1.3.3/doc/reports/000077500000000000000000000000001223671746500147615ustar00rootroot00000000000000bcfg2-1.3.3/doc/reports/BadListing.png000066400000000000000000005704061223671746500175230ustar00rootroot00000000000000PNG  IHDR/:iCCPICC ProfileXYPTK;8䜑$$D **H2`TT$E}ommnO_>}:"#C$cmt)N.@?< y/H++sߦaImboO b#ach0Ѩ_:v6z0fGVS}`;`} ;X'RXG",,bX? /67=_ 9#᡻wֆ3^f [ ց8m`pݖﺐUdGZ&i~19eft0n1<PWLa>$9Y#G M`ƠX`af-0āh$9x%pY a׈DGO_$E n/?/Klw1{2i?USIfVfr0JEi4QbAqIJBep/?>k/HTs3GK;ߎOϷC7vǁ""c):HIPdd3YѢͯbyl.>GG&z)\b-9N> ae<@,:X; <}0$}`?8A58 y ``L9:AX"C/$Cr @ yBP8@P!T@%!4Bߡ5AD0!i BaC#Q$D&"QE4#݈q H$RCZ"]hd*2YE !!!Ǒȟ( $a?5F٣|PQTTՀjC&P -4- -VCt}}}=^`0,*Fcqc1y*Lf3bٱX % –aOSUNj\p \ )nNCQI9JSGs :OkR| ~ H  kB!PJHO $2ňzD7b1XO"". jӺ6Ҿ]#IљҥUе=BOC/DCAD_B <$L#yRINA!!C? #Qрї14c/$I }u;)& Ʉ))e]adfcamn^!a͑q<':gg+k. W2iG\?y#˸{yXxyyyny5yxyo~0St(RJeϘ/1:?ߞ?@@ >&B4B*BB' S P٨&$juLVDK$JV(FTE4DJtP !((V!D!$$^%>$P$JHK6INHHKeHK}v.'%(*S'3*(k*!{M\syZyC4o ~ '^*-)(n*)+E+(* *{*W*0XWEꪦP֪U]R=D..]u&554j45)4ǵjk hj՞ i+{UwEOM/EKo؀ޠ!aa႑QQ1̸xĄǤdT4ŴόhfkVn\<ԢblIJ*5ʺ>{d=ltڍڋ8;94:8;:;I;8=tprp8ujzuM-m؝׽߃#=^{.{==ynxYzz6^93[;W7_?P0X8T-8:x%2>d;1B.331<$/'boPxdVxZh1P{LG,Lʼn׌_MpHaoGb $}{ۿo"E'&JNIHLJ7Jo؏ C&0ckܙ陓6eeEgR?T~|Xpߜ2%y>y)=ѓ0 h42&NYSs9_PR}"xyiG`ٱ**WTV==}:zTЩ5F5mµ%1OswFLYg7lq;ڄhkmvk<E˅܋bOPyPa#GW>VzDIǠൡ]Cj=~s_~14l?rmd˙W>>yCzSm;wƕoNOs/Wj}0-E%V?.-䬲6Tyoqmz=aQ)ymlkl;l{;+@z;ñ x1şbG` `^ .)ҁ|3G n! -jm~IIJb{q44Kb?@fēQLhf, =9G g.eQ^E/F`ZH^8@MOĆt+9>yӊʜ*vyj6ZGb4 _cLM=Z~clMg#`jgaxЩ̹ۄ=O[G?, 90/[\ؼ$p9JՁNk}o |;+;'wo_ܻew~v1 ӡg]/.|Uј177oގ;4<>?>xRvrCTiٌO>ΝLyK ߥ{:mnoOv 4#O Y6ZFFl,kezD/aoF`"G#ћaI^d̼ºNpsqGQ3 X f ݥ"EESŗ$eO7,v޷%T+Wc; :3ݽq+v[|];N'?px(q'>M{9 7M|hs/ TB>ACEO;D'MO@oKA%e5S4'f?8dy:ώ`/i+[{'~$)BŒƒBQwb QZR?ʾ{#?0أtMJjZ]424Ӵ+tt^/dLLk泴Jnyjceo0*v$x{k=wsEhGG''zD59b߭Tb{zgÁլl\ռ|kE%'yOWFTT8橛?Ps.9q퓝+oqwYݓѡ"5FO54<"e镔?m7h76g=F8x C1!шsH Y|DPhA8nB#L/VaqT}wFe8#&jbZ3stpϐHiFkd)&nRfv*VA֋lZlC~[圪\i܂܏yxEx_Qrt/ 5 R婛"wEyoK I^,}Il\)rB#ʇT&ūG  ҊN=wU`cL5176+3X䱲>`b{.A‘8Kk[{Gj6gޫ~y!FaiEbTMJVr5u9]sLY &8}W>}lPxDOdJrՑjS-u3 :MZ/^molwVB{BODDQ_PG_QG߈sh Ҭǖ")+W֤wm(ob7Zs Y[""1T@#Psh":9Ec#} C4x_0G"ޢդ{MBREap2\d4\Ȣ򙵆͕}3K#Oo _00JʼnI\R2 F1JuUj꡻4iqk>g2p5\=z<㼕}I~kA+!%a#e.Ǩv'&J''r>M:f΃Pš'G]+9_9a\zlB2jZZg54^lknop UW&MvvzsDWN^;7=5l>{<eҫQ7oߌLorX3=9v.u/_/-cQzҙN?~.(\5\}Sgᵉu ō+sΛ7"osnmپ1?BD]oY^y6 ů@Է¿|#2p^., pHYs   IDATxi%ו?W{^]Unt8pc<#[d)l)šZ]}ݧzjnnDAAA~׿kOoTF1`$DY~6xx|Ԕ    ܧ:}##C}tW=: /0pT__*   >DQqBFN!(ȇ(ݍO8]XXXZZ    #qv>F0=u'y v    ܏co~TWj]LPPʡWT@PF"p   pRAy/*(䀼z "rrQɘXAAAASRTN Gh$0J)8AAAAoQVk-P'B-"f)*    ܯ( BQS88AAAAR# P(( H*@    ܿ‚RHm3b M~"GAAAv6n0)Z A/A܃C   $Pdn !NPOw6!    1X[Bja6F~f    g2R(XbY)`f"ndՃt()   pl8NaAHM{-dh%#8>G|uDAAAAdQd/MFA!Ej""kRֻ|DyZ?&iž!    lglYf&@bz^,\p L"R{N$v;$8I&Zk-,[&E#O){a˅\RI!AAAAJM  U`ftJueVܪ՚f+j4Ia4u.sB>-w z 0=Lp   =s6W 2PdrF`fW9BDlJ/,ٛ7+ ť[jYVhI$)') "OWӹ ʇ|X床rwalobwb|`rzR d5*EJ)1t   ]4|>HC)(B Ep)ZHtsڈMb'_;uǧܸDbOAPr>HL% `lRnQfPAAAAO"Yδࡨix@衵3h>8Z+'޺+.\>5=5e\)(|;9fX fav"(V;X8)[BZ߼uq|yki~G:| [[Wr%dtj\P)  HJ4SJ#B ,pdv}euk/v7^:^%r CelAXhߩA dc߰DCks}%C K?z/?~ɋ7}}ގ&=`O9ʷ}_:Dg0igrw~`  pa%jXQӈRϽOz&ݱZ+y>g=toQ#pt]=A7qm%TAAbXXPj)WC1H/IztOy[hXXF0 P`ޱP)_sn,P,[FԢho^&V6j@Qja,RFއO`F z~2֙ o.W>ࡩ^p2&w\R=wpY6a ;}YVlsO^vIdpOgbp!s9 g~qo1oСwrbL۩YtnlJL7   w/M 7SeRx eVZV~޼y}Xh+gđC\@@HM` V 3 R"aؕzfqܬ8\oݪ֠5|a OP-V+oog291 K򳴿SO{0:w?\Fg2pc1ƝlىfJpk<%Yr6lb8g\{;,>Leeto^ fg4BE+rZ[rb0 [C`VYDLp/ RD{ZPr>RwablD1濉-:h>R3pe}ܟy+_|\k!sd^wZ>G {❥]\H;ILa1p}Y;ivJJ)2t.&IЩa݌[ql%)VN0DPnSCQn C1@0K@ D) _;8K., pV+0OCb!`^S=.| .뎢ȥ鐅Z92!ӯZkwie8ΞcFZBP(r\\. {,Qzjjsp垞edNƑI-qWLQ½Ě0 >rnNfqBF$IVJ}bX,|vߺ]ZV՚fղ\.84`~d+:T0fv%$3Kki;R8AAE8)#5q쵀AeA$Dtҟ<ϟ\@# 9uÃeStS&?:Ẇ#ff64ȵa +)TIAm0;{d*{˰ypeE~T_1_*o\|w~6E@[Ą@#IK^ت׿cیQYvjs\&mgY{şEQN=S(qLD.Uv }ؕlI9&c'([ BwwÇ||ޥ,$Iш(jUՊАR*S0tM3JVNT*aP;:Y%S˜5ͭrۛ9:V6jX, LMM9$;"kkF8$i6Jeii)Ib!N".wMk0 sW(|DlmGq;ZQitJAI!@) C(+AA^MfE;)5(H-Qq Xڿ7[X@1b(hA  2fǏrQhB@ ,lcbX1[&{SE?Ѝ^c "v 6-^ e"Ԭ7ZzKgк0- #|@y>5_N#NQv$a,LVj`{{^Zj5fr؝V2NFq҆Kq$rXժj^VF#NAk绺rX$J^__w m5 jTV/n۞y .IC/A*kyKKK[[[Ih8vGҬ&388ei4J0 ih4׫vٌ8;RP( 777y]]]GQ3 ~Yx2׆h6΂]0(w iM8j7vlFiZۮ6u*r9'cCPAA{ [ )(͛7J%<1s985\.J3Jq"ZfbFJp> Pj S*擅[8be`20]o % @ie45i5SO١?;y ./ 2}(Bj)AC`VONB [[[QeMt5av~ʕjt,vw tvAHtii)Mx ϻ7nܸzRRqٸk n \X,}MNNNOOObz+WFGGs\Nq^Ć;FQ{{{sfZ Ά}tR0sCCCR rd sDQjr333V]v7nܸufVsYgW xtuu ߿jjj߾}Ƙ[n]zuee%^+'ɬ䧞fv't4T*[[[#\t#v5؂ObL*B Qgw/ÝU|>Z>sgΜ!b<w*a#0 3u_YY\[[[[[pA"]L.Z0{zz~R9{+W]Zםˠ*v#v6|ёեRdiZQ0N8L\8h3|8@%"#Gp+Z< `eew~PkD(jy Jy̌}Mx[oF1)E1"9[0 0+ 0#eEP]>}ijb-+a$L9320Il+8eird68m0@4;^*B{YC޶O>sik$y~d. Wd቉^zuaaa}};;9 g=)I8u)#Z3::zQ+++.\8u;Sh4\D.+ ?f3\FQT]v̙'N;vkkkN4q{]ss_饗FFFL.G_.1cSjl6岳]XXX8yӧ/_永$bsNsD$nZ\\pɓ'~|nnn]]]W\VQrR-Lq55NfJtkkn?a*V>7n,?sZ 98fMLWytѱA\ݨT0nΟBAAA>.HX+4)| Yxw˵F@'(σP qhġ=  D)P RBꆡ(Xb% ɾRkRz-cMM &F`d00;:Q=ϭz}i$)?}5_:8}B5Yp}uPJ Z[k͛7O8Q,~~'xؘ]t*SwÕEiZ.><44$_zommmkX`0Jьy?ݿ[f?ZJvf`5VrwIȎh,,,\|_C8iVsGSЩn)vlʔ^xĉV afM@~lȒizwܼ~Ňz ֭[Ƙr$I:qbG&%81ōqqcGẐ8}!sd[6huu5MӇzhffF)u^zٳnr Gܠ+~}j`pYE/g?SSSZW...+(r&~ If^?x̿=shaihE geX"bgu=zL>vsR XܽE#  p!UJT<< =v#eeQ?~[?\[F!"(n0y̍#ӏW46L ZeJ)200%`,RXyYx74#%RD E~lLZ(NRCFV6؋MY2*00)o3li'{"E٬㒧vp;[WU]H&_=;SfO;s,L{XnO=Jٳ'F2fyƍͯG9tЩS677:DdOyILjуAp̙g}7ިVRi``Q?vq<=i&.-_ZZ~ǎ9vRT*6"e,! O })r\7668os=wܹ$IdLV|{<nd9H\ve;'NX[[V_җǃ 8}`66Ή2\3fxmm#sC#gkϼ.6j(i0H*Ұ 5>"N\^[[SZsWվ>giqwu<8AA%%V)%J D񫯟W|y| σr G~Toz<(l,fRB Cb)GcdP.cc嬬Xwn#7v%AJve) aAD Md?2)\CP#XIC?7Kkmu}I~N:PGh4>Ks\>>yd>/JSSS MF$ngì km4sssJW^y;ŋ pY;u 7ӝ v싙 1{6ȲV}}}ڷ~ }ꩧfggyii) ì$v-s7>(ԸFV=;vը֭j#LMM5gy_[[ 2Y'>q-99qR) "sl:{>;;/~ҥKn`Yv7ԍ͚ed.vv.;4' \~g} _tVT*===Dnxf~%6 w+׽sn1rl2C=4==O+++A -Y8tV͇\0=~_Wݤ7nDQaEټXll{h4$yȑѱ㫕'gCZ0`-R`F6ݹEk`kw' `-;۟zL88AA& <*-]a .M"o鏾q!zsةL|0P~pvo&+% k&` #4q(2c (0 )_2ɜ?֕=i RcYA1lҔ /frJ '-ĤafEw1RjV23B"V%}7U*bfח[2R w,G=PxmɁ.EGaR%o;s,_:;8_B^,$IPp=,N:C=#7ol4===NG.uVOOy?~K.r^ow84u)RX,Jn7ĭjUUhۮvu`Z~>[,xwyR 9M'bf>jF~~!*P4󩋭:6?fooo砱ޮ]r 1noooomm5msdR%F__8j_&W:F:qqiynzr#[W`,a-R "N`P {JHRԠޙФ\$Rʃ6Jſ٩zUTrnwDAA#W J-# c0i8\ }>XS</@v-٧izE>?/l6KRn 7ĩ<3330;(_~WuӀܹinnnk_FFFE+8otЁŧO_eN E"`-rQ;5ZsX"NԎ](Q Zo_}BjΔJAAAx)lRPHil*!%*+ c@E_ (5D R l`5dy TL1dL fM*i(11r%,@ j#tsF›ifB2RTSVb eqȀfV2=řQl{`Ƀn_1.dNκR1;;O՛|c%IRTy睷z͛ƘBYIbW^T*iVѐQizȑg}իa "r-:f3;vlnnnll0"Wc}? ñ|~~ŋgΜx֖k 2=wu}+.@+XcQkFOώ~mth=Z[]]\Í[sh%yXZʩKgdžf ]oG[]~Ϋ&  G&1pO:  hEBn{7o.>EBh _C)x alh_>[ <2 1A[ E)+)&y`&&J^|3R,)3+f٬vR`-"RCo&NW-+հ hia6`XPL x EĮ2ʂ%/q>?66mX 2=tkg-6Κ RT*4u#6zU\=߷oߡCFGG .oZfommmll Vl Ke(`]^^nZo*SnPm"zǞ|ɑcLVhcccsshŁ~tLLL</ҩSף(r*ݛZvĉ~39tЉ'xf  F$\СCVCn766: ZVݞwC[ B.C*+u8OOO?g B]-a_(\<~:uZ$+WqVOO֕+Wx QW]sLTs@oy3I٫XـVH!MwMafO>_;[4I}"=cv80ý9\]Bd,` tl篿pdfkלĸh  ql,s ֖ZX22ο]o2zwrz߃#_nYPjIYؕ,A A Rbn\Hc!O{M֒" `8oD[>]cƆQăDHs ڙDi;&-ͅ@MBN-bSccCw$ucއqyyɓ\M{dfSQ&''O~[ߺxn۹kNn777zW"dz{rr'N:!hϥ;v,omm]pauuutEwVuڵw}GFF9299>͛7]yɎ~p~~~ppi.{Ƌ왢id!v<Fz1`prpEo_~+:ry>Z$P)Ӗx8Vpm<J+g^O)*Ƙzh4ƌ1a {4MvV/^Z$I$)<ȗFq̙8 yKTV9Ǐ޿SO=^hZYNr׏viiܹs>hVVNrJQ}$'.a I @޿/MS߹U(kmf͛>;=w7׷(0Bj?槆{s殠AAmıJaV . .1<_| xj'W (o WXŠ YK "VNgHZ@\'z3~2(S˱I<"0[f%bkI+3I[IlӄdRB D7e1l!R`XS0Q?X+}%2HSa߃eXycW:vdzxxN>rj \~v|5޾tG~ǝ%؝b 92weko>k3gn޼z=(4uw W2055|رcFܹsCCCWvJVWW[+nZuڽv*ʅ }ёJrҥ=j}ivtj?V2i6f3M홙z/,,2Y0UZ":ttG]xRzafiJ(Vx8 f/dX0 IDATnoXQJ(k#ڽ/>箭7mb9,U`ѨT+ΜS@ (P@6ذ8LH m,CpHQ^E Bvs%(Y(OqUȂ"%X84PDeyR_NG%Yp`-+2#9#m\@zCJ{lS lʚYLw<22[FȜ| `27锬ecՈ sޞ@aEv<杝???gn<_ t G$!D -Rv={vffRH r!_ D)u"Z[[AZJU,3+W^~e"uܜyv'L)I{Νg~_w廮Whtk׮U*>j5K8{rtןp(~FnwyRѐYij3==k]z5I7x9s@Zkq͐!gh劬 wܹqFϟ777}~"v) C}BȋZhX9aZCѸ"Ƣ^W/ʹ:{;niۭVK)$?|Od݅Vo.~]X HҖE@8z7TJ,Q/ (P@ *e4U<#xX&j>Ty|EADkko:))9`F A Df (&:&=SL!&J-abF \3I7R6/G+l-&RcSf2,QBbpP Ƃ%ͰL S(di*Z-?QCo8R4, <-+߽?|t"p5-N0>}foˉ1ݯZkq+HVmwwwzzl[nI…c EoH+l6677xgժ \WpH,q_jil6Ϝ9sʕ~ODNPtz]_.YgYja>5EA\Z][[}v$z=)Z՗_~s֭e= @9y(Op8f˾G:HuߤA-l`Ŭ@`Xb,Ofۓ.)!RJd 0i`hX@P|! ѬQA}h(;wse'Ϲ EB)Otӕv^?R)U*ZIXyJ H~{9B=E^xҥK|(&''Eڼ8%99biZo߾u֗W;󎬉 íNsB_km5n>'sr٦<7ȹܹsn߾}xx(|Dx:"F%_lP岈8TWE zVWWWWW׍1ot=>>^XXhGGGnLIY!T"X믌OͶ<poرWl2Jh!]mVQ pf#Q4_ ʁ?ۅK@ (P@-HqP"l W\yܴƢT$tÇCk(Z%۳% `x_[kHLlA$٢ ؂Y"4dXX6<_mphHd ;$ n[RDt:Ĕ; kEj ̆aa,:0k = UC 3je*8 _+rwg~;Evq~E}u +H _pC_ |܆3"CEfsnn.MC; C˗%Zb0,//#Kp٨yD;I -Wf)7O=ɯ#8G~b'd. '+pqDtpp0==-}s޽+Zw S=˗hsss4MNN(9891P9I~t׿yi͓E( RsssЍ)RFAi{p7a&RAk3텪? (I%U<56p r!b3It(=0?.%Z"OnEfֹBQ@ (P @`H hο~ "#P hԢQ-̻f[p4'U)O)PLLR# S7]eHX )uc@~J(JA%DxV%%y!3;eh`lg%`- ;7߿^,ˏTO&8\l.'GZT9h" jHYy4n8t: Y={vrrr0FFgIdQU*sz>H<IH؍"eyh#`p||<99|ʕ+B + frr@^JpQ -OYG$U>JT*;r… VEQnJ"뙟vM;FQh$DR9ג_O0"wwPc/I^Q 4"HRTKsfxەmR¶( 0%Mcd BB@ (P@D*ECMpRDQ|[hA0&/*n=XRiO@yiO)eJRI+4&bE"(1մ^&DNl4qJd @ 0L0΄$z\a08bfbVleì` kckSd6X$&5uk'=30QQ(MQ ,ypmW"2=~u<קƟ$zZ v|uU"2G peeիJ?p}}]b<(VVVt͝V@ (P@g0 XVDUIdgƧ!8NWRiEaX:0''RYi7ZhoN{G &C%eeRP DDLDFqB,Xb&bRPHRgmU,5C,D13 C㔏XZ-̂e+g2[Zc 3[Qw_JP{M+e߫IIR¸r+v7ήz=IgڃR=7[!(-BS{#bi"1Jg,S)0A%bkzz'O %}e@%&c Jj2R)((1R)clİv@r͔}kښlkG8E$߼q+zp<'•a&?Ap7eZvʕ8E1?ƍwIDjl)#1Jʕ+K/o<2iQ*~='IǡVm":::Oz^ rκ8mBpr;cȺOOH7&>IxY~EQ\P$0Dv8>1*60#*g\:q ǜf.+OJEPu" -"אָVYRzxӕBۣh̨\ H^LH) |?;?=n>R "s?4KD0dW (P@!6)[($<46NI&8k: `DPQ9F&p/ Hg~)Lv`€PJYXAGPi E V!I`SHi=Is-Y$`ְV*x*9Jo988TWqCF}fS5; Ks33bqq/^l;wh'&&k˶@Ze(D!aC,Jds郼 *o r y.ñ wJݻ 9| ]vmnnN$c)d=]*J~>J)+E'8q,JnZBcq发ma"T**y6Ȉ$"¿1($,V)%b٥/^|_{bxZ!M ߃etC=\:ƍie1Sh (P@ (lY 40 1me3 8\ٶsOh,Up䅒@e(B(,$ -Sjm{`A̔)8Wf,Ls1 ,4HgfUDR=$`IJ#EJA)h (wî\ v#5x$@pH&rO߼BNժHzU6͋/nnnv:fh4*7Uw:'TR] Xs;qr8g9Mp#*O7[-I<9d$kƍ&9*7ɇh+G$JtgggG92(D!~DEwq7xg݈( `0;=+/{gwZ Z#M@)X"Iq~_zrt F (P@h60 e&R $Y*h(ʤr >CC%Z12unRFHөTF2#H#ŀj{G)}3}?K@IW_Xn|#oua4(z@<s8 T A R >mnaȈOGV=D$!zn/,,ͽk.խ IDAT7nܐ';CNCxʹETzOy⑯Og^8G"".ZL$k(0lt$oq,ԉ:x8vy4UU7m+ ~~dp^F0hIlllT [7{wPƌȾB#6n_ K7w$|3T (P@` h\p F۠"`F 3"8/voxS(c: Q^h~R{P *~n+g`;!8P!3 -l!l^A MVsVmQao _:݋Ɉ(43#=Ɗl&.Tٍ})L 7\*ӶCʪqi0ov+xy;6G.[NYEQ=88\__p˗*Q\PIgq%pv/ Gr<ȳ (#_Q;? 䨙ȱ nyJ$҄Eey r"y(upwbw88[oEQ^o⓯"V+#Z!$]E/aWK~w;Bm !}pQWձqU:&hGp-of},[vM 'kEOź*G^Eӊ؉|3Ip .Ih4 (^Y^;G[7~{ױsZ @"P+s_4Y۽ݨׄYJ`7 (P@  Uh%xGL=|g@pH ;]$@))(sgCoHES7]^V+q U 9G6 ˫+FDaS,$ WkTkasg~}o'A1&'D@( ٦ GJS3_NEb`yɆ <A+oN+8p#+ԴI ~E.; ΝƘׯ+_|jz] _@2A/._ LIpfx<|ĉz!?k#)YǓ׉HsVfwR'3FY@ǿp&q'4nB3Fq<cz-LOBޝ}&1L(T˟+ Soy$F36)P@ (P3#!mPci\?C)Gy!% )c3Ȃ-7d&.N-LUȒI2ԤZxd&kB?0kE2@D=Q1)>-]By::ra(xB9S~y29c8uBi2d(^:{ʥrvg}|&22`0iW/LVJ;'J3gp[@ (P@<"fU%X#4M1ʎZz"IPGI}qTQ'?Ԧ I"VZkaՓ儑u` AהV& }Ek1dIJKS0Jc@!sD\zIJPJ94%kN4^Ksƃ)t'(NdЏ 8MpƘýr< RY>u{.Ba޿aaayy_b$}QE%&H766VWW>h`8cJ g-rU~"rdNG, IGH4/24MáZ}p:|oZr:u4'~?z^+Jr,"jUQN{D 10l4z}{{;MS8A=#jHr<#y+g'yRRF*D}qnʕoܸMcҒc7JD (P@ |+ @hB|r΍".\ (Q %үNV5߸<;?;R&v0`fE ,Ql,lOa"`ܝvA媞菡GRhԟzʃpH ,H%cLJ ƸJ$qĤ_/-U|Y9v'zX@exițIFQ opÍFQ*`$ITT*yr(>ZV."x'xQ/ѧ󳳳+T$^ :&÷z+cɰ,ʂ8H|"JKz.-N9#s)c%R}Mhk+1N뛛F{pqH\KEIKtʋ/j{qg DH "H 勿+nܻwF$3T*yvo(P@ (kE,W gCpXk(6Ǝ5{4o?5W\MhJԦD1@Bls!(=1تb+z3 . +yrF 0$XFPc51:OpLT9svsyc?`zzv[%7aJ4crqYPq5TZT]YY988#Ex qLq\TZp82{45M+ @)0h4E>k)kQʬwUBZ]XXwG:5eXlnn; H~UI(wir$h8&i嶬lSёyfV\|ߏ鬬Hބdĺ< T* oYV FY7wQnV?LcnH[拗Y Jny!|c`]/?v MOݽ[ڙɩr,BQ@ (P)`@QSF" h ΈDpZF)P9P KP B+++?<}~59˕9Mk"T11l7 ؂"ev<"JXc?~F46HCH`Zجi<jkH( OiCCؔN{'GpH/%qCtwm4Yvxxhua2BT:7 ~$ZF`0ֱqf999nE!y' Q4듓q_~}8j"?PkNG~ vrJ%`#l8BV WK5q{;;I.,̋G88䈪BQ@ (P Ű2R>d2b ;|6#Wm L'}a 5NQs*5 GlŪ\NV)6*#xL8 lMRcXXNua5l<:`0 LN^Y_/,rlooZ[<\RJIbStǑM (P@ KA`BCcTaOrm,w:R̩d,EZHo8Muz'N41i"M-IRS*fKd,{dMbLb#g֗qq{HI4a6%9BgA`=|t}?f$z牧Ӯ`{{{qqQ8YkBF j}qV)\ (P@ Hli`c"5H2adZJk;;H3G+4E/~'AZs͉caDU61ldv_^VhƈfI'x1["b$>o% ɮ`/WZI$IR&1&8RfR#xvGZ7E7Qq>~ҥׯIjQS,򋷁,c4sviQQd7텥ҥ˕J{/}R $C@]ЙC/Ww])'&&mSPK/;<<<<<<<<<<<pطL:a'GpH2֬Vk%, n e1F)!R ]5S5cAof{Qk 6:`hK5:-*JzXva8d58rb6{?069кLݲc$Z"B&ߥIk2HB)G)?<ި5jZKQ,|vM)0s6RТBQ_\\|2K-=r8w8D63zF1===77w]q~_7|T*MMM}Wrƍe]$q>]ꇋÔVW_}Rܼy޽{IR6㩩Vv7777Ƹ^3(8Փ9л|G4vӋrQRGn!/]x뭷VWWa@ь#wDQ>h33ߺ3_&G5F"5YN0ȳgOsɗ[oW ~TD[w{\#:8r}0 -!BBӄ(D-ʴO @sެWa48A# # Oi|j0V"@höya>:I5{?Iǩ EYBG}\5Q&ouWmp b²ӤkD[ar;ܡ7jh k`-,f9Vy>~}E@$vhjd^axVmnnnnnj9Kܜ%ٱVizppC)UVwvv:N<{N6իWRW\yfښ=Ayb$X`N>}…Jqui"nĐfsii)[n-,[TJ+gwhjjݻwRΞ9sfߵYDTZdeeT*]xqyy9U399){"1NgooZ>s.]*Jo߾}Da4GCO*I奥3Nq8/kwA8LQ @0W.?'שּׁ}muuu)I\.KxS8 "`˻FO L\f P8Oh@Q~"d6+ #|A c-""P~zH=T 1l^Âa솈8%p ?bkLӟaC)qs`y&z?ƿ5 svC(V68b" bD w !C IKAc8="8l5&ZcO(&8hkkSTFzPAឱs9== /TׯK8*$Jr!3 ñz6dllmnnNMM:uj{{[2 D1ܹXkݻsssW^][[vq;J~gZa7$1ξ⋕Jekk7ّGERFVIl8sss Ƙ$IFl8xf+̒233q ð]vZ{3g~^[[ޖXסByXd[ӓS庌1(7SHٳg&*fhaX.MpDIĢPĖJ AWJJ%)ܹz/_|pp̮ũPؙ4M֒$9S{ .޻wo{{PSޥ/^\ҵWnmmQvvկRV>}N$Vtx`qq,Lzvl9sFL+\h[`īrn{ɗ^z… {{{\ z877WTJ7o޼yS9.Q] : Z` Q{MtzP J)E/\*8f RT[dWcc|2E̒@F¸IrU=<<<<<<<<<,;;;;;;i `nnܹs|YkmTr-*NXQ<9zp[4/r9MJvgy뭬O,7H;F166JXRYV:w ^%E.1Rp¦i `aa… ]$5d7:EIZ]_____o4ϟZ߼ySKhLnFV+r9cWi$It:RiiVA 655ufyݕf)vQ@V*ɂhƴ7n0\tʕ+B"ưmI*8^.ʑ5d橩L?z(僃cLQ=d TK/| *]X>+mYOK Z> 8P!%DjNQ1bA{+Ξ[:06Z "L`Re#H-MPdߋ1Ĉ)8#C4a)ke`P^2QF+ #@;gJ6ӕXNba,R K|$WB#frRB;Eb`,X?3VNA0w]]$8g`#/(N*fE^ .\E*^ZNLLax .0ݻw.^!:( C!)#2u+aZk%tll… 333^ݻZ 9 y?"y`hA@y┇㳄  ^'I:666??`0Ɇk1TI'ɑ}# " Jv&Y4!'KV+ ͛kʒz! 0HFb2;G_{ /c$xxxxxxxxxxxh`Y!@,bBsbɄʿgOUð7Bd]` CW/YP, ; e E <|T`)4uu]!1HEkfSQas æy()չ+d bݏ3u% CϞoXVXT܆ N}RMbe_\̅ &&&vvv_t$QLz}8޸qclllbbҥKJ~/(Z`$ yu-)KV[7oR&)!@q(C6;8I\*^MOOomm]v>ax}:27/Cb;PD}Ϋ8ֺ\./,,,,,j511ܐ)Q;㉉(Wd4IF"K(#8B^/ED [}CE1,,H[] #&E'/@ vťO{*A(D! (@@EPa P 0J ]XO)YۢB^ x.q#0o G$#Q!SFQ477w…NsΝn;==m)J+woW_|q~~ҥKrΝ;"pO_O׋6}fn6Ν[\\ 攩)HKnz,;w!)֭Vkkk뭷޲.,,\r\.z=aC6#wg/xbVJ%9oU[}bbq:H-*t{7)#7G{xxxxxxxxxxx|5l4[(36hS|TKHc- ]X^Dq PcA!B T*Rψ $GbH! AcT :Fg Ѡ G&v)*țUX`N(\e,=45j kZh Bk$66~sϼs8ƎTy"NRmJx{bč0 '&&fffލ7zԔdJV'mp)șyrrrmmM,--]phܹsgww7I9)INޖFtrjΜ93777lժ"xqtEufk8B5c^GmA nz^Tݽqziiʕ+Fcuu@S.@*dpix~~`8ժvH0sQLC:cB h)8B0)^'z<w   /[!In(Sx?-ʕ!Lq ` H QB@u(@H jDmI(U pJaL<dF>Rvd*yHFQz2d3`dmbmc[560zm`L͘ĚRjF4 U:laXRՙs.׫Ć`;bIxGEGɡ,˒033XYYٙ97(b@.:zA^zUk}ԩ\__w]`*e 5r!r<33\VEorΝfY\G-eϜf;āR "o `nnnyyYsggGR(mOGNh4Dbp-ERS±F=X9@oԠCR61HL*ݜ-#:q$ _Xe[2dbiBY;뢒R_r}RD& =`#VǷ >~:{.Ư̹so̻lro߾-)|g:Mp8 $fSd. Y<ٜ>#B+}fUH\q?eý^$1dffh o؍)AR|MћHggӑJByZ9ZƗSKVTPspp%z]jZVE4~8i(`p5-r#w,}Dqڵ BBOI:%WS3if%I" 2AA+eCK4! QnOp5 Ϟ[4Ey`#Α#8u 0VYA$K!y3Zn2Ċ fWv:EaR 8^[#(f d 0fXINR9Ln]~khXhik-g~ç'GA+J$+++bFp5O) <)%1ZjwpyxLEw 4M(ZZZb`p~a^E(&}Pb] n鈫e0jMMM I'2&ZW*}4v)I;;/"y.H9X֢=ֶWc$dllh4 Ȃ8bm9n+,#=XkׇáEge0 ûwdc,8S)tTQEb.nxxxxxxxxxx|P`2c2Z&Pc IE[ZkGm)9Q[ )ȝk,N9R MXnqegg9@qYLY1Y#nBSGEˆp7rjфZb>m;H^!8P c@Dt3W*zHR( @P @@Ad+ ;:XW(+t)&wW~xfLB6jR4sA?ē*oOCFMpb s8[HRh>>߮wA**E# T!ׄZvAR;Aȃ"y!4K*4nx8#Cy v[[nLAJz7-qC)' J 1T<C[O}K#H YĢR~^?CJjȍ_d@ P2PC!($'i&d$CD1{{/O4+*31zbXR{zt(ZH]k`u.Lܓod^ θ0]>e; p!.}Oj:շO&BΨ"BjGqER8]DI)lB!gt@!:9-v EA'iܽFB@*0 u P!Dpzï=?v%IU RARPA9;Wr}h 0Pߴ/RPвU<fi-K;_!Lt 饙@%:V,3Ȑr,ý"٢)X:(?/~r SHHm#T<ⳏ8HBt\a8~v*Eڕ'KVgdd1O$Å6+nC*T1r$brũr9>y#Mqxţ\͓E@BYB#QU<=T?+::CTb(LmA:#J񬫫Ds,gLq 1ҔJ͵zmbvֲ5Ƙ+Tw{+w6~e{AnB15`fg90 N{$0#?:{15H5t foCNg1OT֮Ȼ3/!*2,Wur=tN R|t[tANďy"2",+)lV%G-n#<ᣋpf;yj{Ű mw3D, t1&O+;>@`,jRDX37l)6oHj/՝s%u\)H*^p"Mw~wI0 Ύ.j-ml(,zm2GDf`%Cb8hpS?sTxSxI)~lm:J=xp"긏B[ 3vCl)iM}=_^B=G_tAg7Z:==a5#$7V H཭E _N'''cޖWPf+|_j4ߎՍo'wA<ȵq ?E͉ZP cJj! t-*ypڊEHU"C(P2)6aF $TBbH)Ű`"^ $Dd RQ@LP1  VPHAe|BRZK)ʙ EE2|]]sv#{5|)+ ]:GPNt:j 8ma6A%]xGʥRcy~A-2)|$OgYP a {HLdm&X),,Ch?^7sݹ0hk5DdDQ,jt5ր'n0Vˉp#09S9a,pW+?#+?cfQŽYnc!` Tͭ{$M3[Oj{5M 5k>7{ Eע:~P 86n, MsI~=j1Y9vNi5̈́ʂHU0`)K=G鑒 ݦAL)Sjў !4E4 Ax+ 32H(2) B@ @!LR H0 (*a:AVr RED#$)3PL PL( B ˇ d=rP"R , AF&,㇉A@2݇r5<<<<<<<<<<Zh[%Q;) mʟO߾֢D0RYbH2(gBdCu.G^JA6HU[d9IS&F{7!4{,/a2tB9Ip|IvCFc0D?ٿӧ]wXAq"f{D_@(Gc8T=b@R9o"WpH)nb=٭np( 9sXT9'/0P Ah-.>pf8"YNy7GXR( @확G۔kU %*5ED%ED"LjXA1) IQ#*@7!S[P@D@@ ! FbPC"l1(׊>"!)9@)EL@@-SW*WXdd̏,ߊ93;x0T 0`ÉY׎$ @ݠO§NpoP#hhY)a i>D60N; :_*Q! ~oh ,)$a IYu!&DCE$a_й_?я<]Q@#@)x>xZa'y#8N? Ly#djb1N1RS\sb[̙{%'Auu&)`@Fn Fn{*`>6ZXe `0ma `1cYV" ȗĽ( "3V-!E7^[Wk->o.b a4?hT53jOC ˗;# _!lVZ(Q" ` $N2vLnQBTɝ߮NSc#v͍kd&f0֦¤-`,RX);lNpQ%M0-_R8MC)ȝr|[Z(' zD`GF IDAT DZX 2G}@tq&0`tf83E2Q+)oを#{GX2wR1EkyNɓ uXfrK2o+'/QRF`0$5G{61-34p"b__~_[?&_'^d(0%tPW61(@`&@q)q {P^Ij p֦;$,2D{ 4#0*;Vj@1YLamaiz_??)"JSEObJ3ʸ_;}0N7y[dپcMIw8.kB >ndP-*=*NA dcm3ν\ʐ+A#_p( !`(@sxA9 OU$,X8.F"ԃ! l>\|KiLFp<Ešax㈰s5!tlGئ<<<<<<<<<<<2U%@HqEPyp>r*N #C"Ȟ' EE -:=@AXn.Eq{6_88`d5Wğ61`iB*@p))2RA0OB0|R?ˌ#R eʙf(>CXIɟn'GV`yΌ0",I!0)LZ(&Z#k<؍>A%ǖk6>*4!R8Pw&HKI:L3<^{2h@{B@&2+^$5 Hgf=Ee²83.ð ,',bNc8kPetiڋ\{y5zYztBi-(kfڌqPB!B9T8S3=Ve8l4 3s]Oyo ~g !`PTRW6n@ 8.CH@ @"B7i)98,0`` )aV)Mb%zu%#b5jn͔$"B!BN:Iɽ=}>U xU qu{؏xDiNNaAjN4A)J,2(T38 %,+8PloBZp$t0LX’n0`}Xe9H_tq>휁}/l؈zx,1y !Q\@DȒϺ}CD!i/$dLD_ LJVq@JKSA@iM}輌3B 2ފূH>IiueunR{i]J@0Em U;euXtW2{(,PTASB`5{"ݬSU)Aiyjl!B!FLr\BC"!P5xΑ8sUiKS S aBSQ* KB7`^Ki0 0;)d%K`q8 L2(I]eH a,R S:L eB 'n@X11g\;q=:)鏍N4!9㜹>*O0|p?li2,)s(ܝ;%H8/RZ8*] h'GH¼@+V+2dhA(@aPg3A2'bGW81,HɝRa49)9.gI 1`DžKDAΚ,`PWgRrɸ *`J9bG$dNXT ܉kD1D `B+B!BHI3@QƛpQ<ZE8lvRFUCSX@8,BqՙKbJ(*:FDŽeB7[P:5!n=, [D'!a * bB`Â4-MݺbaJKkj{MRrQ%@8` p0 ɠp^&%+ ]iueÉН!6gN̂h;$ à01Ę L{ArWJbq@ev|>pET(Q΁Gpn?Lp @W PpYK`82@r1&981sZs?)nuÜB!&`AHTpAQ pLR%Ab|M'˶mvP5,_",&tLp w"?tJd{v wvp— 0LH ӄiF 0LH7aOTta4nܯ)>OBBӴL)c:r^UsV-U0U@fNA߲3Ch4razD•7t XlAgeKQGb?-Ess}ҴVx.PU +Ѕx[C&gθt@a큰S+P%A#>p#w%eBwʤh ALGqHfR0< Qvԃ3^aYeLTw{$ θq̪(#R2 0)qiLqϲ_3)ǩp.+D/7p_ePfi!%"wǎ |j|!B!FDs f SfI4XLwX? {Aztz{u~ Հ@Q`JE~i/jM(B:AƜ,(ϔ%c:v xF`?ثŠ`pbÄeuˆ:isu2XE$ҝ#*;E0<@\)*8'1gNfHŐ(^@ )~h_/0-Fxˊm)mtF8+XP tCDC!cgP@20()|STqpn848T8=1v=r;Hr w:=yJaPD؄eNG2p8uAq=1"%7]ʎtȒP!B!QM0H˔,nrDUܼw7htum,q?z1H  @a0%TLB T pvpBI: )*Bq1(ty 9s];n8%p8uv,{FjyDvTfvђ*ݳݕNJJz S ] g0<%1ytJ'D9Wh5JNt1ъ] u.EP0AiigؒR xC3&8@tflҲ{spkN\Iooc4wvs0{q0a !Cn.Fp*1z8==-Pv 8u&NPdnx>;pA8<4;Y/o |`@!B v'Ògl%PtE{Bݻo8Ejl޸m{s`Ih f4e À_b#`#I܉ 0$HLBLQAJ˙cJ&q-=mwhi0 UU벳$Uy2cN's\2E-KDGGeRp[U4JIhWD PޡA``@d7d`SBE~@+EH)ܠ`N@9 q&9LB:q{;pҍ8C? 0&eЊ)R:w %̝Sr7vص4JNlH1l;QJc>{:-㒠ǑqY8I Hs"KN Tء+{ B!cRZBJR ` tY!Q֣Qk(eF)_8x^%?Z5#+!܂x[P3v!fAY @ 7[pp8 ',gM z1 r [Ev^nݪ9)61M`ܪna!Y0s^V2Do\ t>C쳡lP: S2ɨLJGa6g=WϕURtB!O0%`gTdJ40-b' Q0>ݺ۾cz/Y|,GrvPP e${MHdRodX. MAN};ۥ_ώݻk`ZQnB!B!z Ғ KX0%LK" ւD&NݺU֭uKݻҹsk;#,(< }D\O s:5zw_ޥg/!B!HKjBJ`S͔Hb<U[Z6`mLqvmKߵ_w(+';.i@IA7;Ò,+&96abf4k!qZmNiޢIh+h:`a˺<B!BsKa1_0ʠ9§G9N=iEJ"o.5w{$P֑cy)(*֋źa!,R9:UMU<1^O/:%)q S5Il2՗2p,΅e78g'|B !B!@2%Q,q{!eHbH|QY pJa^}#::]Vڵ <],'/7 ȯn )4x4_Lt/:> ZELJ)%̩B!B'4;и9s6 Cp8 0î]0 ӴL 씣s*pfqB!B!\T8S <*Ux;NѠRJ!`G`iA2*bY #c EKB!B!'c*cRr=EECU^ES$pc%|y4^L!B!d+*!= p<JJ@B!B!DH* 4ބ3x$8GpTiH"!B!BN $g *WWWà(n`eB!BH݋Ȓ%t@p!D] !B!BRM0H! -L pB!B!'a``Y% A7o:NB!B/&%!` fBG$'B!B!SL )X!L i,)FB!B!_iIݔ`J0,nQB!B!J ޠHc>4B!B!&Qd/j ~4B!B!&Q$ LdJ3((!B!B?sKa1_0ʠ9§G9(A!B!K2%Q,qmD=sB!B!g @SqES9 l2<$B!B l  Ve :@}UVBȴkH!B!e}K`LeLJq THq B!B!_&\gP9aT ^6(A!B!K* 4ބ3x)FB!B!2 BrƠx{x< 6h!B!Bh.! L!a, ]B`Y0 8!B!R&e MX& H &b B!BHeXpa|Bb N1A!B!KJKHC*" %IFi!B!B/UXȔ Lb B!BHXMi i:Â0&)*B!B<@`/ $ 1&L)@#8!B!RIKjEBJ aDD>_ B!B!39 *3h*|xSB!B$Qі!!ID13Wh !B!B/q0( 4W40P `46N4'Դ]ZBI!BST8Sx"ZA0(A8uKfce(9NT[pϓ/?[ז8e6j>չv| xU&?}GؽetoYhH:mjuuY!B9S{)**|(RR uqϻVV\Ҟru#G;r5gJSDp8E#˻ُ<iZf?ݓf&]Ҿ{q&\L#߼vU 4MM"B!'IK`H0/9PUZ+-KVpJ]C-9Yv?KW#LD˓P IDAT^r'T,#T]B! E o™G<Pw 889 4B(0 7j7NWN\ԶJT{<ӮSJ]S2%NGɍ*IIk.B!rR0 BrƠx{x< 6,QwK[ChЮ Vy'@ڹogRV!wެ^8 ?>ڔ:p5n6}{:EӬjt巿"B!X4`0eD# e4buZ|HOOe~fTO 0GM[P6酧?xmE|[s s £y%'x=ԫg9x %AbLL%ê+'؟NJ׍FMOimÌmԲ}j鼭3֍"xtj:ҫq4{iЩsj>E@}9@x|q) at#:i,َA!BjjAZ0d%t[& ̝R]WuHw7v7{<s%??ÚzKEzIKrR$6h˱XxYS?|wZ |tơeO|Kh< $Œѷ}:]L$ξg#[*?^!Ǚ/]rE~ZF;Y!gp[N]Ή;ƴ]?VOϸR%Zi#ϿL74 煦})ص.YZ39o׌9J/Ꜵ`O~1sͶ//U-˝BE!Bja``$Y%[ >@Gٮ<5֝ң \6{ENqYCzի?ѭ v~Y&]l]ys_T5/\ c5PtC@\b ʴK컟x%e>;"x`y ˟KYfѹ[/rG/^y1tk1gO~=`\3gSR(+cѐ}w†/_g_^]9i~yoax6p4p(8!BꖔRT) E *ɨܳbowcJuZc?}xïm2kuK=F)ȏ}X _ݴtkޖ&LAJ~X\9;u̐⽛lO?QEC .Va΁aRC֏G10maie6'<.I+MǴi/selذ70/8!E=5t漨ݘgtJvyMܾMmjQ?YZe#Wݔg*LI-ZF{s?q-vYkL7w.6"ii@ '_9=8z@ƢUwqWZusfHPw͎%w?R;L)gRTҟdQgn#3shMZܥl">w೹mn Ԍ2_4w7K;g^{>ήP33bEa9KԼY0}G~ v_Mr&뮛ѫl0f\NF.6[ύWnM_o97)(TQ#49". ysڴqŘ}no(@w\VsbYS3gm SgGa;5NgK*:l[6!B6łnL u5Qd]3CQ|p1zۅʋ4;gJj&~{dٱ]uwg& }w FLgxYNo2"Š{JAIS;ܗ}m}yr!y믮{oMi7~# ~xρ0i3ۭtt@K1,,ypԗ|$4իU3fg|'9[. 7t%GOLܔce^n}oگ$i3 uۑfLpfni̇ŝC;=;ҖQesԹ#E.*n8< zW^AjU;iYԿ>@ ~ !'=ݻdvM"(>v`YEtyG9@ 8kWWs >5J kN#iD@q$u G&4<#۸ޡY=^Y3]≁HoϯI'C3/ȫBܽ`ΌV7w,ѓmC*)"n #Ȫ?T}gj ? 46sr}56H=LKO_/ sBVh5Ks =3ƅXUo7 @5aj/Nz{Q6Wg# פL}Mig;l߻MpYߴFsNL[^;uHr;rc-iuz/>W餙{wFgHm2SPmBH_w 5.qZZVe/u$cʔ?;6Փ&[ټby,Ǚ\p?}8Wse7t͠vaK 9mm_=kj푨,Og8}Ւ_=yR":؈|ŒLҶәڵ+Wn5tС}ʤRal3Gbܜ-_~G0K%,p‹gڻ+_ k:GZRRJ0,jD cm8黼 @р[mW=n8 H}x@Wq?s3D&, =>7>@Diz^U@T=kߋr ]wdࠆ0/KTKHYS,_~:gͺWioN_o .PܽrŊ]rG^(zvdQj?Q^8ʤ u'g E[IJAe(/:[nkbJݓ3ĶgU.K+,8@L^q7iPzǧ*'`5jl !D0v>;Jw9Uy:RM2N=rO,wv7UZo廷,F.~bho6cXt8kKvY3e) ~N&P#Ta,呜9;^L9㇖U陗_)MO߻~W;e3ω SϺtEK_g >_OEYB)ZG>0[=F ΝѲOĵt?ܙj19 *3h*|{\N$Ӌ*x3\NiX ZQG? ǔ΁h8c]/K0q2/wAN2#Nu*-sG.Jm%)4;76 uU_ rbp#N-{>]O>)֎Aח&ȘG"J.#|;.lڹSwQ8qɳo-q1cOdwy9sO̎SQ3+yUl[y"<&'m/}$A_r艕Gq"=@t#nĝm+6+̳QEΈ[Xdm~{4<1sN|_`|> s7/xۏ~|YOl{#(8 I O"RRڷ.o^ٲ LɨX3=+yws' yZX wwz`WYU$*c 9ʊjȞ~qA 7s:Ō~kۿ:r R9f_;/to 寜|MA7{6mۮmk05~?_"6Z9f'b%MZYs"4s|f~Mr"zБ& RO@ß;n}wm⌞uQ曤9?c6웇LazSݼBSn%dBS з!33bgeOvTaq핊ƪ@g#~= g;q][Ŏn y8{WG_4?ϛ8o y:yFL:nߗv 2zӞ|Zj&jq$8 @SqES9 ;7cU _m'|"_|$3+<}ŏ<}=e]~~`BU8==+7!_**TaYmay@I˾]ն`^عL$e@+F72fL{h=<:.ki}hjѩ˜7 +vo֫Ɯ1],z_}|ÒU6j ?qnpb[}l?(u q1 i>-{Mpyڕw=$)ު-;rv;Tvu̐⽛lO?yPP&U׆[6o5=XFVQұe3?3xo׬ܬr_rPp^)|{1ۊY@ms {wdԏl*=-z3~$j^kRUlco>E\FDv砠aZ+<뱯 P{ݒ}/Qۿ2 wUX ]Uk<Ԧo苍xrɓ;0ഁ݃=%yk濹I_=x/P6kL'ԎCw =_]^=A*)@((z盽M7W{O)\4KGԬʪJz 6is6fLkhvݍ^~!Y\?~ @"|nNn}EuIKrI/lT4`Σр{PPz64n.ySW%Ͼo0)g8"S{)**|(rÌL,s_[ypdр[^r" ÃstIo/I_6GP5K}aAݵf^=ޞ6tLe^:ywI}V$8/cEWnar])NWvmAK:._|v6D4K~TVĦ|„ &L|ĥݪrzx7ڏMJfta+*[jkF=Nka R֯& `2})Ȟ6d x[*JKJ_&/HvsT2c{M|`ӳgL \.¯zRWU@;ώn9iwzƓ$WT0$BzbU𹁍 p@k3c<'WZָ!^3sӰ_? 'gG/]=~?/?㏕=:+n|l[s*T5w79c-{O*p ;}xN*#}0ij[~q{}X- nckI3B͘(loBjH9W{;DÌ wWB#\_}cun*<w\|*UJ N _>h䖿{s5a v}|ǜar >;IW\7p𒙋*D.{s;j7I/po/gox7] AءOۋ5/?vs/prRͫj_\zoʾC{IZdъ5gqءCzޕ^H#)5Lj[nߞcδɓ'O<o?7=_x0)tv7f ]ˬX`ò-v7s6/'/o@!孍P={H{Jz?U[Jץ)Æ ;[ZZ`/_2>Ur@m/{֑}?t̍s. +DCUveԷW={.PT81h g@)Y3spyIW`S097ƔMBXLܚw]鬟,m+|? yj+**UG?Vd~͙ѝPRI^x,ڤn9[]K*/']dlY;@R.ڥOYKԨ76q?3߽yk  tL,OoM {R?KR(ĥݛGQC{%yɵ5x ߏ>`\3"N]# |Yt2oL IDAT8F5cբgxmDb; wOL4ܻoW<0KT2!6μg>w՚Ɵ?̄@~: CG&, f{ӅW`\].*P%@<˺kN Uq~"oRRTYyzfV(pXAzf WEqWQ 0׿9&vt7gddҸԎo9 뺯0 jH5]T {=rc$kD^WT۳z?wݝ>ؼKXE1799a Q飏uѝTG*]gD_BI4>o\~"aqu@6kLH @s&`AHTpAQ sVIFێ{N+̏7䅶dۍ>n<}Lh~$Ͼw!'k]Ryn%@+~Y5y.6g W^]to/kcEʃ*^;b6BM![kzL_S>}:8:O jZ3푹9tC2GTS5Vlޟ[ jfɟ)ɭZ@,[qZ{nQG6g7BpE펧[aPtV5R%ϼ>tTXcƑ7ˊ=3&aTf}U7%֣.YmJO#[MYleWn\Wnpjx&eJCZB7eȀ*_uCKmRqP:Ըm=Tq]Ԩm> qƀvafvڽ5:.kv.+FMM3mS$KSZ)E:3S|hP7k޾l'>{Jb"1c9ާdږ%p{{ cj1u DIh~ՌoVV,%k~< z/OCڗo,\0;0Q}sk}޾tүlؚܬ{wH7UƍY Rmqx~spܸ[vy -g`}4]yj\u~ܗ#܃{g&+bC;6v2?tZ̍ǿwe 4_M>&d`a/8ƃX=ڻjy^*p3W_Yg.<ظC_ta.os\!3;XBSl;NתOEwj﫧W:=֮'a``$Y%[ >@;d:$BH.xvzM#.C&I RةM/V ظP>z45ASR=p犷^k{8M?\8-Y^:oOfcI|wQm{$N 5Dz @ޤ)QD iRD*ȫ" ޅ"By?]rI.] G}s%37cG 5(נņԸwP-7_ǟ)MPNß譫G,Hhzx "䱐'e?/fV~ uXIHcؚvCʋ9nGj}+2\{>vM R[?~c/5$v݉+gTV-OlaSWv@bgura[B3G2~6g^ +| 7@C[{U'nsrL l7;/l]A=d8M )P (&ˀ5'wQ!<4~6*S ֫Z!T/|"iۧ-M}H7֨vH:!:PxWRjZAU\( zJUi\'QFkota•)\BɃ_!þv nP\LuQ?2-2W˸yOEnWr|reYd+dɳג_(ֵm?-?{1#icSf 1wΆy=~6ܥl,Ev͚p!/6bw}w^N|_Y7DPV2?q-v&܁ȫ^Aʶl )ˏ=vGMg')5n7>a]cjǗBr?\1Pu'6 G|b9ed;N^7F!?7Qs] QWk}d6o3g_M;Y!EkpʺWvhLMylˇ-; tYf)f!رv2bYwo™ٝb//Ji|xu _`'w w4\7 dcaZ/S-X2o |rSeL(bkKJ: x/:XƏXJMsnrĂe˖-[lkN$R?rhLxK`7}r}xtgQᆱ_3E[ UR\sR헭WO/^hc>U"7d ·䘺Yggc~_:Cz'l.I8wϳ-Va;s0vޝ~یm¶vwW~8p+UzlѥrnY% w-UҩwXzj3zhULʑf!WLʵ[^^zBb'W84VφYG,o7>O~ot/ 93Z7q )ܺ! Bpeu=~t#ݳݮrG`< Hp @/^KMrN.̳v`PZe/̖ߧ>m aX@Ҵ 5C}rb[#&VX15<4yg*ؗʯ/$ HpA@|>}'۴ˢ s.-ݴnf%p8עk%0_ϪNڴ0[]h+) ~>)i!},rVxIٽ+`ά$E$?~d0jD5ָ~쏞 oP7;4j޼yxNfFޠJ .s5G9$,CB"G Qb<ˎ E]=>;8Q򘻻\OU3^x/&`@h|\5MFp_ VU0ܹ@_ p^[[txL!y7h+#B1gg٢mi6IJLIu~z^EsS[FV/=;F&pܪ$qG{bQ`oPNޓl֭&WY+˸kf_^_M _Gz|fاOZB!9H&34 j/_Bj>h1{`RHNbd˃NT\)ν*]=g\۾Bņᵪ)2.mm2E=qGZ ɡc\B!$VFg+B!Ndyȸ^鿥a_Ly/^;Z ,Ղ.9[^@ի`KBQ LdjԂ!TP L}sfr!ba5k5E!B!QvC8KLwxDP#6T:WcO\>hjVNBl&kAf`D& o@B {8RO-O21^p-_[wp)#B!R:AbGǀ]vԩt=Pl*ẃ#&G>_NySIwo&ݼ|jWmڢJkk29{16d^}@֣cGeLEB!Bȃ$2.0 (6ܭqe^H!vЮFk9tb~hCR 94h\fȏcC+,擑G] A Ϗeo[v0-}t6[:B!Bylݺۄያ*`3^5=je )Hx>}n tk֬? _ʘ)`r@[daYdiic"^]^ {>~Eu%-}ru_낂b:=өM>So3{U(#Qs&{^c}xOWװQ l+tn: pQ"B!B!2d̃ &3%k1:tԩS 6̵*<ۢrۇv_6룟٩i[0#O~2<n=4h5Mx6>M:?FFj[WS*hdϷ,|e+$톶6[z83K?,ͦt B!B!T\V87A ɀbB4܍IFw]V;fڵ#GۦM*stO?}8pz)aAbڕ5EedkkgR2/\I8o+[ GY95QEx ꘵J$Ft 'Xuj-B!ByHH\aED@ `nq֭5j8Xf͸8+U ?|݁wn3檘v]stԤENyՖl[sTl; ZҚ~Bmb$B!B" .seE!& EBu 38*TKڵkWuT~ν5u]j-${/T5hF[M:trOnu;q+.L\ݩ=+ B!Byh4Qq )2#""ʕ+[nP#b`F+_Ho4Ѣ=q%)Wri(Fx@_/뵪c`)oB!B)$AEbP2ZO5[`ʕG7@ 5=7s3~,6.1h4&k"ۏVzOjnW 9}ã]e0=Lv*eʺWvhLMylˇ-; [uuu3a !B!^qȡxqqL.IAW<8۲UOߍ(k=>׿73"eMjp"aIm(#^>aVg٘R~*w:gE1c7#hwXB!B!ewQPD5AJ%fp.bͶfN֤Js&]af#Mtyb.3. \U{[dM*9/{[^e8./ӕ |ZbdVgdWVC^jk3YB!B!(@dxʨefp0ι;,:`2X/m*Rok0<U w&P+rp֮I}bzo`w!B!B g֭& _PVyY%, L;lT^ӧOjzV;ը#=J8VEܚI@N ڞF7!B!fC% /@XY-KTH~߭ѩt_ !B!*A-TF4x$o373 B!BHiWFPI ē8qeHfk1x4-bB!B!!e+`ibYB*f:B!B!e00&M(Hd) B!BËsY.* ɀbB (dB!B!J* PF ek1 B!BKenȐ8$dB:`HH? IDAT)IFmړXQWDp2 v0^ISWع"r#>pL !B!R4@`̨X8dxHpOx|/))䍕+kcrԖjq꽃^$·pڻw]w'Ȥsk׮ uIB!h2WsS8 `!9(h++EQШys_+`^spct]ɹwï^/隖=S䊍 w]w$x[i4B!Bȃ# ,20 *x!8<yO_.}}ܺM3zvϾ370saޮ$>< B!B!nqdpj01:ʕR?1z7{}ވ-]O^3S$c6lp5j>Q3AF;V|2^.m6l׸zR.Go?ubnMKdu"lJ2ПgTϗk2A0l !)lXgښg^M0U/?'<{ؤ7naA6mѿM`\O.oO݇^zLq_AM !vyEmڵlRBo8qHg1&ߊOo2i4j}#&?/_xm+d^'q3wbSftp !By(jY2T(Z DT,}c9wkeK8n5qW`j?7H=͘ڜ:7۳|pHVS\S͖ƹz.}r貼oao^_@;F&5`M=zծ+Q|~L-o Ɉl=az_n0Yu{c z_6BʼE`$|5&4dx)i G~lGN>U.K<ݤ6R*nŀ__ꋔ3–M(l?sZFƨś~zGɈ;vcкA^6rϟxbwBy4M8q龾%Bun )1rv$,G_f>AO(i0LO>{[nUP!""*5mԸWNc)'^9V0'n|R*7a^ _hJs%Jt=\{WWNs}ral]oC.[Pgu bj׵(7d| 矪_^'|tO^* B{l+ֳOEs^~rQd F틈"'FW@c_xN1߮}\$ۣÛ޴0Cj?ċw{ uS{~*EQ1an|\W/=O>?;SgϞZ sX1s[*dS3eR?OStR͚5U*$Il6_|ԩSꫯzQ,QB!YS1ƹ,QQSJ ЪnmիW߷obŊcuLqgV^QV跭-h3|Vuյ#"siVK71sߖ]:mMi88j3{L);>y×['3ug(zꃽy@#aWʾHR홭{jXvNcw`\e)ʫg^(| ,xop PJ={qB)^C% /@k#22rС;v|uaÆ\U%{{p^v.s[dR_$ /Ɩ`[&$!\f2eGn $T=Y'ޟZXŬ;\ Xk^"|Bfkbͤ:yNd[֡_PĤ)1)ɜrWm,Pv$^w;uet³%ֿ:~[dJ411+-3HYw69ƴJ3P>flۿ%&)ƒrKvK4zOϩ+mrúG}4qDoooAA`edGpuSye VSEwܧ^'|[OZ|q_A'N8w !. UPA`@@l0{Zjenڵ#GۦMS]YH(k9n57 @u{sC@ڣgn c;Tz33Oe%9ku9tY@I8o+QKl8GK߰>rE'ݢiK[#6|-V ep٧ lטSV}z՝;$Vy|/8q[٫囟qqe/=x)ɯ˖?ZwZJI8v[<Ոltٽ_}Z"M31WBٜ#˖M>^?ۿ*mo*XTAϫ[Z2,n͊R1-9nW|eU[ْ;s?Zw4 zOz~… w9W ~\\+J TQԪUߦB!Kd0(\` *- ZZQ0q֭5j8Xf͸B2Za-~夿m-5An/fފ:'?~/w x {]Frʁiܳ'́tCzƘ }T|;E)c3-{9> }*2?OcgϞmݺu-,*VS&B @'q0 ːbnPBLLL.]j׮]!jG>OeϷ/ۻیL϶콘ME΋Mm Tҫ`Ly|d=Z1oy5<ս`-~ֹ?L@,7x܉Ob. 3v-U;H:k+lܩ,gĴ?mUgUӦk:&j~@%F|x֢`iڧuy`F%ܓA@ơVw`f{P_-q+]TPЉ{29oPfOϫ#WoU?O?gΜo߾;v ܵB!H%W d$ t3T[ 򈈈~֭]vٴiӍ7ڶm[4F۟f_X=k7\P{b b%Gs2 `S~do#Y 5TY#m# 騶z&9F-+e`MA٨]qm4bִ/o3]f7^EW xyn-p)%1M(e|u%KݰmsRǮrfJHѢ>=Y| xϊ!ڣw۲KUBS׮Kk6(Z-y?zwTϰ> lPZ 1yZg_' y[g²?qߞk3\vxCp11=x,Z dvc\.eJx^(~/3dqVZ`ʕoF8m|&B ,``9 2Vx2HKhŊ֭9sիg͚q/ժ uZk?azU~Ŵ\es]h@sL0(sx:WOaSrB*ؕ_\>[Gw; sTdU,3%[URHzɧwZ>Q s1ÕJ?lysXǘ|ū\<5!wFqkl٩qFzT,W~r{Mm*VMns,w;Xdm*wܖ(7k*0c϶F]}Qn=5u[~_~U^R,F3;\$9|ͫ3UO^(~`O0x޼ygϞͧ B!$p.+Id@1!]FnQ+WFDD0"""etâ\W p>X~aƎ &hHyύs~a&@HwUǯFw|ۢI\=}gw|F`0ܺ))LNȣ]'U>UֽFcjc[>lieSXv>A:5?< ,넵Wy:Cz'l.I8F˱wϫZNn_ib~'K9O(zdt]9wwC%rmo5Kr"*Ty߿xų Q[if o*U!o?-nshli56&|l ILElU;Gi}iq{ZFM6͟)Y} vYOwXg^e)BǏ\׮]7o={,_|ȑ9aB!%Gen +2$I6T逗 t3,=қS[N[=>B~ybۚR|9kvoNd[P>߻CsΜc%-yeSpoo;=NߕW~=2umleZ'SEyutead1hj;|w; >oPi_ݶKe)]erkG|{ݳ/ndGw6XȵGx]#5leJcW_5;d帜CJk(*[Z\;K5J[5!|y0GS^׺.OS}5{֜Y{ R4@`h[^'CsXv(o?[.٫Kw 3P4/\S+xϚmż;qEțxO/xZ3k'YRϪi1:i|Psb,pPqb }ud8J*Fg{DℼFIw޽߹9\C8!.s5=s f*Ï Ri(,^w<ܥbN|Tnw4v9ѐ7GW>8؅jڠb.^ƆQdsmkYޛB#xpJIRNl*_9ꥎ?7lܘ.hڣ^ ;3]?/Lw΢,)R?j7,˙ˠ !# ,20 *x!8!. ߠk_|ԯ.3@ueW1qo:zxsPBWR{f On4I[r~BxO߰ݗj|3+b $Y_Ɔ./_746y1_׀}bcUuxǪ;* p^ΤZ$)Gg8,tPbȷ3pOcN_5$„U[kL=k5ǘ٢qCnɘxGHR'iy:cY`/Jrq`{| ]\u9OSδeys`宧AC qռyA,C*JQA!$(j @Z`" *! pBQi0}g,[LξL&s6MPIm @{smE)>+j¯ы&D;٢-4ș~3ϑrtPW0a~zwO֣>^Ҹ-K|=F-rœ渙\L̿9W1ܢ> \DOK[?OWQeYHHHTTY ڵk7zhEEQQB!%Ff`D& o@B p!B*u{/tlPo_m %5zbJǛɞ_X>yì9J|_;/מ> 6Y˔?}*a89T8I9~~sy/ BNr}Zà/vw*Z Y㚼X:!GQfAh+(~/J,{&Ol2}þj3!Bc*4%**x PPZ`\ ) !Ĥ &:@ϲE˹v3Fa1ZlBh4:422Z-o=+['=Tp^sAd܍{hj|R* _T*N+SZQ?Lϟ' 1gϞ]6mڔ)Sr3$7lؐ$R\nmBK Ci͒"cߊoIou%xt$b>}h !t=\ IDATApn7wPo'&G 'f_P3Kf2{<e~l+1-}g)~E.Y G=ڵ{饗ѣG e2,:DQTE- !?. UPA`@@l!<rfϧ$s4=ܗ+G!`PT[" 4 nؠB#Ȓ4Om+apÙOKl OS|Pڵk]˻($ݼyզ !8(`!$r&@d-^)0_x*=|{׬[H:Ǵ rvy~BBHR{l$M%zرcݻw b^Sܝ$v˔2%wLa)縊1v *B!$* \f&+&I%`R8oe4Ztnڵ#i+$J?xxm۶ AB"5&ܒl6XVclB An>+ ! 8K.3fx !4a„SN!R qp 9H2$ɶbn h-;40kl9 ]sɑO9W_o׭9{ɐ5N|cXDgΐLq7i WSOܱGɠjwRLqBxŋ;B!B\#27I$LtKVB"GeЀ-gǀV= c4rII>Æ7>rh1j1/vȏcC\Haʠ|eAX֧ʭ]/zQdVb#IO!B!bGqƌxOIL+pgݻwY?(V:#3cFzUMѴ1jJ@O\W-?p ?9xoG6n߰oW2ϙ/O12!<9xƼyzcCIVW,ϙB!ByTp99 f9R nO2Zz}}+Vx1\o4xÜQQy7=vO@OEQd]IDK+QaзoŽ &h/3"SW}ճ={B!BHi% ,20 *x!f gGddСCN:hРg}vذa>'fxeut<hr^utP +صGUF7,4ZڐB!Bq395/?.*nؽ{wZ:vyk׮!!!{}dL2S ΤdB |s5A5s%NB!B!䱥&2XrYUP L @PAepǭ[jԨ`͚5|0 2Ƣ@!B!<^d. pހF2*hnQB˗/8xҥ+>`D R!B!'1bLAkYUMP0qʕ[fٴiӍ7ڶmyԁu%'v^ʑC:B!ByI "0kZ6]TVX1lذÇתUҥK7n/ɃUek[',?ri GzZhC!B!<(0@TAPBiD@ -Qq[zʕ{yfDDD6mV7u>cz>^I{˖4A(qw@!BȣlvpCx|I  AżEA+B@ ;ž&m\ btXѶ'#NbUluϴh7>_GoB!@ѽ 0IxG9 l-(L>`p*-4Ԟ&[OY_;w=^eƿ{\AI|9k] gSt;jӶ;Tߌ>Q1; !B!džJ%@ɊId f8c|!qOL8RvҞ;!$QW B!BJ.[n6!՟ҚdW8||PHOa}q.*k78{ߘy Җ GlNB!B=esQQ P (&HSݞdqq)xWᯜ8~reZй[c#B!B:*WX,@Q #P$Cl+ kWcO\>hjk+O!B!T \TݡB!Bqf4q7)b|1Trnɓ93A pEQdnaRC2ǙE Ԑd؃HpāO%' *bӰVaM֯fIG |Vj#B!̋+>7TM3te+zIQ)U1)Px ᩹.D1 ^* d} wf.\&ɚtnڵ#Kj|njh8n|B]v#ʹ;Rhȃn%!B#ej߲RR.󜢪جK|iھ/)jq#]RUԦ*=(!EQR7Ø`QZ..޲/7?m÷i k溔k-42R&C`cZǯpcxQ7p~ZSG=9sZG~%1o03VF㼄By0SFlE^)?ti6..0BW'}=)p٬AP9o2eJoٌCwY `˷cC:|qחQL9pOu%?Bo yλ&&JߗMn!%ZneCJ|%Øy !Rq(DRj`g9OA2T#@  jhVRۻdUnEO\f_Ɔ<M:o:G]荧C_S̑@_;6gÆ WSh68p35^)}X~]&%C2S:aRWq3*V5 Q]f)'شk٤V+6l"qXbG<,]ڣgQ}X^ҕ'h(!ByصrfTwex0H7Cٌ{:R׷d8.Ņrn75S.\`@hyשm}'O?nVbl&0em>߲ ݽ_*1ދھYK³qБߑvR[߻ 9q!7r&B!Ak_|[^.5PqCNJt#ޫ\fsg/.PA@EEPOjU[h-jXW]p ( *V2+AH H~$$[w޽@y 1# YW^zeҮ!GMS>Pƴϸ~- 9z_c `qWsz&':ppiVS49‚У+S' P}g1iO>:сn%&Ua`ՔQSr0߸|T3'/CzٛVg;pۉOlnZ͞S(4U&k_Xuj/s^rasV\7]pNB!j h4NנBT74v- {422"INdqS)dHתl H1@Y0Z>jߕn<v?ٮbmW˫f:4g7lz{{%kuhu;I_u4z=W]Ǭ-+m􂂘l5nJ6]mˊ]R_%Nd:mL`¾L]X2I娜 ߟ~[pӶ\T.|`yz"x2~Pxmwұ0g4$!Et[rxh:_IAu)] B}yI|.\.Ph$Q 6s4^4dvV<_  b1N3544422ե.⌴QIv* ǵggv,ؓ¸_͡W>J:\61~mN >=՞4K6W`)u(o&6}]{¼B!j\/Ԕ:wZ٘a1@EN(JOO`(A$D"J2G,qkAqz]`B;<[# @R2>* [.lPm2!ea̰ $4];J?t/8!|G<;Z1^['%a?@vغэ7+NI"H6x5(a9G˒WU5ɪэ]}?>$w7)G7lfm4+ $5(&^>%ȫՂV(HKK{w#p';=GB!>DMt\ IRԐ!FK$9֟qEt(HMSs@][w,uZ([11tbi'C;YjRVa#8WwID)VVq G*W7+b6c̔.m3^6ׇaTNfʙ_گM^ܹFz"N=(-D } ۜT_St>o.Uߩ'9)@"ۥqȫeեi!#ֱ/F>ǟc 3O|3Ά] !Bs <0Y+AYןNl&Vs BԠhmu{'Mh/*˝SĢtzlۙKͬ϶7݉ U|qLܚ։ ;V$ȶTG:9*>&XF3_Dܹ.XsP $!cqĥ;>iZ$i-L:J*=;q,Qη{[$<(׭d&QCυJΨtVC"[ŶU+|^Jd~:C !B\&O*n]++SsT/ w,@6bӠqQނ6$ٵ6VCNcs]]zͅ B!/-ܒs"X'7R0bǯy+E n߲lv0I4̱U07߹WzVf{Z23, 0 BEy.Ͽ< p B#p:Wݎ0paOC9.gfԗn:nTq:: 8aSԓ^%4vT?-߱MS6 Baó7>Y|) LڈSfpozSol_\^R_ļ)v5.y%ZNΊhDX1r;+,$TahCRkB49Br"BFγ9|q=1re PCe,#"@|q}ȕp}լ*;ukl?qDnTp3Wdel~ˢU}mWs!y ۶x^<\iPɼz>e()]YY҃Ͻ?t2Yގ9nފa9{~LRO'l}N?SJĎV(wEҾAW>9gOg!U [4 pi_u0/~ZzX4NnTB!Cn>թ9߃8{_u6P {ti0ux[I䫜ό #*JkUؠksVe)J I`ڭ8o>(ФߏOQb^" TX5[IÈ+IN<돮tI-ߟL'LziFfݞ_Rfl{OX3fuz24LW疏G޴صIQ@kw|$֔/T_|H2hxw?ְ/B!PC߹) IDATco[Zio;vرckj/9B6X9I‚L'4KK{RP(!ƜJO'E?Z)+L=/94%ާrEB,1GSoԔl]P>CBQccC-%!B 1I{UJ$_"1k޲CjevX]QI(JGG(Hd2 NADqqqQQQQQθFU202ZL)ۤz"H7k']0׶)SU>[)/fdXn?ŦٟB!-w]:'FH`bW ظ$*]ʳ(nUQt5@}ڊ.d&$!܊=#B}.'[ mS'-98B_*%b{n ÔR!B}ApBorߋS>T+mhj B!`!2v7ܡB!B*NM7t?B!B!T!zΝw#$gcl"52kޱcݬjw, m^QfތooD)1A?v zlGaMG B!B$횼RIZ[H "9LtlRI3gP4 $eӎbUP mO@!ykM B!BF~e/L{Lf(jH[/* pHIنղ M+ϲ~hojYiT N:UB!B!5K!2p`+=Ů&/go\|6M#cbFjSSBi7F)Fy^e'$?8fUB!B!jI {t+3߼ݷF>)ᡏݎMz%~G7+q-[Qzts7^, lgAIgrE;of}=Lu։ى`cy{.N(xr#ŋ/R >~nmtJGwF^РⲔ2#B!B_x~sl#?n(ZժEVֽ@54r<"aFg8ٮqm;*/1Iri~"n@1f8v򮏉d(-RC 㶸̖d= ]in5ݺ ] ,2dɆ^!Ϧ[/38X+RB!B_ pܺu+;;ͭd]xL\X*%ꤤ<6Q|ze(R. ;/b1}z?{pZ}6=ឝuԁWͬ+sw/=z2iא#Ϧ؁Qe~s=姁-e3o$~{bZv52 !B!Ԑh uɓ'GGGKӧWhr*[E3τމ}w~?bٌ/,6p`Q޳?V듴'SJ gyGYw(@NȦ@ΒS7Oe[Ǚ~W~L"02bǥ_=&lH_Yb:d qB!B/RÌ>ݻ...Z5d#ベH\~Ai]`:&+;A,Y=A3rוⱁu# z =1cZQN&ӻ0 `Hy4)u?)㣫.m>Qܛ/ڼiڝiK|ŤCvNA!B!ݺ6mڤk۸dֵ<gN;ZAĢQI8~3C|Ȋ_v)vl)&iQ,mqRyiii\ X75Fҡ hC|LJ;!B!P#8̢ _|}#$)܍AY2mfWRE9~&e U bU^bMߌb}8ϱEֳZVtXNX|;ӄ0ز;v'ԷH758}_(::[{P4cE!B!B1knnn_v횢$((ݻw}պ ݈e[F}*i @4PmDUbK;(.΍靧x0Ӡq` IF&-SxCf5@~K͛O\|G*v!B!j4f9sfݺuNZ~+W_  @PtnƂ3> @k:M_Rgef?G6/zc\Վ2 9VvB:?y@꺽zuhFJE܌s{-ꤳlEf wm'?%,{|_(:G;{nUX5>+R4tB!mrh.J4X2'Efܭ>7 ͎ަV B4t-=GB+% =4LE1V/2u]eV#(("Ӣߒf|C;*_<B!P/R[Ƅu6=.՜ng{_sR= ز ]M\2wUgѳLV[vMWOagՎMQ66 WYF6K9`B!B_2B*6tj{ Bڙ1#3G$)˨aU3[ Iss"@!BvC&I(("Id2 :NAEEEEEE 9Ev1ls6֛mnnUVQ5B!BѸ B!B8B!B!B!B!= p B!BB!B!/gB!B_$Iv9ADPl`<;ʑn .E) DBj96z8B!B}!DxT$yYÇjeybG!$eJ{| ib\VC!B6ueK e[WcXshEWۚCCԀo !("KQJ2VT=n߱(p?9oFc-խ2//9ƨ Aob#{7+=-;B!BBлsLKQFtp0)yaةHΤ״gh┝6$5GL{ljor+ip&}>Vպ>gP4xKvo@~N;UiՒ ;tJ;;\i5J?Hi#M$j?}wOr削|C_ߐ4i\_e12d복E!B"A6(n{a5GSDL@מ=Y fr޼ˎ?hIǵghYDջ>R@aD0X&Ԓ_(/EM:\8eK)!ةsQ=:T6 bg9BQ{Yy?/ΡFKw^<})=HZ2n?U6 c4+B!BGSgQD1ص&$ V X)!LC> rdL%INNk|PʝXHr^}wuM*U8"V4$/ϋ/K7F ]m PfY޽?,8:~!BƬ8H"YUN6NO⑰FuVA/^|Ze' wk#p.Hޝ$ vX5&9L;O>Jep3M9vƸ2.=8%ʣ|`wnˮu(=:Թq/R zVc3;,H}#(SnVXқwˡC7kʮi"3'bS%?D1 *4g27}G Ӟ!B!4j~>o<_?gC¸-.UsC.47 kЁ cUs8~9L?_7cioQ܍s8yw))ݺT:#Ǩ7\.7Ad-F74vXiim&$ދ  }«KfnRV+5͡%?RJs J(QDi?G8E5hzr <{7gH^>dAi Y&w`̬7v=!B!T'2q֭l33377Zon,]c+t^) 66k+ ޽=~ˤ]C8kUzf5Asޞ#,H =z:\hWUB;B_u#vL=_* {Z :­ĿbrIX85eԔ\,[UֿPN@M& ;Y=9xAϳqƕcMk WoF楋1{ϞDyU⁹o]7v(FuCmB!<$s,1yd[[[Ǐ5ke aˆQ>Gy4y_rM/QM{nY[Vԭ1sL= j >l0Yd8eD\ocL]w]ۿ^TB'˷3Oއ'ZB'l3^O\;ݏTnbg$?+ yk6o5Z I~SB!/xL[qB Z}tɓ&MrwwvԩS:T'! @1~K 2S~? D"#K hXedn.YD\r萖2egIgB JV @BMB҇2 ]%(81&ʻ 8475~;n\+YOB3ҋ3۹佋3^ac  zƏUBG~6tPkG[nmVA߽{ťO$,@F"OV'm _ФeDP#Ɋ?{Z(gZv&3!^(RJr:[}'TLp~Ȅ={Yض673o RR)]ƿWw(|~=; ̈|=-_KVf֏C#+vX:l @4m^!ǘN_zqq:M]['wJY;O'fr/B!Pmy@I3PA^ 5@#;;ujmڴIOOXC&$-%@Aj7h%*Z%m*}1h"yCP h=qݟ}2&N ?9վ!jt /]g*SK)ϴF}VVq GRkr]?]Vh9c*iB_~j(9xC.i!#ֱ0O)g.h=,48Xtu56sKV!c

    +rn';WfT$HH8gMPJߢNDM9W/IN.l˒hqf:2NYw^F-;4[IJݟ5g:r9DټST3,}c#+GWl>z_5!c֩]SW^0jǦn%?G`\@fwZ^~S㥔|2Yn֝i:gT%7b#f IDAT=jʠ!>H&[e:}j.lRY| A2jnB] y|W*"+rw:OV Vq6 v59jbƼ+wъă.JD5"Z 9B!Cd+L=fo:M$EQ:::EQE$d0t:  !l9]iͫM&VxJP2UBň+|3KdUnQ0Rm[ Kê!{b0_ͦ'#;!B#eEsG&F@fxu5Z?p߄L _?@?FuSvWY0_\zegn ,^WwwXb}lR@Rԣ] ZѩCR>D3M;[zā z粥V3D!B"vOO~*ےplͬz[Y[ϞQ++jST9aAv&EQT*{d3zWUd%#`,LjmPx\H ep!BUѥ) `BטImO }G|zؐ.~ST NQAʠjl]5Z xQdMKB!90:»k4otC{Cu񮢂B!B!$B!B5z@!B!PB!B5z@!B!PxWQ)H"Hd˫yOBo& ]ó^!B!B8);mH^!Ҵ[߯(0vvEB!BՓ;Ej)ȶZ<[Z;b I_B!B!ToT3tOEy7QmN!B!B4)*%,ug̘/l; LTJ44n^7=ĜcgkK/+ZGvn}騧/y?Nuҫr2SnĽH-dYY@ē0=;β)xr#ŋ/R >~nmt[D!B!F:Ayr8yTvqK.YH1L枸cSr}fʒwE1I{;-M $;akoD1R.2;R?47Ǻn6X94t/B!2vC&I(("Id2 :NAEEEEEE 9֭[fffnnnneOWFyAڝL'Čy\mvF( _u2e|ʊuu9lfL1=|kYpg{|>4┕C%3@jL?y7 *=+Etfƕ}mtŹokgS0a*B!B!'O<~׎@?_z7;wuwo}LzuCLe`Z] d+8|ˈ9qчfMsyLLQ\Mxz/D`MVօ3/pCG4y_sB6R pυw:υ!"}aTB!BHI8&OV B!B!֭[i&==96t&AZWQ%m*}1h"y#zg֮A21Ib 0P(Қ8~!B!R33(—/_֤6/m8 +?Ubvdl%wX֠H}BJb[: b=La!B!PU^~ڵkw۷F}kx;|7 (`>chWSRS*ѲAI"HLYl!HB!B!a -%̢B)^J^ri B!#~H*YzP(Ѣ FY뿸N ʫIB8B=_CyB!T1E)a]:;X(h>oLRY%e [drQ Br8E W%!I>Ӽic(z&/ ?Q /#2"6IӵWCv3UsQ &M$g?)W$<3iV㾙=zG -̝w$ID"Yyt?r>"iKO2IWKbe39CχΤ+g%%Ƕs4l.E1~XRɓJEG'Y+_Uz@HҺl !("(oޝX~m#>͍;Ng_/bcbcs3ĒaѡcVu pcg/hPsoDV>`P|'+nY/Ά7J 9?/9O[nΒJW x÷w0}EjOIs%Y̺ݚj܅B54Aܮ>H=|NG[p x2ewd۲;%ٶGpoQ'3*hfv}Y&[crTo0(zeEE'ȶA_H1@seˢD^MQuEGG$IQEQE$d2 N' ⢢:!NٹnC.U p{ꮏ> !냽(=t7o f@܍@piT//uYO?0'9Qrzo.ByŧB!(3. T&TG,ws9N=y4l.)Ԥ~I#(h*/.m j:E}.f|W>DƸf-,UUzPSTI2ZRI2jH}.R =ݖxoxӧMeWwEٳtHTD:ңO"gծ{gO°s,姄>#9+[^D;=/z, lgd"w \//r|̊:pU:ɿ$ًwNK#{cyy~S"޿wgp$8 !IC+D^2oɷ8at~5Z]@P_Vֽ[WsO.>;dCȻ=tكǪlu0f S&03zl8޶JmqL7aNs#}! qi&o3!^UP͓TV=mZ&Q¸ 4-dXlFܰS N¨չJmU-}˛6:ie$;aSsܴB鋿R8dg/ͣ(G@Vb1\.7A$F7'0rj/*y8<#PlLph_HG3-j[CӥR^*~W>~1 ryF5 rBj)zifoP(T+JTcg\wfEzb5Au>CO^^K}#a*7R|ze(.y-66k+ ޽=~ˤ]C8`?1A[׽KhUt(z,OUg]=8AF6 0y+n$ Q^Foٲė%(hJjaZp%SeXt{vO-I2Xʻ{F맿{Q͆M!.c.[3ǫekcX D4 zF7RvzloλLJlgN)g|VkdxH| R$%˂%AACd;:sA(Ye׾]8_Yf@elc;:v!WB2'tz'm4p>uk\ֳiAon%[Yn B}q˖),MV8%ңo7bhȮ:FC굝׌ݴ7޷{K*>-x.K窴[-oѯ,#ŅoQ/t@w?nTiDV辁 Iulϊ$I V $R*Nŏ'EEǓ^.RL2pJ5pXC~.779F:-A8(Η.X޾sz.Fo{'Nr%?[%2Y |uxyB̑՜;l`yp2b\~ ba$@XR-0l7L3~p?_yeUI^^>^ߣ58 eo0엗j/U6OWa-yzR?$X\SXYa,C}٦c.<6uA\v`syP#\.giͷҠغ'796)MsBM {{k _MJD@jfikMMwVOmj֩XuؼChu֭`Tt]WhOv6A, f66UJj@AJoQ}PtaVz$&Wj޻G_]hurν&n "bt8X-ٳ/'jHA~ߵ[Plp~S)BS޽#$Nh5[wzOV$?*}g=F.1!h6{'8u@)A܅pu!6ѱ=U|C̪;9::9յ Aa99::k|ٖcaT[] yNv65|ZrNBg7HaNvjpAJpdT]5"4ʛ]QB@ѣfm졎~ʓR$ Ion qͯIf(KYg(M*=uR7e0Ѻ-W/72tA(!7_+v2-blnE]EM-6.Q'g qϮ]gy!t Gʍ՗bR|kk`6vHgxo!>F] qC:    D2F~עY_542Aqrr:75g7Dg6& .qIRQ]+"w)e&@*]vf#1xM0p~c F9SRޥECd!Wk]Zg6>u$l;I"ɝlt'lvVXk9zAAAQ=8 T#')ə/ (+)OTQ8bYvɬxh+zπ'B;zߵ4]VPY%An ~WEK#,DkgE @S|'o.ـV9 hO|h֮]F[3:@fu%[)zY*I;4;@ՁWQZ6|]HWuAAAn./L3oTjE Px6D !q^= rsI8l`yq)+MoѠs{h-8_UR1lU["J b5긨M#  RBP6嚛s\.pl6b2A/H$I9EW5^Q|\.k+ ,m,i$X &75mPЂ6tij;D65,mPf*DBAAA*8WMcz>P[{2l   B 4p Ed?h{a   y<1#%']$Hy5jl_j;"e @`jA"/\=̹"eH תշ    ƢEAAA)u    H    {0R%$ۛ-%-V)`Ss 6v%.L\;;;ܼF+VN~F 8ek{[H# QU hTGn;ܽczr :lkj١eVnuUٴHC^[la7{g:}vF#\#2mܾl s*baxZyOikH 70 wvN7jXUKϼ;~ooHr_?y}wbdBa״W^kT6AGk"cvl\ԲaS%\G&M V-{8ȏ7Yw5Oԇd% l<7^߻C@6xg)uR#jtgNB׌p6sao/@لͩ2dg5TBZXKϼX|0l!$!0`=R/ Y_u?;sA%A5ި|}Ր!OWŴr"HtyS` f{˸%&Kd*eOYlcRˈ= pY9Umx}A 2 (AVj>ңC#_S= ,?[s\,3 7)L~*1=7>|ﯷ3yPcG}Aoc=&rlw^vM8PAPR5bFA wog-G>:Y.z:;o~OR@D8mWT`.I92k#/l"-=rcBy*`0߻>O apläػ!KLcorfz1 +VӢzAiVn}Ҥ nfߧuhjR T #ʀ eT-H ϝzʹǒ;-zܷ,r ?<|-<'OI!6}YoٿaG"%f}/pL{~.tZ9e_w{ce7\*GOFx+&_8ÑF!y ^]a'/D>zI@V`VOȫ 'CR{TNN(GX 8|tΕH"حEn_+)*6Rf:vhܱʢh̉|>켏^EN[Ct+B5>vudKhQݓ^ S3vrz!NsKY85Á(YīWdf&4ټ@EO,B|n@Q+O?pzz. o{GDY*ko^s&fSe?kSnG] g3B5'IQ[βA8 Zwj!$,g 4R+UWi9cq/ 2KG+`h!4ۧ!?}E5Z0ISc"]R˂ \0 VYW DVIœϼzhum'݊n-HShI1jOU HهJ˥Nks[Ƈ!etifsOs~F[!\Ml>BJ \H{CԷSg 3\B%%sԬ͒=3j i AFV״s,yќ[; 檾$ ?kfݶh"JyVEmrlY@'!^{=m_;:3sZ_$7znpG455g(9dW6)&]]ٛ&50Luu_ha2 ՙ~|P58&76*A#Ьr8wѨZ/iVx3,Pl89TUH'Ng|gEPuё5ˑ_R\s}gG+D?[wX㆑:-R~9wƻ&~d+[0Cit;qQ%A;|ʜKqwA/l<9ηTx~8jZ!hum#^-HHL݈nUrRaT(Hr͹\.p8l6b1L ϗH$Ĕׯ޸qى@l>z8 ,z7|cGqΗ@o|RI9J^=|.@bwfBM Neg=qNQOm|,]׎Gs;M\Сo_=<z˞šݽZ $nG߹Ԕ6l+Q%@>fe@rPb @Hm&lA~ؿoʩ giTJy U[:wSNV2 _E z.\ri#jT8'/\rʕ &u.He3# mE?N9|Bs%UQ/i*?=;F,+\wGܖē}44! Oհ֭^&ssvv[۷?~!CrsJ!H G:N菉wyvsj vpyr0lZPE. چ ރ %܆lݽ:`W'nsctpW[ߵ[`x{w5`rPXI!ު:QE:9v9kRc6~n;=`NegUi?u[SVW=vL+)\N>n 沠fIP5\JV}v;-Ve mUNCg <ΧbA`2bHiw粠z^2Qn ,f&f=p6sƆ%cCs]Eiv\.mfvh9axl)s3(͠Smԭ[[}^, {0Q6%ڳ NZLƾf,>~翾Tz59n[ŔRV4Sc1>ĭlR: /w1 ]8w`ik^9N .Qݻ9ϾZx9#v1p6sc 6jJEv{:R'8Iug#0M>+B3YQcvupA?Yc%μ z~o AjjhPSKiׂRBZ6>65IU-,ujFSNԘƃoرVYza&Nh@~qR|,%!٩5ԫ8e{DXUr{2Xuޓ,Ni!*WRfCM,] )e3s:ylJ{CwuM%ZU,}sRuzEn+(1k7~&A*{t J_PG-"BIc,ش,!x}KiA,$2$).b!O|OÔ>;Wkmmӥ TxKngpi"| [߷d:40xYWg9|h7E [Lf$JHxiԻJRČ)Qv{~ [^S1U`^r7mUx:B}88*f8? vG;7|\z:!4)0K1NeB[F3p U.ET)RW-t`4AL <8_ޠA={nܹ3" j'j{=V@jQ*K*$HR)T~ lR&My,'!4WGKV9ϬJ[WQƖ<%guɫI6ӧ[IX0:)BbM\xmyFm׾s'{J cnHG(BHo$&1(#}N=Dq/~ڗxl:nJա4GH1$ AZSG"AG TS.ja,AL S9Y~?.+</6{5ӣ$?ITUue7%Way(RuGm2U@v\tPp;Ggj /RWۮ:۫+_Ӏ˪1<*kJ A/@/R) !f _|uCq[aŃ iLq ^Ӌ8Ԫb]pƣbT2toFƁ7݆5Ȋt7oN{g-w27#!Ez#h̦Pc~<ۿ3-æ֟e32&XIJ#(jL[/zb  }B"NZƪq1!&0pEDD|m׮]@^=mt& p@LfdRwl!-ݵUX3W5aha2:ӛw޽>Cu?`t޿=bx?uIj#wmL\쪹/xzRΎZ?fvȅo:V R,(,^+!Jyjxnd{ыz|5ۯrD3včjg8Y;h^ ܘz!/vGPH8,xER3wH./_. E!FHt& {)չ$)oZ[9Ab/^ ILLҥ 2\y0eVdmJ@tL`;@ȉ^ 4SPx~ 'k#[ V,9м=m\ ?SĨ:{f\)Ϯ?V,;i&ԊӖ̍Q%?WM9P܂m>'2Hʑ lu@;ԶBЛ]hGO=<@Rǹa|pp YcQB>\Xʒ>Zjѫ_Nb>[]ee9B H*ti7]߹]XlzgiҘ҂ "U$UjM#*'b*LcÇ{xxauM?:znxU' Eb 5탺 ]J~jh]>29L=b5[wQW{̷IaRY|DZm-O_}$D" ¶PuMԺĬ=o~=_yA@D"0s#;5:߾lF>nL=E{7= _VR ~&BطociA[n|e/?=LBI}((LڙO=M&܌W=|8X";-q9$IՕ?DpfIgR,={}d蠱o9yg^S=Tc69B H玄^J_U2ge<]+^R! &yw(*Yps=J V-cNk =5api :h5NVHs"O8yi]Uw@EaJ׊Br7[vdkмw߈-wҵY#ВT3'Nu~&CjzDn%- x u\ی]`qzg;%xJYW5 XR"juT Q6o!gzV䴓tx_B1WNhP"~M-F=b@ʼwJ5XwTd/ 9؝]3,@)F{>jUxVK&+[QEZQ Z*:=yu&3ɟǷP]w-ZJls:;~Pmo׏oćcBXoզSs~i(U[]ú&jJ!+TWLK զrUA,n|S:7aKA*?v, c+UUݗ [!\[ R|%|I_Ŧ߁Z)H4lw#P˵/3Vmٳ@L5 CQ/aTS{޼TKROtD' ֫[֭[yxӱ۩4N>V|llM+H!JkN&G1%հhg:iy)yCՅYudȃZS^%Z^!uI1jC% HVIy99$ 6V[ʼԸ꓾MAt&u 朼($IxJ+X HO 0x ҄B(px;muIU(ˢ*>r`\IsL{>z(tD(!I`xV` |Jf>;т [M҈U m2W9)A[FJHdd$r\.r86bL&AAK$DRa&Yژ/4X겔o$Kџl>z%T^ڗbeBb`1mEg(x<;>yex:+PB [AcnoWXW|O״a$^f\Eyk^C;"\[s(DD"vk]@{J%&yrh.E)}Jw%pUFW-`LALD1p R2c1lݼZV[*ONNJ*2ӼHCm*gOe_yu]  4p|w,Yt#ŤJǎM)k" P!W+.!;yeP^uXWY8m)ieFC  b2Q{{򤜚 p_k 8|;;S.1)rMRLr~xʗ/)9_rI ,k4w`D?ok'׳`  RAתqK Mel_́m af AAo8仦xAA1jnA    4p    RA ȴԸ$@AAAD37GPax665AA <̛b*ʽT >r:OkivoB|NkaIȈuFCY|4!h0v#gugrQ9g?ܹ`ΩwohBHD]<^M#d|t7Ykݼ7ϛbJLi~zjjG y>+"`P2(`Ckh`T\.Furn5%_/@iw nG{|w~Z R*ϊR~N_r c3眼XgK1012}#UPPHM-|@igf9ulmS^~cW!Rɾq`qS~f>gO~6B_^1Tyx2 Hd#ٹnݺo>~CWNvG$aQvu6Lo^ ^ n>nWR 4cݴ7wv+x!H9eQ`۵Xdɞ-% XQU8%YPAZC/e5Vq_g>(; )!'/> _۾f&h(X5=zZ$=xek4 XMQyX2 H4#oر={~^xqĉ6VRH6K>6Df4bOsgdzFXY>1!>]P /T(1zGuíShFp%6z-O1+V|;w :(ぜL6™\JA @ (du6SkNl>`FSUn0%7'YLT~z ӧσnݺչsgCK5$AYdҫ3ӀM y<;/5lÿ=Ϲ`I4mNA_'?U~V3鎡Rb@ZzPzm€U1JFAO:NטL)ccik{Clh҄#wAcGF)^Q%k )qZ{7hb͖yoP~@o-jYX}Ɉ2kՌsC|*ByALi<V$4MF}3G,e!s:zi^-H[G-W~ÇӜ7i(zT5CUۈDFFr8.knnr\.a,dAD~~D"H$&ళP9ۮ]4mb)k(oL[ `mW_˥:2yyXJ^mY;"60u3tk!Nw{Z?;x+W:<'w ,1]B~k\|h{pB^vڋW߯"FߵC^lъ(,(}wDZ7s Ek t\ηV5I+M U@A,&0pxxx?~ŋ}΄$&&vXUxE?ds !Gv׾۪v~/\)xd`?ƷO[,(Y4D28}׹ B*ҲϜ R ;fPgow ?t5.5b 5 {ڟZ&dՉ^\=^Da9vzloλLJ7)$8q WP3)'X݆vޒdn6^ 0ȫ_1Lyl:W;OFvܾ@\ve=`՘%w Wf[Yn B}q˖M€ h;=`Neg>vSZG紭MM҆& Rf elwwСSN)q$(ˋ^<z{C%mp6;p!J bڍ @XNGUe?GSvq5 Ĭ?z)j lVE67'ŽzYJlX>CQ@!c@}&Vbxt0D<# g(,8-y/Y7,Ni!d?;>6^ReɚM '$*nSP@ze1AQl罡sV:.Ԃh:O(͛ ]2A!Hdߣ>Qآr^}֤)$H0;#рc|h# \UԆ=xVg _k?mxxV}8{C[ٰ?yǕ! Y.3GS4?$X\SXYa,C}Y RǫftG."@ҕ `vtЈ(\Dmreht/QZBmd6 #5Z~UbTn99?,I1KY@2 Ho.6ÿ%zDW1MR*ATJ&E^l|I~]wfae_hv r99+<(! NL.Jh9-Ȏ .`zM(#/ZHDG|*Έ†c@մ3@<#^E4~k6Ӈ(g ]fA `+%Z=U 7K[BQ~=n ;p={+^χMU҄aā+YՁ<ȕt·UA/2֝ HY@z0EL=sUk_ϓZW}7i= E`xhQԂɬ*JY@*5Abʇ?M-HLK)4͒?A!ȷ8uF=XJ7Y?.6߶ ` /^{uOJD@jV]%.3߈#H$/6wUqd[5D5J>=&D|/O!!-%otfDaЯg(!l+W2?͌z)i3I9HFA19}Ԃh:r}?oGQ5 |[GGGGGGuC;5*)wBγU`Ŭjl\ ?S{(ZEDEm})bD%{GKdO)zGUAxFmRlp~Sx>ޑFNh@dLرcǎ;v|HFAQBqMPu QSVmm2OB=8§ 즮:ͯ+O>HH$&y.`d17.%$GJHs70w > ǨKGv88R2aupr֠ ڵg\4Q Y"QNzңП{MTKIUӁxFj/}}dr{kp|M\1L7Q Kkg\Rn4zPQ_ IDATząjs8xٍ F13t¯ H#3PpQ>t]uD)_ULyT#qlt)Ң!JN=nehrpkd@}ݾ R䍅 b< g{U^o1sCiɻ"O'u$N;"r7()E%Ԃ CMyUkPb*ЃAw a\(E[=y| / ݽyTbYv xk.cgU6vpEUѬ]vm{|+g6uej}><_i{&Rj EV_{ ʔ* iVW w^f>FEU xrG BO&U\zyҘU_F-m+Q qt$J$c,e)娮|W>̯HU۪!e!sq>\R&]G K@2!j؋A긨M-ŮN?6^w/m iB!p8<85b  s4y 1 HY:Z^!uI;oPT$6R"##9577r\.ld2 "??_"H$\ 7W'3E=|=7MU% 75mPp^{Z4<֑ɨV5tWa̪;:8%/NP4 HY:Zd$Н7i?Ze0 & ]y6ѲZ k`Xu"޵JSoZuo AA)Ak*]~\  ;.qC3 |+ɿA5AA!R[IғbS 8gS A4õ`q2LrnjtA lҪ7߶  Rv)G!) J:س=|5FLX$qoφF5)x]YJ;xe)"IQ6@Ȯ w V9 `Q\J)qAAAArohѲo8|g#>u 4L~x:;[xN4Zc䅓rM\ұ^~lW;4 z:!/LnA|7WN@+!9&.G;УʢZ S  >_}swuHE>PG7pxZz ro<LsF%+(?|"N/ڻYQۋF4noڶF8wpvrk_eY;ֆ0>P\9,{g) qyדˋu2TAAA.aׯ޸q$,[ _|5 )do ZZ*ɒ(Fv;zsN~saΧ -X:wLϦH92+Xö<ȑ#G9fj)ܺQwF'OůjEkCAAAAyp9;;׭[Ǐ?tP#owQ\[*`A *vbŊ{'1cXcb-XD,XQT5؈P.v߇ew2Cx *L<|.j&~9k:Srli̠ndƬ:pc7?X)(|0B!B'dFp 6lСC 3gNGQ@3kӢ%񹌤ve{#ޛnh6ΰ%& !Prff˸"Kаj6|0d9 !B! (gΜYfǎ[trϷnݺpeV|R4\1qSVvSx7er UC7 E|H h~nߐxb;Dਅע޴x/mb 9]\}B!BEjRboB>y%\zu5jԈ/|s7[ d}Ǻh.w`dj7{ 7{ @6\SBy?2o]:%KKv-Yi ŷ}={zxQ !ܐN$!J9""`Gڶm[bkBЌϙi nu~]g$[Uryyz`=NB{ ɑAivumbr2EƆ󹣶}B!F}npHZGHG~ rpl5_UB1bL5VL1BJ믿?ޥK͖ϟiӦHڶZ00򥵏vu|nWb2ogjfqPFH21Luʼ`$6|Bu.Vȁ" (rBHiƕ&޺Ɖ+ON YsF]'7wv'P{Q4)]JxW B%ʖ-[Fqʕ5k>z6m*z{Z8R8]uswig]޶({mwJ皃߯^-;,h8PPu%8-Ϩm=Oߛ#&^e^!$Wm6|ߵO-%-K+lBȇyS^KC%2O;+[YPli.3bR[n OHH-ܢl^ >=9-/#6qK]T -c8S!qء`^`Ӱ1 7|ݼNZHM~CZG~!$Ý[q%[/a$wϜ}qo*7gpVGqlݾJ2r982kٮ0ٳ g>)hײE \5GpgFdZi6umr۾& nfٰ{tg߿Y\ڭZ԰r>dsO:iռEM ]Z:fkUK ѽCN^5Trk?xyJ!𪴙PN5y%{yͨogv_~D*axǎ&_1M$K|d1H,$ J=}=ñ߲;ߏ{:9=1)\fiZ6  d ^wRY☚cT't0Rshz͞X 2Or8lbTz`хs7V/s1BcQDw*+<"Թc:3:zZ~qao|Q}jִ7Qkx #7Z_m>uʹ1Nիg;yZPU) ԆcwQNG: *NZK%e%[:kυzu?sgĪOB!Fdۺu?/ߵ+pJN3Doǫ} nEo% },fA\0[ͤI/2Wիgn_+QlO,QZ8z~^̧jlJ >cVPXye1wPSCxm5&aͳX#_.FkC8Mw+M-yil^$B1، KԸ}m7;}0zpCH_ 0Y;jV>o)究]w1wFVʴ- \t{5Ew Jz`<0ʘ]'$x_Z=2dY Q, ӤuyiW=@^FdKܿ2(QF׬Q]ݬn[IX3m啠r0x&\ЬRɟw߶ǘ^$BaCA3Zq>*eDO\foQYDxcI]l A2|Td'àV<7> @ӷO VK+ uӴC-tOؔ`RBJ_*ڷ7~ko^llS;$$:V,/Q"t jt8E(SĶڕ'5r$ʭ#$R)@ 91x]0 #`4o=_/O>"A0iV!#~S~߇wDXѠèFR s.9Tj'Vi2+N>h7$/#XY /|._ئs:4j΄>E|\c5@Z-9P&o]'N &Rh=~n5D|ԩ׻oB!f C&mÍ"/ho$ %Ԯ''[΂jAk?4ɔ[8 _y(&_j/U?u s^zOrfS56%g[#l*;87XR1_{|ɗ~N0g.B鐯9r#YX{V5 h]C}[Q ~oz/iSYwH&dx _~Xɪ1k0 5R^\z $B>|ӻc ni-f7I\`VYTddS>Xj,Mޤ%=}e<-g)F1*A)nlЩ*:Exk|f~>ᗍ6lyf^Cg)M^B$Q^.uVZCpiҒӏRigKo 8Y*J39l]L BQ7iiiRifRZø$T&e!KRiJ\2Qẑ_,e_(, AddԞ@ps_ŽkY7  f ~{y\&-橵ϟ)%R?YB>yLAttWW/qz޾ jǀiC3fck櫫Y'm[ α@?PWgc16asԻM5l&cRgS[]yC& ºmR9?39 !|=bCk=J{J%Lvk9'DI7UWh!`,b$SglT+X3?x6&^{ڐؖ@HG#8= !@尴C*sxCNUqY?P/7Mi8%dv'D9pAX23lݽ#Ψxe;jڴi>=#9`0U-36NU+O`Tji?{:S1!r[^}LT[r0ȫeV ryLiY5f,bf$ԎDopk^ \ǟ{{ښ5ӛHpy}(b`_!P1f\ O2x,ۦ~v5/EGgN̍n EDrCIӮbqRLTR*D"G;R*M_$RH,Pt{ rY]d@ r"mėRGG<M9Vt07툩ؗC-Bۤ"##P( BP |ra&;;[T*ɚ&W~RpgP@aa|>j!e} 86aӹE'ƛXHTm lx~uTwmeOBռ6i +Z_*U4 jժ;[\!W !~52y> + w,f뢟m]U߹\jCJ%9!B!TɗsO@b((5?+^N>%eLٱ@\ }`gwrNwhQ4? !BP}橒LepnٽS]VܺQ(Ar982o۹#'a'oGOAֳѱIrDu}[>yn۱i^2.Mk܍vBJJ vv.[7a+U⫤Wo=z8ۖ RR \B!BJ!pl۲E) "}vr œ_ q s0a]j& IDATG|$]eVz3_Qe-T pJhK'm RدOJ *~/J ՠ B!4(3g$'';;;&d2BA9"8g:{ d]^,,c}xi u*Ie-^3uԹ gYwy1?@fV^NVۤ֟fviQ%RniB!'~RK)=JcذaժUp_e˖•glWpg^W5kUբf rrXm.L:0$ϝ˜M=fɌeY[,ҩgֽz0̹!B!^tp 6lС;v|<~#nN>h09w욟kjs\yhϼ9f^Ecڕ..eI%Դ44^.bS%upB!B!\ tp9sf͚ ]tz[n]vo'2*L'3,Mt Hz_չlUȊ֪o޼z[uf B!BȻVիW7XFB^PqKvSz|5Lpp쿃_.QTN!B!*gg爈=j۶m!Jd<0ɋkyY8;z`{;y:LGh{_&B!BHqS>}-!!!ϟ?oӦͻ;iE^h?6?78E|hnB!B!}+IFl2bĈ+WԬYѣGϟ?ߴiSኒ -G [w[A¯o8=iUB!By_Jlح['$$bnQ-L2so |.՝B{f_6wP`UTON!6Ӂ3G vvX_׉'M}peׄ@XbKHx"cVt.SB]u,Ĥķj+՝JPΥɉ !B r BL+E|'EQw߫Я,2Lr"o1ۉE~sk>u` ~{F < ;;*t]5µ[eZ_>}+v]Wj!H \@aa|܊to)ddYv V^ZY>B'+3n|wAU9kҎ_BK c_ Zl~oo\Q޳.^elUy.&yqpYt ŚSŋ]M/v{*(w r5&Gf݀Ռ>u WOdS%!RHpn WK)GsF'*S'ddM{j]'ػiR!6;rnr"B?u܅my̪hYZ(wJhaB!;u+>wn8R 71yPw=*4 E1/BJJ) 1CC8paL&kATb|.ns\ vn}R{6tw[rj_66N @ytߡWdmj5ܱM\yv0eŞ 9z7-) \k޸aȲb/o3(#Iïeʴn:d=yn۱i^2.MkMUשZJ{z,pi7믛j\y Az{q2= Iۯ!۩y!^U;|?F)pmٞ>z82O_ !kܽc{n5,e[X3)zu7:6I.oJ&wҔ`. S"4V9RVRQdA ̉حq15gTC}u3e^Yz|p偞{M $w,o+#kچ&P%G`41$['2+|xu\PAL]}uǖ5F2gt)dHIfO3f:^f?c,_˷i.t };Uh j?0OaW!ƅѵgޱ/7qj%˻7=~2hQz-2,g[X3r~lա9B?brpOY N1k*eZLe"iO{>aŖoBZ( B@y<ealRT*i!$O|.bL WkjcmSFztw/K+Gvut?U~#[fhK(,k=2X(A>e{dRo-x2h>糀y.rIn-Ǫ<.YQ{R# 6qn& CzpBn"c[y9Yo"~Z@څpd?Nkccl/8M~?h&fs5\&q٘1em-gۼӏvZޫ)Tͷ~9{@ay XVIPb1ZdYV{B޳`bY C@iIs ITmCHiGKB/<~H94XȃqfPPߜ!ncVno1y՛,lebIS/ Nt1!@N_&G,|Rgx_w\5ߪh󼈽o qh԰o)lN57qE>zUA/a0Iwͩ[שgֽz0̹H>R3`y53$_lHؿRʃ?65=`!蠧'îpO H@]vn+@<`ԭ-oBYl-fZX=c۪XYk!])Ǿ ,)jywuSi)s 6ITeCH)!F k5. V͆FyVnm\%[d\288̂ d]M_o@h0 T*PqSce|OwX؜\ Pk*X6LMHwt{ĦJ4E=I|Ena";{o8W{i6ΰ%JMPF\Ouռ7nxOw_{SV!!"["ضZ[=}vefv̂X:&$S`] z4#Z ϻv+ClGsT{۩%ҧG-𦭇7H5pЭlaH.c:X ּ`uܵY*U]3iLvUտ7o^y_#^O~B8ѐJݭd{{dEsMHM[*~৞zKs8NڴfKqogć|.k}B8Xh-f[X:"SbMu uUK 4B+_Bȫ8[|Al\1i3bcO2 d2_Ǫz螛Ghvxn]_m1w˥y\=S wBaVJu̎ލպ6Ɍcy* 8K l}ׯcSmF uWdF!|$,d[\3,bKu }2iy%RJP!7 yPOC!yyz`=D/"\r#2 q[zs.]:$-g+KF cAs6~)g,F3-Wj[=] k ƮVזlt̳mg{wޣ2> 2eߺB),dha e`I˻)]q=$$88ľOxX|Ⱥ6PI,.#5 A UUN^I*T4<UOv Ox+gϞܾpT7=(E|h8 I{iƷ+a!W5SrsӦJ;Yr+P 2 B!d1(H [ʂX:Z1X&-fb(L R︃Cd˖-kwѽYlٲ)0!eYr,V8Ϩmo}3("[b0sO+ g~ܗ07-Pާ-fo&[Gf9sS7P9T.Z|O >KF^ɹ4]_ћ~++h7+usR#/o[J90.Wdgyjb_zcdM2= ! t}bQggHurKLuRb9%Rj*ѕs$)S?fM:L"dis jŘ䶒$g(UH$ׯGK䀕EI]ѫq2L!8|P~_M{|Yj~LҬǖGdL:{.=W;E vވ{-e&\?O'>?^I\גA-'_s0(ٰ%{:g~db&_F8t9NoM!{HzsFQ9u_CqBȇ*+355D"MH})(HRSS%lPdDU3h9(@ [`, AddԬs I P!%Yy>ctnj.ļ'vWj l51WA}GT >&Gw?0{1בfG>f_ma {!g鍵 !Q9?7U-:1^-2RslڻE JfV}}:*R8HRp7>Ppg^LuqD8qN;t_˧@}-0Y ͤI&1X'-c`]!޻QXA' ڽ{ݻSϤO?+4*lџw޽ې+F@S,7{ݻ =,i2 5m@Y!Kڳi7;nݻw>fDgo8EL7nc&[क़4WyWyÊCCĽ|sX"SDžmr\k 3od鳀yj4v}CgepdNS,֡Afͼ; ݺW%[Nenڦ3rި4]9W:12-߲%;czǎ~7kKE5XfN- (^ukR01MA!(ur[,-2fU kv΂,i&M2m:i1)) sf;/ sMy8v: }|xg'UO-,&= ͼî{M n,OY/51p)dH}GUz=ҰϞMXָp߽Ƞ:oqPB"y)P "ccP)$t |iNXt]Xk5ivd7iR͌6.FdK2&U /.W7 )rVe+:[:cVrLֱby1% B+}ﶅeY |dub2%|oBZ( B@y<ealRT*+*'Ml^S viQa}5|.~ԣu̵_DsecƔYQ?oC7-o!q𞮘|.Tm٢Fs۝/ѧ ౶%sTQ]Qkmk|ѳtmQem7mlr9wN!  xR׌.|3Ov:gIS\:ש;wVRT%7g4/1a ;ya5Y-ּ>P^P,t$[rnbçOFTtqc[c !Rn[X6Y̧IF1X'-&c`]!{h~v~߳LؿR3ʃ?65=`!蠧'^x 1Mկ^,I[ k7)4>e[,A83(fj17\7= k?}w==4ݺZ9~{j_W rq֩gֽz0̹xO^st޷װ~u론R8sYӁ+l=/Q έju9K/1Y-u?:B!)mspd{@|u~6ΰ|7Vӻk9b MBĽ{/ܾ f#O֟x7=Y՝0"OSnde&$ܻqvt bS5;e]MٿnA_E#$p콿ۿПJjIx `c8B!B6#o&xq:?kCzMOatfPğ54n߸GvX4ɒu6gȍ1\/JX+ClGsT{۩%ҧG-Gތ>g{vmq4yʣRw EBh pk8P#$v..&:+5X<*ɂjQzMۡB!暑˽l0w\^J4|AlܻO2 Jc]ev4m^9 XWbᡰ@,Z~TWQT/.]Q9B!SS q 3.@^s-mT5{7IW*bB!Bȧ [+{:g'gpeĆ3Np07kpz!M:,u:j"5yRkE.>7u튶`$w6/_LSk^?ə\;;,ē>k ԫaϯvuېbUǎ08R8C B>fbXǧ~RK>\;8̿Pbdó1toZtbz{9MX֜*Y5Tz`хs7V4^I?bzTSύvͯ6yQQ=u\[ IDATmLǸ>~`FH!B!|+* *5䚵B5XMZwokjT+H( a/_/UK^;^0_p> X{4xsL:2-߲%;cW:4h֬wA[?ҝ#)`B!B'Q#&Y1I") T @%DetT ܔT.gx"Qn$/cRr .͕&_s~_|{DHƜ4By>6>%½R|Qdd@  BP( >\.0 dgg+JRYaN],%6b .&.gV]jn?E֣c'_s>Y B!B!QzqD1;T7o<`Z,\Dc#$Ĥrػ Jf~,-2 "5)1Swtv5uLWFvZg)*gk[opW%bB!}m!%:8a9S_xFL͘58DOV7{ @ֶ.9b+N^X2 J{(*Frk]f74|]F@MC>/a,˼}zGdtCؗt,/vصdnoz(m!%:8̪뼲ԂJ5mD+ݑӒ2S%YC-TIbXvmW*-'^il8;j˱b .ˏ/ {Yoe W+5o<]A*$_͗,6uN1sD2?F=joe[mWPL%[B}E&vuD2B;'73!I896l )Z!fQWTT-{!/9rI:,jdsB'+Muyu/T|x+ON YsF]'7wv'PTJ{w-2!%:8!.o'{jA~Ѳ4sX(L Z*-^`>5Dj*K:'|N G; ?^R.c>ލbl R#B -8Lo㒭0gN>r俸73k+wSY #8n_uʵlA;م3 JuTskk٢Nn (×ɞt'R so?vV9O4Lx̘aQ!ߥ!'~a*;8d8㟐r,ګ]kwq_SfWB87_}12{7۩b{tg߿Y\ڭZբ `! S iq>Y̝GŽzʔq.[Rz4uYRfB>ZIvq)3ʨ9\qZ*oc+<"4Xg䠎Cg$?Ω}ˉ2FC< .} `&wz y警:y8g k4}0?\tZg}#ԊVl)lkϯ9!!56wq߸ zdʐ ,o{ԆcIsԮȀ A.s燭FpZze(v8^6ck[)cZ ηIEFF Phmm- B@ <2 0LvvRT*4'jejUyCH9O3ZTRnYZ˽v6 )ݿs'oVj?Ի nYQƬ:鈐 FҴ'7 b7קUi~雽1WGfb07ֳv~UJ=cӋ=4]05/[[VvsQR( [k֨nVq7-$6> rS5PG4A ua@Vd_(uN!(1,ox7p-ǐ ev,bǷ;0TO*}}T&_Z0jy3|̦KR*C“@'srs^ElW_(]]#-\,#Frm &i`_ /O>"A0iV!#~S~߇wDXѠèFR s.9Tj'Vi2+N>h7$/#XY @+#gÐjG/pL Ryj">t|.L]@ x<PWyk~u}QR$^y\jyj7y{:鈐wwZ6cS3Fέtjd8;h#|> !t;;h՜ =|*$vH2ɖ |ڏjf54SןV:}{ b 9/s9s5LH{ǯ)YXϒy2v~'L||zQ i"%"I>>K?'ѳ~VF`UZM{!Vt 9+^#t z8ZWh>l/j5P@/6矺1?76Lp/. &}Ͻt_TZA;<}I6^×w>VԌ8yC !hX/iSYćfg w 3XeA,R7 j9}7i)qqo߾u57P B黸ײ `,UUNS'8 w )RE/=Le7c~^7I_;X? XxzRS%rm*t/.ldy%;봣jR$vt oUV"%rPp;/ii-Wdԛ+2_!:/lC-E|WƔ:'ZDbJu )H'r*GCY5<T^o%RJM ee\of.?w|:_I6xS%.F~usw|gŞ |V"ǡצM 铰_ [qF'a'oqm <v.Lrl&v5}ݰ.dǁc2]4o kjR66r=wI2~.Su˹kg/qkY|M@Fݽ*!-%cڤyXxvkv;y g?pe:DȃVv;#W8.[|F]x" &f(YT.k d7}+2_!:n8Q#WHYWF:'^ fAJVNXnɅ?ieX1G/λkgE/R$IǸ_q}>9kAc  w?J;{.nߧA3o7b~gn^?5]imä l}E6.]'keqii{W~>݂[ڕ=/l{^$W=vrխ3&op=Y]jز^`= @ۼzkȇ?L3ܾRIBԷ\BlN=ZxxO>X;~c obW-ղW^ E";С4O?N\OFSk *a;6Q&^a!׭bs!Ϩ$| gϞ={{;\49!Wj z do, jwM}qa$R7.MTZYJYmjmuRE\PYʐJ.B9矆=sO{<7Ss-$?B*rj?O.ˣ?/zӳ[c^G>Au>}s]z5Mo԰/nOێx5|Ѕ䴌[X{$^Wڇ$i&"@"BOҴ̭Ǿ۶!g|o~>ܱ>>3֏9n\}UYn]` >t~_풮[];~ḷ 0`] XSKpnY<'TBO/Q "mLa:~qg qWNo5}і۶v|~j"Ν  i?}9w3 EݠS>pS:[$( EzK'nI9C;@I>b,t3&E[S`*JVHDZ(z3TIwN^l 3ڢ <ٳgϞuM T(3d1G1 "99Y鷒8BHI My^8V nhtj>V;H#}qq}gΉ|Cr +]kPXqZyN6 4fNu͸5ZeE jw_w;btpVG]tLf΋^- =FH* 0y(,:OS[p YuŰq8U#艺k3۔ib ;gI 9Rf#Q$9P\FxVx^&'tzM\mz^:nm0ǭ|-Q~zɄڍ>ێ6i.4f9GB{F [a1Kwxo_ l9LMiiB+/3,?v:%0Wf*4ΡG5VA {eib|)|!Uz} &Z(uN!|g_~<(sO]j+q3^rZfugvwg+lhxVO*,^PZo۽~L㩳jlݧs) /U[?:6t}kt+9%m. @3#hv5^p($BvH9_aĆ? q8 bUZ ]oyTi!FhBM~Yw-UxQj&AYj% O 3h5V:'}Ԣne3{- 8wֽ"b .ޭ AFY]q6jq9 ?B*r[2a!78CKuV0'`틊&^x@^ҢuSM=ዼ?]]Z:f ^yW8@|Io8eLn& K@6J6AK"[\O=T`R[: ]{x:7TxL=?fPxx>5EPsDRuBA:;۰YyixViASG&$hೲwv ~]3FYj^VgϚ.P9!~T9L}kbP_fviK£d6Hwx$n>+Hz@$uco z˰@HWΏB3=,BuWzuU UU8 !B5upp'[] M^9u[ Irp%%3 7rsڨ)\ %'',,f CKO9O12#nE9+YdU< apReuXo߾?i]龪RUN7^3ME(!ҭyB!Rͽ[B###"^ 8~~>~}xb1yuDOw,$.WZp@oHXx[ -C}(J\<ݦu\sd^{&2A.?waQKh ~vb*B[B!+98OMom1V#֞\`%~o$ag0 ۾t~}j$m3z{^7=bZ&7}qnR+ XLAGV_~ g>eq7ohҕcsnW Z"T<<++5k@#EG5O!BJQYwpHje7[\vh٥K/^]d&,<~՞Mr)lrz{6ZW4˦N`ǡ_daP~&z9{vhؼΆgjC`fM@W+nZ^hrs?6xͣv3 h$9Rmo,xO0b#eOl mjtlQk06s0y:^^I%"R!BJQVB*Fe&DkGOX;ԩi+DK%bOBAcah_H&Bv%T%.$/r  9u(s0o 7*lkB!%$$eYDbii)H$ ˲bX$ BaRj-EmWW>2W IDATsu_jGGRj!stZ^L[B!BHESB!B! B!BHGB!BB!B!upB!B!ҫȫBMFl\TՎK)̸4ΖQNfYT@LB!e{fH;B* ~F:U94lN_Vi7\8}ߡ3~LjVBH%vzW&ڮc)}΄Ʃ-dQJ$pIBP!Xb^|"n:1H,{ϱ%n@NӨ<5^ VYt/|0I#!v%Rm߬i,dox[vps"*Gא`%W$ӖU TX/beafS,)WPJ\*=@"RԚcd2&B,wżP{~ReU>6ΈS'l-F(;Y)G5SZ ۄ򏁐 eB hb.gX^#93Ҋ@*S G!T|1'f-7ŝ1 HqU<ޞ?z`XJ!]^kcQſU !EVs6ړWy@qCz7ѶT2n^v)B$ͮC.m9%j1苁Nމ;j^zyã}_ 8ps;iSvih?Y^.vme`Vi<[߫^K+3b"ӦO=ڛ+]?/݋w7:9%69%cҡ]\ C)Xef]?r<7~s)]@l) :{d{H2n^~,H,w0) Ӥ_ !>)IpXX-l95zJܻ;4^dt{MQ/~l`MUc z^ӺbZhP i۱&wwbk  ՝? 4鼷YFܴH!aLտ^M&4mDg~>͛ʁ-jpqC;( =`P"0S:(}GW8  $6{jw;OK;tyO}dҼj1WM.TP$VLۧ5̚-<!T_@yJu?{C0 2ӱܐ(m\iNN֊.j&7EJ1CHGЭqc=~~H5 ΰ[F{ٶ-+(RdrT1VyVgsSB8v_avo;sn :;7Qyvv_[ ^d-)2 X-)tQ7̳NQH!kZZP0ƻxm[7 çoyAH@HE@-TB?| ΢w V/!HmNN03i^ D-YB7RXtX}wO}v=ۼ~'|[KsKV[~y*J\ұܻ˶)Tr["`9o5 T*Ur3Gw2oSWx&tDOw,$.WZp@oH}bmsN^֭[W3O[տCk? ]ahxWJezRSbp~ ~~~~~~^fy!2R.W&dDD$yZJTryBT< E Xw7 ADn5~2,C}M{Ô"($"@HGd !/y <[crn#/q3:cSo{CŦ3vۨ]Zh:٧&xA9#ARtO?Y< "Fu۷Cgw ͒}uyV^h_6hUDO VS+Єջ3[T8!GV\~]2WP ^^z+q̫c.i8`? \+4r-/ AL*VeX9f99nFc FpRibv%vѻ^8vƿ4m_xj- ^I?4g~rq;\Z.\h$9m#;& Yv gn~wF>~m;w9`<;ʊ}bK0Lg$+aX;tQI bc]9E_φSoBH*߻V o&)TXˁ_SxP=MJ"$$eYDbii)H$ ˲bX$ BaRj^Q!zS=gb L,MSȒ91|,j;Gux6m ||jՆ;HdN*3*?>(殔z#B*iMVx3$:ή|kj-5+b!8}@;'`ڠ}Lf  !B!:8l=?; -Q9{r\wz-B!*:8$6NuV} H\$y&8hu[whLB!RP!Jv&!BhXB!B!TzA!B!J:8!B!RTkrC B2LPժC%U B!BHGB!BB!B!upB!B!ңB!B!Tz !B! 0;CFq6t衼ccq{~M[)a_*ȓػA7z[2νռ@⨃B!RUw7=0Y_Q.XYQ1Y&Mթ'yKY#϶wq8FR0fw",pHUCB!~,pzxL/[{v XV]#i?؊Yȓeͷ,|? ߥvSk6x+XwB!BH٪L=tĩ\`$Cg."Ojw:xidžߜV&R P!B! i[/.WV??)K/;_0 qKϨOE̽O&R]7 g.]|B=:yiŎOy ӣ.EWo=W!gD Κ=Q)ثg]wLʥi>ZZ)8yȭwfoߘ(ahۉxFhۤOn-ua58<' }Ǐ߱U3M&rJX59]ݴ30{8q l=a>0_MY;>+D{Y P±{{1*$ B!{,O!$mygŒ[ `x S_ijIsK:ʯ ]g vv0Ɩf3;1g ^kpWM_WۦI4s|"RRR>t¹]5(p"!̈z#i9}O <=7sz]R(:茢 M1ծ Ҥ0֖AK .V.|bm#]5A~O]OAJupB!Bʐܲ=fcJ<}j=kiݼ_oAAԍ1 -Z5Q7-78tC;Z3zcÈy'G޸pR[ލeм1V#mgH~xv6e&#|'~X$p\-bn&ElQjݧ׭Q.z7NxI+LwmC65$v6 n5w; :8!B!eS^] '/VzIJƸoKѭ֜]ֺSMOPV#~Y:zm  4_6/WQbŪVmˌ-g kLbi,c;zIШ[;![+i?֓<6H(h˳OHDȶgw΋3.S>?f9}#׸5iݺ􎮉'V.O&(!B!U5P;`ƍ_bܞ5N%ј(Bar u|B-[05[Q;Z P>@%MWliNi& F~M*ˑF?вVOXޚM },V@c9%u_UO*lXM$ic cc>HT-`.BJ B!^S|cQthvٮ l.>zw4-m ߱c]r&(Wz1`ɨ.>N;ٖ'fP]|f 4.AmoVͬcQŝyn V( iр= FB!2ea0G0{Tmj߿x:/7 J3k0WÊ몳!a+KjLXb:1.]1OuSRNk6HBupB!B^ ,&.OL./D\ qŨ[?Xd˖f9l.Eg A`Yi-iL%q@VBW:@o=jCZbй@[0n(#ߍm@mW7ou95l>p?ۭ >j9M2JJupB%(iie6\ӧ*ʂ:52 ZLOTq\FGj?go0RQ0+{?Xyޱr;}.?zPp\Û/mǙIP /7[}qÇ_v"߬u/_qoKq B@,=]Q{I@}ܸwqV(bm۟r8#Ez|*˃ݺط`I봟ſ)T)_9yE[>A"]+ Eܝ;e"B1$=]Xxg99_[H#i?؊reE*rkUB io}~3;e"M#o}$ɻNٝ%Ρe IDATG5VAe2^YڮȯJ|Az/R6rP@Gq%Nk7l;ڤ9JJ }݋t&w-} L|ٳgώwY*Tˆvn)|H5YMmBX.VYǬl6YMas~`!fhDMVѱ=l=uM'Ax&R={ZuرCo! 6 ٷ3rw7Paϔbc5F`甏k4}pGj+ᮠu)̮Jxњ/[.Az,8WmRkjhx>M|ָ/ƁrhˣWe/>~`<'#ẃ' Sm)cn_ueώ/FO{8p^^Q>|;жIZtoknQ"^Aƫ3}gEeTDĤJHKbj85iH׎.vF%b?v1R+ji"}iŨOE̽Oo퓹?;~Buwl`v g.]|B=:yi*_$%).ۛę`cP+/ Rk{,21&AW>,+:TυV(R K;SH&%²D"H$eYX,B!0 deejZMT#|.&`A5aƭ-4cFj4 Ĉ0mGg5 ̈z#i\́t]&`0cOAEוmkǦBsV?5]c2Z8Ժs'ly[~nI_)&~wtjWxJZܔa;[ÊI\zLl=n0^=rf{e$8>VBBHU3.ܳم;%a^oঙ/^uɄf ׏=G :8*"upTqq"HpM$`2;O\3-Mݣo)/B{mئR#v6 n5w;1Feм1V#mgH~xv2l?ܧGݴ﵇8'pֵE- ࿄/?$Ƴf:wKQzr /Ej!ֽN#|'~X$p\-bn&Els:)¿oaߴ+7"k%,.)E"eҼ~&]%f~7mK~}E7cںe[{ nb@YB.I^7eԞ\7ӄ}dvoޒ? wd[Tyب;XŏZoʫgm?o#\|~r"GqkozF'V.5噭ڇg[kLbi,j/KqwW14V_ppFpr{]G[a:@Ա/n6]o{=qwlvkҩ&4jANHDDݘOje[w=[~zJ\d}'~vzRsv-|vx]v,ڡ۩x]gA3s}kib mBB!B4<&slWпE'pߘ9k8!upBr ,&o+#`cY.$LEF7rVQawT#7h?{e.E,[0sqG!՝hKlʌdBJUC %Z%MWxfdxEnZMωOgWWe\@#LᘼIF4N>ϯ4ClVfW)Hu%-Oۂk"Tx ׷%Bk%SRS28puw7'kuI>;$P!$ ,Gh B *sK%wDi LH`o?O޸uaUgqS0>㮓\1y5 aGx :3 4SQwЕY6F_ڷ}Ivcé8d;tY׻[yoi3`X5d4 WYe5~-BvY%%Bkjjw P! x%$:;=i,&׷ŸB!gbYGBH e;v:XXSp_@xuܵ0+[ַ(%:B!aFĦ[5,Q6ۡyt-UFyjZй@[Wyn<@=7S}ϡq@VBW:N7j︒35#3\ZXVڸ{KZp8)Kk{fv@.1O.!T5AABO/Q *NI봟ſ)T)_9yE[r`*\V(yPӗ yq>8NP >? ?V;D#}qq}g xޛ>ظoC/3JfRDZ(z3TIwN^l ٌ*z B@q7og@˱'AP(X:q=LYtÿڳgϞ={V_#)Ʌ LP''z+r;BBH%;StKNG?v¹߮kKi&/-:rYbݧFR B,^PZo۽~LY$klݧ9Ǘ@jt}kt+9%m. @#Id4G`8_}VW4碯ZA@ ˛R[zرc>Clogi1>FhBMÎ Yw-VFtnjöǜvt6_缋\PJ# :p!NwЫhA5oL  >5FpC-bI ,+H,--%D"aYV,D"P0 0YYYjZV+*Tk:_U F!JM?zRG99>.R+ ܴ,Ď%\\ ORɸʢȅ{Kv|.1+ A!Bb?v1R+CztH@~1)R㒘NvMztҵ]Nۗo]y س׿~x؋@򨃃j-ơk"v>4[iPFY.̥~C;MgLfO-Hʽ+B^61_nq{,{isDܺV[ o 0Ϝ>Mzz؋@򨃃jͮÂ}feu$Yc|m5nek+2Rj?p=!!9AW#W\xkwkEңW_ȗlÆumQ/!$}'8G lEݾ=k1vb9RBNHʜb%@?ǧĥħdp:nN֯ak+2SwgjXVu(Dy^B!e-񢟿@I_1̞ý[̞X?Fpr{y]G[a:Ѻm%,Zh>9]I !upBlVVEfvJ-5k@E,B+#:r{,V6Zb=*#9YP_g#SiSs*NnQs TmA!B)m;,!XQvdO~ 3)R%ρRQ!B!1P31I[u*\5qϾtkˁRP!B!.S//\}-kaQ{'l̟9B B!2 +ΆH0e.%A㋁Meo0Gr TA|B!B_)}_BC\#)Lfe2Znz t9B$ B!:^<~͘B{7hTea)YF֑>K㸌3xa\V(yP)BHFB!BJe ̰hkjD3WpK.8CmkK&dh|s Tm4B!R4=K"-.i VڱoU?g諯wĺ /U[?Ԛ-R*XRFqm_ryC!LP*ن-CHz@$uco]ȉ E*`b)7e)HuZ$no aYV"XZZJ$D²X,DBaajZWT!B!e%cafI\|2I%ˁR%+*B!BB!B!upB!B!ңB!B!Tz4(!Z&&BHQU{n*,!B!BB!B!upB!B!ңB!B!TzA!B!J:8!B!RQ!B!B*=Qy@2b]jWNfYTP-!ʆ{fH;B* ~F:Uu @'}WxY\'8E.+i7\8}ߡ3~LjVBHyQ)b܊s)2${og{$zAR],=X-|Խ>5 8{cm4ؖxɠ*f+Jl&0b ¢ل#O, 5ħeOḒ}4Ram2}lgHDZsy PBiBH1p5:ثek⯬q4_~uyVZl|;#N 8dY:հwCOI9l?B* M15 `{юtX/坫W)T"W̍dM BʍĹޛmV;wˀ!8'H,tyݫ ENo$8۪go=1UU)5dҋ :8!@Ê~F{ <Ñ#H\:=~H&ږJf͋.Euȥ-AD7vtR}1@;q{GݫW6ox5ߠw_A~p落^Xƈ`єޮzGgsҽ{wSbc_2. ޸~ (*]fڬ8yȽWߠcw_Ar@'c/8\״S46B qGVoзm0\JyUJM eaibW3m"aŵy=t0ߊukq4k;[do3x%ދfl9~}hinAUg&E²D"H$eYX,B!0 @4Kse3)Gfr3,Wٰ,-sTbrJi"*;p?,Y?<鿈+-8vYe둩k .Ҩ^k'<|5읠QЮN8m_٬m<[ypH}O)!JK0T*]ٍ"EH R"f]RCB 9nRn.5@tg i{>r%JsqU؃"* "śk/6]aJ47׭>d4`MPa [>=eݸCv+>w|`|Ct<ޙ}7 cؖUi݅8jYWZ{5鹾3n ¾S}+aeD m9k~!_|3ٰAJE߶2ԭ}; S)NYwֆ IDATkpyOW-8n{ˉv71.Bl#h:Ј'݇nl?5e[}iDxWEG?gv _uAbv޾n z['fېJil7DDs>x6#e/BZhS̺WHT71!豜I9"PP1_ҷy*dH6QRBiWMbQ[fxӿ֤sv? nmF eR1}nm~-xeC=t`7 ƥw :vZ 9%9>VTԹS˧x`TQƛj QSOp;.gK0{e Z94y5^힟lET9]/-T KCU惱Mn[&t0jRR8g}OC{ZjWΉH2H ͙D,$E)HMR )us= s?_r^ <}5kۣYϔ(idSOFKrUVs<2bֵ'4cŒ@:j/qFuQ\|J:xx呷[aIPa$knO9\0jPH:cQ44i"/윛h׏CJ 䪉 vZjժU_)} "*R.zy .sX%o繌2Cs.-D"f&xk5)Q[g{2HQA8k-蕥W_Γ׵^\ܷi/}BF|@rXQy%p z}.>:2Zڛ n?O CWGFco]V/MX3.]x=pX ۽  WE<-6S=w҇c4tyw_ ;r1Nju1?& :E=e˖-[֡˻nߜ!J^3BǝwjBіZV{+*׽:6))\GI9$HI0nRn<%fձq!GD%X.8p7y)srqbDtgƌlj8@wv>kv6C ^x.b6Y ZW"m/}y<@*e76= WF}7=jхk..u8hf)ybf+!T.yJܳ= 6L7ƺ&DU >:B/|5Q$g k]Y0ʧ<пT|Tʡ!j,?hbWZ .W/2CsH . 餛{iҐM4R?9A= *ѻkUHs`NT6ϚFpJYK>lj[L?^}Cmє쾈>ɿݸ3[rQ_?fUzBʽ>[1 c ڿ3^[jLfmڴ >x[_lعq^؏z u&m8˂V9^PΜ?\LzO%+V+ci'"[v)?QP]ϼ8)ʆ """R 2|[6LH5Պhre )ډכndB`!UY0itE72Lpm*/??Wk7&LKצZV tGV}qYVп6!ta$WB[T^ [BC#%"""""" """""""* """"""kpiZ@DDTi4ˋsTeRDDDDDDDT1ADDDDDDD%DDDDDDDT1ADDDDDDD%DDDDDTEťu9dᷨQ!' - nMtaB^ Ua#Wxvs%& """""*x7|zY}@ĈIuMzJ{(IDDDDDDTĪier5jp'jy0+5-#.2LpQ!5s]aqb`ڿ}ht6;=T{v5[Xy{uJJDDDDDDTL*Gl:Au两ܢyroϲj8zӒf"* """"BtZBOf0nTՖfezCt5Qk;St3%vc~ ],sL7?@Ҙ ;h>IƬK 1Ab QVͲYfJB@}ݺ^"l;Y &8) .72]ޘv$H-܂J&82Mub-K DD%""""b,ͧAdU6MZ}a(1ADDDDDś,V{ý .9?s[TST;ӌ)qyQ"JDDDDDTĊi*-a5bN[T~ro?Tpz}nd!RL0ě9/eNj^"""h߫h*ktDQvr+yoS>RU*JEQT* \. &d2q  Wme"""""""* """""" """""""* """""" """""""* """""" """""""*E]"*1X4_/_xBSb/_& O ~ʢ.Z2c|\lrϳBDDHsq `"= U+ATړ_ #H [%""*O*R'lXRl3lYI>aßϖ/bN>vfүSM DT "Gt4@Ԡ-.>nR>m叾x),, LjY|\H7X^WN DD=3j]nLp8X8wҭ@⪠4˹mL鸾2 nP``1ADd1;;<Ӿo4 RcyQ^)mS O_J\iʯQem"TE],"!Ş<#_|y Q XQl-̅̚ y7DZzmT&bJc(>ً>[O3)z;z]U V&9' Mxu̍Sf]Be.Dzυw&O F {~v1[Оݳ}͖-F? Vo/^kYFjO8~B` 5:3 ӣb.ͽ I떝gd|uth߰C355@w4ٹ5aa/ ZO{uhGQ?LcrVk޺O+8Tf9cæ~x'y 4EOշK0Ev>4]:[ޙ[{M]6yjС=xk}vlݼiYJ*[tzʔ_~۠Gn_soؼȩ Br6K8mb o*q"ܯ͏ T]:_YJC*5P%B{biaEgμ_!ȩ9x2c?n޴+5BM }l1sI>xw❠;jꪯbSR:Yw㑛'sdw#*\&D̨y-pVQ0cavh.p|a76iֺnC˦f]2Sg&lL -յǬȩ >evr==,>c=v52՗>zn8$Q˞-$eX`V9HEQw-ۖu#8\ 3ᲉsN9챧]8,Evk˛6QɥѤvt:3z5m Ce3SWx|w$\Cӆm]k{Zߺ32qȒs!S$!uWxh]O %jt/j乿M.JrwwWT*JERP(r  L&D)haLHiRJc.eWwfJǒ}Yxh]u=v#SC' S^oQ)4$.ŽKfJ-tWGDHnׯ_݀"}@ד谵Ry\VZ _p#Er¥{;PNFfur`:3s26W[/}}Y&,<|z6yu^kv*[4mv2{!g\5|?ͫgI-Ihby*oГ,VlҳwJ̹h"RJ_(\Y#Q.-~]ڗ?B4v勍e/?NP9H,8㰛ͦ=_Ƿ"MXfL4͒lpW-!nQg:(pa׷g'Eupӂ,'6i1UV&xڄ|WV˗flj̠Mnw쌝/Lwool`v;D)+Ԩwvگ~cF~J~eB {fq;}y{E3>=ێep.:A 1ͷ. 2b,}NF|;5u]v$9u )Oy!=<5z}DEN#oE|`j[텕GDtl7Te;`0ZvvR!hO;>mWn;7ƴ/ *?H}ԩ+˙FB2@}IFO^_r{A{649G|/_3A'hh͐ uz,~QUN_>Du%oW.EP\15xbz z{(}0$>8JU{7^N_K|6CfZ޹ ZM)*[eMy86\-M,} "!/.q3KxW.iLztiyДKkd'*zLp{JSW;VҬB Sɲ[ Mv1>^@08)@.>SNRrwyrY/Mb²> F8*m ;s <J=17p&we]O#wrٹl/Lp|zZ:4-m? IDATT4iYNG&zb/,^ny wMlڵk׮v9c謄;Xi"胲HbCU~[;p K?ן7lC {H.Z'#*jLpnxG]^ =Tq}:`%^>炗{ߌ%-^v:.٤Ş2~/jyۛ/W˖- %˼֬}|H2c^n;x k_Ξҷ̰dCܱع,ɦ6rKc|%g5/ WMpz+VX15}s "*u丄%q:.!!AǷ6\@8 S~z,%-xEL~?%f R0t:v-ɹo/ASZFs1v"Hk|p;~|†?-_]>a5Xp3KNذ聛2ϷGqHvS'$;D~^/oKBAsnPjȯ0՜\4ˠ);u. s/e#*AT]߿J'"9RəhHSǵ*?wd7Oj@Xwo]>vzw6f__eLǵ=4y)d̠kрO4~[u3Ua?lAYokW=}bB[wO< cW/P/e4\rs:ҁmie|>v nMIÄm4[׈YFDDJƟQ?~9TMECT{Wp!`)FD6|n̐M$ƻS;-/Vle=ƽYrNkmGUДX;NB:!\Qa9/eNjz#,5.pO\ ¤Z%fĤ%nTY&[VkD*r]Մd OU\S=Vд_۲OoY>z# A Ag= ٻc26ߤ% P^&r} ADTi2RZhKZe]\c 0\/gN5Gq\MIݝG|t+6Sxx(*]RT*QJB j2L&iϿ J;mZ +R}>Ώ}z?#enǥueT!jqFo4j~&|kDDT깎|3r?;yb!,H*hJu=z:/^ѣQxta !+kJg +>jnyרU| !ͻOU+Ԓ3/N!eL<5my1$LpiZM\4QS57chNx9crIYcn$o""l݋K!n4Lpii7/KxTO|Oqb%{`ؼ#2Ȫ,T' R̢cu(DQ6Q*v{Q "*DMeMNm-ri~fs,4=X"""""""* """""" """""""* """"GGf\!҆ """""ztb6yqԨБ. .Lpѣ[W˯LuڣG_S dDT1ADDDDD߸o4`I`*'J Q)XiQuԒcذi=\ǓCBuD߻+16^([n{ܿB'?x^!b QCJ)W R """""*CӆsX>l޽{חpn@0.2'\3g5HL9i9u.*+-sX]<9묗{ DT*0ADDDDD,Ҡ]9Oy=ZPa:.oG@DacDDDDDDRiPU:7³=Ñiy"* """"  )C?=}ϤŜڽ")f7~ԍ$AB]u E8\/^v:.٤Şc4,s|!_MHPTtr#NR-,Y* TYQs%mʧpQU*JRT(*JB!A!55d2L&NQ!""""Bgj *JBDDDDDDD%DDDDDDDT1ADDDDDDD%DDDDDDDTqQ2-oHQn[*KDGpQxLpQxLpQxLpQ+)qW}X EQ """"R)q>ޢGO$] )+ZQJ4&8[:g%y cGGu0=3ڒ,?RQUKr95k:'i[U`,[*#.2LpQ!8a9:QJ+&8۽}huVOФvn~ƋQTcj *QƯ%""""Btz֞+`͢WٍfͺUoPgpPOD%GpQ!u8h_װw{ۇa*|\_' `MPa D)QDDDDDT%[~@D3nmF ᖡhJFD GpQ!x/gn0%sHMY ̪cgbHr g4 b} 1ADDDDDřҫV^M)'j8VrEF8iP?\zeoʕg=BQgnA,zxv5z.Mwxq-*D)*DDDDDTH[.#/6=n'Q: """" fq04D1-tgg#ZN:t֒3AD`6 D_3",R:V{W&jO9UQJxx(*]RT*QJB j2L&Qh4.GBDDDDDDD%DDDDDDDT1ADDDDDDD%DDDDDDDT1ADDDDDDD%DDDDDDDT1ADDDDDDD% @DуzBUr2ZG,TsZ\(*j4bQJ/[14_UuQ&8(] `&5(J8xAAǷS⍊O KұE&?g9b@DDAr_xgT(겔61a/_|(@y7#GĚlټ}#۽}hZVZkM==q6g\9왳%܋I+ oնގU5 ߺםG.F%k<ѭGAֳazMCӨ{w [!l _^~m-O\ۻs_;&šj.m[6oټcIU#w S9{ZAuΝJH3ׄm ?~%Q@==.7ΚX۷J챽G6V,[VOhڽKƕ4QiRcU*c=u M8wuH_6aMa W5)Mj~`LorNZKdgn@Αuhf@D53*r^@v*>Ε2'0cJF b e6%3O-?i~A5nζW,"wb-[Oa%0׿<Ǵ[l[,ֶkm1pvsՆfWoIРnلW5BrƑ޶wLG\d7 kbS WMBin9zH ADTJ+NX)vQ8 gZ?x[;F~5HL m(go_y3S$:( 24/۔K(T*wwwJRDQT* B. Bjjd2LAD#Tʅcd7pk=[xka]3=C# -Ghա<}%oNT"BZt%+6ןuK/Ʌ؏ǎ89ZJ~> |IEXvm*[L]̧\pF]j_~8a^(Jmb8}f^l!=Tj薛 "*R+k$xsk>-Bqu;#燵zcÂ准߮ 57xM ų_=M/INZKd% 8-,ޛs)&8*)bfڋm}R u xS9='m3TXBOOhY7nJOfl-XBi׿>1Sa}~x `uYƶN.S!CF|;5u]v$VNno{Ep^Hu˩GTE}Dց1sv?nmF e1}2j~V*xe6DxTٙo/@oK1{YuUTҳ)1EFX> k-*Op;>gK0{e Z94y5^힟lET9]/-T KCU擤Mn[&t0jRR8g}OCQI2q63xu@Hi?qJzv<>ͧcnʙ /,nXd&wRk4p kI .O ϑ1FDv|3xP)=/x̬:v&rriy)d_)9|Y=-˗/X1;=߸6^!. iW}$R IoڴQf-?rH-zs*EZ%J=17# enM ˠF. .S *^ " L%4 .=rfʕg"pa)vy8Eƺ(@]uYw~>yZjIDATCv<~z}r|m>~*)k?ܥ/3gאV@quzcpA ה'Հu{zʺX^sy7[ϼMؑqZV9h $0eN.-[lٲEg\u$? ADT$ju1wEgbZm||6esxK̪cBޏJĠ;qzw0^_/Y "zVފu/Nr4:o$>I=^Qc'l^tPݘB^ngnfh ZYL4&˶AXDA߿//@gYHf省]ȿƹG-5uͅ\ݥ>ެ>t=OleQxzb#j}5 3/X{gݦ4IXW6ĀPvT r "*ѿwl0ʧ<п |uEzXx5bG=~]L4iȦC:!=!JO.^CCMX~Į}5L]fi {y ,^Q{V4/;tLnlD Ǐ ŴN8Mi[ۓۍ?E.0{YjaVw2*dl=P~;#( yFtm֦MņuӬPgs^,aޚc? e:;.kZZ3\圎t`[ZY϶ [LbjY^>L8m"kܲRbC*]OSfR5QWjCpsRm[fx7Bh"1DnX=6z{Êg )*8i WASZt $>t#*BD3"۹gw.f vQHEڧ\vAD Un jOo*D{7*^@X'8L1o]Yʭ8GO>U+i zA.g}\_옣׵5Z (r*gkxJib)[7rDDT깎6avPerqvgaG"WASRwy!J)?EQTT*JRT* \.ATd28El3'_ԥRh\SIrYI448ޱbզAukfY&FZWƭJ@#T*/**;u`<4>xzA4zc.~,`%_Xsr<;[F"WASR s#z *#~< \ Wgjx|;Du!=꽋{=gΙ rI6I5M1ZLIU.VekKkmVuUs+E Q#Q5㚃>\<<|k}}y& e=/O0vu,>(pk>QIM}Rk]֤/8_}yL7͑-3)0yͪ(Q+tu$^/;f*+((d&3x_x72U9}(ʃ##wp6>pȷ]spE58ԫ[_mu=Wĸ^8EQEA҆>uu-r,gH_ `oRE Al6}@TA+eՂ?0J@U14}4#8{UWG4^ñ!b*VQAz\1oT({QћF(ګaXf^QSkXCIaYf0j~/@OK* z9Lf4a0,N4h mEGwo޶s5!ISG|pQxӹs *W_a||KR--dX"zg!q>f3&;8hZ|6M&I#8f^W(vQpk+O (*=ggjgJTI;Xiw%&OL?w߷4RL|U2339+**8NRT*8EQwd2dJ&<}DQػޛ=>&-DD߭9XId(=wErh^5)3voJ;FD=2هkLd=tХDQy>//OөjyKjf`01`0]lߚ-J`ɳ#Hz3AS>/w(UΜ;`N开?9nXy7V<ʵONĢaaa/^tUUUFQRx*f&n`ݚNTܷ!+N(]U֙JeT $"knfzw#(T*Byy\P.WtaNJm[~S 67F.,qkjjM]b.g-.w~!#oc=@Dz0cKJ PbV٤D,5N+O>N|` e h86`] a@DitFP4ܷR7õ3&ZpAEASN:V:P.#MAxܘP\j_}1f[tN`(JkUA2rCD$*~-3¢=d6lF\ ٺLm` &""7OT[ttvn>ņdDm6&ЍQ?wJ^siκMֈ_1KܥVzG7K{d y'vx}NeYu9"tRvvvll,afg-v<9WIDJƧoulf† ˣ񪯉}[zyDd4onݾ}`pi4ъBǹ: fK&[Tw%_[MD?د3,:]%Hk8@{!vL ӥdFv$$$,k ^Qn u?TLC^عy3f̰lW^<`Y%P=8aQ荴Z`4LEAt:<ϫjFRj5}XaiB n#85N2F#mgnYp_T{0  vw޾}(&eYeRRR`R(Jfƌ2VzkіԸ:6@#- +m+ʤ$nRRJ?@S2xfrU? Zt*ԯn}FMξ]i>.*QxuoA;q䄅?^*vk(p@ceG?'":aH5 >9kߦ.>nH"/̕a+8`tG>zqGg}򶷲A/fk,/pMՅiRӆ.9BD999sΕd){8!8R?%PqLԉaMyq@V]_C{ғ=XfW<6a-m{cMt&m\eС>hhh(0 uh0C5ODNds:QKrsYZGp_@OuQOOBtW[gH@[ѝv洴z"_wӜ&ĄIOM'?rchGCbfbM?yM|]ˇݖ7'|n2yR1!rd5^#<|+㇜_1LphK p=fٳgs ^+,24af ot{|"b&4ztd}_Ff9z>=.#{çY{zOuH̸O~$Bo uCrBNlɷvdq}|DD`}Z܀˙wjÅ/~|~LI7md];wTQnçDžƢg/^.7H="&6SnPNIs.mh;i+Us>iU junطA`ZbU\wk;ȗ6_I?:4EhIk煊b>KLS<_{9"l̥-Sml*勧/63hoIu/HU6SgݗNz~rD‡[9MLvjM²t"Zi6"}\zel$UhF\I[^:pC{6njӞN^{c{]9۾vqG &=#7瑩߬Kw>ҩ)̂ rc?AmNdLzdgPۥ6tÇ u7z=2L.K6`0`ԫ<#cć&B^?IC֊Vnɮ <M9wݖqGy>'"ƚ:4ODősצUUWޖ"279֤&k39GEDk4!'"|̉$2e`?iɻgpDsSD~#n5 oNىZo`Tm\W~}c\[\LBS)SEg'"jޘ@D"y˳ě=rl=%pFfW@ 2ݚlT;h3`+xQج&* O ,S3ҩ )g6dg{ZJZԎT %l6bX,zh4,+-kXol6@Fn9!Dt%cm#k,xm7^kּq޼Ucn[8G+pj+{ޔ2E30)nZry|l+;Ky'OD4u->䋟Ee'+_&"ڧ(A͊U1wg*g|^ED2*<AecĆ"[IQMkn!;VL}R(QuҨ*edlbRbO GсQbb%;of 剜"Jv+qy& $"wG _T;{nC+tWu}:"#C$cmt)N.@?< y/H++sߦaImboO b#ach0Ѩ_:v6z0fGVS}`;`} ;X'RXG",,bX? /67=_ 9#᡻wֆ3^f [ ց8m`pݖﺐUdGZ&i~19eft0n1<PWLa>$9Y#G M`ƠX`af-0āh$9x%pY a׈DGO_$E n/?/Klw1{2i?USIfVfr0JEi4QbAqIJBep/?>k/HTs3GK;ߎOϷC7vǁ""c):HIPdd3YѢͯbyl.>GG&z)\b-9N> ae<@,:X; <}0$}`?8A58 y ``L9:AX"C/$Cr @ yBP8@P!T@%!4Bߡ5AD0!i BaC#Q$D&"QE4#݈q H$RCZ"]hd*2YE !!!Ǒȟ( $a?5F٣|PQTTՀjC&P -4- -VCt}}}=^`0,*Fcqc1y*Lf3bٱX % –aOSUNj\p \ )nNCQI9JSGs :OkR| ~ H  kB!PJHO $2ňzD7b1XO"". jӺ6Ҿ]#IљҥUе=BOC/DCAD_B <$L#yRINA!!C? #Qрї14c/$I }u;)& Ʉ))e]adfcamn^!a͑q<':gg+k. W2iG\?y#˸{yXxyyyny5yxyo~0St(RJeϘ/1:?ߞ?@@ >&B4B*BB' S P٨&$juLVDK$JV(FTE4DJtP !((V!D!$$^%>$P$JHK6INHHKeHK}v.'%(*S'3*(k*!{M\syZyC4o ~ '^*-)(n*)+E+(* *{*W*0XWEꪦP֪U]R=D..]u&554j45)4ǵjk hj՞ i+{UwEOM/EKo؀ޠ!aa႑QQ1̸xĄǤdT4ŴόhfkVn\<ԢblIJ*5ʺ>{d=ltڍڋ8;94:8;:;I;8=tprp8ujzuM-m؝׽߃#=^{.{==ynxYzz6^93[;W7_?P0X8T-8:x%2>d;1B.331<$/'boPxdVxZh1P{LG,Lʼn׌_MpHaoGb $}{ۿo"E'&JNIHLJ7Jo؏ C&0ckܙ陓6eeEgR?T~|Xpߜ2%y>y)=ѓ0 h42&NYSs9_PR}"xyiG`ٱ**WTV==}:zTЩ5F5mµ%1OswFLYg7lq;ڄhkmvk<E˅܋bOPyPa#GW>VzDIǠൡ]Cj=~s_~14l?rmd˙W>>yCzSm;wƕoNOs/Wj}0-E%V?.-䬲6Tyoqmz=aQ)ymlkl;l{;+@z;ñ x1şbG` `^ .)ҁ|3G n! -jm~IIJb{q44Kb?@fēQLhf, =9G g.eQ^E/F`ZH^8@MOĆt+9>yӊʜ*vyj6ZGb4 _cLM=Z~clMg#`jgaxЩ̹ۄ=O[G?, 90/[\ؼ$p9JՁNk}o |;+;'wo_ܻew~v1 ӡg]/.|Uј177oގ;4<>?>xRvrCTiٌO>ΝLyK ߥ{:mnoOv 4#O Y6ZFFl,kezD/aoF`"G#ћaI^d̼ºNpsqGQ3 X f ݥ"EESŗ$eO7,v޷%T+Wc; :3ݽq+v[|];N'?px(q'>M{9 7M|hs/ TB>ACEO;D'MO@oKA%e5S4'f?8dy:ώ`/i+[{'~$)BŒƒBQwb QZR?ʾ{#?0أtMJjZ]424Ӵ+tt^/dLLk泴Jnyjceo0*v$x{k=wsEhGG''zD59b߭Tb{zgÁլl\ռ|kE%'yOWFTT8橛?Ps.9q퓝+oqwYݓѡ"5FO54<"e镔?m7h76g=F8x C1!шsH Y|DPhA8nB#L/VaqT}wFe8#&jbZ3stpϐHiFkd)&nRfv*VA֋lZlC~[圪\i܂܏yxEx_Qrt/ 5 R婛"wEyoK I^,}Il\)rB#ʇT&ūG  ҊN=wU`cL5176+3X䱲>`b{.A‘8Kk[{Gj6gޫ~y!FaiEbTMJVr5u9]sLY &8}W>}lPxDOdJrՑjS-u3 :MZ/^molwVB{BODDQ_PG_QG߈sh Ҭǖ")+W֤wm(ob7Zs Y[""1T@#Psh":9Ec#} C4x_0G"ޢդ{MBREap2\d4\Ȣ򙵆͕}3K#Oo _00JʼnI\R2 F1JuUj꡻4iqk>g2p5\=z<㼕}I~kA+!%a#e.Ǩv'&J''r>M:f΃Pš'G]+9_9a\zlB2jZZg54^lknop UW&MvvzsDWN^;7=5l>{<eҫQ7oߌLorX3=9v.u/_/-cQzҙN?~.(\5\}Sgᵉu ō+sΛ7"osnmپ1?BD]oY^y6 ů@Է¿|#2p^., pHYs   IDATxkeu_{}u~O{3 @HDRHJ؉#+%?\q>_TRXˑD(`A`0`ݷ}+vE@`(Pn=}9=Uk-ކ    }Ǔ$iiQ{{6DQ}ԫAAAAx֣cccJ$IfVJ(VKAAAAGJL( zlzһc"p   p? "@D̬S x(bP?ZkǓ&''EAAA>ԉg5 bV[!suYxaԩ>8AAAAPr PP># 7DAAA~$q_`&PΡ;D!DQC=\qvssSAAAAG1o ]MwtPl Ae5n"p   pD9cL=5 cF/H   ptO{J|{/|cXRjAAAAޗg_R((@[(QѝCAAAq?JyBBo@S|DCAAAXb }*{=t)ΆR"   K^1,,(V1#,` ^ {p{pAAAAnKA`Z ᦱq ;t =8]=    Ib EKMtg;   _meR K1Z0p0HAAAB<F"5Hcv7Hyx?u㇫؍pAAAá pX8Z@ LJ8ޏG|uDAAAAdQd/MFA!Ej" 5Z)EZO>,r]\06طw=X@ZkURbAAAi |}RP<O RifĚ7OzN_qc r/<|ڙKؤHF=350q_yv_ {3}0UOOW.*X\   p0kE`t`G~;Ӥj:^=N}lD (@@7>k*W8?~+o\Og>~f6heq%.uFm]<=ٸ<[^gNc6CYQݾ❇pCw.v=Ggܥ{yOO|LAA~z+r1Ĉ 48Q7._^|'OyA\VP e  !0؋O) 0Wʂ,` 0`em$I-+f항mbek6P"e}f@W*7op/}<*޼g7N%.USpeݾיeZk;gewDwd,72Ǟp }'g*tjȝeO7{ȖĚ{#  pRؤlp8U&E+Ǡl~8\fnOk~ `ើ*  B?0]] ̍4q `b0.!]pazꭍs+:ѭj Z@Yb[/og?й$?K;N I~'o~et&.7cɦi[at6\B%h/- s΅*?[ ZI&`vƓv7k'"dK8(\AشTG>AAa@ђMHҝ>z%iO̅<9A{hH J9LC/Q[ %-' À5ĚlULD "E5) *I.,u (1\XEbY3GxQ6,W=i_Zk #K~ϴ?,~L/pt @IdgK:W޹0fNӴSPJT ާs1IPdNU9{{dÞSHDZp٬;8'ƘzDZֺP( \.W. @Ez^ZZrl.'SqdRKݷnWvVjfj|?e>'y8)(ǯnlE>%LZ{Z{nλTAAAol8NaAHM{-dh%pYG&p$I>ݸO#"CxN`]+CJm3MiLڋ0\Aݕr؂y6jL l=eX<8ز"?MA^H{pro7F.3ziu"-bB$ͥ'/ll_ұcmyG,s ^nZݹ\.63O{nni) 8&"*;>p J6XDZiRj֭[Bȑ#|>|>NGIf{NhDQEQZڪjbqhhH))a:pTrY*[,٩eV\ tyq+njZ,&''PZ5VNH I4J$Ţ8#Lvrw 7u0^P=OY(jEQjiU*A"'0 PAA{I4A\&נ"`FG%pbKk_W_{naq!)X4˘OrQhB@ ,lcbX1[&{E?Ѝ^c "v 6-^ e"Ԭ7ZzKgк0- #|@y>5J#NQv$a,MVj`{{^Zj5fr؝U2NFq҆Kq$rXժj^VF#NAk绺rX$J^__w m5 jTV/n۞y .IC/A*kyKKK[[[Ih8vGҬ&388ei4J0 ih4׫vٌ8;RP( 777y]]]GQ3 ~Yx2׆h6΂]0(w iM8j7vlFiZۮ6u*r9'cPAA{ [ )(͛}:BKwoܸqJq38r\X,훘s+Wr8"Y w4s(rj {`0 J9 +.Ljŷ0 ]JVvoܸq֭ZTήJ)g?p}1nݺzJEJ)WNY9\=ȏp@OOϾ}|߯T*gϞrʵkVWWstvZn]ՆyD__ѣG<8;;WV766n ;էh/===J)i=L".\o28] B033o߾A7o޼p… 766\lZ n*pAww}8pȑG.---//J%cL܏zqYeJiZz}h`䁱rХZ֭oTzj47t۲\z Ow>_zx<7;8[̏n[˷Ɯ}ZeHNu\=tիW׻{Y$S72RՌ1Gr…SNˍFMrB93[m>l4JڵkgΜ?qıc~ᙙ ._tL#pۥ777_{^zidd^ 277 ]]]c+:ƦfM7jlegpӧO_|yuuaq-HbNv$Inݺx…'O=Ï?\RV(r*.YYjj̔V#T(&||a+X~V5"$fspZ/>>9H#c@.^QaՕݜ?d   |TX_&Wi(Spƽ8aq?o| <R`B1m8fֆ8%J`bXR7 E,PD%`8OuX0=VomLlj҄@DL`h01% Ѐ!Ā JHGf4[?ַ IS'J%8|hN4k4v^j7o8qX,~x'GFF.^066>h=Fpei#G %Ir^z[[[.wU0t*>I|.ɓ'/\ꫯ>䓟g}ʕ"MdG zzzi\v6gufWnT#Gh^{W^yQ):l "rG p[c\?s?pv8O IDAT" ;Lp7tdaWW{yH[n5z 2Q_ݬ)Ѿmn=|W^MmZAAA0kE`t` Dk((߇8Y0,_|qaaӟg?IիWǕRQoeYo?$I\e 3ol 蟼~ߝu4۰V4"32MXD1j~u>zo=pt>vsR XܽEC  p!UJT<< =v#eeQ?W][F!"(n0y̎8 dƲR  c0)b(Va; HQD*$:PkѾ bxֺǿ̰  sJ[Lla-fɞHb6}/l@烀$bt~㥉e`?~gzgΙ`O{G7{(\Fl6oܸkksss>ufWW\=6Iu)Z=::zС Μ9ϾjT* =Ǯ}3';ͤ"}ߥKKKۏ=ܜRڵkJRڈxY$3??r3S}< rٕ8qbmmZ~ _ :'fpD9㵵5fgFFϬK'ylԐP*`ޑ0Uap5j(@;}Eb^;66&&&ΟV}}}lyq  GKJ4SJ#B =8(W^; < hA)љ(&xPY2 p!HA0 H R^ƨr\,YY< FoJ -NHwRH)4`=Uc%`!كcbRj F (?|6tf@1i|r|>}|>_*&''\$Iڃ_ + 4 ]:p).^?;u<!C'u['H@bb┡-Lƒ%bP bKfj*5Ё_+ϋXv)[0+D;YmkZm(i(L #dJڊJi3)&D JIJNF _>8LwRh1*\zO>3n Ə=yw(,pY&iqcG=}j:qtAgwNh4'N_8+JPL+fy@# ^|o~.]r;|p+p)nh,#}p)h\.uѧA8i<禦jZR!v3K3--^\;wɔ;gq >z衩~{JBoKV3 Fl UEhn6L߻{/e7iƍQaEQ6/048I?:v|oUr}X RP ̈&;|b_yӿnW*==NdCAA>jR`YnB" h 6$"_~[G7[hg>;5u_)a0X3l D3h@aHJNyMc `d^!7kV]M8l & 0+!6PVD(Rh%"`}e1~`C U.ݖ;^t(zzz\ֹ=Gլ,N2yI ԩS=#<2::zFtl1θ[nx駟tR.uv MS_*bTv@[VZuv|w`WgQJ Vׯ?b'z+АD\p/f&=죬fGl%M=j(Z]]=xLxlnnAjX:hpkm>%˹pcVڦ8WN&[\.h4|+lZׯ_wJWR#x<;u`rbbW+0ݰkhc' E(=h `S$)EjjLhjgRB)tA C?xDݪT*\];O  +r1Ĉ 4 .u?y>B)x|!fupR%ր ÕX)Y&K6,`(b D(z"0yU[F)[dY(uݹոZ̈V;*`0sX5oe, bXO.X-<e>|t/)(*Ç&kYt\.裏fFCwW$IܼuښK:LzpQ.VoѣG]h4T4:YMZW*4M'&&Ϝ93\|%ٱ2_ˇ{{{<811144T.KR2|yyڵk.]qFZUJ,j$M|>yޥK}فe 3ӌ팶a ? LNiuƖ4M=ϋh}}}jj~_l6R) CLqb\T twwaWE[[[ׯ_|իWۃi@4u`777_}}s### [[["jc7:z4zӧ2P"I0c 9JsX"NԎ](Q Z~t_MqBd5gJ%]   Y6)[($46NIJEktݱ Xύ z")a6cS~b *&ef`]P&4TACCJ| f|{#4VYCk!I)s)h+jM8d@3[fXYJI( 6k=0M}/oc>8SnEbgsp|t&XIT*~7߼y1P(t{dvbիW+JooowwiU4d9mEi͍?W^ ðP(kih6Q:tرcccc.Qka?/^wd#dJ՗^zirrsss/_nZNqD.ިj)̍+XDwZ Z D88w|rf+CC2.z¿znBְfG+<ڗN]:;64oZ;ܒ\R^5AAA>4{iPdE(]w8|߻ys./BkJHcCpbaN ڂW(RHYyL1uC01Q =>蛑b!(MY1[f-ki*xqjy]e%FH3 aÂbRMc(b&v) 6PLl-1 x1sٱ6lZ@!o^>Ǐ2Niy(JR)MS7bcO]e5}>|xttk,u;qiPoooZͦT6"ᩩV/[J2Ev u)q&{'1j(666677k~XwJx^zԩSQrlMV;qġC>O>|ĉ[[[Y<3u#I\.wO~?tiBVnOMMϻB!ˡQq秦xO}SB!.zŖ0/ .wO:UV}8Igkkʕ+訫Q®9&۹\nUl@+x ;&i0='ώIڭV¤l >of;8ý9\]Bd,` tl篿07={dbvfʵkNqb\UD4AAA`69kKH-,h_7=;_]9hA`Rv7i,(,QMvNh)n1@~$鱐'C[=C&{F>abkIA07-Lcè|A" $9 \ L xYB &H!A_ש;̺1|\.禽fHgΩ]]]x'{{{_x}75lۛ[[[q+p2z=11ĉNrN}}ff;緶.\:b:Ţ;Dպv;###sss˿g}͛xռdr[o a8???884=ELQ4z ŇUX:ky̌ 9h;!%`(r?i/]T՘9 Cųhlll\t7777==_._~MdA: ܹs333?x__֖Ɖ~ǽ|t|՛M4Z(`%?>C_xW[%Z[,gfffуc]ӯ (t%lpe7L ܸqc}}}``on  Gg2R(Po0,RN|O?ZRG.O2r/ 4Hq!f0b0CY&fqCN,8%E1[^FuzC>dV(c*EAGJ+*z"*Z$eشU2 0̮%Ǝl‡Z*e26jngZXiE~gptpdcL^o4cccƘ0 ]=GvV/nnn-..nmmmoo[k0tRH{hcRYLq=666><88rRfstvH<^c/;vj={v}}hE1|w4JyԩOӾ?7np_[q]0/]tܹzj||ܹ]\O1?D* 3gӃzt*>jj@S(\T499>{ӧ8v:EY:I$I#8::Zժժӡou'&ɫKX etHR0C)0 `=v کoߪTZQuϵֶzuJu{OO(m,!)0 o:3ý[w sWP  G6`q c RFjk%0E+pck/~<+7+FbPJH@Y  VT+3L- buk=i? $-z$]-$i@2D)!RL òE )afIU)Ӿn)|2,߼o|h{%*nZ׸]p2A>w;/]G_~qgw vBD.v p1FkZ~۷Z{̙7o^J4M❽• LNN~K_:vX8w|wwP數] R$VF(7YVݱvJr…G}tddR\tkϴ"pdkl6!ixwfkfLt{{{zzzbb^k nLlU7=z߿>ٳWԓ4]]]=sz\!Y':ť١^'$eq[6<4/oo\1'pXV/2i|⭳KKK#}MFKRO2ƓxK_*S*JNb"X M V}~ag{u3 DAAhk]Yz9}z"==ӣQC=ɂb0C0HqHq` 8?DAqFd[a~DIKtO{nYdzWݪ:Z":ttG]xRzafiJ(Vx8 f/] IDATdnoXQJ(k#ڽ/>箭7mb9,U`ѨT+ΜS@ (P@6ذ8LH m,CpHQ^E Bvs%(Y(OqUȂ"%X84PDeyR_NG%Yp`-+2#9#m\@zCJ{lS lʚYLw<22[FȜ| `27锬ecՈ sޞ@aEv<杝???gn<_ t G$!D -Rv={vffRH r!_ D)u"Z[[AZJU,3+W^~e"uܜyv'L)I{Νg~_w廮Whtk׮U*>j5K8{rtןp(~FnwyRѐYij3==k]z5I7x9s@Zkq͐!gh劬 wܹqFϟ777}~"v) C}BȋZhX9aZCѸ"Ƣ^W/ʹ:{;niۭVK)$?|Od݅Vo.~]X HҖE@8z7TJ,Q/ (P@ *e4U<#xX&j>Ty|EADkko:))9`F A Df (&:&=SL!&J-abF \3I7R6/G+l-&RcSf2,QBbpP Ƃ%ͰL S(di*Z-?QCo8R4, <-+߽?|t"p5-N0>}foˉ1ݯZkq+HVmwwwzzl[nI…c EoH+l6677xgժ \WpH,q_jil6Ϝ9sʕ~ODNPtz]_.YgYja>5EA\Z][[}v$z=)Z՗_~s֭e= @9y(Op8f˾G:HuߤA-l`Ŭ@`Xb,Ofۓ.)!RJd 0i`hX@P|! ѬQA}h(;wse'Ϲ EB)Otӕv^?R)U*ZIXyJ H~{9B=E^xҥK|(&''Eڼ8%99biZo߾u֗W;󎬉 íNsB_km5n>'sr٦<7ȹܹsn߾}xx(|Dx:"F%_lP岈8TWE zVWWWWW׍1ot=>>^XXhGGGnLIY!T"X믌OͶ<poرWl2Jh!]mVQ pf#Q4_ ʁ?ۅK@ (P@-HqP"l W\yܴƢT$tÇCk(Z%۳% `x_[kHLlA$٢ ؂Y"4dXX6<_mphHd ;$ n[RDt:Ĕ; kEj ̆aa,:0k = UC 3je*8 _+rwg~;Evq~E}u +H _pC_ |܆3"CEfsnn.MC; C˗%Zb0,//#Kp٨yD;I -Wf)7O=ɯ#8G~b'd. '+pqDtpp0==-}s޽+Zw S=˗hsss4MNN(9891P9I~t׿yi͓E( RsssЍ)RFAi{p7a&RAk3텪? (I%U<56p r!b3It(=0?.%Z"OnEfֹBQ@ (P @`H hο~ "#P hԢQ-̻f[p4'U)O)PLLR# S7]eHX )uc@~J(JA%DxV%%y!3;eh`lg%`- ;7߿^,ˏTO&8\l.'GZT9h" jHYy4n8t: Y={vrrr0FFgIdQU*sz>H<IH؍"eyh#`p||<99|ʕ+B + frr@^JpQ -OYG$U>JT*;r… VEQnJ"뙟vM;FQh$DR9ג_O0"wwPc/I^Q 4"HRTKsfxەmR¶( 0%Mcd BB@ (P@D*ECMpRDQ|[hA0&/*n=XRiO@yiO)eJRI+4&bE"(1մ^&DNl4qJd @ 0L0΄$z\a08bfbVleì` kckSd6X$&5uk'=30QQ(MQ ,ypmW"2=~u<קƟ$zZ v|uU"2G peeիJ?p}}]b<(VVVt͝V@ (P@g0 XVDUIdgƧ!8NWRiEaX:0''RYi7ZhoN{G &C%eeRP DDLDFqB,Xb&bRPHRgmU,5C,D13 C㔏XZ-̂e+g2[Zc 3[Qw_JP{M+e߫IIR¸r+v7ήz=IgڃR=7[!(-BS{#bi"1Jg,S)0A%bkzz'O %}e@%&c Jj2R)((1R)clİv@r͔}kښlkG8E$߼q+zp<'•a&?Ap7eZvʕ8E1?ƍwIDjl)#1Jʕ+K/o<2iQ*~='IǡVm":::Oz^ rκ8mBpr;cȺOOH7&>IxY~EQ\P$0Dv8>1*60#*g\:q ǜf.+OJEPu" -"אָVYRzxӕBۣh̨\ H^LH) |?;?=n>R "s?4KD0dW (P@!6)[($<46NI&8k: `DPQ9F&p/ Hg~)Lv`€PJYXAGPi E V!I`SHi=Is-Y$`ְV*x*9Jo988TWqCF}fS5; Ks33bqq/^l;wh'&&k˶@Ze(D!aC,Jds郼 *o r y.ñ wJݻ 9| ]vmnnN$c)d=]*J~>J)+E'8q,JnZBcq发ma"T**y6Ȉ$"¿1($,V)%b٥/^|_{bxZ!M ߃etC=\:ƍie1Sh (P@ (lY 40 1me3 8\ٶsOh,Up䅒@e(B(,$ -Sjm{`A̔)8Wf,Ls1 ,4HgfUDR=$`IJ#EJA)h (wî\ v#5x$@pH&rO߼BNժHzU6͋/nnnv:fh4*7Uw:'TR] Xs;qr8g9Mp#*O7[-I<9d$kƍ&9*7ɇh+G$JtgggG92(D!~DEwq7xg݈( `0;=+/{gwZ Z#M@)X"Iq~_zrt F (P@h60 e&R $Y*h(ʤr >CC%Z12unRFHөTF2#H#ŀj{G)}3}?K@IW_Xn|#oua4(z@<s8 T A R >mnaȈOGV=D$!zn/,,ͽڲj IDATk7nܐ';CNCxʹETzOy⑯Og^8G"".ZL$k(0lt$oq,ԉ:x8vy4UU7m+ ~~dp^F0hIlllT [7{wPƌȾB#6n_ K7w$|3T (P@` h\p F۠"`F 3"8/voxS(c: Q^h~R{P *~n+g`;!8P!3 -l!l^A MVsVmQao _:݋Ɉ(43#=Ɗl&.Tٍ})L 7\*ӶCʪqi0ov+xy;6G.[NYEQ=88\__p˗*Q\PIgq%pv/ Gr<ȳ (#_Q;? 䨙ȱ nyJ$҄Eey r"y(upwbw88[oEQ^o⓯"V+#Z!$]E/aWK~w;Bm !}pQWձqU:&hGp-of},[vM 'kEOź*G^Eӊ؉|3Ip .Ih4 (^Y^;G[7~{ױsZ @"P+s_4Y۽ݨׄYJ`7 (P@  Uh%xGL=|g@pH ;]$@))(sgCoHES7]^V+q U 9G6 ˫+FDaS,$ WkTkasg~}o'A1&'D@( ٦ GJS3_NEb`yɆ <A+oN+8p#+ԴI ~E.; ΝƘׯ+_|jz] _@2A/._ LIpfx<|ĉz!?k#)YǓ׉HsVfwR'3FY@ǿp&q'4nB3Fq<cz-LOBޝ}&1L(T˟+ Soy$F36)P@ (P3#!mPci\?C)Gy!% )c3Ȃ-7d&.N-LUȒI2ԤZxd&kB?0kE2@D=Q1)>-]By::ra(xB9S~y29c8uBi2d(^:{ʥrvg}|&22`0iW/LVJ;'J3gp[@ (P@<"fU%X#4M1ʎZz"IPGI}qTQ'?Ԧ I"VZkaՓ儑u` AהV& }Ek1dIJKS0Jc@!sD\zIJPJ94%kN4^Ksƃ)t'(NdЏ 8MpƘýr< RY>u{.Ba޿aaayy_b$}QE%&H766VWW>h`8cJ g-rU~"rdNG, IGH4/24MáZ}p:|oZr:u4'~?z^+Jr,"jUQN{D 10l4z}{{;MS8A=#jHr<#y+g'yRRF*D}qnʕoܸMcҒc7JD (P@ |+ @hB|r΍".\ (Q %үNV5߸<;?;R&v0`fE ,Ql,lOa"`ܝvA媞菡GRhԟzʃpH ,H%cLJ ƸJ$qĤ_/-U|Y9v'zX@exițIFQ opÍFQ*`$ITT*yr(>ZV."x'xQ/ѧ󳳳+T$^ :&÷z+cɰ,ʂ8H|"JKz.-N9#s)c%R}Mhk+1N뛛F{pqH\KEIKtʋ/j{qg DH "H 勿+nܻwF$3T*yvo(P@ (kE,W gCpXk(6Ǝ5{4o?5W\MhJԦD1@Bls!(=1تb+z3 . +yrF 0$XFPc51:OpLT9svsyc?`zzv[%7aJ4crqYPq5TZT]YY988#Ex qLq\TZp82{45M+ @)0h4E>k)kQʬwUBZ]XXwG:5eXlnn; H~UI(wir$h8&i嶬lSёyfV\|ߏ鬬Hބdĺ< T* oYV FY7wQnV?LcnH[拗Y Jny!|c`]/?v MOݽ[ڙɩr,BQ@ (P)`@QSF" h ΈDpZF)P9P KP B+++?<}~59˕9Mk"T11l7 ؂"ev<"JXc?~F46HCH`Zجi<jkH( OiCCؔN{'GpH/%qCtwm4Yvxxhua2BT:7 ~$ZF`0ֱqf999nE!y' Q4듓q_~}8j"?PkNG~ vrJ%`#l8BV WK5q{;;I.,̋G88䈪BQ@ (P Ű2R>d2b ;|6#Wm L'}a 5NQs*5 GlŪ\NV)6*#xL8 lMRcXXNua5l<:`0 LN^Y_/,rlooZ[<\RJIbStǑM (P@ KA`BCcTaOrm,w:R̩d,EZHo8Muz'N41i"M-IRS*fKd,{dMbLb#g֗qq{HI4a6%9BgA`=|t}?f$z牧Ӯ`{{{qqQ8YkBF j}qV)\ (P@ Hli`c"5H2adZJk;;H3G+4E/~'AZs͉caDU61ldv_^VhƈfI'x1["b$>o% ɮ`/WZI$IR&1&8RfR#xvGZ7E7Qq0 (A4333sssn '!8vwwoݺ555u$In߾-n̮0)pgbf"f0lnnDZ$EB-)D$S-9s;CH 3M` ǟ+Nw_jo4rY=V-#7gxDX&I>P7(iSEQRԑly`X$aCbBj_gjQbXc#!R[mZp s!,cn՚u fzDp~p_(I!s [?Ȱ5c Y@) ٘Noopw0`T-AC1QT#$㶮,r ' )\)ŪԖ8&m'HԢnJ%qHQ@CI2^Ok=>> f].&rConn޾}?|$ߗG~_THw E]@a>쳍FcssskkKr|VMOO+a/sT9(;#N"܂?%vGqc8ۻ}vјiZ ZL 3եgJ~UzoiSnU摋apҥZv=DZD%QdF$6tzsqٯ]; @J!a0?+?Y7߼rgNMMj5]p*iߋu8i@=f Fˠ-QTV,> FH*JԂ,|T࣭ɱz*`q 2l-`4Xa:"5(.\MekZbX)' R@^́ a)85 ,_lm [IAjWwz6F` "@ dn|aQkL˫bam a(n('pfI$pDvI+Jz}}ԩS nv#.ٙS._:;;+W{v8 DLYXXN͛TAGvjyfzn*8En7w,u.hpgϞ>}a׉|J^vh_t{{{ZTm K*pu4Mܹ#]*kZq !{{{atrR޸K_nc P#PW>tЋ`շoW剉 "qDT'n>#ԒKN'61-3w-,R5Z $"B`!gnQ,`B͔~͘{ٞhZ*Rj΢=Ch˟V]o2YM )ܴAdAA{4@2̩ w.S m֩fkws@Ǡ?@91sj7jͱڻ(R b]káp ̜iM(gD/_ RK4cg*Mk^zhLOOݽ{W\!t_v7,JSSSǿܸqcyyYj$I\O0{ffW_T*7o޼w^$r>xjjjuM1 $Nt<`$F. _:Ā#h\ǃ]*͛7\rkN:@@n{K.=zU'y;P4%(Q/̷nkQHM+# >|t/}ەr9~_*$#׈'n81\_q=L~B+eCK4! Q2m 8D7U `pG#Èl$%Sv?4LF%Ph5ڰ-x^Ϻ)NR  nᏤzqBQQW`eM[]U[3C0:#B894)Zc8FQĖd?wh~j ZX -'c+|p@Yke__#-*$In4}矯jZkyN7gIvji(PJU՝Nl6Ϟ=S iqUĕ+Wn޼t&''sOP*/&I"ӧO_pRlll\~]ZH[10s\ZZ֭[kkkcccu KsF($kZ柦iZmZݫT*gΜwmV;YYY)J/^\^^huuU:LNNƞȯq@۫V=ܥKJ۷o߾-u9PJtyi̩So~K]9S1̕ ;++k_[]]]:uJR`(**h#*T8NùXD$ǓFZ&Pc)Az:lJwnXhHFc`a-T_!x#>R:.8` [װ`"!"` yg*iPJDXz;ߠoq Ü7 ,,`k`4QÝna{=ABoFCR18FX)kk&[֘"88(2T*Tzg윇DNOO juwwN \ pll^z= zSSSNޖ &D w.{|իWnNߙV؍$I1/bRz7vvv}U‚1fcc#Ic1Apw3$,//ooot܄(b0z׮]֞={̙3ֶ%u(E!ORiyyҥKƘ;w\vx38MPySfݭ? x7o@0VN8k64Hc}E{o51nT*Jp08& sGD9{  VAe9 w=F(mq3vwڵ1֒}ƺBIuI|NAa-?|eʰ>pa amF LΝ/PY{.#r 8==}vb6Bp(rl4aeq\z.Q6(Rizz:`nǕRRI쩩;w(^|˗/2kqv&Mӵ$IΟ?ԩ{… ><<>)wŋ$tmmի[[[-]jO$dp:62qq )jz^n6gΜ )\~^xqrr^pޞc^/..U*R͛7oypTp2z0Ǵ{pp~tT.oqRl `,,c_|i~7==/ʥ8ق)U%0v+d0n\UQE)qqJғ#8\573՜[Yۅ1@$`H l `"ܰ3/i|GtfXlj`2G < V`bp9Uc3l2!PGjX[Qa|VrtG4Ӏ@$<|Ulj;fh;3՜Wyj|D@ټ|b8vA$Rvw][[<*z>??ODյl<@z}kkkgggrrܹsNny* T4MZVRߗ*BpH q_~mr (e^`0x7Vu]GBʉzxLjEţHRjmll믾b~뭷A\VJHpuuu8>}zaajZ-;;^Jl y޽vB 8;kGpP")J`0l...wsc Q`R```7>|?xnyhkg/Kna}_!m_"l`L?սTlnnʄeO^A1XTB!P Nf&VVs2uu? PR,,?lMp .i5龨ue+M` !&,HSQ`yZ $"ȋ!!D 8̰`6L_$[ b$^A`9PրMɛdmtц,eq8?3l>R 5< XZáA#bxO^[wE `m#Ӣ^~in5QT"(Kz̙zX3 X]]r{&V#Ta@(COHy d3\ʹ \( ւ,LHRXF\J巢"@ Q^.f9dA@P P^B@0?;Th/_DEj†H>R `Qp[>2I'dG<(VzRnnn:ѣK -\iXXXpk׮---I-ٍNQRV7oޔZt:8S3nQr\i&I:Tn'AȩMMM]xl޽{weeebblJfq])PòJ Z뱱171.]rzrH 1;;;v[J9}a@rd yjjjffF2SD%$㏬0Jrj|*6wYG.1:o+_s p1J[A/}ik֮ϮAe S@@Pl$ -LcV&@AȤB R a"8`Lf71"&a -e#MXE!sZ@&L >Ιut%a)X8aCi&U&نEbm όU+EiG j']Wu y0z= DZ g$E1~1pIrdȂ;v¥Riaaa~~nI A 8RD=H}ad^E RNQd^ Ę<<70 XVDi,P%x2!DS0 P'Gn0YGXPK A!(B0â<#b~B"38% X3M]h(g]WHL.8RYTc؜ðiJJlau1YX ;:jiIzF%гgn,U#vp+eTfX;g033s…ׯq<==$F,l^7ntRjuuw8I@^]K=r?!i8EjV%D͛7䤔żI#$(wh766VVVvvvd# ʃz^󻻻W^Z:ujffF>]آmCx \\.,//WUћܹslj595QK#fG/3'"q`T*&''߿o[^^ّ'qxӑS.y4 na:11h4\ a7rQǡ2ƔpQO,w 5(ЅGp 5J7d7';<<<<<<<<<<>(0˜K `M~!x7Lj8*C j% 0 IDAT[gPCJcca8D!G: " A0` G K€hfF\9  L2t|'3(f%p!/[)٘4ՒaEal_0FڐuD@H4)>D)`Z>)҅%Q  xP'pPTǧD]~][[xffFR-7aշ!"+hZn?{3<355%}^ŏF7F6 $ݼy7j*N|u eZpbi8(i͵_}}}oZFcwwWs0H ?0bGQMNN.,,:u* _yWƂ t6)Rnvu=0# ň>=8pp(n{t@hlkcBhL9^-{ jqr(FI8q0B$4G(@gGa J£m@@" C@6ɻP‘A0l2u"- 7,is XQXa1@nKqQĢŐb2gHS MH5iaAd$1h|S 9{\J8OTZm||Lyqq\.jV~033S_AR3 PT677WVVžNG*siZ'\䆤-H\jfsrrrbb֖47Mqn"ZEQ^oZb!8$"]q+4g7O!1"J_}|||vvvffffffooowwwiHWm8PEQ5Y 7wJIӴ듓j5.qV1ً5\XZ9SNL1XF@`\l%c)Xd 'h6IAIf*'Cp$p9?(%L"aQF"E9rB( AE`Fbr~ZStD򦳒 0AMdEHhR38 Fj@5B aݜpd9dd0V#ًi j)N1L^ |"$.LD^YXXE^GcĈᚭ [ADZ$I$ݽ{vE 4!r, N!r!MSihpxx믷Z~???_*v4Mr dp6rT*dڷo^]]F1 rRINJpyyyjjJZ+Z}߹{u1y2a݋Ai) ( @0pFCیIVX>żɎx41dXT@r0&#,Y/X9Ke !nl0`h^xӧGDM J Z\kmt:{{{NG RizzZ:g \1>©$$Z70 4q秦i$Iߗ\kڝ8RIpeeessSk]*FgE @yV!ٵqy+!t _׉hzzzii1>,tZrX8ZV*u=kxE(֒;Iq,>4aaW<<<<<<<<<<<>Xሱo@;C'r^{a 3 $)*~u&l֭ YXZPNE|+캨>\d:m isy-텏;A^ /k/0I{d qEpE˳0L: e<GϜ_nXPm4QPK 0 .w%H$Sa)0ѥFϝS*fO\3=Ń  DJa `ڵ;݁JC~ !/g1ʙM9zaQYlIa,Tk$_kMk!$Ąh)PVQAǻH=wQ:Նk!,cm\dDb}}}mmM8g(&"1D iCå). $T*ȡeXyJeG s] YL=>>h4ęBD~nޱ;illl||ܵu,ZNLLq@2p u"|wۮm\/G ~hw) D!"DJAU}v{ݕ_$Y<2P"E{x7wЏa:(3S02AQS6X55f+Z>?rF|GQU|5eNdH^> u(P*j֧aD#w^Mypܐxv뉂B4G 9Oq@:q3Vӻ{1A($bV%)BOZ|?rmp0* @#92pf(I>ş"Q~ދJ kDf5a)~A"J2:ayXG(tpA6WEg/ji¢3DO+rY*:*%\ȫKo*,up퓪 3*%b'Bq\F(NmhA gR-#GpY8&P`N"p-oG8GQFw}wQ$P>!*b cB#p)k~feO&(+@A'EC"5TFsT5fPN䕆\G-Z- e07g +w>*f8lYZnw Hzi&`GF#ˌ92pHh 0J}yr\;0R1q#f2P,WX<δq݇ jyivɒ>S8p͊FPǢASGg4%'?rsm^l1!(ʝ_aP `a ضYN,EI6ȏ^L R ¤[F?ХY~)[mbVo;&Od|Q\M>Bs䁔Ә;}\)F"G3r\[#|8xq2/{@&ǁ B T)@"&8'6VW$<_?~w}FY(,@Ԍܢ D)0[pP`X XOn|z2G RicRkuoV0p1VyW#…F)J1Zc:2vC:28+/U*e. {Kﰊ KUA;{]PcȻoH? g-dw;ۢo l$qdx5ܟYhDE1,,H[] # f=-C`?ʟ}N(A*5T#d0#%D8/VM"$&ۼ͛!8 ~u\I>Wf R mbH흽];_w hKZ`[c&_cU?x]=<<<<<<<<<{_Boz NErϋT +XF ^ͺ>""AO4cc$ D]e(tw/v} C6S87ɣC)'86s$5Doh6Ќ`y ?_:/Rʪ*2=QTK]Lk|q~T*=[(EJ`K JcN7oM Z~RD(1\¥r8oݺ{E0(23e/"Fl8A{`Dt}*DQnqT"L8PA(1<&RE1 !$fѼ(R(9ʓD,s.EQ_1dAR &bB?@rHnS#*=p" %⑸ӣ#<R^5B{0.Iڳ~-!r7R An)0g L[E}[)! .;Y"7$e-H@Õϯ EH,# ([38X` :#F[JfmSA_,~~쥗/PO׶ï"*!a'lPo ͿVNOO?zX կ%HR#>xokQ'+IƘn_-Y#&_on#8dwu ]#=rmmg%1Cc)Ovs!$CҥZ{ ~""e*0aG@)p( TLJ %E@D@1 ( RJ1,!$8$ q?)'@s@DL+*$# %g$RDDPTRrB)@ _ ydל^%_ Jr=ndr*}a=<<<<<<<<<l,s,A[X2MwI6rTG?ootP l8IYFV12Bx'Y5A@ )q P\mw. ,Ze 6Q)˽?+]45` +Ukr"Gz.r7LCnin 1ʏȇOXsi#pi{'X las^)IӌzDS@AF@>Bgd{f6ZDdJ@MG@ 0( B+11b"VP@LJ{cRFhȨ DP@BpXeC &P*3B  ĵ @HJ2PJP~ԕ4#P7(i,)[7-m?)"" 1" AV|Jn@CkbDڸjy<` FBΗJCCEô߻5:4KJ8IpkRxV]p{8%ifQ1lؗ?t~#eEok aVxIbbF|$'E䳚G oTT|!\d|s^!gkIb]I Qn(炑۞@XO( V!Vm$LvngXXd X3%qo,ʂȌU"rKHb'/E>UZ|nxxxxxxxxxxx1h=0ZU mB??_{ؙ; IDATq,*ȢLQ0BP.x ,]( )rѡ )#UD3wO>? Ejc"V Pn<.D*'iT1 [긠H^<VG";dLњsbqd;C2Hh(l |}ƽ b", y2IAQxxxxxxxxxxx| G} :a@.g_|W% y ?ԕ @ 0 6PyJmWڢgj~0kio?D78̟"A"ʎeha@L5SphXn A?3OOTGѓXRL#2n!{NMj>~YX%#EvS8Cùq{ |+٘pLGsg-@(2J@" "CHA u>^P`zDS -=|-2u }-`O8_RnF*OAQGq(h*y 8"sM)AhL@U`h !B&D!jT#80?w\> d9!"C@"1>bQpGDF0N[Ka@Q=-&Y͕=M,` -)i4w3-i #G 6`..LS?' 3`2cTǖk6n^/3~^1!}Q8|Pw&HKI:L3<^{2h@{B@&1+J&0 Hgf=Ee²83.ð ,',bNc8k!Geo+x㥪zYzwBi- kfƪqPB!B<T8S3= Ue,8l4 7?}OLgj\T@+@B7  ˀij`Yh $ 7%| ;|Ý2k*  ֐vlŞ"T X>0w^uIF孙RdBB!B911)׾"CU^n?8H)q]jDFF-_G ),H͉&y @%TXe3*vvƂH< 9rY$hE0 $ $0 b!`Y*R8\FdvqW|I,bq^a{}aaa0Q`Ig>!i/$<LPH)bf;-<(\,2^h+PHV>nVNPN M;+sKRA[$VK˸P[SZ'Ew%WZ-  7 }[ pr~NTssr%(-IM#B!T*IK`0D/92 qu]C%0Lh* ]iteÉ;Cl ɝcuRD ԕͤʸ/g1{$Tf7RΝj1;ʠ0p9p .( c}p8k a4{? CH03'rN s4y[0'!B!,0ʢU0( 8iYլ1lxz5~[s0%B8*T 8˗ ӄa/\$<0Y^ C9 R4a!|  jU].ajbCv`Tޚ)%a,#OGz.sjΪ& HNYvohoFJJL/(Ia,e19n} RJιbOr U evR" *8q7{1%T3!B!"K0SH2E`:ధo׹}&yKmO4|&LU£P9r} ^0P8tz9YP)tH7@ \>WK1A78 ˄!yFv5Whڴ!!\UȈw#:TKwK!eNQGz<&,9wi)ڄh?G|kMA|zi0o7 XVDdDP삍h's I[D~ % :'[`3-1[lkgc \!@"!1@F1( Sg>ss0`)1(17!ιb`Bp!(3cpR 76aGf83n_n7]ʎtP!B!QM0H˔̱nrDU߼w׬{وl/YjP 3H  @a0%TLB T(z.Ck9u@ST򄅼ew(%d+Ӡ.7SCE~@+EH)ܠ`N@9 q&9LB:q{;pҍ8C? 0&e)R:w %̝7v~T'@1l;Qc>{:-ǑqY8x$^sS Uw !B!՘RT) V(Q(wE5v+/0__VmnvMɀ)`He!#;;*<|-(,?ц-Ri8tH ='M ȁGDFxk6t`5iƘig`ܪn.XQIXaJEs^V"*Do?t!VقlP:(68'Q.\}.B!T)!˶="Yi;,Q5ޱ}]{qǪ[oޝCe${MHdSod#59&ґզG^=ҺS5ccW+R B!B!TbAZR7a i麚 DX54aBة:B4ߤq-vjt[2etHB 0phބ$Mҹn/!a H ,Ɛ@\8w4SqTH:oѮ];#,(ܟClgM-3eع/[mN+=X ?*xO !B!gȑAX a&L)88v?{ c,!^BB#?v'3r<^φcɩ;)iB00{UL{ {!=(ٰTpVcQPӈ Q.lKzߤADxX({YmY_#T2C[}_]]zVx !B!EZRHRJ0,jD,|ҸnmԴk&g=x)ipwN{X AeAXa5"֊iTv[%'4iشEujFZ [@!B!Ә[ |AT 4xSpnؓV*hj:vkwHҡC'O9y"5=5=;7OaZ,K@Hi`\STMS4U y=qQcjֈWNLFuã#U.Ź,{OH!B!B $!yA3Xh \"9<3[ ۫o4o޸yg3Ӳ3R332s}a覐N*7D  RJΙS!B!BƤ:"L,@ȵdA!B!1Ue[H Lb B!BHXMi i:rÂ0&)*B!B<@`' AX a&L)FpB!B!:$reTM`"Y"'IIF !B!R[ |AT 4xSB!B$!yь!!agB!B!T_BLaPh*4h` 8W pB!B!¡0HQGE /9(A!B!j11מ"CU^PR B!BHe2p 1@i !B!B?p(*8cx< SThB!B!T_&`AHTpAQ  B!BH%)$L"K, !B!B/L!2 D)SB!B cΎZ2K tYSrpB!B!Rꐊ`0#D~QA!B!K0%`%|,8!B!R})%uSZ‚)aZ\ LdIFi !B!B/#% {rP"B S@JB!BΤ%5\!L`"Y"'IIF !B!R[ |AT 4xSB!B$!yь!!agB!B!T_BLaPh*4h` 8W gB!B!ź`10H3q@<*BUxPg4|Jv`} !B!02&%STTs T"Wq B!B!T_&\gP9aT ^ lPB!B!՗T81hg@SVQ!B!BHeAeQ * x lB!B!T_\B@B)`N1 pB!B!RM0H!s,LL:(A!B!˰00f%rA7e:(!B!B/)-!S@0 :r-@'B!BTSB mq@ Y'`Z0rQB!B!՗bAZR7%, DdB!B!9R17(%b-0D8A!B!LZRRJ &%|dB!B!՟ɹ0'T@ePрAS8(A!B!K2"'qbp{ MQ!B!BH%4Bspؠ)Gvk}CagZғM5ݩY*B!TT8S("TA0(AF'RLZiӦSӢΤO2 Y9Qm[ݎPmqـg+Ɖےso2U ?*<}/X'oP/:}g*2Gݥ}\:P ciA!rv12&%STTs T"WqJQsޑ>xV-X&bwՄD8OxbЀMr`]wU-R+NÎ]YQZg]UwJ6JM\CD9z˗>]+޿~N IDAT+o.A!d2p  1@8G& pr;͓oA1э|͚+,y]# >V_!0 @^tYtr'.;W\Lje=f|awW9]HVuk#gUzS_[jy}ǿ!,h& B!&p(*`x< ! pr:b1Mz3fڕR]՝jrmTifkoox?g!von>wB!o,0ʢU0( 8cCV]ˊۏ3f^}޷SBPsϞ>Z {եw4i̙mz}ͣSq󆍻=k_ybcU}ֹbuEU5"bvN77Y'v\6YE=yooީqŵWUj^v_ )y~Ko#۫B!Sr f SHԖX7H#emz@bbe/&kx&Q1);Ν v 8H!k0/~Q }=nJ\Oݝ+nԎ[E͠ig%B!D iRÐ9Mn5J0>n8R^ Dܨn2J9˜'x7(c?g8c=U򫟐1.K!]zիW_cWB :Q nLOdA:ITn??o<cRB7`rɏ7#R C c 牽?tX1 8)`n8qYj]%)59[3ht7x%K/tbp>"0wB~qB4|`zf!B!%!!`Z\ 9$O3 Sw6F[^[Nw` o8_ =p[,|d6:v.2hI2%%lĦ#Ͼ4Q_^t6{Q HřN?v*W;/,s՘ {rxg[;}rNi|{4oӆQv}7٬zG\[Ukħ/_[g趷n>2|ߏ%=)C|'{`ۧOCݬ:9ȽV0czt07&?GG=?5ebL۾~]W3'BF Ғ KX0%LK\ ‚Dޯ)*yI;RKQ)P;]wKK sXzFC M}6`>pFKs,ֹ$Ȋ{HDձ= e{|`܍K!Ed%0{l}+o]8G]cJBK|sS o~?#z?p]xk[Yj!S`ȏn8<={_xkYIh=e:蹗oٱpt@ݑncr*r8S:i?̍I~tݵk7r"z_~h?/czz7j^Man\X-KuMܻ$phCK 9kS8v4l#@5l!gG^;02O p|t_9;߱cWW}2<eVlẪrHm"M $uˉRG&:5۴Ԫg:=}fa?_= k8x| wܹ;3VO]CZǾ!⚵o̠A/cXv@L|ˮF ԽH2R9y]拿._;fVz,}P}Miy:4aͭZDZRHRJ0,jD,|naU8S(@6v -IvW;ƻ;N2g }j r]8mHnY`q^[lިhAUTTU2xW\ MB\y{gUٺ}Ղqdib4tB 1]$q=*y]Dx2*CIbD4\2Y1$jw^}[S3:k9W&^pqT3m/C]J8eIV,_ +dVpG*SkrCiyi kys'߼-!"/=M~?g|4erxl8]o@Anqo>a6STR|UBCߑ3DM~+;H?[}>$cԞ/ה;>u|)~V{;eفm>{ _fȻNYփot~%t2~~{͑i1D(_A* ,5O<;~2U}g^&.w̃41І?@ߧμ8؃ }1M}%7'x[4 >j|`6ѻ6y%D?4 UȦ58_pr})OEQW%sKaaO0ʠ"\w29dzn)=s+n}ƈDtT*\}mܿ y&߽T_,47hh`/ Mamv1ʦ)hPY~\:漸bF(Ҥks&;ٴkm .EбG@;gK7>;cjKSMJ.ܧ]ܤO~ ;cѽ˞ztן̣_)Ϗx4RD=1s~9~|jl=)d2D"OB812D! ̕*ײI.6䶥`6i{醇  '<|Nww%)2$w"e!kZk:K?V-MB? {F_p^۰K?o`>ݮ_dse#Q7&Y]^w%)1¿ ^f#k}+^<:E7j J)棨f4lUt{Iܕ&NOgMU'pf6Y5oWnԶ'ޞNX_ժn}ծcfg-v _/Sy/ >4V)uAO3ko/ 9WU2] pM; n$|ʝ#;vo{C; Bg/_Vk{ 榟<2%:)aӖ|s҇ݬ7z"7dX:` ^[wO'&c.($Byިw͜xȜLoMRѥ/-K`_xu;_kڷ<|`l|=LkvoWt<~d}䏿dG7n8fPwח0/تیړwh]I]*<=IEݸbmSR=bƵn1]n;WlU9m/F}wp׸ǏiMY0q3[t:esr{&5o '_k{f YN]܃NDo~tݳPd*Y}]z;5" urKiM97uS?0!0=>Dx_E2q&:Kygtר)yC9oyO_^s;?z~A`azP[ҶKg10H3q@<*BUx;׎}?I'Z_~2 :FÏӡz[?F>uYQj[\ԏDFԬ*O;hy^2aD~=m3>@4 yl[[GOo_֙?T(-NgO|^WvuxOJN:v/^j<2z'9|̢T{HN ?sUw3V;y.Ŭ˳*aNr&6W^%ZS߹0v홴PLr`,Y\d%Ьیϧ(a9V;oE j^Ǟv!Zgb;Gʏ68Kmsosxmʧ@6IMq㚒8koņSr(iYnmUP&9GӚ$?jfܤ;@꯷=x~dS(#+zA-mX+5e_Ivq8CesV(Z{قf+(*u=sO5_NNuQֲ[ocJM)4!iv~5 &-eKޖo\,E778/ӝl\3W_7W>  J_vȩ}$`(S. prN`LeLJPWEJE77,^fΆ7ѡ}&$O\u| CCgFŵ [>U4ya[ ֖UQQ娚߰}!3F: *qTvlu%gA[: %vxyG/I]`CEy;.Ԣ$i)k+͐ Ѩ /{ϟs*|:uY;V;_W\W4@DTx|kܝ^6"6B}sgMNz+r: mBO|u_@jGc_5.i`٣gwF?H  鿟|~4l}u;B*q}v1xG^:%<Cӧ?yߤkz|U[n Ӥx@O>} H~/ Qޘ`رcǎ{%Ww,tQE1^~׫S(h- UL|_wgé}1%zU^]h!l8V/%ƣB{ ,}233'aԝv{3*ًn:dKN(4l'eP/(cfΜ1^-wd+ *b =  p@kzLg{=Y|kzzE ~RԪ~]?C~Yol=*PVK |"PI[p$33UTo]ޗ&u=;ӛ[Jb $hmF#O@jcϧ|M(c}f͚5kcݳ.fS>g55?tyi!=n ~wz ^~-]~+(ok^@f"]rW}k_HxA!&"$9/;oذyWZL8Be27w+!yG5_kyNq #E- hѢTOpJtF'`3&FLu4 ~u<33A`w8wC̈џL*RWWà(P[xgbFSQov/\g6}TG dzk.oY$O|݇>nozsIg탺L."ur!Vqi }o3% T{K1p362t^,1vl];Nf8t]w%ɭS3qVfEmnԨ&*'hc,g^ym&{PF! [7uXnpUؔ UJs*LQW_ernwcѩ; t+%sP7sFwhYx0 .Xqϐq t 2Jffz4oo@u;~m{tX_u XM*HZ՜9/}VҾm2qEn?ݴ^_bRRӉR{f*{ogo7o\Iԟ\%ciN^esLS.,tagJ~hĽzUJ#^Li^i ){yĽz(eEmp> 䨁UVf|¨-9h@w=>ϪP6!?K^/Q'g&_7cYoĕ6cu[tÌҧOE,zjjj&˵[C>G x?BF+.@e[,#H$.a!J]:ʦ TwqDUyL`Ix|ȷԯvn^l9C:5ʷw?9jKٜx72C% /؁_]!S&$D^oe[#oxAe/\j%{뎊G5jxg1ϑUiٵeV^^ݮ.( "iyļn/Vlg[>quΒy)'2wi[%@)!St\sbT ʗEUf yUf9/sFJHڤ ?;x`5/Pm$=7v& !?UN?3PʶiUes[ڝ. om!+O-(Z<֔t960\&S)JV,J=~p;Bz6%dį06ulZ$c;8{ē5׺Y.OjB@@Dk87R`tJ 0v~#F2݇7TIs" g+gGChγ;PO>7Ľ{"6[!X0. RZ{dxx~!{Ľ,#X$;8-Ŷ{BqI6w/`2C:0'8,e/&ֺx)g>eD?27kޣ&X$u(_q61FW Xmޝ~;.r|)BKꞔNn0v Uby`UE;uLP\*Ksa>RdO{ۚ6T)x2BT";mYϋiO}? kEǜXw9yW Hl OA"$5"EuIZܱ]x9.J+Piq'/Ae9έmCF%trU8 qJp@͂%U"IydBbˢʖPw嫕)w#튺U]Ww/Lp9DlZ˵KIgCa R⮥SZGn|LNlVJ޵Wlض\ ˚=">t`bt|99W-mH3?v7ݎc_ ӤGիcFJ0ޤ,yG{DZJv+Ohݲ(ǁ~vm7T#A{,jƇd#&B~k*k%="dv^68m۶m۶z6$ܜ;mi+[L[3ןǒ/,"Z5I BP(Jh% pf\ f⏮t֩/eJB!K~tȼn>7q+DJI8bTJ&{}=h^7~[3Ǫ`I!'wK ŗk'mޟ0GozLJ&{}ZDΎw'{zFG7wfdFٶO`Y'&E+P &ʿ~c?܍dxEvgQM1p-"=g:}R&R:LP(>=tSOAZiˇ׭[nݺ)/tM.oΏgL)ĘȰ~YQ945j0wqBo$ʄG eCL.앝WvM+ <5/kM{<o=!ι?40l_@"ǁE@l50WC͂w BP((p jOJeoSygvW]?3V.L qV|Kڑ'ImZc~HʬO3uENT  Rc(iӴZHMldeXH#")ËSWh&_)m% )w!LҞi4fgxuikdg Px Hz Źr6[+85pS_cfx׺CW<13Ѻiڵkjޭ˔?=up*$pN!8P* Ԉ@`!KPKcP(ynmBP(*CBV,6qUwJ83bYʹjc$ddb3K>fX$d C`%87w#|eoK3#W$pp̫-#Uq I83{{[oՃiuakq-0rǑeޘv&[[/\YnU 2OVٖh4"#41 ")stЁ.QP( B|#RsqCH0$HܤFcbӰvw yF23/YER'~yLaK'Ͼaa?Έr+UƎq%-3a`~(J$EE, !, BD 0 ! eNvc_fEﲕ*:( BP( -a8dt}%0ϟkFTU{}dyjQG,Na |/e n\ >GP( BP( &3X'[VJ~iN60u`L7_ber(Y\KvS{zdGV\ "$ DE BD$ArB;W=[Y}fP7٭yblKG]|W,d8w6BP( B)SۼN mO[*j10 @`NL` E ;`G5fRis);WG)c[P( BP(A B0,qfX! Rbq QQ\3~XUCkL%<;}G} ]tk/Oxvϐzif={.\{ V.;ӡwBĥ_Y6pJ&.if9Oowޭq?5tnPTh۬Xo Y H4++TuIS( BP(  X!Bb%`$HX p) q\ڙMzv_xfg,W=V:ݗ+δ/1}܂ tUEzszl7W3$B` oGWIc6 yx+ŢMUB8Np4s74z@?N,1l!BP( BP(V(a'aAd*̏ڙfm`%T x[:0h3gv}Fe/Dc_$rDW&ju->7L$k߰)c~pDI;6ΕX]0uX @QiCsRƤ9s@z BP( B*jp5PqVF$8$%l#ߖӳtϟ7mdRs=珊#c{Pvsoʞxxn ^Vqpސ";Թhs̛;ڷ߷ֹ\3GL7ha;B̛Щ~Iϰ"o~qN!SV= 9 BP( BPX$J'o߾{6mZtҿDX&*ݥS- ;K5f"#sfOʻ'V>k zc''")w&{7_bFEݿ~V'llj}BXYK`ըJ`Z\ (jN+ BP( BP a9N VPk *A" .9sãYfڒ-[^rkРrgyWC__?wOï˂=`"r[eHr r@wKo۸q;ijE@Q!Da{2ח6T7")4BP( BP(! 5%_4 X,b98PkHJC>Dp}L2ze˖}̊{4n? >kghbI6"FsC>Gi {;u5/FeO&veCiM*o BP( B pN4jjR$@FB ?"8 sD~HLLez̬x`aKP( B۷/MPҥK~@8™qs1!%,8+W5)z 7J{L@;1Ģ֘v/ʙ]շCJTHk*R3N jlsQs admRfh6sި!} BP(Ν;wD4[1]DBD#"?9wwІssKc~ kWI ~-a׀&8+qܤiQ1TiuY::;,ryV7"T[Ai]odCyU i (f BP(WܹsmoaA)Xh8CXVX"BHX 寡9E)e 0Za/eJpfv҂CTlԡ}6oC&t0q BP( R8={}5jGpV,.$VLp(H*t%,2>:tȇ]TrD*up)fw}17777w upsssss˶wx{Y_5 BP(JaQE~I)TG{:yZU`cU6m5Q;^׽O3ȡ~6q-Peҝ.l{KEuٗ-{6 ^cı~/((51+#@BL @n_ٻؖR( BP/X: ʁ}fSF jY(F} *Glՙpo۸v[ֽFha5ݫ'5}_jX<@*+Ϻe"/|>^ٝ[nfc^J7tYLĹ# 8\Q&I}^N1aFUĊKc@WV}Վ40X jP`jUr50s\!P( B|e=>M-k2bVWf7|8&;H{sؼJ{jS+o<#9&кk1bNB^5#K&Lipl;hZD38{&8܁/}|76GWJ{Ǵ7yf_k厬TARif|7kqS^69xhM~(a)ʼϲ `C߳g8`Ьo`]k4.{Ic)Y)bGQ *B58c@%jXѨI%* BP( +"#2mG-ٴd]|˻d1дUG|x%]"H:u) ,޶gǺiInS8pE"Kރ OxCO>-st=gI1IB[ڌ^{of }&&*s&U;R8 qJZhV$ Yp)[ BP( 05l=iqP_:׫X6t pj3j( BP .XQâs?~l-_@̺6vzua]uw N7vsஏjsiSz] xR$P¢ laUJ{wZwBI0k _z8dBNTz4f-Nf1d㮅./K gI9Cxخ;oory)?ؤ5_3IyQD>ja,[P7ɨ캩Zvڵk&F.tgԗ|3t>*scêGwqU njp5Bx:f"]jJ$Ż}]a~ZMyK0BZjѢE'O^H$q!B2oLȧSɓe BZ U*ճgnݺhѢ 6i&W|̞={߾}wpܹPIjRΉRI]2AX,p(2%kHN(T$2{:5={63g-j93k%ki`k^ZikXXB-ЧC~(VLKj]8D2IQQ#mf;uࣨ/$5uotv8~3s-6ҨZ)r>y3m>u{OdDTq}ݒ3'ˣt=6J%մ"$Qͻ sжYy{(mPNƹ{wz1:.q/ZNuҝvHճGn}ԫy3OݬRg@SzZ]'Uq]kѐS޼XuyWsO1qr@,ZYY?c&w=pC7>}0fBB+:Jط!sgRYkcN7o,ZhϞ=fff$USe$ǘ:ej&rYV>d~aJ,ݭ[{ؘjopbvbe8Ұ\SQ>Ϛg"*X@$@`0B|>a#=J1&sGۦ:8\^`CU{Vs>ߚDQsynumlif]7E[tKGۦrK)(yx+N|׃>C Gh*3cO^g r)u{~Ou"vuOS!= C@Ë9IC}7ZX:8Ew_U {'0jua;{Y-s͟*9N4ѱE"L"+MPrǏ{xxFutj\P(T*UPP/^FR!BGp X$Vp䳒YmvF 3gμ}7ׄƪ=ՎK7aisU?lP[+g 8 xY_~pʭQrQYN?ڸD|4 cWJc@T*|##rIia$ykWr2ބ-X v,ʲ3{9ZNb͉gF4sTE.*B@bb{O_G~o9׏.ܺl ~?9`9ջ gqo{e热! Glܛ 5F.S\}cwN$|:`BupDZ,˥MyέOφ 0&M BPq B@"DR ~98Ytߛ6mʞDg_aE֎CޝXx@"k?qϞN܆8R-JەXZ^Rև4h޶۰9?,b-/8%Űn9Eb52oB%b(? "C\\tƁlޠ}i9wOΎ@mYod2wND_(xxn絸{ cxUϬ7VǷӘc-Kf89ܬ.*هgm(L$(,{xZ$ -#J8,YJ V}.+Tʇ d9(~06=mkP&u~j.گ?wOzy!+V,SPLQFmBP dpbK@(`䗃o߾{n֬5007oΆVSԣK7‹-柮krc8 Ni,7 'V(*Q^u%BU:eς3>@Y=ewة$WE*9\kFU4:$V`f‹ Sũ>ٰx'Xd&uN7M<Ul~`n!^H+`IbUZ.K^udu^ryMg?UViZI8{®|Nq6a=A?ٱCGT)lð/s)]|-)^\~"wN=L&)U[ɮ+C1^~P/]|!W9Zm>V[WIIḅN(q#%+sD))Y/fY􅐝'ϼD,ݴMu]ud^EhQi WdvtLFYdV٠Lct OKΟ?)kkk#4 BP !@%1  1@ʟ|pp9sCвe+W 40Y0rϞHI&v:P>"97ER 'KNrntb# ֶ0rtB3!HO~- vb\ٷqwҔfPe&"BjzA6<5#DO"n#>g[ܪv] c6#?d&`CZֻqޜ4cKjpIL%p|g[mVt)tF{^1V|OFl\ea;SrR壷b}X ~N_)spi{8 c.;m&?͆/ӵZȮQ k2 @~mYI*S]*eE3̙3 ×#f<#~jn0 dkc7o7n$%@JD%H*~ J51@HD 1@.eloߖ)SFlٲ_Ά4"7b\qu"/bi`"qI @|smqGڿbl|ÚS1;MYˁ >uYZ- HRk"jϧXuwjMۼm\P&|8q \'م@aoWCl 㽼wC)mgei0jDkZ8KJߕ&)Y"iѹukXieS fpa[n$E'w8ܪ4,-Rﵵ[/ڽn(/x{n\z.hWɁ:׸J]h k @m(m ;x?uv4lqիy5#S V3ѣG.\آEJ* ` {|hN?4uH=hAB4c7!ꑻarUG_5ݞAJdp~|mVyF9̀FUtE%"j&r|A)E,X5Aq(A F*Z>88 *MF wCTl8 nvcV[~^ZR%+{626~ϋ^W_Z=pHevcܥ[ Va4oz#u[WL*!ם՝0ug ~?sK.~9su J!%rϐ+sڧZVsBm{{re_V.M_BΔnꢑ9~xgUk6Y͜+r3Oɏ+\^V3@[ D?GYͱ;jqHш M_Sry23c|j?ڟmŋ ꖄnܸqРAz2R؈&7]:vc+:LFJ_o8ՠ&q{2?Wg:f5Ǯ7i5o:ƿ4m=)/@G*a"AnS#K5wl[mzsA)<*  0$ZùH,*$OMe'Ozj9+>焁]qrޫ !m1'v5*¾n}vúLS<oS89A#NVOsȾʚH X"5?vct 3m6fȶ&Sen= ߟ4iAZRhP}xQu/)M-sokfiu&:0:7&ם GSO͡?^vQ+ !9d˶Hn_Pʷ iXSj,IE~&ټyshhhttovrf@K6EGÙ]Ss͎G#aVk*X߷iT_T,*攪kuYea;j>.kQnm/'F|ES=+,lgf0?t:yE*[r""xV4S%(2o2!ILN}^#*ޔ{gh0!#Cc˺] S<,0rjYKNNb凰u'8jimgK'k3iʯMjuPDgm|,"D[vjkf6ݘbb۽.M.5&4m2OwR*VZJ8 9A.">V(G7h?(ؽc ʂ9υt 7-'Un?M$L.lMP򐌼$[nݺumIFi *ָm[FNbE,j- F1QcÎ92JIw;5&yt(ѷMvd7(*=zNw!LۇȪMۻn7 )$ Y[ +^XF.!0#dʥ4R,QFFFf4a"JN(Jd?@IZ*}7 m՞{6|c7ȷmQxQ DR^tU 4|FSx& `a_1.2hsq ,1.f۹|'ז+NPҞmؤk+d7*-cf #l2mߒOםwE JWM?Ue凈# QK,q#(4o)D鹝fA$ q=[{Íq2v.6jm<_miFؼSܶ>|].$zbKYG`TvMT-7.oQ:Ӄ(j];+늙kmi8O,qj@Z,j?]DRTjF1ު|w4s9:GT?${L3'77gD"U.̘՞{a6:3XIm.`gŦ ''*zϳ6Ozܺ+?A@v?_/Yk͎ ڌ^MfL$ș]շCw#g3Xr@"{\{~[~劶[ҧ~ {;Ilr UߕQ*/1$:EumMDe%Ee&sEO-ǂ@ h4zϝ:+ҨQaÆbYV aRxuuf" QPOTtseg[cŧq{ wy!*ݪƗ'O(rR%ZÞ/to_SR'W)H9{mf @ (A B&M=P(7̾?fuz75ڏO O qJthVk'׮<eOnSfMTe.=@eދ_-LVŌ]6C 5$>ڬ uMK·ϫvFi~~VG)N29BHJ`|aĈx]_1mu~jNdYB2eӟեCNYx!F-c߆&UZ+e`XYBĵCxx@\M|"EL:0>~(XP嬨X_W}zP&S*Ȁ11h^a:TyW/d䐈bkVY5%6.^IbRhVZg|\M21ɤ>ҍDzO7~]?A9g˖-L>jժzJNM6u?`υ4ͫ{g ܆[ȁ"mHُ *zMlh_8E\V}BlۖVt!.(囇1D@DBD#"upP(H%95D$ujKZl)倭T TꐁfžFoI]iG2Lau#OJh+iԨQ~Ξ=;l0&e!X ;!`5&xg޵>5͜k]5K6Tӫ Y'זO'Z#m2{I#$K Aar:M}tKX+!  `Xb!!!` upP(Ja@/=땧or~jnՒEݾ}.튕,ͣP fZOzJ&ƄpAo}h95]4gfBvz|S1kꁁm+-=@c$!,!R&Bbr>Pd|A13_SgYUq;** v݋`kݱU@P0[BJy{Ir|D 5pP(bYZ.ӧOw^׮]x\5[˶y y yԫ]]EyŚ5kEP(J`Xc!B֨BPJ(SLiܸq֭Q(n(J "=Jv(d$JԍBP(D(@r !#ABHK3lPBP׮]۵ke˖5lذFxv}%=3pO:O wwaff BPJ$ , 8 X@mLF5:Ga_?x؆3vADA/˛P XƟ:ͺD(P( BP(k^|R I wv6e^?N,$1<|@&*ֹ18skG/j`PvtaV -Bi1Qq~]EZ륚Y|_usq2%DZ3wlH]6>p҄Z7(p `0J`N` r#P\)իWWnйs7ojժ(5K9&Ho u$s ^ LH0`3jS( BP(?6 'T6m ws牯}V0 nQ7}i @Ju ;`n| E.ϖ3A,[im~ r'-&_^]ak`m֧d"r~1WfR+`2% 0< >))zbbbV-ZjEL C"UBP( aÆ <G mm;:Z \]z0D Qu\0>k}Y{*V3 zܮ%hS 9^@.&dƷҾRv;Poǩ:ʲtCó\4RQJ ǀG`CD J{jammݻlo޼)_|+S,H BP( ER.x7 \8@ Q9}XWLӾ7ls-B^%7i@Aƭ7aN xod`dd-QޟJ%ŏ? u֕kؼ~SϕfX!zՐ(E!|B`D%*|2D| mb0p… )gϞC֭^"E`UB7YϰBP( RHi,Qi4aNIa>0,$%:(@hcy3N9wv)Dr]{hӯVRDN|}9>IGX IDATNd[>%jZtp`OI҄{Wx{aΨѰ8Mi(9>|Daa vqȑ%K:thҥgΜᅨEL>ԔsV BP(J $lgb 1{ɸ϶+H"=}x P"o;yzFz|$ cYXqߠbz'!3Vr$:`ܜ:FF ;Gi wϝ`nC`f!LaŶM]nܸZıEæjkl'@Tl?BP( Rx;nEҭ]} BEp|2|J`޹W}^WCїVsknQ5&\:=;6 VZ%Ƃao2 /]6sCi.xGYh;qӮDW( CĄLjxx9.5)ygQ$G94?/rivSc.;b",ߨJ/cFm@} ]+i--:q u5*"y=i9~c($ }g˽EN:uVP6=ۤƐD+Į[*Ɉ&vόKٖrX,!Roi%C(a%j04+( +ڤ.4.8iּGpP@@@D*8;)!TT*[i0p|_]:|+7M `?IQ)?& 'mvMa'\;Z17 Z_:(/o'}Lj҈מ>y24.s,YUݬy͝mD!R&7=C9}|}䆱dQqQ(_o ,zS&4i-ص_=߾141sPFՅ_yn{im=ʖ9͞¨C7,0yDFit'=r+e!Cn=gE`$ԦqB{B$ީ~~P;??|Phjo߲VZ Y 5) h᳐sX``\9@iيU!oo5iJ`i7u5zV5OfI ֹӏnm>ԌAofd{R.2c>vsokϮus^~Jw S'q/2Lw]շiMhN.oZS?[+n*9,4D篖@m! ~KUD "nu)Vo`k]94= [AQj]ŲWi泚Z WC%5W?T&_֍3x}U#8Ufz>͖YM祐C G.HJV,y~zӕ2Ee#&U한ᢔCH;o^ZwZ _y}7C;&3߯Y2޽24yv RT\!S#Drʠ|GRGzD1*umcJq9x&?\>tΗ߯rV \`GhH>n+ JU]&vuO御6~`evkK`qUa_eON˰nH\VܥF~R_OW+mi؄ װsY+~46*ItYGBp^s 䐳8( QLٙ+n=(?U-p;3ggNgՋ9-G|4}!q;&=ף_ĺ-q"ܒH\޼56?*Ӂq^ʜD| Gykv}D+"2'Y P9Q;]dj[yd})^7鯝7&?Q7,opU5nӴMն22PmCPf. \JK1b-{S|E ӧ@TOQL.r1Ky?-2"zjZ MÏH,-Dp 5Y珟͑JZ8)9P|_Οޯ ੏LDF@CzlODAcdo>PKM˾=P#*?tNkmi!H??}x>bwqv@:go2E8ebK:mXV 俗DgL#$s \jӪ I_Nij;V(WY>}6~ %ԉG^αk4cq[3 }%wH"'"Co/A(%HWy"2IyI] |2rj>I.3]ڶ,:Ĉt(hٸ M}C uqԚ|cVmFc}/t %Y wǧZ%14$hט[$¾9Y aW, f"օhG~l8.1]ـt-l]6͘)ܙZ+ W0P|e˩R~AKT 2A[:ո/ yoEn۫kY՚u8&wk!Uoxp}lb@~~h B)8qs0T%0'0 9(WPcdK}!vFpX%7pyo*6}9oQ'2<\Nܘԏ#qYc2P6/(Ь r]QkIUg#?OmB+O-] 1IuW_fGpCv^ ѥ,#ٴce-tKY:w8F;VדYm:J?>j˝+t:ݴpornC<3QMD8QQM!n)hi\NgkdNO?A}&nY@$nǣK$ B!#D/}Cu;"{~kв|-v=e%&ծ!e4.,0}m9i"A#|ji ߆ۖ?;ou 4= LA\m-2paë_+Nn %Q24>/1ER$\0#by70H!EGYh`OkN{,BDABR5/u1zg\ugZA^ȼ7qZ Ku1ރe-k?n}!҄K6 PE:Gky5l6Zmq3 ?E%HZt9cmq6r;qE7OwÀ̉ރj?v,zsv$,p*-oU(8CxX&}>D,Q8}(XR^^oƍ=~{IhjZ.̻w{*ͳ..vPJ=&6HBE:ǪҶ'LRYbS#s΍@odf,Vnװ{ڴٟV龑+J'K)H>ޭݥͯS&<41=#d4zʳHX6S=tЀʉY%1赞49~nf|~9-OQ}kJ޳{ҰJ-Z>}Ƽy8@rͬFoy)T*|'FT0 }e.lgT^HK NGKtN}f< *1tmoÖ{Z,Z&-l gnhuHBOnn1v4p8ppu[l.ǁ;? ]\qqQ.wխʣ.tԜ|ztGB ʔ]+}tp9X˝]UBw zť{:J?u3K{{.k'e{`E‚K9en{Bg(]_63a5j-a-sQ^ɨoy)L+_wsCkH~jؖnkLvϱgwv϶iHAͭD͚Pw~usǦU?>&JkVg:Kg)Oj9!==W`_ZUGD=me/K Zvxԃn2Q,vBV 6C:3;m|+VjO1Gw3mv<4b%}ƝRg3pB#/`(?y&?[7jv?e%K Heffb.yӥPeS7suIR>OU8gsIWof k1w9h޻|OFTL_J8QcԉB5|@l[NhR 7C_ʁ.szL?/dWJ1KlqqqM#&{J?S0Nv"iJ R͞B6ʀ1M`_S*<[|#ԡqS t 4#}^gb]>u?}ӳݤe [`gt#y 9:oH3%`{vNNNNu!pjPY 4hg}tn vN:)c n_#gcPܬ?/u`H Vn |D)ip<<>B `"Bn-lNX6\gğwСo<}Ҟe\LlGo@e:B)dm4j%fU&=]{8x[b~mL XO!1Icn۞2UoRe /^0Ϟ''b^|ҁOl[ہkf̲#G9/20Ҷ @a2{bti2K<$ `t{g 9ۆ,ش4= ф\6NGxު!4XXD]4=1c)t0SuY祈~,ķLHGԞ{X&S멥msmXtZNAJ;Z۾Tj|z jaa=qvec28]6,qGĄڤϤ՝@6?EA.3@%յ]Buav h\ѴJ߽Gfn(UNylnRH N~8RO:DV2,OLx !~p8R2G}'dwV|6{j\wsO/\ n${{&+th%Lc:2$󗞮2FNI|/qnCo9r7 DPS*sCݽuяHNXj&L.1k>g<-|= :gٞB)HbߗXE.^⨨xиeadbE"}odddl\])%$1))I(7xa IDAT2*ck1YbbT z$c %H}MB1bceVFQѴ(~Ѹ)URb0wySPhxII211(cBR/^رc?VaÎ*{bMnnKRwscHcwèDӼpl~&W+k?X  9Y$ܛ`n7}[a#!N׮WfC2f7N}n4nlKBE`|~MC37-S"nZb),˦q@x <& #2)g&M޹[3#X$(8DžX 5WnXY`l\.wsR4= )@4n-ŦavJjj𼔘:BnhJZR>x]1ڵk/X5rkS3$&#geb(?pMg'iKYT#LgzwyVVu&RZc < 4>؀jj>da2kavZf>[_1#to/#9凃/s,ȸ+3 9Res i P$|@?ggm\c&֍*csMC䯮_dPIF~1C _Jv:KtkLLs`~-.nhpq+$&< ZHMPCd (0$R%HRB*pN3">X޽U+MxqZiy/Q3]x.Ӱzjcg^ÆkX=T_oyY#}| }nm?6W&95u{IP( BPQb@VG@ŵr0Frsd, )8O }Ti xM>=G#> nM>||s @:] Ns_ΨL  v>Hw'ܴ:zdA+ƼcvX읇wݫ2O^zt J`9N YDR*h"CߋK1nەGoec?4Fe鎣 ~p.`΃uMXgJN *;rGW;9nz4JiB΁cI a!W@(i Q9A"Ά@קv_+`l%&N$2Mb42&3aY.E红A~]4Pڔ) BP(>=5T]Tiˤ:lyj\ i&nաMTvvZF#ҘWcDUIQxҲF4ܭw VY&r y/-w@]{~@W<))8 V9B*F HAPX:اQJ xdrE)^l}-?D㹻6vU@wP( BP(: _yanxq*~_!{źJ ^ y I8ior?p+|I|J*y{fK34T( !q D*xa0C΂㠴.*B! “mb=LcǗuIYV"ƺźoܸqΝKT2,>mZDP( BPJz"g(ؒ 3 W"8'8@c9D2p.*z5L{j%&dIm 9 ƦBҗiRM/;p.ڵK{V߈BP( BP(F#<"> | fN,\z͸qR7zw+CA{chlQ9]}[ɭA4ءðyGWO|l^+1:R)J d^VBщev \d:/%*|Je~ׅ)%*<pz @U LCzʕ+kS- ۆemGqq!\ڰGB> zG+K=]Ȓ#.+䤲{mYt]6˧{dL&5>zPA3>h1cֈ !D><gΌB6oEd6梧y/QװS*{,b)n((ʷ0`3PZ7yj)qw$MYNPjrSK`,'Z%5[&[/{lP_o{;Ma5BP( ByZ*P(% a CK\ݎm߾Niӳer+R*d.T*oRkj=kf~.''eFDh#bQ`7K&,bb$m߱fؗܦS=|BP( BP&q BcD%*|2D|rqEcRB.1A!KL ,Mu +KLB(?D$Xx BP( xcǎ=~s&R,^… [N\p$ˡk؈dr&IY3KF& 04d3ݽ(<82ghgX@6$ c}E6v BP( BP(1ƚ!B  lM,BP( Bj>xOS&Bp)(&5(i=?i,LmYOB<[½ Mcx~_P`Z_U־[}4/j6ilؙ-4I)X!"B4ύ"P( BP(| ާ6: }-<]VҮp>k{xN v W[;:TuҢYZKKu1ރe-k?n} -1n-ac (ޫz} Drsqr,P@f  Bx*n Կ5P(4s$! ?80r ~mY 0?hokurG7 ~upNy%vF@UbASwz|ܤ캘❭EGDz [lEq3c9@c=)Bα` RT(HρEKT( BP(J՞&cZ9/QEg`/5[̠vYݲ*i6:e ˒:KͤF\>!5]ꒋ46oەif#_QK j>}aݓ)-5ԑ# S D! .E ʐ$We BP( R5%wtڠν4?/Qmi I/pbuۻbgƏ܆4QEmYR/ oM˻ꥉC@i΄LQ3r&`{vNNNNu!pjPY*I:c0:yF8qR<\(VTRXpiAF BP( Rm1,@_Sԫ+{ v}J"W{UZ=q~ec2!KS;jshus]s{{,t5Vܤń@!Cvh'xuv< ;v;شtJǒdXV, HVJR(…lѱ&Wm_DZxrA(|+t5wuCxl^a}w?Pzzzhmi"&lۭ-̴AVgƒJ&<8'C* WHTH2(Oc"XMyVB&!Ç1:%1ÇfM+ĻdEbögNĆ,RoM>hX W?V*{N \=oYYNK.TI[]J/3Q҄ng: 䈳/&r( SFkrseq- m[3"O*7 `_x:BWo1~ec1Nt OK3[H8p2:c6P?[Ś WCF˯!O+^vM{Fnmjja3q9+ +7;_8 )j<ӻfϋe-̴ 2y_lQ:~+!\9,8JC޼U XK +ܺF-::[h$!.k-=7Q8E뫌7ǯO9q5W}ڹ=/މNwtX %_p"MKfl;H,]7粏.Hȡ?}|~M̓1qrlr\t  Dw[=sm#}*oGۆ5Ȼ/`b/.P]t!HM(W|UIH^UQԾ;cua- m[SbȨW-FSGzVtMlZ%+nt@ǧ ߏ%-2OX<.f0i½cn^5Ѱp2>.Ўni) _!ԉG]z7| \\ho]q }ykPgg';]i `8'8@c9D^J WW2Dp|m׀ 7y BQ%" 8)gWF;)'[mIs( z!>Ns0/t" |`=V)_6f,M _$uڷI{N(";ctfo>W;E;a_CFPv qnG@XM&82RiS|c@F[ڶ:w?}4ەzL"~#hSC4(j2og^XB}Cǧ@ߏ%-*wv\zj:F ;VjKs'[h%& ?=*}d68a%ᓱ4-SJck9Jc'5~.4kh-AW#g H P&OƸ- VE<.h5̀?9/4ppBB̙yMq;nS ɞ:)>~PEt%#1O2#IxihhHLH$p&W٬iSo7߽6ЮkӖu纞9{1 R: بz-˔); MQD%XkYWq&=={5,RS !=)Goz_A[Q+j@\o׉q܋kr';ab3i@YFy ?sWäb-׎5 cy͝+ {fcb3E\})W'Fxbh<Qe^g[yv.dI_~JZtI]ԫW}1 XWk΄'=T4q샰Ϭa F| 哔51(SM?k^ؼJuAL"'nhge xA8}T8ڛDkhuoۺ"7M 8+VS4(vY5}R!066-NtBæ[ǒ 7 { .I}tg-8_̙ҍwdUЊg=>f6Zt3J-硕n0myõ?:~> C'< D#A2:~+p `0J`N` r#P\ oܕ "n>.v-~Cf'El_>}MD[wwMr=|Ƕ$#Wߌ,O@$. K E >ݵYwQl_6)aH" ` `=[Ql |vH(SG*;?v%vY9sp;{ggwy.Y?sy}.iޏm,Ź3%FK}e[fxȤCdy/~ݶhyp{vv?!`}EV8 ϝ5)BZ.CӆJ}vv w7n”b768}b4ﬞw;3濩גݿ"^o|̹H).TX=sB:MX|k驹q]}.TTDw=>ulOA:8b C5&;M@mw-讬KB᳨tېjA':E>ϡOg%@ꍣke1K.һw:ȳ0 ڸz?9P]J"ԻAjbCR |A'yett_H]~V选qc2!XLB!٠à+_.k'^kOO@õL IDAT\PqA0ШQϤy&~7*Yi?etH i.oENݍn'$u 8 r^(Xխ Yc "2R,zw߈)Sy9)Mѳƨg? 9ŧtSsKI*DARt Br  4N1mŀlbwp|MGcKpL]G$,ݲ=&ϭWݧZHN lo^;!@t/n,LM )IG+ (A͝fJVHI]FtfZEkhHi>gߥ[XڎrpKά`)qv`maR͈3- Gة['CW‘f v>Gd ٵuu+W8.t~Óm Q2 Bi,k+0b5ؤvKҏoUny=١9@m OU[~]ڻTFyj6Qȁd @'W{ΖZŤ9O?ΪvFQk_ JEr}Xk?@=Be$%9A[ؙRJ퟈R:_m^Lz]~j־\x_̜^:zk7~qJ?TwTm+X7GLq%U5~  HƖ,Qa@ @]~vJÇgΜyQ/H @6j lp= @䟒ɀ<"ڶݭ0i3%K yosߣyAb |n4Ӿ ݽ}9\4zw'i`5MsP{u͘ 5(a5Q@[00+IPy Z\)ofԫnJ٤lbf#n t 2{Xԅ仚6yan ۻ.yOXV*$کEjM[9iaƬo||3H==,ׇz+2G̟7!>\deYtd-k(CjS@̯ؾxjkS~ƫ7;]b3m*U(in_xgϞ*˧P㷚If@#Xt:򧬪1v1ct&Mv &9rDqaNBdƀO2ܹ>dwgk*6xQX2 6 V?Cj7#7NXB :{ 6ͳq7v$ `ł0sC%~`|;ScGqHn|EQc`ĐNdh~8444;4d_,)"s#5d}м"wc C@PeIy]\?'A;J4T2bm"ɒ`k )^ jh.]u@΁{&n|s'WNGf}:E0 ^yڥoO`|7NLQu45yߔ|4 cLYAZ3aAi &唗)mY}QS*ԐM^ w򱗒1|oߊ d҅RʫW%0kZVY%d2P.{ǫuiRu15-B12e*YiU:Qfh$P*۷GHo_`iV\QEVuJ)}ᘵnBjj4bA$Dd $ͅH(-V98-,/4j(66ViZz(޶UKG vm@QD V!$ 5} Wih%&8/:#2*#2*%52%52***%9_f^Y'nI0mfm{0z{܈'2 {%Vpk-=[ s$+c$|/."Yl:wՎs{:svx=^_n+u#<3 U813^ @鵏rd~ry+DD|L(#")gB0 -E_./Y/<_nXT<<\)Ȥ+ȕV`H* T})Y[{Rs%W6.MdT8uD*Yi,S) )8UR7|4 .;b5Q#*Su+,Eo5\X @Hf"䊐#I[Q_9+/DhzB ؿ[ZA]Jx_Yѱs|= g@d>IaPCx%2AYIX""cjnsÔ,.D? l]c@(+u z%i Ϸ+m%ן ;ͅ6d,Fh_q/,mīQ4'-sIВY,=Kk)kٽ/9_zy D> -ϣJDʨޖ .$ Tgo dP$ @ +\#L;v ;?M)ƨ&;m{vu*ŋ'O٫MOd %>$9BpԳnrr|\jKU90~٣5w`I*^A=@gb2vW#d^̔H.Xl(i4YǾハJ*/-CV xJ\JVZOL:.z , ek(Jnɖ$fwfgZvoꉒUbRPTf @ rЈ\2Sl @LX,Qqrr:q۷s]~ܹ28V"U}9ajo5GɰjsÚ&juVLn' ҍjZu2v=7ƦM >koRF)j,$$?>0K Nr%4~ʲ5XL%_ .8 :;jid.¤ |ysINʱ ~3\ :6ռ.Lt"S%5YG^ҭV}kv[6]H_Y ܢ1M9nVnsi#?{FAj]j O#5v+W?a~}/ϣ@' 2EDBWF*q@I]O)) @ %6cUo4觭e fu,خa %[1}&Ӿ%yGYt[ȘiZBsP} /P+Xד&v$z?Ui([u+,G8oNs,;nG@;5VUTjȁ|ӄmҪԸG,v, `;]WNęcF,`=_ǹ @UV aG [Elïg7H2WLt1b" V6gϞ]zӧ׬YsڵIvv9|=~KXһȶi/H;? ;] [A ~QY1nOgYd}  2HV s gi֩ڭ6?)lmmmm߾v (9 [kd.AmfùϮ]+=yT#ٲ ބURZU%ꍮA ef@_01JEEx| i*!i(qwX)-:9&M}(퟇:~lT[ABnse. #EA@cοn͇E~in܎3}%ΖnCj OLY28O;2#SYcAd;Ɉ!V3} ?5W\)@٥~2I{4YC6ɑu@ݷ5~0 DV. b@d|1Dyc:$lb?..HܢW }ۻ0"|S!ν@o4{}fҧcGd/}v}vRGkٶ"F nw_-9cmear\X+m=T;^sC'ycP ҏWfN$K~ʐ=f3K<+Z*/Ƨ1KH@&ɻ Abp@̔#~Fd| Csꙗ}PPl7vِ`4ruJsժ 'NK Dl$ƪZ,14tmhi>atP³GTM1xY)CS RˎKM'@aUԟUV?۷-854ig1 ~A>msK_kC'iq"(Ec|&`D<1ﰼX % gmgzיŶ`˻k{]o[z7Db[:&)QuKJ8{\C;[7rϺ"hJj*+V($5~e}vט֯b誙顎4 ?X5J(˚`ZqZ҃ ZAH0TaMa jkG\;ڨbzJSvAik.pg;Nc:BQ>Et }ՀMQN*$4k(cGKUz9y\'Ito5`k,60kݼqmM&MɾɾbUPQ%W.HGBlB0!ҧv8+uԾt8J45T"|-;5-@KRiLLMQ 8((o܌}:_\JȹslIKAAAQňt827={+rs?JMQ+jW'Ÿ*^ZI[㮮Mbd`Ekks.N?foVvi , EިjYn <^}!XGO ܞ&'y\۬uRH\%$$I\$t @ F88@B'B.Gf&g%{(VwppGYF}z$73x^uTpkE Sޛ f45kʻ0jFӤopHb|GyvyHi)A5 Us*_ƠhРABCRC. y0 7/f.G&nnUè|>RG5Vr3_\2oGAAAAQfZڟc˓IGGQ;jۻi{yk6.y87DB{ _p Bʇi*H%=_ bvh[LKG5qOvh쎜L5 ^"W,AL䂗 E6\oBAAA[fTs((((((N~:?U~.nTǛ#e;.^?mƅYN ]c1DcŸUw0+56$i@ 7Rx ̮}˶n.YK -7yʄ]{5G clN|"Jin?8uwOAyaQM*(^Jt~ߋo+$D! >l!EI\e(ɤj /U.gy=; IDAT693}[81i)[̗Vk`" 'wuXr+2o" 70x; ce0o4 5~V)ͲzˣuuεC׻L> &gRS7oؙ[֪LQ jK)]=&IpE` R ;7F98(((((((((((7F솞iL8kx/08wwP{t>7@vߕŭ%O;dwN@?oc|O1@#}k,Me썔bazs  W@ fnG v9ь:L3'!+Ba] T!BʪUΟ?J/!8ÂlRˮblN5©Oj*+VjE(ʅtdΖeouF Xg@u&`H?񺿽U&M ˭j((ʅVޮTe4jͫU ?5҆5~6eêI@*VͩOJP34ؙͬ9{od~aA;lXbB$x_iGZijjRʁCr +Ƴ))f64N02_xr.p иMidmbbbbb~]GFuBf"ҮgVBװMyx  hn yaYQoqFٙ.{ [JٌB{h4&ZpeB:QVZt:],׿Vr6#{NhoT&b/m7֏I}H3<tM`E-Lc&f&J+\r[4l6[CC`ȃb1 b$_Ӕ@,ttOƧDGK%>?p)Uvm R@,m \W:ʔ/K< l% ٩T*e}U@AAAAAAAQqX,SCA4-77W,|j{?$ߋNxrp[9e\”~sTuO&v+t * Je SFI=;8X|/A9{egmv [iUKNKQMԐ@ 8UiK߿%]qfCX2qV֫Gys=&uH'I ѦIܰ俦sf@-+k^ ڶAm427h ZzSUY7g|}z_A(J2MWSGi'/%L?ؖړ.& HafuY¢l2(cLeHyύ7iCVIiAfZBJjj*`h5j^/{vY @VbɚZJΪej(aP^ªzrnuuzp&;$ k{u ;},[|j2wZE?_>§r{Uxr@w\5ݾk7:m6c1'{)3@Z)ޫlfL=]NSGOp>n IxMF\ Uz*:$TeTcر 6 8qℷw@jq8z<@ H"_ Sͦju}ʮl G1obA;\,!޿{&i J桔2,$>! 0zo\ }]d3vΈH.ߣkr[ iVm>s(YaJ ZfʃocF#+4o2Ogfy+{hȖS[b"Ͼy22, # 2oNҘ/_w5Qm:J͂MHk:1}>ˡ(C?eyv]hQQ[)l EUP54:t=☳. gm NoԎe:. O+CPԗ=N;ܙ wKܱ7lC{9_ي5$/;"=Yl%w"P4"|,,}d50qP^(1l ̀;m6;wXޓ" `Ͼ}w\WU7_D-{X{5b]wCʘ18y^PC_ZuرcƌYt# :t„ }nFP0^y q3$JyI=xºR/+9.12/^G *[´N"2I_(%2a,E?]qccO}=3KBʦIcp%M_< GvmZ*QF<ߝہ iWrC&| KmNv Md;X.&g=0 .@Ig׾]4111Iщ_@jYXXiD@BVDfh8dP ˤ ^q$1~+vNz?A$hLkui~~1i¸Рg h4ƱVJăuHyH,_ l;37GẁU' NVj6]±14t Gvz'O4> {6g2^'>ݿx ZӷiHbaNέꊲ|)Tmd۬@)U" $>={ԪCsh`c o 5@ހ֦vQb3# C#SCM qAIY&ƖLQѩjFӤo@FW?xEVĄfe) *W)BjP"?ܥK ֏I`6 Nu[洒BZhr@{u'hNSRoAiӧwHp Y7aNӷw|y}[0H \/CD}v|_!ڻbgٺ;mח-++?MkI coLYtp!4\_L1 )@d;3Lajч/N KLo MI-{KJTcfѫ7Wa$XCWIVYFjkX}lX-Py87D>aQGK70n[F=^0F(m>g͞UYGu(}_L<> `ӦeIHQL |V]wC]Rӑ57_rly2(%Ť5ww mFѕ\ zĜ1pϷ/PZؽ5T}NNN߾}}vׯ;w{n3SV-V-Onp^d%erY t;F3ME>8>W ӖV WD{'4W\RAغ/\QؗJ&JCQT ? e*T0]c1D?7Kvl8$Q6Kmj/!Ir娧UJ=OwjboЍx3zj r~Orqa{h*Ϥٵomŵ0aиUw좟%s33jP5_x{{O0!$$qƟ?8(At v;okKQځl S0h ޗz62roA5˸=gr`;."ozeeqC'/ζ`,߹Ms65$J Þ-$NMyi-(-3?ϳ#3y7ӒIީ}ACgަ˦24~ުģPW۽"emfBKxQs3NOܕмAy?/%2{~Ihަc]'SEz.O]V8ʪ3)id-[SdV[ڵA~cjk|5Z5}:\2PnS_Pd: h|6ciS}VunEhdt@ ҤV-OSOsOq:G|6wbn[dѻ]߁A>sq $(ygb]֜p0 UV~gWP6[-,H7&5Pz+GQn:2F +'Mf-eC \Nv Dn^<#x$/1Ҧ\/ #Oj\Z]'Rj  LTgmF:FZVYCȑ#NNNA899雈w^zM3\s. #s$uW1\f}||m[saK,">SƆEp?i|v|~Vr\حO.LIe86c ١"rc_Yanhټ] zi1+ n[2[υ7:R=*Eu$yOn[<_8M.в(3%.%%%%%.% &HFj~v%ʊ& {ܼ/( IJjX~A.h8lYӜALR2Q@|Kv9RٰnH\Ygs !ۢ2#ݔ9H͐XڳNo/eO/ww5D}Μ& &Y9f@H`” I}_R2dFuinS.hn-٘ZQD".yƠ<-_?k5jSӠ̙3u6:HuewG$T!*IT ?5ĆJ41<%\.ǚl&DPgEu0w [s<(|@(4l%o/\|iWWѾRO)_Qn܌y\׍{5G ZVB訥^|w ٍKtHON|, |߼0TM oEUνsMqضcK8k3tͅ.ik.pg;˸~MG HYumkc=i8wMKLˣʨ;LT?![umk%hC>pgN=&v)*VzNϠ& 2? P`%jJuDk.@x$D6, Mwc 2G*AC+>^pZFP1Ys`МՓ&_T6IB`%1GM ] )m;|'u`]g(?cD}H#í]BcY0H :XՒ}%S2/@rڪ ;y p\wO'3-xw 2)V3:){ȌnzԼ$zʥDװSlX^ r=S$Hz쩺|9d/N. Dn='ٷ[yvs.CߡHVtn<:b=㇝.m-C]46xEk{ IDAT1RD9iV^$]8lo8yF>ik`" 'wuXrd^|~͌ X/<9$2`#w=PyJfLoů]W\߫kfnoݶmgm-Jhu[Qfc/3 o3rVyW._şAB~mMvX1aqA+M _MhK'$,GVN[7h{^Ͱ#hS`|J0,kUiuyx]hM^Zeg%_6[Һqy #Ͳ ܠŃwkmckkz xX,3 T eˈ~E$!Hd~_Qf+ pϜ Wsr7kijO$?c(!]z?q]2hޮyvD/ݨH [թ†BI՟mCoOzv7յ_d2={&Oʤ;H'XlH5z7<ʆ)?Qo/`6\_1k=u \LHԯU'`伄pS-$4uGdr&Vm#}MWjZH$$GA-6{` ՠzGm>fdJ& /@99B@FRZIhiǜr# aך`ѻ->\%%'YLF%y*K*î^ og_ Q`#܁!cL: 6&E ̗C)Svy yBKL>AUX%1KQ$~Ơ |+OF%оċZn[r6KZl%R=g s;G :>&}!cx7T L̅g.pyʆB9՟l޲쥾l['c ܳzsΐS|Ry9; ߽N9  bl1bw[4v=v-FѨŊ"(boXhR"w5nGcwo̾}F]`v1`DRhQLJ벇V)h2dI%{4C-))όdI9B*fdg0i̲եS2UVTX+ֲPА |BKGGGGGGw\1utttt&PB3[5EAHqSB<3:U,ދ55c93P%($2-4ߦdq h+y4zݸ6C68>Vs%\6lDɈxȼ[<0&Zf*zi/A!7e{⡦@ TCflY,ж2*AS,  2׆+kiQyNQ I^a3% ꝠT&lB"U!VRFTڧ 2;vuKOa(7_Cڐ]VC!ábLMKZ (Ju5 n0gjj̻WS?A@jՊj AOh|ȶ)?Bͻ/`hc9.d7rĪw,ْn nG:AF\]swB<|.(/.fkzce2/o/2[}CƝ+iZ4;)(\;9x;\a&, L. ,P`Ih$ :n3&x;.c*F.vP=R#Ʌ }OyV`E]5-DГ'Nz:u[T#ωݬbTť󩝥UBח cd +([mȁ(y‰ x*E 4/JJsi (T:^9J-v5EsW,>^p;ұ 'vz`Go<1_>ۍ(I;ךa"ukTI2Jw(d<U+ȫ*29Z8IyU$4`߲Tqya Y(ur>7;nYHIɪS hNmSBL^:B4AmaxM udZ97F@{ӨUMTFw@\ego EL<yw,mL+&lw|#gJh!ے|O+5vEѾyJ3ulmjZJڿq~\6[q9t`QTS%m.+ݡ6ZDpUz 3s%upi-khʤZeeV:9ky{u[S()YVEnl]__fwۼ˂4~u> Zi(ͻ/Z>E^jZ]3^`G2_·z/sGriIcsKĘJ7O*}sh 6*K.lۼy]6`@ |]ӆDW8θx_D}tٿ:e< Ss3Q|`a<8CݐZXZ5O5x;mnZؒhռf'Sѿ-^c-z o(n\ѵ9㎸\9be^Cbt>2lMGsVfvUm ^R0(*Pz6d> PCj f@D"Sq_(ښ3x0%K%Ohg{#MOJJQ<cll/qizRgi='R22L56Γ[L,(<.ǣeRM=20T,N`lUU|VUZUP?Tɳ,7 n-"%  xB5Ȕibb9FCW%IRYYeCGh2Cc[XʔBkT>_CJڰn ovgϞF]?Pj:Ug*<|>\.aEQP( YB T lmojcPVG*Cs[K( 1ʇ(JkTgV!S qpBEBɒ]<,giKyPz@ eυ\88Bœd2g>sn? KL|d`z9й7c34ʡbDhq#&/ eOvl8`%L B=RSP4=o KU[ڷmbo'33nT|7ɷ{UZ7vͶ&i{\H+@ T(\2d*/J+X2IQe~W\TIٴY6r}jm("8[s!=Wܨ߽~ eS3ϝDGܘzi^5t)w @ BY fmZgkطvb( ^#/TsuQvV8Oi@ ֕nBAƇ (k7͔HdAIQQ՛0ah'Kѽy9w(T Qr j>ZtdPr*Je( ֫էûuU{G@ Peh8dj2(>499LsbW߻d}7II]? bZu˚sce؉}_ݠ4,q]K۰ѹ ltE)lJ+)5M?dGNG ,@*+L5[[BsSl5 *i*Y$փQ;æsקhȦÑ.!'rQ{ώR% BH@S<w1I2b<'E~t5_w6%RThB[aQM~%z{[ߵ/$䆪VTz[G!Q]7V}$,s}Cѭ_>o[@KIL @ TE/mʦ`%)V]ˢI_kR¿7'2-^G. &O֩i.nh3.C S:,fE~,ДM9ߵ1e!/Ŷj[践)ۧD mzsyƺ:XB;8=Xq%4KMҔUdjv9YuIP(@*@^mTjpB3_Ɖ7Ɓ~q7 ڶk$oR`h -Zy-w E՚fǍ7=z͒"2l>|q6\K;Cٔ3gz_Xz:q]h^{5یxg;cG1@?L`fӦV,]9nԝ#Gڡtx]2*w#3:Ȯμ˕@zrq;͔R߿{'_y^>0Y1Ƶ 5sJ;iH"E))N/?!SsbQqw%TW*,ٹnݺ_Ur$2tu?nℿ}\hW[{w_f\'3A]=4h$eMZ={7;. {ca&N'^ ;eh{Uu7tHSaޡWLtρbѱcCka>ҫ|_ٶnq!YS @ ՗KdQC׬D%}~^tiҤI/Va2EC۸NjeSKԀ<$3["3IZbX$KLJSLH`#"Qrp]:.J1o&LѾ!qcPΙ9˻H} \>&̀7(Y A @$\믗 Y91(YGGl+K37~A2VmdVFk;x L')%iFL»W^i% OxwySGZE!7W^׏.~FmWx"JaGmmҷg<ݔ޹Ż2,).N\6IHB7Bb.W ZY^@ T;,[]zk]ZJHB̌٥S.Ŭ5-JXߡ㔥KO7x(͠|%+"<ʪYఱ{n޽ܹsI?u %\1_ ԛVڮcd^񮋞co(˻ %rY 疔XE8B@ @(MLm(]Xf6I R@T.*aaa.]>r?~%*^^ ȣp֯EM^A9:]{27&*ZBa @B4@ @ P1פI߿ߠAw}?,y=3 Y%pwVUF{/)=^77k?_WhV:M=06$3oմD@ @ T3*lo߾QܢE996oR#N|},r{J@ pi3#3=;P7$5um׽-kd_2k|~c*b gC@.yZdCZ"9pM yeuk3G(X e?g݃+xI>>UWMϝX86EqZTduY2qKi$H^7qCcvL@҃+<Ң׮5.1 ;.EDDȟv[Ai%P{!Ki4QZLcS?Mk, tkT9a(b/~=xQ=n*b Uj,dV٩*J?kGFjO>};F5[&Q{FoBdIͪmsO;[egZ P"Q,M[}=[kR3*Ƀ?/;HxFjI|ZG>&2w[O<@jد+A ud栏;}vzJz=ػ5>CjJ]`q!B> Ѩ~O -kY5(>_"f{@c+[k`^T\Aci\$}(y3p&iAL2#dV"tZsWνk޶CB+pW( KSOB$ﮞ?{'aķCZ[p Vƾ7 E?U҆ 2u7],,IO/~,E '8QNjՇ$v/[+`P\($Jutm S5>>u Zw{b/r«r|[ jM 9}o>~dNٷU?D+WHWͳxUn2]u5D(!Ri~ؔZÒ7 !Brb- F&P.ߥE׼ )7Y-39{Um驘+OyÜ?\=_j% ^fi\}^Y`ޥɗD=|ܹ^V }{~^5E>8ݍ_ۂ}'{UǾ\z\BqJ9WNBΧD]퓮0(_C0]ƼFn#F@ћKrQ&ʨl bcRd_+Ʈ]UQ;_f4*9Mja eGjxDw#ݷTI2:3mO*iAjȔZ[ӴV$rYDQPZlq4v$pw#վcÃ<.貨)GUd8nnksܷ)"աEDo']mMG\(}3ݒ:ڧ觭]<}ϹW￉+%(PF6w4$kr5hOOOOO϶#_00"J xPdT@"8 Ǒؤ褸D2Ƶj M ^U}>Zxb\mWIk&R|m,ak\xGݼ9XsN;W(0P K'͝/ lHyuKGct;މ!"6{kFΛ1 k%0s'1-;<վ}49:/)&U?yp?s>UIFiAR^^Y<^Q(7_Cذ^rZ>sb`T'ZDp#|nnzѣ_wP>|l9sNxe.Dy؉Kftl}NLLLLNñ9p_|8壛/:uun{L4,I- ё>*j^\RCv^ ͷl @:UH 5kbEӨ~;>].t8=Yݺ]Q#f^xȑf =~}]=Zh(P6,]E,1TjiҾ{Q9G;y/̝)-<{=|sI ( P6,]E3bC{.vՈ7.m+37M7ůg !z{Լo4vm後?w7:sS: Me˽s{+'fN<VH2UVP(Y_h9 vOMDܯm][[; aKӄO,_ 2_O $֔$1ypeQ(5pb41ʳ,. $'nVۺ$9J+3"6)hdHԪ HWCVn: L,eqah6ךSxЪkpm(_CjP]VFu}Qv?yy%x7oDƙo=|:ELf5ޥsܓ뾰\Qe\/`ڔ>In /r>ۉ\H? <9fw.dV :"qeL*R3\d>r j/\wrH71,hj]ǔFf =4h~3s}Ȍ֏%04E2Fޑi*h~pHxzfP H^<8aayTYtgB+c"STm29mv1/8܃`!f/Wd._I#KBGhcJ-2Žuދ.[RQTSmX]V&u/8Q8gONpQ?Zw5Kd'=p(GӬY%𸸸W>l5xzN:qfɛsuֶ{zLժ$GG2$?*SW)Rs?ݺuC֡\;S?4 IL%HSճlVVǼ/Ԟ]{Ãl(-˹{Ūc'i{cÞϻŃ.cryU=S\$*xIY&)!$avJnUrٔuc;;c98BpDTS%:C:/=¼(_Cڐ]Vk{p!0600c%)uF>lPĿ@`^өq]GoOa֥O1vvKLժeH}x:{]9; V^piW͝1sЙ3]!e\୓;6yn1UJNnj0r\(6Mޔq~3Wj޷-Tup>>?> Cs܈dž|2HUQ?"1 VGoKR)~ @%뫱&_aΙ4-f U󐧽mfr޸Ya&ק|%軹ubblL/9I4sv 4f?9k'j51!gV~}mv~kYYBB\;Oٔ@mJN\ySc d^9@jG=FɓN\Ǜq,m n(Www=ݞBu}lrh)屗GB:2ީ 8ǐD%ƶ<\K ; D5 eET @&I*[x! r%GF[D]H>Ae?($i=۾[? y,_kذ{t ޿>dΟu}s2UV|BwBgv_4_~\#w2,BҫcǎPZ>{ѩE_Y7~5#*(-z۶ggYUonLJdgRǯ|IhXt. <C/#%>8=ϳ0(C|U㻮:GֺφgD#OBjaOLR_=B4k<|L=7ɉQ?6^YDx ~ ³?Gi U+3Tf5Y{І+1=hz  Kf>ܾ꘺g~Vk]=zȣe#/h)(n萧(U(zCFQW{MX%-L+@QQlP9|<0Du v:J>30~¯.ɽg<UOrΜenJ0o`%Z=}i.Ǯ%m=.#ukTE2=A^ϳ?o_D7Zfzw"گ*#ɫ7-\■T؅oyT=}[Ү Sۖk*dV}(~mzC~ͬ2/jrQ#<?f=[a[A c~Xbl64 ^&~Wֳ^w|нS8VJmuv[({pӬsZjʤXnz /zwgf!eڎL-4GVvi1g{7x= m-o$r:Vy[cN٢ڽV6!_nڃ[7DDx2Jz2JC9 S2c֚|בn*eK\x 6VvY7kncV8߶LՏ%R[wnЙ5}k}y "@T ֞WP\z y=9>R +@\VZoWͭndjh)wmi!kxG9e 6'e\9JGɐimٮV~squ5e b gCúӼ7\ט_17W+ql `禁FۼI}M'tq/0nb#C;GR_|:ҥ,TZAt2h ;7n)~y1~\#w&3$A+ͻn @d;Yι^O^s)9{U"l37+ET/,_YV1_(Y0$96Y(yZ2KRe׿%|JeKG0[ʈ#d2%Xh/L$ft)vH,QU;Svޘɣ },(Vn'L.Q(P:))s2+LKg̅sXDb}ڢ1p-m/D迆OuaE}ڵ'O|Y\8whk艻}UP6{jKu(b/CoB4p)+_Or\ɂ6ݗ5e^yYv-իWtL* <|>\.aEQP( v:hݬ1%O|sgwǔlYcWhi[U^Q<^[d 7Pg e@!=F9Q.E l\wjhtDOH k~"}.}tͯ|C٧#ލ"AjUrrp_.eeI9inX[cZjsBQq|~Q?L}_ujsY&i44_IW_Ytc. mmGd?REQЃ >S{IZWt[/;aZ|%I׏̿~vSCZJo|=Sd}vP_8`y"B9{WNV\Ѕ/jgV!ĻAz?p1obR+z@ GS/OaAxdFBz^SN+/b;Ҽ sʝrrp\3fgj10dH'S}ћ8x0r=ۭA?.{/+thC#A&G wi>JEǎ U/p\K^}efŅ)-[ac{G&ꊔeZшБ,VUBgԹ8NSN9OSy.Z۴̦ b|q.Yi WNy4[=yR:0PB";fGiMh9SZZ~Խ{J.S5pT Ȥ(BвD/#y.@ȇAON4 IDATWaF.u[ =A/BsʛEȬ]:]h$Wo"ѾJ1i?zA۬c"bL\Zk Є~٢c:l>8;84р>]cRʣEEy v eSyR*VyHCpa;]Y `j؇ Zӗ@ A998:sR|.N\6IЈjV^:Z?Gε][F/`씁ꛥ̋iyOY4QdgʗTC04\ҝ@ wv$$7~ܟ)znbRI|k^/_67C˳"v@ ÇW SN\抻"v-OXXI&-:oѹl$"n\xQGO-i.#8>]J[ѢTĤ,.<,9 2 Zڻa5}B؞q`CsMhӊ@ P^U J2Z"3ucѤ@MwVdH}z㑦ׁ:y wCut1D`tm֬+ Ezu|j5 [\jX FNeUF=^7$!hz܃;CI)]xy`Ɨchv_s_K m"@ B5"8^\=Ա$k4U4%_31YX$w?={ؚG1c {=a˒;.^:kVBҘ'~zj ˺LYOp[v{~F@ɒ_^ٺDE%4Kvq}+8aگʢF}k,\Xb2q.mpdG}Ǵ*Sm9:gҡlʊw5L >|[,33;O>Cٰ[YpH&TkD"QB# U\nT=_CJڰJV'EP*eP9#<9`gWmnq^楝9Ƚם-HrJ?WMdx_x2./av:j~;ȭ4OFx"m|y\P^Vh]Z(>:DiEkZspFFs9ff7l;yэsǜ/@ @ &eDouNpH&\4_a28n6b&<h+f7l7:fmj.+doG8$fwO>ot~ux}gM2ך#kZf\+w0cvkѮ]=F _U:H^2pS\mмWLV}/,okaN׏j_+@ @ (g"Q x\!dmEb1%gllQ#_&KT$Ve04/IZbZL56jY!SBYYmMirvùb IK$YDB =&6A&-0)٫$P!1OճH5 dzi]qJ@@|>x<.pl6EQEeff* BQwQ Zh-zQ:i>_ȷ-aSYtt,مLso욷mAin9/ 23ҽ-3 5;C;[R@x *Zuh@ @%&P*H>^*VѡQH%s@ @  BG{.$'@ @ *Ax MNKKI m\Yƶe3-@ @  qpIMGA([?o>jܚnRd2/ZtzAfRy[ ȣ̒bħHcc5m-ĘLHz O; _:۝ai28%.)99Yplj5iZYD>ϟ|ؠ\*ЌL&~ViYdT u`QT,Sl|(Rbq:OhE*Xx] 2+bϔ5ddb%OXrT 6,qgң?IA͒I7WL%A3?<4Mksiݒnҝ˺U1 %|m/TGEE)^t7Ҹ!nXIJ˷,RT-xw#Dq)88J NOS>"OiΫ^Xڨ]g&ӲX)à`_Cٰ(@Ks/ 싾t;P1 3Jrb7(y) =_(bZaVqF}Z.<E:|( $%S7'5 !S5H@<|v`-9?ewUNz!|uڸ=YU3Cb6|~}q4Lm’*c\; C5 exqoIBRz\6VaVKhطKB~5tZ;Y΅BjU )ظ Z`kk[ԕ$|e,a]tMbZv!4/hʺq9G)sxm. ؀[IihuY2чW"?֪WϹMf9%yNY7nō ܖ\&OEl&P\?37d9M~r@ _VžY,74ڸJotSZԫP@J@t 2ckA,9E?}$gn)4[:\Žg7"%,zW3Η##6 @ۧ MaGQ迆Oմ!yi꥙973߃\T7.XL +δO+%I}PWAO3؆;] |> ̤ٚ['RlML4ow@\<18 i~.Ys'Javyy/ixrI^:L*vVTGz{>N=Ү[7VMȣrBG:j7a/t%1$gkGv|w W-[+U}Ҁl_%cwJ~'gȢ?4}tSmH SU5O&n72!p͆24d"w;@xJá^??a|@}k:ѤPc=p'P3(I53Eo~4_{W%`ۈ5ro(سO]-=T.Qiy4u}qB (K{ƪGڪ{*Nܵ]Yg*nEEq`]u@2$ABHB^ p?^λܛ=ue߹snqǼ3%LIZٸ- 9{Uaygǜi / oOs^ڸKm5ֶ1O~{Z:eL07 Hxz0Z+ !c[NҶz&}ѷDc.=tOCQ4XO)O6쳢aֱBoȍRTd= 4Td?4by8V6OJ4bY$s9gѳUԝi3jxkod;j{kw/01/;wn=8s߾.\W.٣:;WCwp:yW]5K!;}~__3­{Q Q"o]8}othsWmbG5"^`&]&͛;5Α;[W2g̚|n]Z>=~sF Te쬠˗/ҥ di41)@vZJ~|+i:T|Yύ4hjž((<[fͲc:TyCq Ə?zOFEU=o g +736,FFͦ^cL]kv㧡 ? S´u$eJ\hZ\ 봨#cω( JU#[?>pr-D72xԚ K?o?y!!g/_*)YcFCQXO*\O9yPMQL@~~tm *+_ ~% \8"]'5TW׌?v?*tx墹jg@F[kebм޶e;_ZQ&͛ݷXjS3qcg1?K4UӼJI!Š֪Z݇voU-{ybȭdUJH:Wם_E 5d:{uf!h@;ݙK/: Ţ>_: v;H%OU\s}y4 yxpLИ+57aԛcݿ gYfcտO֟[ǎ;Hm'L80fcbv\$w(qWg<;ڽFqn6}])!*|wWͬf >yi,43e-=Q}GOM/{26l<1| ?2#Ș}<k.7s3]L^MjQ?..^4?{PuUf(ţ+k~ҜֶJ#'(meMx&ҿf5w- Ԇ| -2Rz;뮧1/4*8-~^>,_PX+h3jXxkXyzSV;JǯVzrnFrv-[94X\H\q,D.rmѽ}/:u1e~K;G?Z({-];Zymgͮ/KSӌ)EaKo85^k@G]MSo ^JJL{26%3 peҒF9XhTո2![ %-b䝎ٜ hČңEUcU]&fj_f./66a)&T)P=@1Ư]z9SeQމ-^ɵ$- #p,_xR11QΡmBݲb$wCaiXO))Cֽ&ͻW6 SyzV2s) 4[mX19O}a }MY41۟ĸF)9+`Akuر yadMBF?Ml=GFgbcp}YH"Mg԰d6u*ޜ 7YX;@%_螥|iֽU+k Z3:Wt#dž=>g9'gS3.ph߆Kׂl5 4RIM=ǿ_9<%yj+֎^{ | ;tg~˫IA;3˥Hrb{p*>XY桁5umeK_O4Om&h=?ޔ] 76Y&^SF4nw*(QYvUU(чtJ4@"[@\.:7TQW3yy@i"&NLl7[;O䦾M̄h&nNl  O;VޕS(IgS ҷDr Kbm^tqp4~|A%+W |RN^~@tβ{qخ-Sen+^?zjQknTL,3"-|Jw )ƕ:4 7#}"\P0654Ȍi4aByĚ}txL{DgwL&iJۉϩ.q KJJruu 0֌wWByx;<MU;?mzΡ~?4_-qh.9$u ǁҩ퐯]{9}s׍+ZYٰAd\ٶ7R }oϢEn;W{6v|yZ|-ɬ[Oj,S :8[Nm/jywq- эj/h[Frz{#Umߓk] Z]']ICY`TBɐq``LC.A\J-v9ۣ)y©vηWH{Gdq<dBS>NS=R$\gzרdT3!ErSxm4 ScnE3cRL?dKd=$/0s+M߽m@[Gqcn>ƍ7.KZTkt1nۅ ;}Ϭ:&B4?Pç qS휚Uxpowlרe@+N2K&ZS,Hdk s7+i|E?Kt9(i=ϝ%|`f3'/ԉS_ͮ~J<1ӢZl E1bZ> k3vssyoZp-1#LouSZ˳/df뙍:Te&[;nPcTFדNo 7nDdI]"PrRzVCfdEH1.A'U3qcuQd #G\+xP6ԔJ\ݎ,X1%%1!!111#'l~Z#${}ip7yE\<&2JSj(ma+yjU*5Y~@PWL4*-Z4s>r /} YOͱށC=T?}߅/j%JҢq(y E1bZ> Y>e&J& K?$[1'Td^LxmZUhrC%&1nČ -]㫂g$ooz qo<۬}U32E1 UtW^mӦ $WU}gXyhkP{#if>PL(tUA2dz&$)s{A dRF7^nM2?yX5M3jku<եVcZ^e'G ОVΣO40Ws >Q+h}H-zz7k*2-ՊYoh~]B<*N W|{EwkvԱaN f#7'> U5pkUo?- i"WZM d4Y0ިm‚Gؘt&c7Zʿ=xY\+VLB5PFQ[_ mVJըK2ޮT1CX*G۝:N 7ޚ1ǐ][=5J77ji&*#((.Nm?li ĚsomVi£m6؋„E_p+ZF?cZ͡\.|OQFۗ`tWyebTzu5jԈc缥xYJ_g~eTWaB вlgQv.25Bx화d꘮.2FgNM{6:::5E]iY--3Y7 K+M0o׀VBI$*%G%U ӑqkddJ漢WbKmPR 2iB ^30 ,=>SsctQU=r+w꺿qd>2c3i845I=degn7 d;GgZYfSJa.g9xt1͇OZaJ2hT 4hиm?ce3C rm6xiߏ*7*#z6Ve$WHXW4Ppr3pD7N.v{yfܗ4J<KpM-& j|zݜ4Z K笮<qU , &%h+Vj6؋΢M˯ mW``+s7zߗP9Ju!mG@:|͞j+SP4Y˗_yqҥڵk۶-4$4vJCʟj5:c?s|jT(ÜAp^QҦnnnxJkϵϵqԄ"7ݱΣ3C4\X&)yvv:{J&z2lsr3yn<”řb jۻ?za/ -Z!N R%9ߴS#\ʼAۋL&(DWhɔbY6ЕWfJ2 X9 tYYz˼džr( B(%*;{l׮C\CBB޽{:֗=q'V)o`iCr ޾,:MѷCySv)x`<>#6eآExuEBayoخgv=`T*ku{:Ա}ss5%; ,%m݌l<-LB& %_gÝy¢0vvV[şY_tmXJƞiǔf_ o3>ق V6 >}8| -\e6E"+ۢg԰+[!ZWR(w=z[n|ݻw;w4)}pm/NȖUt%dq7W |~k%gk.H"_ 8C]ۻ}EoHS3Ykʳy3Z]~hnv\K{K_IұI='AثdԇU?T8 %K….\tlB#@(T,pb;&vϞ=ETvEqcE2T*?G_^Ä3]=3Toك ;B`|ɔDhcv\4W *<;Z{ioK #@(T,p`ʚ=84Rςc[n9[A+.;ƺsW*5p*;SxXa[¼BB|,<@ B}! 7(ST IfJfLϳqR!Iz :X_Fr0/H$(]yr(5%3 ;ΩyF*}J38xEI@ "E{(en9| -RY2S .9E`mdb#|J^^nK~)"#d2FhjZ +8B|=@ @PSj(AYA/2=-?GoS*˰Tf`!E(ݔ=8f@To @ @ Q׭$4M@ @ H!WF@ P%*@ @ J<$A @ C@ @ J$ŋ|g7w6@ "KqM0t)n@ @Ipd O(KJ>NS@#FQΙuZL@ @ BQbכ=Pz ZQT>&XLz@ @  R J&[hѪqcO/ aŸm]\*$hr@ @  ;#9;Wx 5nLШ &q(P,(.-[Wj+nVΨLf1g75$SPy4rz~~%R)Z-6ɆԨ~9q/=QXmYY&RRRS%ֽjMWhNMGNixmW`HR_*ͪˮ~JRS/"kMo(˷)}eȾ0)r_XހLv=*<2nJf =u3Q$HHKIOv.΂s XxP |L_+fCGA4fpH^ݻѩi nN<ߦnpLWjz("Lbr3c$_PӜ-l2%?楡[OLh={ 2"Cz>̓wǿ!9::Z㏳Xz{DZN]/R\[J|Q˻Ѣ@Eoxn)yڸƳTЛڈ&ycFCQXO+CztmrάvFQd86-*raa s⽋U/ ʁp^_@|NWDELד$N̚^'D4xܢێ>uƽ{ѱQo]8תC{<@k\بkVB h惑W>62%0ހ+S4 /DCrkJOwPPۗ dZ|iCV>Qw~<+UʹŃ\~o9,~)ř *sSj(UF0(j,B˧!만u%))Ϯ@rM}nt=, fvgצK&)gy|T[(eHJ]W`v"_g~J +L,ߡ8G#x)S!CWx}sR6;>|+<1{VI}K ,[3}Eqoմq'v_o:k_BܣOvG56I}P. y |%m:s̬]e=g8'~~P@0>'wך& om8`H(T5S%[\J2WX$7Lo(˷)e)c'zZż`,"baqmZ~ThbVJq~ֲj,.dWojρA  ESJ].1 qJ>5jojծ]o߾ݻwNտEJ?syo8qMbuݩ͗})..?h:ng }7|6Ij'A !'ßԉWXXʵ52\'ܲe]ńKχKֱG"R' 2_6sIӧGqeo>Ir5<4RHHtSx"rn7DZ Ltp(ܦz 6E[h2d{߶həwQj6؋mٵiR 7ã\ۚxZ+9eՐo HysQ34'2(wV}u!p;+*iW}9eW YG/VIٕ JVhc\_7[j;5jȑ#Ϟ=;z={TuİffAk71dET8D@J.;XB8@0J4Dߊs6|sf]?^;{sںcQG'LSM ߕ5I۵R?lBOF5wmBmI eV4D(O߾R1Aʭn9+Q @{fg`< 33Ȱk)#(7+AQ;<(V\ iCzn?W߽p@Ιuᚧ!7E +yg']ςc6om]9 S\U#G{i^a`1gEeKo;Û>2ƅWio6}[+}0)cl Ooࡖ+;o^yW4h~Vn&5ł[h2ϻ ~ !8,mC>~tmZ!S',l#̺pCݙwx$7l5 -s@z[o<=c܍9wdd?~ܞyM綎yO#Z1j@1㣎nڵիWHMyCBBBBBN=zCϽ=S&>Vg1& .%r IDAT޶ന Q/7zU$[PErbImxG_7*6;n {Nt?-CŽq*4e[BF q` zLVɯ"GpĐʤӁ%Soܺu/}%mӦM0ab5%3>I=!x+NEU0۰5oima˔ ǡ?|MfS- eJ`LcEfm^cud9EAjdN$p(&Ѻ']sڒ-󫱤#Z^[_$di0dx6J) \xTϬ:h̓Ke>}\pYbaRCQLXO*\O9y)#犰QL@~~tmZ\YI%fE`֘ X˔_Ӫ雥7Zvԫo4ƽ_<{Hκs(Q./ ^8RyLGTˤKW i{twU5 q4R 38WsFqqqfE(T7imw9u}m/N|J-#ur물xVNBrYѥwB6`LSz'J O~>бy-Dn8;= >fUV|q->(/>dF H"wM)Q=QsQˀ^*`Č/9N͋ SNPͬ-/IzlfH7]0ӼB@PFoPקɺ;26l<1k7`;Ȍ+#cĮhAΜUv5w_s3{5%''=˯#+?{PڔT!}1ACdyix/+X6Ň[h2vYw=Egݦ7|BQ+yyyȜb\l';V$6Wxu (n6vj9p N9&Smǯnxm&{ Û0~Q\i'WjY puu~]w˗/۵kgFjz7w5c-l4c^ZTqAI$=:@(kԫo4ƃlY;| 7G:f}q2@`8^z|A /^nHiş&{S=V2{M <b"Cg٦,7ú_ϛHod7\z"rޚqV\GRquk-5՝:B$πnR\W~Mrn7 :jebp۷ٳ]ve{m۶kBw!C~أ{xUqFIN%pi;֐A|xh`g#BKG=C!Dn Jq2@%洶 sERdO~⛷bMkӋTTk0x"t*$}U(JW繮IvA9zg]y].z\ S+4xBֵʼih8wE2\/~Vup{ e̷k?lIE9/ad4Ӻ_5l1Ƭz@ O^z;oquCaXO)(CS{5ɻY) Q5XBlE3ήMcIi`U[WXp;uN:N4ooM,j0kt$faE.)oto (/̻jeq&v=z[n|ݻw;w4/)@㎮8h DܶCN©w=rkfp@waD+QYOZ\쥽5!; ڔ*ikuܝ$OqBM{b3gWu }sT?uVeFa@]W |(J!b1[LioPR8* 0.yф)֏zykϒ+>)' k _Bw{gYb8dml쿲gC2O:_.˃ּok0傂MCQXO.COY!ݸR˂2EfdMIf<;ζ:/e@'go%q,q6ZgϞ6mژgn>J&d qFjݽˏ<^׋ jIYK츪gJ\q[' fwNЂ;غ{|'^W ! BiC$L*%L,Aۓ+MslVCL>x4e-C{b>LJ޵kUBoO;3h4يV^l[W)@ D\kcp=3n>8m?c9Q 捎I|<\޼BF.(XMP4t;@&YPZ{pV_9mB۔BE--342' &Gfgf ѶsĔBzEjvg}!MtkPHd q;+Y,\E"Ӳvn96"KOۣI&Rݿ7y9pP}N 9??t :ݕ]}Jxoux0V<>3qs"xueY&yKSun:>2՝E]&-R+7wǡMYW4[Gbu%4{۞Ng 5i(>=)[!#{T ]Ǯ~Jr?tU6&(8 ¦(^,B˧!`W7b01yi0̀~jN^zZԄ̴ )gѰTŇb4oJ I3?T t:z58tCNVת6ݻgr]z9LkBW ӛZrpyq/!i Lc5<Ŧ^}/eY*W1 3G :O ooP7$ P<r>%>{Zߕ8XU $JDŽ:ƚ|26u;fѴua̧리 omp{U[%Yr{ؗFfj1ZՇ-W!:mcWv?"*?vk6bֱI2W(Q3VLIIOaHHH6[eR, H_63C9ʼ"kEZ`HL:sQ*=uTmϴRp _\ˆUj|/{'۴h̍KC0!F=Fm|yC7ŐDŽ%oV|4E[h2dwL&PTݎuדy)2ML&e<6-*f\gܨQ#FiRQAE~+P5YO+oW<ko @.6e0 )JPڜsJ[FoSxBNvqOA~垛.uOHIA[;zczN 5|ѹptRX1읠ӯKrѴTqLpDQHulbO_v_yQ{t^A>^zԮ;򰐶X3EN4{߻D369w\?˱Jپhvͯysg| -Xl2yO>ڽ{^ltLmðQ^ &yiVPGU6"Zpg1Z8:фmSyk5FxpV2E1h_ouܾKÔ$⪱7LQrgpK07 J4JjޓڤLzE~vZ} Y$Dnn_ZFcǝ_H ~Ak9uZSN,SUYI[T5ĸL>Zڗ6 %mrI"Īb\\hgMaVɟ102RG^sWq/Kkոqƍ9hoBZ)0l^i'wt .-H[yE';; deR'# N,"뒴4 J2SR3dٕվgĿϰxz1o`h"L&SSerfMHX*ybk)beggTüz!v+? (,E>$JKqޢ78KqN?L.Q(КsEL)H)ekm]yef$ мt12srFWEP ˷)eXf^/eZTޚZ]BBЮ]̻3VDDD|@P\9@ |>r( B(KTظ2terq['wۼ⪐,BU62 C_aQB5N,z5ldz'tv3)"Ll[:ƿ%yED4-|rټAƋlY)*Xuy% bEϞѹ,veB#@(T,p󊜲@T4^KDsߩAEՂ A;noYy@ Bn^C @*2)XZXy@ Bn^> {pB D v H,<@ B}! 7 KT@ @ x }H+nyܣHvvNN)@ P)!e%o-/>S62U]j̙tFQΙuZ-7q@4ȸ~Ɣo9ww>5@ @ 2I p_o ^CmlOg3~-g>fQT>&СCݿS->< 泣25ʐdC @ B8 T>pT~ürkT\_kC*$|-?+8}|4#tғ\l @ @ eȡ"Mq婢IiW\tVYp<ܸGG+ 5|}y$ A$ B vT梣-|JeL,R PZ)~UEŶ2Tn/ߐLQdr},G]|%ָ1cZzڧCq_n={)ڿS_qn@_!$o\O(`cld2iMNkӖ ᅐߎ6#{Yt@ @ $%>0Ǧ @步>CuYfcds%NЮTD /b._f~?lݦ3ƽ/3uD`|UXùqZߨke\WF ;p[FP?5@ @ &6cԨQժUvھ}vm^:'vj(/eu8MW~撏?_bEˊ׿Omp;do <v~݀Y-ҡоF܈=z]'?34hl}JN>ꘀ Cy1 doQpgdw=|ctAQ&dȿM @ 5cԨQ#G :t 0zhs'ͮ ꡕK q(Qڗ\k Gƃ ZD7l~з @Tu(rW"o 7ˎn0Xz"P$k @ @ ebҵk۷o_zM6sکϹoN@u#.F[lYoVs%ɀm Ӣ)q!16啙]z~i `E <ηH!@ $S ի\QF\\6_0yN@b<~,%xǯ3sTW,9N12F/Acf !@ $S WWׯ\|evl~xhV2E Ϩeh\˱P:/Ww$YǢ6fArp\ȋB)NiM/?{d*8 =pZukupmW[뮫nUd*I,~$2^Hy{s߽睻A/CgL[jY8IT2E V]%D&d1Z1ϸUmZTCPPy<+?dĈşeP\0;NW$K$yO\5 %gϞgvIɓ'?|вe\|cOvMX]~~.RĝP.[pMMP\j.vOS]C(⋏E D !0iI* IDATzU/ssryO?`*fiS?**Jդ S&_d񩇒,ߪ]^=uRIR^ߋ%W+bNMvkSW?79& Eb>E ;/B+Ut3h<1(bưqmZnTŠsR^˔爁"V_\7Sfm=ʁxao^}nGMn,';<o߾g۶mO֫Cy{/S{ H=TU}&%v;K-?{;3pP#ͽ'I@AC(332[iWz4 iC?/&LTwz]%;woj0IMRe/WXХ/ܝņ-S\/aP"oSʐS848y1K|dd<ˊ3 3jrB=u`RH_/*WI$tߒu冼r ):_Pڵ+,,,&&& {FZpl`iM=t}<7qwt%wTϕ*NF-?n ۆ}Jh21޽[|[yMxc uN7h"(}qukZڅ;wbn4O/::+QGa sů¸l*}hbҧ|(_?UrYtgq٧79A! J=>q`4OY0.;> -}d2|ʘ=A4/c\ Fy0Q\ 3ΰMˍ }^ڸ1VmTѬ^a)c^ZsGLm wS! fi[cjP6[jEB pc{V0|s0Jz0~4;^?!1fbʻk=87otktc%yzܲ { b|x{uy4UZjzG)S'<5- k1)ZvڶaY] RH>{65L ~ rL!;禍՚T{z)>0?<(ij[ˢ~XMx;vq`&R {g B{4ZA$~y ws;ZPТ-ji3=.?NУJ6Iil _}?HͿs핲)tܫ#G.} [N ~SuJRc߼$U;W[FyIW1Y+aoBX:u1)BXҥG}oWAfRiiO?*}(;?nޞBcR=qU7G^LJ\*GWr)f{F=eCyy ~=6¯Y̞2y~]OCFy)b,f gڦB+L,4zm_U ќ:jnc~\zJQšciHGܼh* )F5)Qan* ,eKi}AdN"ٳ*J4HK'9]׆&;,Wp]B0_RM2)T|X]#7WVq3ݻ9Ś;fk?uxɥ2S"?Ι5d l3&R_xw?ȲfTó(ޏoǶX F3lMQ9Mˠ[/yY|nnL ^Ӷ Ȍj#YχѧFNە%`$߯1 uKdL\M4QS|xqwFV 3᱑S7ԟ|'%k`](؇. jg?T+g*#g˙ڞJTn< W]ݻ7fSwtM SFoLݜh̼鍳? ~gL7Bv첥fC?^2m5v!h>9,+'G֯Yv|N|;%9}r Ec>ŧ ?eP=NYa(hθMJEChf C2ጩ˾ة㏜-LsS= ɐQxk9`֟2΀ڽ']|Яu9ܽ{){tP2s.a<ܽcкW<ݏj_m[~;-UDcJ~q_ET^.MPA K{tcR)V.7nt1mٕ|raDI*&,t-a03@0f땈tu۾gl3**q.8NW3Ǭy :L@wUQ7zzO%hfjvֱ2-ED|ٙ^ly ŀV+,ҍN=u<*ya,Aʪ'R`*t/WzRV Ǜ45!״-(UY-~tg(͉twGR(v؛27M#*D'UA{͢%xf}Fcxt aWl]䶟:] -~ ]sj^#Hh9P+:Yox vRU%q@Wu=ԥK+x?8OP_O<o_g7;Dyۉg87zn+ONFCwmK& d|X¹v%.|:5I33O_.[P8oS0S(aד@n~tm*+Nrya-Sc  O(ȓ$iq.?՗La.hjs'>qtoZwyvzÑ{e(i6ӿkGKأC-8Ur/(@4-իRcReZa9fm?G1wlsH nmZ9B ><&Ѭ8Qim֞YMH@m{{e-2-GOLFj 8SHj6h޹Y÷saZoP~e@Tά~eHE}g8H θ?pݞ3Ez:Uo8 +u *"e>ycfWՖOJMxȾ=G79U]VL\ML8wRCSX8&TICEJd'>\tTӗk7[h24ww=M2μMcdဠ\'U2f D82zwf]ͼ^Cxk%kbZw..Y5aYhPM=cb]m풚/+;0<*y|]CS=bu4WuugWp\5[88B\Nm<89~4Aȃ*?8*Xh pPJ*Q@w9Imy<*5͛7qI_G%MႅTB2 ΰDe*{;P/}'Mۇu f0coێ.n1@k?f+[1&T6nq73ؔ*m8h7^o2rVQ>kƝZ]c8Hx|]llE\\ ?9c`$ .K_ƥEK_$Ib6((FߪM3EĖ(Y?L3]?s7 (oSDʐq/OnnBi•SNFj_Y {Ȳu̱"Sgڦ1@=t\WYNQ)GYQMVZu]Kt0>h˨>m͞ƯujeC8#~803 *oOW^w|~,pP%k쬚\f"(K,E- CDOݝ]iSnY7{2@iq+ DoWTM} uHe6+X_?sYK.-}v):כ=kg.7D RT-?654(gҦDn#zq9Vdg!&ǥ…̼5>MSF\Nu),V 8`ĘRYtk'L7qM)cO=3wEHvH(}_2ME8~tnR\th!ZD,Al91NXU/crT,xҽuft?f]jHa)^晛(%ǨXUmOeUL aq ~rbN; OQ fNa"*dj@T1&˧JrPoSS/yhx313hr"qп3**J)& j 59Eт[ƪj2vol +Xm@wRܳq=hW+p}#5ib ;y2*5 ϞJ=X˶w6{ndQA:U+PƳA'>wξR@s}S[bШ/qO߼yo o0i-] `ՉGTRVUx螾Gʽ?5OeVcSL-40[gҚkoŒaAQ07s%8"pP%JEf(l符<2{RRs|vKzUiH?;kMha|]7?7[h2d8F5v=̱8|b+3nMˡ]ܿ3+&V2C]vDRRHtSʹ aFO#s&)fy >.ѷťiGLl[>0WKeB_<ty];jR._"noеٹd MQP?=1㛵iFCW(>erTr9 eZTTӴUW)P<}%o#O48fa] 5[J7 P~ɣGnceT^@ϓ 3bZҥtt%aAmЭïfR a5P·-+fvI.R8sn},,E >eV坥PP(D2qc3Uئ^EV)d&1jѣGt-YCE1k/t-~%kT53??JS0֒W d@(s N4{onЯ(hN\uydd%(m ;tUON?3_PnT0}w,\I7wra_  弜"59!0zWx) {* v0=nO6NPid^'OģEmhJrjժUnVci J3Cͭ-̜|l3K}~g15;H(500`zwR!ZCݐ tq_9p ަi_ժ xmbEܠ |n]սriXQCx '/_aP.oSʐy/0jhR2޸cf` ڴܪJ 2=#/^A(r?u`7*NYgVsZTFޚҩ_4Rei;{z7;} U;5)5O%sxjrdXejߺڏ9E"1Klg yN. ^qI2͹\.yXx<G(t̶qi8Lp!TP(\`oQ&2qI<Kz{ȩf S.J% Mj[;tT}ټ-Z%M$Ur5oZn~e^'EPT#;:U*Sl>'A`CyuXr,/$Mst a>}L#f E>b>E a'@ lܟ1_,xkD²: )t5+"<<|;;;>y<p8l6(U*Jj(nZa~J׬ p(n2,`XyzX;yx0w4p!2 NBg 5Oe`E&_(3:sf4Tj7!L|ucģj{Cω#tȁ7RPY,U*0޴`*!ӗ'0k( ۷)eXl^/-Fo {0u y_ _sPԮ-l^w}s7::GO @ AB p 6ϯ|׮]۳gώ;rN z_6=µ>.^:/^2{E7n  'STxjD\ٺGz޾E(~VDM @ !'Ncذa|Mv4Ϟ=;|]v:6ȱ=&7ƍu\%SH>E3KSs_jX*Q$Ji lD$J (_:(2߂?z5S#ӫXJ*e8Cx2?xUwS\mu@ @ rJ!8<<<_nׯ[jx2o>luu;3H|ضm[ΒR-#c=Ůkújȯ~io .1ɨ)| # w@ PP1v ޢ:^8&ߦKٜKeSw[` jV;Y 5t˗~5Df(ڝ?\We]H;.o ` @ @({x.פh2voJ 8#~\on9Ts; w {`OܪCz$^?]h_ nCnr@ & E6@ y6s9O$no]P{W830asڽ9~"BЎ<`GwMQh:kǎ SP֟B @ z(6&((%d@"=O"2B@ 2@ x|<r86MQEQ*JR" D#r1@ @ @BQFqтF×~άLjLoJ+BQDм25j2{QjbLbbb>% 3)L&I...CLm05\(Cr=saF.ùw+;ч;erj>_222_gYP oSʐy/`UޕIOdîǼ"kŌb!ִi9Ta\N3n"yJlRbRL :8.nx<&HocGmgLe9=dR1h낎rjK'Iy}/B\u"H#96Ϻ&1`^?eF՝hjCw_g;h( ۷)rehE/`eOdOzag]E933mr"ѻ:t.᧺ξu+1Q}b!w:.ؽ'SE"Og~T[ﭓq7%Mv̼1*xk߰$M7YeOu5c&_مIyPPʷ[ ]on_86t]o'`%[96(+"ƈ7s2b\?妽/39B-}`2~ʘwQO.6@Beca 1086-W*f 0SL ;A󯣏v܂íP ʁ%q 38B!@Q>Cַ0'.bC ͈8gāO^՛=?)i?7iiןcZ^YsN岏5+`{?E+'`E/e?ɇ@dMRdI#*B-}b2}ݖi^ƸIDs1086-7*yĻ+rTjQVun\MzE}r귋5gߍMRΛu_=O\^c}P+'!'ž}HM}۫+ר:'QG5R]/Ӹq, %1!JXm`NM2[pyʛO#"ǿ{-v,WR&͘z QyMz˿4Lc EI:5c!(L5˖ ⦞;#x{97[-M[3+TD r+MOͫW_$Mߺ)hdžu5?vŧIp,[5CeD ůi=o׀5nQ1~9 vUt̠@iMtC9$h#G@'[{y|vӺz)8:j{y bb궫5lQi)ϓ4?>z@Tzp~ĉ 3f[7֔ƮI(|Zyڅc:9拾+@4iĉG)qF'|Gۺv1i 's{tͿ-:2s6qMMaԴsE.SR5sl^#Hh9 EA;4PIl%k)^*pzLi5x?8vKt߿> 6mKU_w^Mחѐ]>Zd|XBVI8l _?4/i~Jsq} mbTٞ䝦̜+D)S*iVP_3f3i[_-l*QN%E ֘Ψbͯ[CimtC}}o-n ]3gK6%)y8j#u~: ָRvLeifѼ A }(y_p7ݘM[`iƉÒ; [k`Ag<:r,5?h³C> ʰ4kykk39Lɓ2EFBT眙7ɀ %kZ"FV_>>p:&萡Hvktu#8[2M3cFrIȬͲNҒOάV9&]ߟp݋qSrS~өZ(҃vPԩz/ꕺ nx`3"e>ycfWv+;4~㥷":hFUVKtYy3s51';3=Ρ]jh1X8&TIK)\vZ MFiA=(iUg74h.oaP&oSLSyg4986 LಥWb>U$^V.dwį[{R麰 5 :wԮ#mk_KPߺG)0Wun.W'M\`y|nH=1Wk;8/{<-oVH{'"J|/Jy|>uҾZ-"9Cn29?~4w7sIu URI>E+( p DWJc^z#NxIx76[Q/fbY}oڵ們:TurĽؽpΕd͏/ilAsf(.jm25ʴ6SN z^KFIl|< Kݧh֏Q;.O`1gPӺ}}'"[֔o#lFCO$[ح?n>aI67 web__W`t+qՊ9P%9]WWYM%rn[`,4\*(W&l~f'Yu:B!:W㎝2Mܰ|pFdgىJ+ 8sTUb@ԇ&# EI;yu;d-(fwObZȇnհּ|+ijV4Q*4Ԟ>Ĵ   ;-EdUU̷kl܂')lԟSɣ;>ehщU+Y b>E t&f'pu,9Vd<4hUD^ mnkS|>J*uiwEzJ}-4ʬs$=\'j[kmy7I"d[xjK\bF~;jK6+S"_<y1QOIUKRoȺݖ6,YUP.hji˨>()4 Ÿϫst_ W<PaU(oIKc5(l9b-(5]Ovt0p׹l%3gS뒘ݝ]b7,eDsR4a"+*覈Iŕgi6_ڵ)맠Zy/{?) B-}v)敺4ZeNP8ʏM (Aƙi0Q<"hP;1p*2 WX5gM|Εی5>@ɚVr)qՊd8"Qp`bLC!W\,hb;~m=e{"pQotރGϡw nEZ4nA_FPe P?w2&P p)Gʴi񄎖L2^aX8Q5aںY67rf]&pוY '"p޽30L~V,gcT{Vq1>݃;|t\M-- 3c(4 O@S^ij E!b>E -=ew~{$l5Yq&mZTKڕG-1?{NgK3-ؽ)VSTї0s`````9nB"}z#!rHn;jH.F=$Q eݡ6My|ack=_jQ(n;aHo:7ΝsM9?3؟&[b ?Jw`z©SG9_-YH 0 d5"&KVKI)Xzױ\­ȆR"iY;Qgk3cEb(Si7oB[*-Ӡ2ՁWL\=|@T9θw^|si=A'X]`_7,7Ǧkէ1eaRYrq\6%_ޝyM}("3sVly=)JQ64Tn?J{UnP!ػYe bkZ1 6 Æ} mZLԨî9Oae-i9SAy<~K%=}oiG}S];@bE}v,ޚv҃#oLOX& U (v:̰}f;|'"Y,MܮQwm+VФoޢE+?3@`VOwg͠i]v)0›/\qzt2˼2 0eKQm'O&N,GO4͈Ur@rd(.zkWoNӂo_lqL}frJސfLJ/o7Ԛ^'C f]%c0+ _ q<Ԛ!|nv5ڦLrl?noWC`BGh$,T2116&J` IDATQCllL?&Z}?64 ufP7: 4sy =4ɚ/bCZlyu. K|jω%*h8A@u)g%EHο?ON{43Z Yrd>P < "İ(DlBۧH!ç̺ 92YŌKXRd1ʸ6-*Hϖ!=G 6KG2Ts[.kQ?==;i ק-P.C6ߌ|zrze1;kfyŊ:ilS\۪|G#KN3% B{w /T슟~r}B2mwoߪpiFi} ;|r~}i_: DՄ3ˎ9PN׷mxUӺvp^y͔X4O^ѿZǽZ GCo\>yh۷o7.*I< 7ݑ ML쿮o߱Uo{A9Xk!`mU @ۂ|m:3LrI^FZaum$$CVo]L^Tr]tW @puzcj.s('fzd-)q޾M01/So{ixןEtJ]6!%~EA1hWڂ@,3)2=FFm/Uxu6-xdu֭[9'x+on!eoQ>F斟x.>߻ m{[e.[lټIxk>jrWwnޠ.$w@+\)z"mF7ԫUu54ѫԷM.Z2\. МpL,9ϴ~z֨UE~;mdƔ}/q6ήY:KFogg|>rfSEQTzzJRTD%`H_1%[;]N=C_)BJq >5He2d=h|eJA8)"GZ"}h]hV\/#[W# nqsxgQm2B=Bz ؗ=T863ZY\N ,y[e"|G&G,,0W  v}|NVf%7!L|qu7l sWo̳Y<'CboJ,ӆ} m\6+[FEeOaC\<7k5|K0ڵsRgKW~G8VWd35o) }8 zkv,D@ C`_6sō˯:mLc ׁS_=c}u˃ 7@ !_q_+p (cGbTmV̲p0S2:yvMGdd9n!ˣuV*:lԬ3eB+BoedJ}yQt6oLZ&6n@ Bb㾐WK2ױ":]᥈;98ٔj.vOʘ;Bѵe>}} nB^SV© }B+pƀ|͔\wM]_l%AQBED8i{Q[먈=g[ѺnZϭ ʐ" fr=wy.(B&#\@(&l2W)*\{}/to^S]_)yFo<2z1ǖ3֮ ބƥEK; q71jF_˧G( 'ٸy@ B6n^Qak4xoZ,KO9 ~~JB!>-~ֿ,^*JeW׎!*yT&"U,n"=)Ή[tA5|=/ڰnݺ{M2˃@ @ f }%F}u [\@P{sT6#ǗԼzj-I=f5z4NS/#la!{iM{.}z|S3VZH @ Bigp]54723gM7O=+M^.{{AlAZ{lL+ axXOIaKMc ! ~y\i6xC*]S>וWi(;oؼysߎϙ-VзJ @ @(P~'D31* *L@#NtV-Q0qjTTR<&$ĹkinN9e#}r}kB@ @ J*@()SF( B@y<˥(LZVm=D<=@ _"qHˤ(@Y!gOy>GVBɷxn;B@ @ J3(_o '`QQsn4%_٩JF( H6@x.EF+sϑ[h2,"v$%}8A)[* Ȓ{>jb-@ @ y8R䴠r(`oxh\rLr|zT%hqM@ @ ZH,B:>>fO^@ @ R pY' /nL2E_&@ S@ @  @ C@ @ >y {;G/STZWPMb/,d\$ZUBC'^\HJ! |:kRcS2ɑSթ*u(rj3%0ِ|?|Hupp{5яejJ,5>9%%E*VQ% V]-e$&d 1Z1R1J%Z 򽇍"R!!U*9ν/(T4I'P9׸7SA# ׆OՓ܉{/d&'<8JY3bɇ@d͒I2٤'žw=Խg՟cd=6/Mn{}=L}˦0-wlՏLd95~.9v_wU.xhJB M{y'RZ{2H`S{lo4[V 冢} mV]xUj'4Q1cR!?zM&pvv_ Pĉ=5 9}E(r ߝ?_W_"05l޼yF%3w  *)Je@ Cю :w6.$xe (4qi2D3)2~ RGnoή&5u.SROrI[6|wyd5E }TP<dN+`P'oSʐS:䘧a+-𾆑Az,+F$V̞\p(%Ug-^zPP$V'3kXB\5XI N<pg~ N&5)[4|𵏦(1)P=]Wۅqg$P#Όأ+siށ#ьJ]Q?G D]G^s\*֕=ipgҧB(O\?M̬]:~ߊ`,SH<.G? E1b>% Y>et|u}ct=l3EyW/]m܁}X+RB8|j1W8ƭw[o*Dt :wFŅoEV(@ۚVl8o+:O,(c.ϡnG{1<™qt9?AFB@:t}]:t"lwަޭc)_$Kčt|;kX;7CK9Kh賈tB|=z}]}RF}1ڬ#n_q`Jppiөw#SbcSݜj7ӑU aIݎ%8; ɻb9G*ɫϒU|{ ʵN:9W)+nM|k.&/}Jl̲w޷Jv QJ_?y:=rj͚6uwI!=ʵNeϱO xeGQՓ|D_F䧟]48WK|.4[}d09o@A\ֽzfkW$??w M^ݺP*iI Lyq© Ob Xuj njU*N_>ĥ_B)ts*u#'̔|^~/Su޸NFyEI).K\8mUWT\J~:<\JձGS'QNRbdo>*Q=:)$Ć'W櫯=:$8VX}ϋ^ @qL$dP#oS2ːS>_5m?G~f[QjVX|kUw@N4ܸ[zFlѼz.hzvb9kJƐy΀&t&dT.sTIϟ R+wIOVѼA:/ QƾQ7]}MvzWx]bFqqOoyJOvgڇ8΅W)5켥.֑o޶;' qO:@o%4^ݳ{~s.PoSz0S>v {.m(XحQjlCVȼKZ8>u7%?ke܉ݙypx&C*FaHٿd9\8әq!Cdu߷o_gߴ?yw1hlx:}9AcyqՊUZzg~G}_!}w!k ֣Ft֭]6[޹Ɲ2ue<V_GNdn1w b~ș} w^%5kPoS0S)v|cY( tů ~ \R]'uVSPӍ!/_9:l5AmtC88xA@]o-Aٓz7-zɡdۍ =W#$ٞn:FMf\)oz/'fѼJqZf,$eK]{x+_weQcy6 xlSӵA ZwuEOm!otKk{O IDATЫgz}Z/- ^%' ][ k;9z& -H<ӌ{kWjx~F!SO(x:x膃Χzjۨa,^ /|S#>tP;u՜g>$rs`[HQb!\,ple);:Wcfis/3ulŢkgmbV(o1׵?ͧQѣ]tOЫo2dU_wnNzl,4'a*9R?ZxwgӘ2S9 2ڭ=TGL@CcNN;r}|/j(ٗ/~x$eN#CKZjjv@tάl}.):qkβan o]1{љ"oE2P6}(ԭ}PlËDʴ{] -}Uzoٍ=YŖ|șMЦfΫ.'eqөC;c7 96 Td6E76Ϥ2-koŢpHP%oSJSVyg5ETxp@;>cEG0f$t_A P19jYټ^CXxkY7_v|)ƝK[ .4ǡ7|ЕXa]/ѩgO.0<,߈Y.Ư/hq qՊUy p@Bef]tڃ 맳߮ЎEۿ*3dkVtl/l7텳6u~b[~ On]opAGHߎk[o QoLЏD/5V=-Z{IAn6!OOMݻ@dE ,pk'hW-g ]ÞShJV@C?/,969999969uм N@K{˖+yw Hq(yߺ{˺93(u?7]>Je4J'cKպV=Ƅӝ',igΏԑnJtUw= -#'ҝ']]̫yK5Ds ?9QeE*--]H hsR%?߻77xd.4|ԯ3%XY.MEt,WKӀ^=F4iLϞgˆʺ= S `>% Y7&&^\Ռ鳳3} cfV`^G{K|n|\0vѸ 8p`A+Ihܪ,fydO>üF#h`rVt$Rڗ=hL.;{[uZ3dSVU+ @@*l|`XOO̰\JTf9MlM>+F R)\dr*hTYwqZ =-8،69;SƯ׎k/dY:~BL-pRqΘˆ6-Q[ڋB!#RZSxΐzmɠ(]7[L x,nVquwd{K!#ցvv(o8&@SrhE>Oo5GFld| MTZt3bR Zִ I ]:?JFQx2p<7ϋ)l4nß:xФuhQq S+Z66[h2&_y7T|9~L >1>ϊ 0~{9vyd%DG?6=p#y5%3u;I C߉hJyjb#9qG>3_P5S>tCA;\̌ #b/" -"2̎)Tps>:9z{u֜=s3).pB C(Ԡ ^5 ^URJ>ջ P> p~*9z%!hʨssmtF~}1Q/+CwF0psbUhQ{YzeP%o|v4D/}2.gBuI+t4.VXߧ9*W dh&tG O^J9d\aX_?N|?"ݨXƗwP1oSSVHy7ԡI1T*u:EEQyĪQ= u`<+2 -`񒳝%GħÑݒ}vޚ)ys/'>>ͅU+U+0]2Th.@jW89P>4V($UimvZxCLX8ĘJz0I3MGSca/f(4^kR>A=a% i\x+=R]W|t^Ϫ]U|D7W\xOَE+( {}67;cӼu /2R$ZsQ*-<+dTR]te;4 Yɏn Ac*k?}J4,Ow/x2 \SXP5oSSV(yh 1|*."E눎fxObgiI>D13V(Zt8u}MA\REz8|Lޤ GbB!\pfy 470' |:j%rQ)KU̙4!^_wiXA&#j6'|⤹~:4v-RW2Nsʓ%rs Z XKx2˶ks6jdQ! vS[/x:Bw%=p{P5sP&vm_zexă¤|L <0HM-]'L ĵ&+qHc.3_BZfi`vIkKen"+lTIh̓_QrC2-s'Egv1EXΗ'dZn,?/Suc]\#Vpʆ} mZl<Ԩˮ=O˦}_y+rLބ2T :,p§c̍f4QiqKG*?RwFRy 9?w̦oec|ͭ4W3kp}eFcK;J4w5oܿ~F+xi2s}yp~{פd~.L:~ɕFH\KyEW}GS(Uc</Tmi5qdTINTm~A*>i9)iϤZܜayaY?7wìC_/@ɛ7P%oUCQ,ؾO,COUy(*{2|+fX"ČR>@~UژSǎݕTX*=6{vr|]K t)vwD=nI,Lq}Q~Kkeo}aerU@POI¾~kٴ]_Zdqg} mY,2yvh~S Q{:i(b/fqn55AYNoH+ m w(IMe4y(@5pr,1 z^⪱7TQ38gƝc czO;^%wv; F;ŧТEmM% 7I/j'iwwnN- _0gNЛavCIEV1ev̕S& S>Zz4*OkH Hw|ww„cj?!l?]Lۊ² *JmVӨLKVt`窜YžV5Wk(CQS5ʜ7h;m!sɢiMt4fdꞳSV݌CT3 GqvWpWhf_x\Dk797eMaj* = O=pZ[ S|4hРA{Xe]'R̀-4$8gmnis @Ώ-L*|#  NNt?E<h}IqቴBگh,lɎn|lҠ l~%\.EQEeffjZ]|!OgfO9Ur1]TR{ Qbo]8'V_4]Y稒^\5yE-V\ذJy, p #۷)eX"3EH;%U+[rUd#ǗHLNvNL}Y$a&q8+-pPPϜyyt V-.$l<@ B}!7)cb ||mNS{tܞ0]#@(Tlq[^B L;t(r@@EfC[2پO,)@jW)YB @#\ 6n@ Bb㾐Wd%*@ @ >yH@ @ 'YB (>euQHe.zJ^~lBۧDaF P!38@ @ |@ @ O @ C@ @ >yH@ @ ' p@ @!YSmI"M @ d@ @  p@ @!@ @ +,K'2{zxxj' HO-4? eHJ]W2@ oN1lz2yylٰąM`F3.;$A|dwW7>CvIKND|1س0[a|.% }3H2O 鴟gt_uj{ VPӝzh ``T5S 7G2OR$75ʼn[h2dݻ-g^{/"b&aqmZ~Tޓo;2[#8Y~uW1GVF^ oQKU(`}9*6hU_w!1~~uJc9}m*]z~=Wz822"!"LھjZy: {}+bV<Աp^>eR&R%mzBPAk/O?y5nB_7]n0T H*- SsR(HAA)vuITFƣY; @Tƻ"eu7N nUkG$9c}j8"B$78mnmmPN(Dn(FlBۧD!˧}TC'g~fSGy(b/f gצKE goN $Si~'9gW}wx^r]M\2pP'rlgנ;9%FUiѳw{o.>}Q;}uzNYH ]- yvK^%72X@\uՊ J> B@c+/M;̕>9ӟ'{L {I&^ڑ5:tz6 ky2-d!v-><{)ԇI7RExþtI?RN.ƃ>X,[H BOUTȔSqZzh[>R/l;g\0(p{vٕ=ҷyǤ~@<_9o+%=}G N<)-=g^ĘQt.J9}-q`--.֑o޶;' => 2QjlC>~tmU*w97 @뭻9窱$w&]𱬊;4?o.'a)7vvslg 3O]4hsnV}ƥ}Ҏ6%~[ŹJ1?|uM)q1 pw b >ew]OQnjnӬP~nD>WquXZ,WO"sp}://t*gCGkoɶe(K};Q붌l:6t@4N#9Cڎ bDʴ&ٓ =Oׄm4aЪf RE(f)|C\brl J>ou3=ͮqe9y;~gfn4Yj%%y6BO_ *YbG%NW6iXhVy{_lg󏣓&9)k?Mlz-Ok;͙8phqdTn7c'x-Y%c:* \4Ǩ/E'ĹE}xlWV)s~is`^aCë"ppocN '$'\N94%%QUथr >J=ψe>Kʐʒ|z Fin~]wbHs^Ykmkzg\OS; ݲb`>% tgM)M8rw(Rf4-H86 Zʘ]1ufQө;OMIef-ɼ\vY{"oMs}XH*-r֘%>4&w[m%Z)8^3  <ȗɌ2Ȣ՟Υk"uUZSThsӿ  . !:]gijhjZ1x"q,gQIvCaؾO (Ck{lw3J3~]89B^<+2qvmK*14-O>II tDo"e2vXQB7Pb/:Vޱ#?u5pMCIŠ\e oHh4/v/qJ$A F b I f=PiSjW]aكw[37JN}ƆFSEH[PWF @gg&$ I#TYl>Ę sao 6Qt Sj r49Aւ(l}2CLp Z޼BGh>ݼʈG@ .lӾ2g\'Vml쿬kS2OQ.OĿo)-}Jv ):6_"LP:4(M (CٴiQyDȠbyUdRkN}26˳YykBp,qJ$A FJLi(JK`bBHGu=Z|mxXn+2bt 2*~ͽ3Q{WņݱjV՛3QezkDmW'^:"Cl[W@ oߍ )7Wt @iϋYNDTyc`**[wٛWȨλ8uC72͂{r4>݃G vަV+k6&#O;`Ө&dV?g* Vsk(۷)eh)+[|EM;z!a1ꌳiBT3HC|Lɾf)Z~'V݈{cSWTANQ!J>k %hjtY so_5S`pB%20-k&c#y{40JP@z,e*wPt|DfRjjoGW)ϚneĽy#&_{} RGE*3뻆*t&e;&LCLZt^.~ϥMT2ۻ6Q'i7F|E> 8hh<ϝ*j6Qg<$]BF~u!?ػ]~ ȫc_ 6 X} mZl<Ԩˮ=Oʌ[nry}C N,Pm&9/yQiQ0.֜E[n@zஞ$BM!@\5+=Pqy~/.i4 Llg sϼ(Ǘӟۯ-vqoPh bs[ɽeq;uyB6T:k˔;E_-9lqJf *i&Nj(wsL8maw]= 6鯒1[ϯ c5C{; j]Vqžw/3fCW\J87BIXkRR\lC\\܇?&Z4|mh@iaudi$+ ͯoF2UɊcL.jKy#INTm~A*Yч_e0ߦDJ?ο-9R߯&)`?> mՐף_zNZ Ya^ U%Դާsnc u9PܛR yw m7/;Jvrmߗkvpr&i T_Vv|m,XH 0^e|noCٝO!2"鄝=V |vs.k|?wϋ<//<͵;@e4_Uz,=v;JI'*$3lvmY!$ZrۮG R>W̮yg/פeW"S0 )y٘W4|2uW\%—@L+S@덿 8egx{X]@J[__+S~ -k럙I7)r'8I[ݪ5 `w/X5Ŋ[h2d{o{<(~c?.V<ڦaI-iVaP,m$kEzگeŶ켵VHQLGo ܾĶÔ$⪱7TAfp%W{MtbR2ک2 YuaKU&TN:=Km/JƝW5sf:cs*lzx-8oorM01/Sz1GFMgzN/;aLoV 3эn3Em7]\gMaV~ȏ}ѳ[ru6.]޾htƍ7nܤ9' WhBޒ}^l :W pVd*膃 &g@` nO8ăD7N.9<@|).Q)$3old@hioA8{J-Ea$c/܉6ݚ֯,sTI.ov OKy Gd s˚%Z"]CQؾOi.R"x-+E֢[!Z;2ew[UwZ[j{:V;Ծ]Zu=eʐ"q!qa#&O{_&8!Xi=8!J;%'/!BHrXɋWTh !B!BJ< B!BHGB!B)hB!B!%MpB!B!ģR Eq|3ks篑ddp{+8!B!R!B!BJ< B!BHG ÊVMU+"B! B!B B!B!x4A!B!A+c|jJ G4sUШYZKSd&GފNVPU\dT|xyy8#zȯ?2K4x֤6<(SSRRTÿR +WIu}多Nh4*%Ӧ5$I E1r:?kCCu)yY+i p豝,]qeaZaJ$R[w0#F2>Ud^*& *h4/JDSF@62͛U#fJ0u4%9niҠտaӿNx%#%د4]|;::Zw&Ne+r)<]٢ *L<>ex#PSF@KTqml9Q24%Msр+C, ௙#\iLn-=7n٪q%$o|7{M?ZuOKigk!S^q*bp<Ƶdfڢٍ`:Y?Ռ7dX{(^?x]nȪ iy =3r$U*ΫO_>R ρЁWp7@%{`[[]|{,xtsFʛшk̔O=}ydu\-=%CYu*PyTh3iyg=b=оeN6={N={ @\I^[} :zo6ӥ>zBuѴ^C+.^!)X+Trw+ݴ/EL`yk-{S+&jLT2kLXXwfԩ$l|ޮ%Y[c|5 px]V"Atë(V_Bm(_wJh83vdjx'gi(O@ Am& J^CʎfĊ,9 ы+њ1ƿ#'Zd&G^<1hv &X֔V=t! IaN3NÇ;1;wkngNxQlneuρB ՊMpn.nd^|Sf/ܜNsq.FnZ겣[ү0svUo?pA-OC s/..\3MIzӶMƏԺ+}1F@U1ʛ_X~72[hAv3q3M4VFzᄈ%20pY5'zCƒO9 '9RZ =޶,Le&ۏwKk7jFZQV|s"o+ /sDaڶۏ?֪1Ļ5yQʱ !M{Ԗ[cuyG?['Y< Hc^u @xǢ~ӥV1D/f'?n~zhkdxftU66}G}0rp~2gFy.'_:>͡,2,@۵>MfpKT_5c2:=G*On)DkR"~:4Yhձ=d o=Egf/f:,ogC _z/PC5g@ 41acsZZ/T8CIL=% ǹ [e;Ұb) {1wٲe NQE@Ӵ?2M.!4d:TqV]_u 4Ea%ܕϒm&0sS"@4bի+cHo'=&e}0&FYficг4$6}F۬Is㿹!S#́,؄3o5*ru҄iLS{諩ܭ1/RˑoO&I8n{pł`4Z{ިDŽs&lk~P(+u0wM@7M?hk-XT}9WF!_`wWpgAu;E2}c* Rr3Y;8d1J#f(Drwc%p`7}7{>QF=qbvdt^wdv= [uޙޕ?%1q ?SVࢺlQg\DҰo}fx<; Z\)J\߷pFKUߘӜ6YZƏx@d'P&u/ kQ̓ޚ?N[>SQ{6+£{\h%D^jblRfSuTIvL&Ig+ct߂H=6RZ?nWgd2:/sF+HtKJIZ< g!̸hDU%Դr5Zqξޡk_s-9͟`TWZLƵU'_jU:tdG~܋>,,:uRSc0fyƮ]msHavPxB5gAou6Ѹ4e0zP6"T)c4pTzm%.=ѣGq)ғMC5ΚO m]`̅bA=?ooxNbA:ڴ4 #1UW!HKbF\y/)CLJndQ:tn}Ia}_jb{b/}kng},W땪y0QQWOu9UȺ(5eňgr:?iCޣJ g"6 F4r夦C9#kۧe!\y7.}52ctxE,gYi_iEN>a=I =,DuՉܼ(m|^X% S~x[wJJLw0ٓbeFMegr:?WhCG|zM UMnZ9#ۧe>  fƛ"Er ~ɲe2;⑑)Ưæc/d俦OkW'oc%Gᮿ{s1 fK3Y J 4J(YZQ%JŋZXQ}>p7걠{ U7=Tlgѽ庉7$g?{鏁goD<+TnfY.tQO͋ݘb* tA2^ѕ^7aUG4H/sl_I  zdX]ĻxN@Ink֧ZX+JClެKj^ek|O K+?,n?J[?uzr7ը1y>/vK\ meTwۙz5;gK8Zγ^aFFy8>,7X=Y26{3HqݧƥÛ߳EkReTyKqPT BJ4Vih5X2n]7s+cm2xO:{V^wѤ?$~)GivrqOF{;s§nu[yp:b>W1iL Ȼ|Ekףқ1\Y?t!Z?8!S0EF,^Z 4;?i Abtq2rn:VNa|Ĭ2::]*'fջu VXMXѵ،+5uK\ eRw4i@K,h3r<u 7VאwxT(T+U)*4}87e6˝طFM>*TLfwDVI[)(۹M] JJ(8[*L-U/y5)w5sDb{yѣQxјD\>S7r)WL-YT[scJuqWȂΦ1aȎLC]E]v!P+Z3Ʀ{`r&2ۿ$a$'h7pٍTq-w %R1=x6ڻ{. ͏CZZHsdݷnBjݴBQ<8 K!ϣ̡ 4FŘ*=a7#YPi"'*S_xԴ}r.-o/>wأ{r{0-jZ#іIwox{os) 9PRNU$ʐ/ OIpnm6@`^jAq}gqpnf-ٓKFKl8 +9UZU'?nƇ2_$LKh;M{y|!P%3:*ozw}yai0FSkmt52q {;Ԙ}`omlc@oi5Ɩ̼kqOa {Ó6MQZ+|{Dgx\6e\~ ]m̖W4^~bae–AZU`qMnC[q⊛hPȤI 4Pa{ e<7|zU9ܮeO&ìD+7I9$o[1}b!k&۽3Q/s6ySG|/6@\ٗ6m'Ɓ/G,YV4r6ҁ$6/A"'m*h{'2̮wz,xKd{;j1y)T_R ĵ.0ຠ_I94iY㫾]fW^fvkye?*Ժ/{,솸Q鋸K/;9wיsGUR1]SY-{GV74mۛ;;ǯi:m혪ovV<̻<UӘNc\W`iSjJXCS 6\K}UF5j_meE/e+v,fEU'mV7?^~ ,ɑH,(^`1HCn}|e_ ˅/)"7Crj-l1njިj a~޹U\xp˪ZS yeQZ,fVd7vц؈y 0{WSt쉆(9 !QgM&xgj'ΣOo9eH$=–dOP>y1yjʶۜQyEkZ϶o.vLs?r,a2sfj DlGʲ^b:Rƿx 4 !_ƽdV㇙:5.E=vHNN0D$uҌ F"ﳿ̝)F/-'MF4.E"a5j_ErV/hrFјj2]Lf6<'/S&Pj^RwZ 3 aμ^nzF)M"AOWԌP*psyz}z*MP-[]*O,y1D2BYbFv;%t~.نu0ĺ"x-Qm]vRzmPZ{B &HRi2eRT*H$bX$ Bat:RZ wԕߟgJwk=ݽ/4W6Qb)muz,x4T&zڽ [V// I.l 0r|U!%{;1z`]REnGQ윿ί4a{Tږ_F֣?QV+)hBI'O^~C˃ B!P9y,+ztBH3Yx4%+ۡjA]PkNn 7sB!*'xEAK)%P4Q# A@hAWls篑dd+\#T{pB) zDIpB!*'xEB!B!ģ B!B!xDRbEk%t~.ن.Y)$h" B!BHGB!B)hB!B!%MpB!B!ģ B!B!x4A!B!K!-.UУ@!BB!B!.&8!B!R!B!BJ<!Lr?[^A26Ӥ6fҔ8Z"C"/'@Fd J T҈Oc|ȏ?컖-mynMV&%%'G*Q S&yjvj:[qujoR]R[Jy'=@s:?kCCu=bcwhv(Ev*H,,P XWFؔ XW.q,|(RNR*y OMpiI7=:^uYifʹ?f%ZጳY/S?|V4]{gMH.fZU_{MEOśײ"HE\j) wܠ;i{jSu3oxZڨO~Z˽ m_{"%t~.׆ƈYCdد8>-Y|[>?*>s]#>=?oҊfX|3ʗ _ϪU|~{MqPF@KTqmOB}ȊHmuA]kV~, O}~5 7/u;vq%ym-q,جe'.2tbJ2%.;x5jQǫ<EK\ yec,;U*ίOWT&uQ!ZUeM =lՠ GV\@fPP$6|/(T#+8!.ž}VrC0&/0ABf(\2ؐ{#nX({;IsJHu[fv];?ps:?)D*w&KM|aa!b 0Y? [ɋ\ېQƻ|Fzx ͼY5H IDAT`xEI3w'bS 2v&8}2%FݯEP3Medz|y2Q 3B|LXBz@`Zb݅QRTjzERy[cd\"Bm'õ܎ yJQ7(l_Bm(_w>#fOf ߊEs?6zmŭ+"]:W؜R铖 [ME'Z蒞; ~Ҙإ\͋ˡǎ1N׿OI}ΣN̶sc%)=<[x@@ZjŎ&8q}ݳCwaVDžN|KpsH53g_[{~@]?]k+'`RYkXtsA'yQV~>3MIx4{g}w ra,6ElYhɋLPjҤK, nc_=bšDMinփa ;xQ,?wb1'Y S p_w2472Oq>QSΖ\w ٹBW͡,tW?/MO-mRueܵϮ [5ɍOo9u` hnw`v}a㗹{ SfmΎ<-n!G}4j9!Iϖ,^4#o?LZ@Bm}K+LO E?S|kC?rIX@@ޟ~dݺu_-;P󻏷?ՙ]B>iLbb4Z#煂ڵTy7Y+>PO&0 t>\M ^_ASFݢԩSL?~kz c7[1{;2YO[\zjgZ`7dj=lio>y԰(ka-2>~,8U0 +med @ʕ\Unv#-}'v<4laf5 j6+{֭f3i8iAk,dPL M(:+w' m*;7 (4_BW0Q[1yfό-|4ǟ4[egU3?⥵jh~t;=>r J2e]UPn\]Ac-ر_-n[[N?c -UoWv_~yQ'n p_쭼|A H=}F@?*&iGT0ȍD+ b4Q.la#9YCDž+__߰UϚ2{V q2PQvu64iӱOJ톮QUSOuHk:Ug7,<~3\Y&_5OM4$Wl_U/{FW4KveiRjb; _&-n5 SQ{6+£{\h%D5^jblRfa[GlPpXю $1`x3CWC,|2[ MFYdQvKJIZ< NJ"^Be5!f:mX@ӟᮞf_Pw: hw+0vC״t }Ʈ4m tH]I޼#)0]`{gLon} bl_Sx\ø*H=6Bd< Jʘ$'܏|LH@j])a")[ŨuhSÐɑTn}s%u iiF !bVB&,!Č6Yy?.2^RB,44(o`T iXߗXrwb!jYN2:&c>t|+;:#>=@r:?hCFLO|5療b,hF3Eπ9#k٧aB|ۀC^_δ`bcyȄ^^V&k\qgnsv5QC,lrV+M=?yDaF!صyɣLmlm]\gĽ~Z{_)T+hחw 5n+,xDD+Ѕ r]Bj\.h|!?2lLе'O>F?t%Q *wf?!B GWhruzYh<;4"$..6ǫ @sJԙW'r֣=-0Lq4[i[Ml6}SƬ|:NbIK\ Swf&^}ip@p&8x2du{(:/4D>hҋe}fddBPϗ*c5rG;tڏ¡߿,LV䊝3D^YRV!DGAȺӺª9hͤH[vnR7<D5W*~4>D,5SR~ 1K+<" ƔZ%QVwq,G{{כcׁaUa df&Xlk}vf}h#T/+U d' y.}+z-=a^vwm+zWӭ^&4nFPϵQVHuw륋H$VhM mdLxû%n=Y$Uf푶΋dtzW&UE0PV!B#ś\>{E1/}1zPk2$cK\ aO3n,kǣ^ |-{\rb {SLS ` z_mU`Wfqcp OL甽4}~?3煈j'O̊$i5ăKu,_BaQ5~4 hܐ?OX2͋U%_ϋE6Gw+lZ, h+y2^zMsof [e)[!S_&yV]oT> +cLlW l8 !,uvVß'%3rew&tiQ CEfG]v!tY4<5 Hw]2`fl j B\ﳰk\v̞>"~ Xͼ^j͋'~9ۯ4|ySG90 Oί۱bs/w)VdX*_;z?^VH k!&ydO5|pyj4j*9\+e]v6Ih%#g_<;sƬ>bA=NaʘuWT굪6gfw=ճ'zUgMş˽(^_RRlL'66ev؆"y~ӆS}+'y0@?@T*6Y}p76y.+}K!HNnP({7%R1=xjIhoyf)έ{r߬CUE1Ļ(6_Bm(sFLC=2r,%Un,r2HUّ?~Q2}+όbl{jצ6ߧį}8J mo/F*^: [yR(Tsx "IG< G$yٸL`jRѕ:z14 }^?sΐ?&NAhq!m_~䷾SB-l/jI8Qŷk.-Y_iΪcszCPUȧ)\LtYV&n4_i1+[>n^<$jc@o52d ӯpzf),a+Qf[j[+v -M|bC嚷|t92Bjlg)U)^xkhϋem [iyV67( oʼn+n·C#&V@hСbxnr>3E,ػDKuz@M$m^0~b!{g(6_Bm(]w>#ufɬrKߧ; 3VD5ugFΈF=&ǃ@B]rඩ1#a~6\c lokGӋ(T_R ĵxzp]Oni&O5Vqh}gmqk6/j̈́dd]K,솤i[z[5!s[CdLcoPm+*ljr]`b; +|W9V%#`-fYv2__9̧1+}!͸piUİ\o"~X2mӦM6mewg'^jk!@y}^-[ڔ} {V yyrd*#!{@l`+ ||"vPʾHl\eUsa4njިj a~޹~ŋ .ox^Yeb`E1lTP<3+)nq쉆(F_BzmYw>#&Lm94Lfߊ򙅙L*!`'({ecjʵ%yw&atKBb,a2 iloG PJe#=!ʈ}ijuUJl K|lߗYa$H&+OOILHbQ; ]ϝ)F/JM0ud~UB5GO5hLBT*,/E};;Xٱ֋k/^(`Tzg5FUH&]Qѧ+UjF(ez<>=Y&T(mbzs ʋ陝6,u/YʨzTњ1:h; Պ8T+TaaaD*)SF*JRD"E"P(daLNh !{GN]y\Sy噠Q^r_|8@Zjղy4t=E[ѡO5mҽ?ک0sHyFxHGd%xx[AmK ( _BW۰ֽ*^dm+QDk[QV+)hB6qC6{Y;ñɋG!R<r9!,WI;[#?Ղj=~wi B!P9y,+rtBqjШۋ'/!BHrXɋtB)R.NɋG!R<rZB!B!&8!B!R!B!BJ< B!BHGB!B))*BQE $_r9Ek%t~.ن.Y)$h" B!B!x4A!B!&8!B!R=8!8;]E]B!b]A!B!&8!B!R!B!BJ<!M8ժ)G.fWAfMnj-KV[ѩJ?3Ym|$_7^zyyɂϚԆ4ej\rJJ yWY~[ڽ'n|X, N@(H]2hoH$W^EFŧX_?R0`%i˙:2C"/-zyauż}CG\~Lh4*K$|SǏ<~|2h1/SULUB@yرlHFBK/<Z4Ak(O޼9:= ¦-ߺ45]}+ܸYr HYsy,30FBܿ@X [ƳeÖL4Nf0[zf7č[j\ [>mQJqףHN~~7dՄ^,<幇!VB <*`c u & SydhOrT V"HFc؝l\_Zt!pW]bS7@03ءc˄w>gB"N~*275*B <|<+^=rc`%nDŽw " SyxgK=| IDATí^hcϟJF{.h2Bƚ/T̛ԋe/]h %VG!G.j^M-a\׶ɵֹxI@VuL ag:u.&A{{wiU؃ ӟ׶~ۧ9/I7jճO2@v9CU[ }nbf-[W9_TnѪL @Mq)J˪VIFLyzXg`euxngM*xC (^3Wt1_O^&d:ژ'/k޲u'e˗5VF7飧nXM h^d+X9֭0gO>jUtD  rtO١T;s-7 ӽ3f=w|8zLDI8ӭ){C\7VgS-= 1*=?||3ɳzDA{)ڕfcrct'z+{gFӻb h9z; Q+U^e^f)3#/8sS`4Iw<ֹll;ϝ 4%L&=>4{ ) \Y?t쏟9sNsr{>Ņ^mzRJ'/? v{6ti KeajNX2,ӟ6lk7Rk5{㳄3l?"-87 }׌3M{Oy+^n:l5K7px˥ gx8x8t-,K)gC3oI@xэG= MfB*5/o[E]a"@?p8Q>̼8'?n~z؜=t( ~+_1 K zU_}cէPo#/q!`11rBfp%U|WeW3&ADnER;, i`~> Zȋ>U+^]ڡ'@+~~)L^[yȌ$ێz Os{&;xp[`hTپרWl K>̜zM[wkU9eޭif0S* =qYbxu?2hMi'3/^׉:>{|JmKƙ5!ŧ?`m51.Ty@qZP@`ym8N.-/+/]TgRJ4QsI!Bf;ұqOI9]k>^(Z1 Ix94#sp1khKl2Jn.cJ^.<@\KvٴD<6O߹xɐq|N^)*CS)!5->K֊,q&mek-IUJWk*ؗ557/ԛcgwC2YBBSzݲ}˲ %q,iNi>[tFX`eOa nSMW*P18TVIlƿrըfL,1i6uhpni/~ݼ!LO\{4.npq#ҚnڲyVnP1ٛH,ܳ^Ѣ`&CP&M }zI>0|&PqǞ>0kRfM^Vde ix٣Qڻ6j ה閨¢*SޔsʹQuOEVP˶y7{<;3mcZ|J4\W/_AVm,ʑn˓%$  2w))4]F,.IXvD%Q䜨6ʇɆ8Z(5'mI*3bB$ e n[@;k&M82kǡCluGv Շ FtfUڹUX}l $Ҵa=K qp%P |{vEJ)$y5M"x_|O>43:UH֜Yu,vwWG;iFd44Yb33Aq@h淴I*g{LVgV"Qq8fLqekL RՆcِuŊ)h^&@~Aǿ Ω^ a͞qT S<Вo敇w^FZ'1Kn8ucf~3ml\b=L4}?uh<~"Z%ް,ߖ3,VC6IKy헯2&/mL (Yx_.M˖u*8KHӔYfUA qp%d9iU*(ڍpfa^;Yjۑbr%GjYkҺw^_hg{p"_IQ߻g|>e˵w^-&=ѻZYsAq(?r9?4uc)9>H4Z<՟,,vJWIQ86ho8*U.}/x^ߓi-l*[{n4O5ɡbka溘 -qF4^teZ8d&ޛ<45]8IEl d~H+ؖвEzFqi_8nڻY䜐 Pg75Ym2W<n!7  |8ߡ|e%3OVLIQhM=ƉUA 9E@($;qtn5S_)O.hF `wAhڱN4׬ R=m\]p+$Q((-ķ&tm@g_ rq/υV2}3'| UXD#'L-3|a&4ryx-L?|0{뿈Qѵq|VN 53i׆n@ը)G9{rXko /W ),v-g|_MK(u77E0Y4/~' zx`1L U:kC?u,= m_= !{6}'Rqe3aq PIaW$̬~:hA^8*YyLHR<3Tw* LK(󉈡ex% ?7Ƥ~c}id umť @oEa߁rP\y'9&i=[Kʏ4KL|=ëMi^c9B\QKVg?fι"YJM 9߬3Y@\M5-MnN #&ֹ P>sI-x6zC[yܚʞec3ţC[_[7< wn \9>>&:!&&&9cp "{&Enev̸ZO.= OZk^<;3}NfWkJ+JS=zsw.=JohO\R4kT6cp}g&v/A+=<t} J RP} |QX0(ͪ?6֊#X4L2~v|;wz<͜3'N7᪡2(Qxs}Ņ#7͚Ta''}15A* i~@#zaKNK@ -3+Q%*BG \02Ta6:^WPр[J`+寧: jӽ~~,~srPJ !M2 Lh5ozv֬`Ow@VU=ݜfxyr@h 14^8~Ol.-| ##w5vs},TKc@+͔pU?-06nevo{5^reVhiC3;zn|5yX.=GE.nMh'p(hiiGeF<67?Na(X2ww&6Ո suv^Ò{լ݆W\O3KYlv¡wK_cakk_ ϭ- }l}C0`Y >}a+lqylf4*v@換ٰ4yh9V?nU CUhH3۾tA<v+6U|3c_ttQE?PTeV_() 4 ')Cx% 2@(&9Ht=}6 씮_ZVY"wlO38eå z:4w_ì%Sb+;b]rY u@vhn>aRjZ 1"2" |glOiھvK:\g28c=LP5F;u &`܈Kmc¨ܼgN.QLY֫~7j{W`2 BSq3fUty_9| @T݁Nn+JeVrEQh4 YB J3'wqaF~'{<*A>[N.&HX9[]?`98bdDY7ZwmPj1y}cZ3Gۋ'vt_h.Q f*cč">5lڸV <Ճ'J8%@ ݲK(l0[$8,|@ X%#͍KJ…b/ s6tX@ J:VnXxك@ X#ĻK=qŚB"-nWp/m [Zxِs)Za @ rK+xB8-E& GR*%,XJZXr;YO|2S+ԮS "ك@ J"Z@6G `+d\,Q!@ P!@ @ ED@(VeuB\wn9~ beV] A @ C@ @ jaD,~lzrUљx/޳ەmBy" ȏg%4_0o.bh@ZH3?L| S,ɚ{B)+/g>sCUG>9Ebk2ˇW~}}S78ph-^ڹy6uNŃ^X!MPBGdS~2 ;7*ҬXm2ލ:g.]fbs+{ 8X a+jb)v)H*[ز}x~*Q,5U0C}!V?v=jrwOJtd)Č@~RW.E@fp[~El ݤO|}uM쿣C>k5MKM³@>{ 3rP)eY\Ǎ@>7}QMc~U=$&`vbV<䞞|_}գ_qmaMº/aѹ oEqԘyU2^U2)_~h Tޓ06!%eƯ[ں+f\q;\߲ŗ_B3αɀr-_\ @ qpşb.:4N&4_G~Ϯ=\귮g8ܐ0{a-&E\һ$ qN=vmу__rߺ^GLT9}wx; Hߌ[Q@[+7%S-Z0_YP}|pƥdJc'^gm׌eM³Sݾg=xFnY3z<(Yӗ ްzХע L)JU#2ncY zXࠛ!)Hl+Uo߼uꒄgo=u8 j!=W/_| wa/J=Z6v@ bǥ՜^\3Ig<@ޤ_~+fܖrʡ~&e/d*[i&ȴx!>1Yܵ/qtZMEꄏC"/&RP}{:2JM}^6NSL Jy[Usw!*$6խ_sQĥxGZU<ѳ$LygNVno|A mGwwwų=p @LU6 f^XXEGmM–%W]qL}`qP"MzP ֧6TC&l z(:ArL~IxocrvW٢yJi\ 8WmmprEV<~ٲIL<p>ޫx}t C@B}- ߼|h=zdȁǮc ݘw #@p|N0IG),mobeQ#ֆGK9:er_7%,*hB0@g%ZaKd @Wf%GŚj!GZӤЬ$U?idxw4?IV"ll6#i¯OxsWUyY}!`gf๭vqB52 ņVA  {lI홿6"vVj4njuҝۿaW*4M_I~*{I%E1oWӜa4k,۸qe:~o5@NZQ^W7Y}a< ޓ,c !OߝyW՜!VW C)x`iƁUwφǓrk\ԯE#=K@M?13 _W.f6l޾W-l)kBhl{L򙀔{Y\_leȸ@e*t?yװ!]'9 (CziYb$q ΃\'^< ܜn6\uEe贃oO1ۂ3VyX(^TijXF|Tu{旱KG`IkT{  mfnܥu/.2(}ݍDޕv܌NNFt,˨7&{hPW1ūk;}3g_9tӧYNf#[;lGɽ*% [KXHyO,+j}`-͗">K֊0k>E1; s_%h7[OPv7j->t3A |`Nm\x ǫݑXRދ1N%{Unէ#y&t04pMstplQ''(!T'9h\)uEL{yT<> gɨ[Fagu36PիDƕ[;ZǶC R9% x#S>4OȗRGmB|R~Xc3BX~?Xhb}y?9%!`co.R*\k:NP7n0YugAJVKXD\dNuacO8y);д$O4>\O^ꏉDC?.z>W%Uf̼/k^8e; Z;b&}Tg˾Y`|H+BƄYծAf%Vx Tj_Z,1JA D-c0i3Rk4XQoWFq L9ʾ\yهB$IŕBR6.5{Iʢ7aPT..PιMeP?9vFPh)d1[Nkg$S_s8e䜖/71fԭ'-={U6!ϊO˂RSo٩weI92{s`Y-ʟQ^ؚǟ?ضӭgIğml\ktR3.yf4B!6sXV~ү;gۯeBaBtf˗.M2&V7l"! 2~o/sXɪ $Y\|q7m1?>,*cqN=FZQs@~RM!@)ZeVY*QPxaLCTT^Ɨԙ;|/YS&t]~7O1^"j듔 Q/^^{M)Zx[MCB8w!s*.tuT][1K~Pr(/JӢlT/ų9^ʶ׾ _SxodIJvU+rًg40#4߷4<$<-QOl6U5[+_先HZթS2 j;7"lǸ&F+DnFU-{3*W_͜/#tNs,UH[3?$EwqYn}?*Q&_U b9!3NtF /cK]y~y,D4:V] h.z_'hQ(MCƉUA 9E@($9w6?$5\&2)8߮b!YcPdyp8CK~ P7Brv]&/=rdA|_0ԄlύQe/np^;|.m啓3(5/) y)6L)|:42 :4sѼ:>Q7 GYt]3ō񹔲Ir&F :poۋQQP &i4ݾ9*Hnx9ʿDV3UUjv'8TõK7%r('n:`?V8wWyїȦwERqN(nbfVJ#8$an)7 E*2.E|wil8E?CIu 5޳bh%^Ɂ88ӻ{)L̴•Vl^xg@(>f0_땍/RLS\4}Sr]Ժ:NzFti7U}Pߵ+Bߔ`c O?)) oyb# #E!߬ȯNyajV&(T*%qJG*n3oG J QuO;e:1Q'W@  ^#\1 X %}fMWxOPVu":jesAYw: .Ur?Hb.l{nh^]Ϟ~>i2֒#Bc) ZG糧Sw04X[=nZ2ލ^svDlR`,y!b9$b*7'je0xD sЉCڦ'h&!i6mAU閾;/c Z8A.SQLth}Vada e՗>!Z`z^Ey=@  R?Q-/³HNTZ}) *J &w3yר}O4F-}ĎNl,d*' qŐM)oi (F'Es]jmšr%%vfհM9nUGBwʭVxpJVhdO"s]QҦJ 2 IDAT+يl96%ÁˑJ,Yo+Z(R?Q`fڎZ=rE_B4q\(JKKh4,Q!J 6\9\-u,jL4VG `+d\,Q!@ P!@ @ E @ P!@ @ E @ P! LV"yB"0?He.0;YO|2S+.&W @ @ E @ P!@ @ E@ M!GfZ@  @ P @ @ E @ P!{p:6h횓/)2yXSG&-p]Y@g}V'$$&&*Ke } S}༩YfER,_CJT5.HR*gk[.K`v| ! P8VVAҒgKXPTh@X؂ }YG# $Y[ -3Q9?بۦY#E~\HYC 7yGR)CC&vK˸ɻnNПW 3K+8٥h8r%~Oߑ'fL=]$ȒbjMqp;vW߉Jɽ><0|N37VpD-T_"'Yh`Nn^^2b;^^N* Y,;rxrz>n{Df,~_/ˀ0kWи?{=~t1 KT -0/ S,ɚ۾e [h9zI&^{I*4X8 FgnBi!nW }벉-/b ?׋|od *g ĦPMcN=-ōFM &;D-\_"ܾ{EXɱYejBgk&z6#8rOȜ`9.&b?IbaH~_$ lb~ KEA  Ի ŃלHpD;dStEbNsi4_U~SժL?X1t~0{ZA~`>J}J4 (\ EӏxpʙEݪM t˶wy۽]xڷ#6 ;t,HRB%,Z<8PK.h_}8zכ1>rx98$>XNx~2Τ@ĉZsL0,?0IJW$ޝ7\6}#_ V188O\y#{x)) ^h(~xyy{ʹvW)w>\{4\LȕGt5*B[.=;yUܔWnkSɥsg=S.jWM\tU'C{Ԕ1O?K+Ui @B/+xΕG2J#WRa._;hM6]kN֮aOxp:(yM=EQ>~/z)ND<\ bVу(RDb#*Y]ZlΛ^wբ؟Zg:ex75l Uҕ/f^xXEؠw%B+'e3pGT& %= DM*!reƵX|.խ9/s&\#=ӧ^Zգi&nB%O[n`reVJkMf3vR`÷z+Go1bh@(?Z2;w6aU,ޞN=zޗA÷2L[iŹߛw)0{GߠJ4 @{cݙ{ԯ&-:tG'}@ΝGoo^򛣋W^0@@76wSjbбA-ψcl?1j)w7 9xP; Y| m JZ{U{(5„zjhm kq<8>n8`.Jl;v`7҅gk? Zaf+{cF0Νwu^)E36~.!#^4,/+OsVff_S{T3qv=gpU끍ok}K[]S,n̻h%4^=PՈJ egJ|6Yݹdq(hߕw]Q 5]R҇[R*Ese ˄̕@^~GEdgf 1rBfp%U|WeW3&0bQ|}t].k3h#/zxrtʟGxUvkkN8t#83k8L8$2ij#{m!3jol;b.n] *pN>iPDmႡ DPg^\ &Tjǟ/0s5mݭU.5!M5ا?`m5ahTqpoԎSAoJ˷8=1~1N!=-=>%%̚뿭J8۷_77gԺ+;J Vee}kijXFLS_f\J}sȤt?pP9ᙡW݌ EwCȻ`?Ӯ2ftO 9y E[|k#Stl;sSz `.KԎ7%~ Y~d-Η">K]AaY)cMVC(0T"/ok Zpֈ N!0XӲ}˲ %ݑ h O1:d͵<ӸݰbpCftUE}|O?E?ZZ ?1vqm3mvnL܋S݂)MAyB "@[%'Sk@ťU1nLJ[ _{Ӷyq6Y 8%6=h3@K+"mJ:1=Ɣ3^2SVt,kDquWeS-\iڲF_7`YbSp[yNO*l1n[+٦Z*3]oÙb]{[:u7GG4G,V.urJJPpAR'_Ĥ=OzeFxX7cJ^':4´2h-۱-OՁuˮ&'\lgSLU2&ӹwRCO2nTyLN6kcYxQg@+ڦA͚n/dyc[2 4t-(hמn܆-f3S u2:3A0JK>bllF- hF $7Ϛ$e&-aj^fCC2!B͇ )uƣd@`l'j jNR->3'<~p뢃!bx)b$jH8E2=sՔYr B!bc}0bq^866u=g1/T|ayg*׳xp:ʼɠ(ϙ?Š(gGkDOwQk./QȞ03>Ye%P◩}bӳ CD$OtRxܕM *_SA` ovy!ܽ3VR[b4*0BL;_*'PAG2G]>zlrP>0[>51F[璹(U =kJ;zQ( xM߼ߌ3{UF]F֟BJVZLHqOBnr6Twt!͢ോX xՏuռ(p| WԩXI[J9^^mx<;=io >ᱡSBjEU "_ n!@O)uYΓSRogġ 9E@(yqtjOwثReO T Og7P\V爜1h䦟Cxvj`p[)hB7|3]+-"E$~h#= ?Pj Jeo氽W|?lnO>ymrƇ`4#wLb9 T&imClS[K ^)AU%G9w,@05E+Lr2nmz3*J}UzdGW>r@ɮϙ5F( [/dP+)=MW5ttu9" ? ۶å qpN(nbe•WW@~:Y!pSlKr]tܶNK*ၡB6̑F xfrOD -p #}y'!C׌Z7|;-]\vW@Z|P_sO/76Rfp\kwk~*sK7X|{mզ4G:h:(WVsʬ}7ϓReDG{BWa"GIqڏԢ~}uUބO*JyvII , i^veԈAFja);UPs/Kǖt5$ZhryAG޾q۲J],Ŀ|+6ِen s}4=GcS(ucoz8G%T9KRg7 ? 6}G ReםI-J e}QI@=u'l]d[yO `El0NuGQ#(ow>eX͋װxGT$plí*?< J yRP)m'y *XXpCshUn+~+#O=zĨKV#ӿX`[/hIYyвB 2TE |Y_!@MCMu'U9&^2ǒ{Y}ÞfO9}=# R.J Ah:(JAЗ#FxL֬fp{ܡ[r(II\ })^2q9DžX2ϞTu-bcRw5Ǯz|nF|bg_oL;NS)$>륃~qz&f'EmuTL:M|'CΔyT;(hNLmbgμw|4uΈy2ʷ0Fo^zI{_n(=Ģ$"O0 j5W7bo\; wC%= kx tK 7,h>Ex_vn dVOkԠ2zAhOؼi`tUx\kp^N(hmglx3aԝL[o@}YFql[h\T-Z_bG@bYq}{αh\Rw"{0 p8NlV7-?Wa&r3~k䅨CLJO9Cݚs~^-fN#;f=PZaC -}PP' GF&3}|; `nRfԄ{.r7HNNSQBP C^ܖ8 S橓_9^h{ >63WO(Bf\w Y4 @/3XR8h_ɍ.a)a?6ojsaJ$Iz~':aٚ5ˈr{X.Rw??/D䨓~:|+5E*J>4oJs)d p+_=j8Rݞ}=rz*82% RIzC$2_ħ(TY&ЇCT(S5/R_bE4½ e 0v8, 6XpRiYHĥ3nX*nnR$k.CBItc_+9+LfÔHrKC66 ヽ .:2{x$:/'#O^;O[f%3`NmŁҞ܌%Vb P(.7[z gg~ZFmM;Yo=fFVj^8ŋ*ivŊ6x^ )/5}98,!X.C#}dqSDl+~9@n/o/ M˻A -dÈ |\”Hr&ɚ~=Vhߡ7۞ӢRSxZ! 5/;5onFl[gϾm/)p7%Dq2h1.hlݠ4 . c{x3ӊkny©)aJ܆s.?'~5*MOA--P=Dbc$u{R/-1 38AG{߂}ׇMiP W?,9lacfg(le&uEUNryef_ia4wm;qP(eۧ&\mR?2rFb;I(`DckT*[nƓ߆|h=&3vNG}'3pgcs>τR7K_k5/:_bE4{õ \~}1F˱KE`fp^Ow1jx,Aaŏ G)>kw:R>[2nHs+Q&Mj-&E}T Gv .vSʧvZ(ঽ z{= :WM!m^k\4io(zeдi޾rܿQ4J*\fpppu_M򃰣/>RuZ)I*ivs&<5d=LZJ*,ce-O^ۏ)+=),mş-ʏJKN١zu=Bd  [7iQ$ky3L" jB`\瞕KgrZw⒞$K@`(|aΉ_{PmS{N hлW&.ٷ.^yrݮAZ}y~Bz߮6?-k k¥is6 vкV4;?k$22|ϮvӞ6Wzxd?Ĝ:i9q^ @3cNc/ H˄CχfŸrNOD|EO>nE* RsPt->o;xy8o0$H?p̪7PMٲIWgnւ >`)z,t 坿N3 0vQI_C5o:+Ҵ9)E3yxٚȖ6WN;)3|3x:al3xvjW?UܠF6в*=8an-\0Lk}=5<7ULvjL) 8{[lF=NSw L>Ftc)YN-\0g@0FaP?1svz̤G|JUkڶ~n*p>z7(e7o^4ḑALnIcQ%׾񴑲}7/mFU{?xe߮~CQYn5ty-}]O&ĉ&L=zGZ0't&p􂵆UYڵŹ^6j`YNW%4k㮽6|]/@#-+eq`;]ԛaX6gSJdϲ/ll|-(r.z7WulcL ֩6 +p&˺T h~DžL0,iu* ƅ|qGh?g\{`LZ(4&tbw\Ӿ>wFS!؛/{WaC5/d_AQB"GnKa( p@~*Λ81Dp/~*7I%jlY ׬YBAe]u ǐ-ol3xd:};k!k>a!VT C)|gA ցۡy`$G?mn8bQE ۰}'|]:ܒ=z _iXShߡy7~;T~ /R&wGW?=fœ~m15' ZP? v K}\ *lСs$4[[6ݜ ۤuVe Xk.i^B*׻=d_/G;iܰ&RJiKļ@pFYuOQK'%3*qb-|YO7.Tqq\M=aܜ"dYeύwXOi'ճWyب^T [j@{|Z7J['ۀ13>>didL^ܧy+,woUtWS2[zyܥfi@Ϫ %LצtvQ)b=t)yg6cGh4ng0L${D<ܑ~{vא74_aUXl慎kX pVچtq66&\onN+qAaM+]Db js&ڜ;{q};v&XA-KPfkW5bt̽+ajinYuWUJpX yoeUr nA.8oXQ䍄o>Yt+kdo_('@K!I j4{qiovк36?:7_o׌#>+j̒&ބo{ħ(YLW }Цi@# yczLM lP&oߜ!?zrTR4jV˶:YC[no ʋp?br):Y˨F9 WB5"F>60{M qˉo^vַBgwx8 glS(P/-?|Д:Y(!hvo~a:?vMPl+zD"Z7ʓn އ ?(AMo$#*uƝk <%qp%fJJɗiG+SrdK&gũGձ"B~= !Zũ%zؤuo dgj꺏4xez<Œ'9zhG{Cebu"hVt!Y,EQN]L6 4:2^\Fy@r1VU ayҀ6 Yj>I󷄟T](!2DOIŇqSI; WY>iO#9rԪA~\vPSK=bβg%nF {BEK\掆kXOmXpcE88l{S.}!pRBt" ի# XjO!{ˆάjU'oC<Jim{ZA |Zc0U0њJ{R/`g׈W/O.Bhgt*hkM8/p]!(eOM"*GNѷpz-kjɦ#.IO2{ !M`ټ$&P@Fײk9| (R4[1Uuu11TIi>olnwR]jEٺ,{Q W+[ "zJ]=U =~}ȷxRSs>s}E*jDݺE:k6k/NA>0XS&1 6WB5tp*?J/Z E(̞Xr]o؂ ~k 弛."RMz6iƵ_z @L˻6C0:{ͬ2 bh}P!"S `f@TT^֗ufa6Hm9隼.cz5eJr\nLϿ %n\~ıs*ײ4joyl:. h cqݒL7iV>zhk\Tџ,rDž2*?7QI\yyXճZFQ%ka.4 g*}C [MUWji"q8UϚl2͇&[lDޙ>2Ѻݓ'ռp| G(iE^mx<; :T!b1/3'ƜPax n¹.-uF_)_(yPJq8q(AANQ!J>kʍoK| [ǻJUޏdF'Sȓ^ ڵ|ȁZ&yzM s  idpW!KSHXUo .к0ƌ\ύ׬;JWyh8ō0GK,3C>dzLvo{F;=xar젅sm"B- x0kEfCNetMRi'FeO)ThpV2j$ X[4p)w-֦G9Y)cJCAMt+0mVsV ${=f}NXZ͋qVpim.?F58vXp%}mwp"-~ymiWdQ%PSb Lg;вJ J>^/#nŧ6'2v _Z3OJۻ?<Ѽ/@|fUbeN8#[8j ][ n֝)Ы~0c{.ECһ>En[F=;2=ߦmzd!5e2SϱZ8yDC{Yog JsN IDATѬI+U\sW_ WY̎VnԐEݲ澢l\0vwDT;7agW(Y?NJKbɩN%Cf9OLJ{IB٩}Nzhx~mү+RߠRsP$9gp9Y(7yɬmݖ/2o|&0I,?H|I WG|]Ŝl^=NthH7w{z=:| L?_s5s^wF,ȓQ1SNÀtJ(IB @ZϮr[yKW,Y/iћ |7o~jkeSFo-r+lpaKچs.?oN|s=Oq.MWxUmÝ1w"{0XQqCىi *kp*7m# K]woHm*}{ϔ7kh ')CzdPэH=t-&pIf_6kU7WgBaЅ{urE}޻!q抆1 W3g++@; jj54Ѥ> d_DH +i/piOKg%fg2I8il_~m0EdW *lƶC g4.Qvȍ#+?0Ji!W{Q^]iሦիW~gm^lbُpR>bsQhBy]y=:@l[v슒k^sU <fBRmAe8dB&eX,ZVm^VK3ׂs[BK{U*B2R%8 ~fM#8veZJZ+R7?q1S5/R_C#BHx4HD[)4qyL -WDDDBHTT)H$B!MSEQTffFh4d k📻/N.~G w')=rn%_GK$Ⱥ՛+rP5@M5pH,&NϫwU*{i:ڿ{5Zk=cwBZ( s:*w 8v_(c'8U"5$82l)ڻ-LtsT'_aC -W\ .ptXBԉte@ ܒqp @pHRm~ Q ˙\hj{7vڑ%G `d\BA %R a)ia@ X-W/=8D$wH\=@  8%d @ @ bqp@ @(@ @ bqp@ @(@ @ b9E@(QdV@BsOkwX"EpH"KBI @ P!@ @  @ P!{pY+(j2Q֬U @ d@ @ qp@ @(@ @ bك@(0o.HJA;Y >`̅)KxfrT`ի{ bRTPIB~=v]8qM8ƦRSSRRŧl2ny&P*ד*7U JEzC(\y:Uݽ|8d׺cw慍kX(f_V\! cǎò kT*gB!TwSX^NL>SZ$C J6}ޙAi6nu6Sxۧ {s[Uwռq| EV 6sQf$'z|o+2rn#oA3 CU0E_>//_ߍ_,hU HJ+NpZϸ9`(zNojƗ /Ȉ!~}Τp}c;Ўɏi>v+0 qPEPn\jy뫋?NoEq$4"#jT*[nmSi>Q[w qoF PwXl慌kX(fw8O񁟯e?hy9v8uF/nYa4=Db[,Ht܅VeWq'Yu3ɩק4i\pbL*چg݇ZɵX\: 1JA |ˊ<ӰS'/?LJO5Y׺܇GW7n0ݻtз~ F!ٿenӷ7QS'/?LS>uު橓nD;wIu*0 GA_E*JЋaO_{TIk9S{OՏVtJOUl\<}|o.2M-i>KܥO$<=pwruw Yk[u5Oľ+ի^ z )ƒˉ-⵩XN䷧#bϦdPP={8eGqEDp*C7+6u\ět_Aݘs1)I xtpedH]Btx٣H5u5\s)c}2tXE6םcm5/|_bD4í ^~~ѠܥtfqjN83DŽkBq̓nb]Z|_a$$y;=,(\x~}hicY5l)md/66Mg`$e+V jؠIw͜`rmVJ4Ε6ɥsaw^drnvFC+?#0 0Z0 qp%7}ƕCFm>1y4qNv2(uKh+,>]?&~P者 ~`zվ:oNS7zf̂Flyиqʁ= yK.'Tб3D%17F \pa/@͵Bg?~ ?r9|J EF*vUjoNa`0d;xq8E9ߤ9̎ۇFL٘h4Jxޘ wf|FhO־Xc& jh]ͅuƕCGз&5h,yݦsȖݍmN 3OAo [O\xrJXN]65eX\?9VaǸa Q0Z 8z:3xU `NT &p,] :=SwUռHp| E[ m8Rג:8 .?B-Q^qx{d{XF)ޗ21K< :9#dXs/mu=-,mex/S mֻ!nǮ]n?W;9c| i'>ԚnnrP\i~߆7Y֥Z@;.gaI Si~_P@6.bEG=ѓ|^ x>2eQwռp| E] m-GAr^F(W8 V8oO;$&`Z+jJ66E?LdƬdB5eju>eŋT' oZ w7Y}aCqt15?ݹj_.Y9Q9hպQ^uًUܿ8"6u\=7mUm~"Ou3n;jč> ϞY'ZpCn_|BZTFەB9ܯ~ T\gܛ?`A"f-%3X޿gnTF5'CӨ]//lm9zBڟ%dt6Y!;%wAƉŻu!>Z 啟ӽF[77gȊڋ+L?x~ݱk6r̅{RPխvw [jTykVpymtS?IQMF}w҈ jbRf+_UZ/dj}VUg-ar\<(oS% vQ)rÊH*¿4`jHo#{<Y;wءռq| G(i@mxAGac[)ͽuKAKvZsP։P"Kp+b jmZ̞P9d9BY\Gݬu2 &y-Jxb]ɽ+Jy zqQ\Tt98I >ynAK -[ -Gċ8((}PII[]+.uzMr޶&)֛alD + 4 ȓTp1({qiνphgBdߢ=v>"/iצa{6=Qn`@Z'=+#@i^9ͯlx>~qN ~>E}t 1<@y+lhivh51{K-b1ٟ9vq[O &5 h?A*j:bhJ,J0)C&OJ惂Ajf1̞UËJ0Nl)]O\_O3at֯l~߆._oW-=\[9xݙ NuV0t\8EN(ivsnH~ˏo.|4<:hZPv˯=:3m֨5HAYd̄Zܡ% Pͫ6TlhvH&d]0$~x-DOIŇq曨0w̰CƯLjO\T ϲyk7U%aYauS`xv;M@:aS,JZ~ דc^6i 4Ug6 5tyۓAQ3G,ĎFAPޭ6Ip' ڷ4メ;è5W(dOљUW۬V(>Ybhd]&aX7V%E$OtRxRJ65L~MQ3=ߟř&`brYM6.u6\8B"^}LF@c(SgOӯ ]1I01l5A&׃]}TC]oEQKOΨEs%FTI/쩢fW6qV//5ܥVsYp@#[{Żeڱ+o:ܮGSׇb|d#mJe8 bU, kx 052l~׶RXݱ.ռhq| )i6ʏegΘ񎦳NP5n [dCo=dvn 6gŷš'F _$Cf@"YsڝvlPTtRMPVU1;#]bCQZh4 &i*>̌iU*pvkO'C IDAT3sfa~Yb/vɅ[ jFKdFcVW%'ǿ|ʅ_ zwֵM-)#k&A/[(M[Πѣ{ +STXI[J9Ƭ Cσ A?G&vLzdd'[M*) hcBmRஞE2/16^S77^U a |<-0UW,?Px X3ήH>4-c%y:>P )?yv"zs{ޘd}cCዪh$3u x(ubP'mf]Vd8E8q(U {` jZ&y{{PI2jԨA}=*Ux<+(a<)C<}F(.B[sD&1hrA'3=&۵VeX" n>n>o<8} +Ȋ UܸpkuԺZgFU[w:ZV^ e I? r==]@fVe؎?dp%Chgy:Oz5- 0(y6?L>2gZ7tK~AHrXR4|;յFk4[mE#'Qm/:2˕WWwh'98LڈII^/mHZm헃*dƤD@+_h$^i/OaF,Ad2\.\+ {N[ɽX9b&٦Kb@D1޹}zͺkn3sFt.!ں.͎f}ѦwmϸYVm~2G{/]ps5'&k&M=8`Ъ/c2­zJ,9]^~:ie20-JME|9`chEnV@wn,tL~OzsiͿb$NnS:99!>'!!!=kq ؙ"> |$3pe<.;&^(e4_c[c udב?=aT9Fh\zwR3AVO^y25#}J{y ҕ.!'αIz47ktDd[Z3<_Gd00)rAOΊ~qA+]Upsh&$.8i/G]59QܑZNw٫|xzF rt7:սwA˵Y3}NY@/~I~pru)[M !^IeҥKtԴ4Vju:ZVTIII:Ϗ aJ@G y8O/V"i{QNg>"^ee:n>ʧݜ)G.ypgPNjT^W#VuuǬ!]] Ͻ:|h.`s\̭ snaŠզV8cal =WM\4._i= $97RuGFHpljc#?׵#9sf_Hݿx;|8JIguVp=M'ɶWv%kLgW睻v#pBq䍖!@,OH ]JYouDࣅaI*h~5#rW6J{&Yvf_Q_PaZW8TR$I>~XV/66 :ۻZj^^^:Nx1u~O/ NXkdRqW6(lYM^ ~шf 3Lλg8fj7[ͥˑbSv.Smݻ0j'XT"&ƿ3.Y= yIgwL|;/8 ?& u'}$ټoܼ|X)͑`m'>BI*K9b!!!!!u?1u?u /qp0]lN8ڕ M7EVTux@vL޺Ay+fw _T )r)mԗ3ӺRmIHnb999W@#l'ĖXI8IcL~6WoΉ6cWu_>>T*N#IR&$IT*%IR$1 -* ξe'([.^tN:ٸ癑@S77/d~'2bRy o@ud*;h w]7RdL~ry},IkK]O*%R[#\\r$G8+{غzkN7ř5:Q\|+butpXB H|||ZmFF=`(\| )N|zMxϸ|m0~CsrdxH$ruu%|qXSN@@@ H͍6_xZ^>oHm݊Ñ\<\(#b.^aa!d2.Vd"2BW_yt^KT%,:|' %PLZ J38$ IxxxzL38z! JRxtQȅb>).6l8cżWE0pSll߾ݻ(ֈD"(pqGq-*%q[]yt^KT%,:|' %PLZ@|(^g={gFxxR4#GXn`nQ(\r| 0*J*J$$e2`(E- \\\W^LE@@@@@@@@@$!иqC@@@@8Sׯ_oܸOQSlز%d`@@@@@@@@="99YӥRe4YeYVV$I\.wvvvqqo(&^F[nk׮(J[TJ0nnn(ITA2UZw:S-JRo tAuVtRL=/ h*MBU.)JR*EL魇mf[[wѐT4zc#/9`n{?> 5 AhS BqE-@IA(a74o'oR_ź$٣51gVVj˗1EIӱVVY4г0ATd~cj5d)5gkƧ'TT^vkHcsnwQ/MXi̮SSȀ}݊3n\=Z~!2SGHgRJΎ_@OuZVߜ"a(6'} -ܻ{XY݂JOQT:oF>ۅCv!VINEf~T){x\gIs6 >7ڣk@9ڏhk+;ws2MEӷ[Eڵ/=-{k #Oء8=@3*?$wi)6n/7[ֲՙvM;m_~b:Cun4|*ћ'!?FkuMUk)ΐ ࠹?i`܏ȵf~ϸ|\L3~ѩxPyyu˞+Dmџ9'S? 37 {kBm큰zJN]7e޳lUQu%gezwn^QYWG?(υ6_#S%DCwaU,KˁJ{-D7EWS:*}uA.n#-^\?h6_Mq /ʒ釟@"V嗶[O~iffnJ.Q|+kLZv_'xo"y]" }>BXNSNP(Sw33[U?*Xp>h%Z_T*S5GP`7$ӡtn&Yo+ ؇Ot.=`cö7OP(|}9n Bqy]y>R27߹ FRn /'FtU!27K">CG+LSz+ EuiDb~:BXt4/*bRZ1!1>-әBw|4߂aWǛcΏ!NW3ğQ*J6YezJ{rbڌXAT:-yl )רu5*Ȝ6p^5딓*J%ۦ)NLiB-ꖫ {N"hR)wW)dgv @?w1NRRL#v8;#]TSgnLvW7vX*9K@_) EgQe|y,  V!ҕJU\A%]CXDW9ڇ=Ƅ= =>n՗p϶۱?+=hӎ2xg=\e`g᎕ϟ=]{.&KmOgPW`v Ksc?ZԢDƷޠHvvIRuɽB J>>ϜI.>#9Ž =Ù#Z8VpV%5ɇ_Wi '3i!(S'Hd|__;7݂V8E o=:lذ#F3fԨQwmevtp)O .vhqdυ:4}7hwܚ6"}vIa_�tdСn(BC c@HhZ6, ]ᅦѨc XOK_?;PqljWh3w!,*0YJxq_14$l|+ |T2JјHeƤ/7,;L CsU,9QGdskRXM}?ͦfe4,؜iD۫noNi::=ԭ pd!cwouN'5'Nr K=h" (=s-@E{MhKfD:>rw`X"ii`r;xC_c(rǔŚ{ØS}Q9WLPn>K1XMXyB)lqIDAT Ӏ]W$s5$c̩#c)* I0iF1$ɛrJmMdR`kӽ&#;yUsg \lDu[{'gl,[Ӊ{$lGFTv`$9lHVt˥! R,i!Ö,J':no-ŏj~&G0hq$2H~mYYb%#N"lN4L.Iq7aSl+*;AEbş"1 eFHNDԙ >5+$4-0j}21=NVj*`4ob59i@|+FɺwBd`]{K1FgDҭWjNV^վ~  z.R:"ZHL]%0ye2v3^*$Ndi Z EoԈAgP~\|2ziwթYCתzCd7jBK}-CDeM]6|3 ]x~ \F !;ǯ|LsǑdI͏Gy͔ k6+Wk*- *ٽ{Qj4W\IxtrS{#].9Eψ!k9w zMh, ~ĉsjY@AwYCOhתMCؤo=KָzviS_WG PM j,i? Z!sW/=SVnٸyg=(qIëWbsoز.}xǣUdvqEy5&{8>䯆wrrٳ?ٳ3q_bmĀڸj`;.;_tԓLK?ŷŬqѰJ.ӿZL'n^CBE"ot!@&X6{> 2д \*P5*5_ i@ZVͺR`xۈۃMOqU4MXYw6yh`;ߤΗ4lc5e X`4pp+HIZsΕ`$t haaP ԫAzwv1r:4I\ETVA8]Jڨ oKR9I2klewҾ $( & n,ddV:Y/Uo5^jo-nI~^kbf L>y>%E}1.z= |wY`0۱n0J оϡI]AJ8jR.>!T!f^ҳ6;%6$4 l_ڄBī탤y[u FohژXI 2p" So1H=7O:!LC収\761ԏʈ\S,1'8x'|Ffwѩwe y>s>E큑ǜb,Z{ʽGX0(+^ 季@*slp"_^%Yc Vyc8V9--T$?}k|=Jg9Z8Y6US;3-7;^(zW [:掃XרMGV" =ObO9yBopjMebB:Ca}gw^=3ٚ_:P~:~|_N+[>wM,z66?3"v}6DK]?']A^xp+0˫SKg-ڀ71.L7p[r/Ƈ,˲F={AAAiaX Ԑ&DFwI8$sF|S{)H}q>ӰԽ?y =trtKg~R? hPƍur(22|eγXi tF7"JTj9{= 0@lRe <|E\z ׿rڥ QF]9lVJrnm-6lP1[t\vj EQz}֙vӱq(ߨ+e?4Ze|:MCڴT wjҳ 72D gr=?豑 .?@OW4ô#&RPb%<@ޟvca&h^ NDqj3xiOS $sהLC5Zl7kDzU\*Ų1֝(@aȇP k|<Ƽ}A1 i.0]*bJeN J6K[%콧b31?éaaĉ#qbʕ+8\ ^(u'y;і={8Un,4uvքp%c̤ pekg܂!೬>JkÚ͗ OzTƬOVx3P^9!sc4zu;q뤎]aAZ|IsKMvQ$yR'qOwkGlM(#+7|ɕpm=;'779/F%[ :hZZ!դWn3asdtC eKPm%kgf 4v<,&wW{Y }ꦥJ׽UG9eS)H8"N2u3ʯ<^6zX$  Ie!%@JTv,_|邩]*x-*sɺz]do#>]`~p?^XLu߂sQmw,X4LG] Ӟ넍T猴N-͘U=VFWGێ1 @ r|$^!uT''N0a1ق/Ov c͐ZѫFN=d%oxLJЫW/X. ȆV3uP;rs3I#]n=q_k5Nٯ|sr$Ϳx|I'28 (ϬU>>-quUɞN7kzXP7G*[w?~}N!dK?[ Zň格M*{<0p ԚsqO8zMWߗi]{@ةsj[w'iRv\z)e )cy ,'3~ړyg |&O?39SΝg@oh2.$Tv%nl}ոvյl`K?l⑹gzdvo[VV?5Ü>׶('*+'=Y|90Pw1 iܲGM(:O.Ġ+nI *m+ʛl=ft/zs{G4\_1*gFa]w)Uy6"vo*'"yNJ`tǃӪ;"ι Rٽ)_*2Srrv'4+g.RKDȸ=DRiCޒK&жO#MnŁ<χUpXM2;-ڶmaZo?E}h(c?aGix ж]edmnmj^C{u.J5ٹ :h7Դm^Ź̈́GQe\#$O]'?xNMe׸}/W^)j-yY넾3]g׀Lj'"vD>g{J\o/; À/"00 eL`iiHW/!2 }yV?PkpuuejZ4[hXRЗnsԨS$%V jN,44MF? FŅTTۧ&GJJ 4 5)4HMARDST TBq'n0m㸔Jz‹$Fpeg3D4g$.?tR<9 w:M2%uGpqjQ,ץ,k)7n~\Č5UTP~ HՂfڥ,EZYo嚎j8Ly4rmӮV n\~}h$*zFv*nMm-Sa_ӒR(S%iQ7,RUq3!:\l Yi=f^v>Pi]XZI<ݜ@ԫf:3TFHdrOAO.-lHU1d@EeJ~"ݝD6XRFʈkFU@{* +:~bv/ZQYA[3/ҵb`V:Nx )jr}4;qY>$*;5<9yXu>k}:5 0F$ZnvֺGcpks{M[a@izW䃭ʹP?XLPsdVo?>;tbcN7#>1")&M?Wj΋']N^lJr+Q5СESɕ@؍ 'egݞrRk,F1_P9e*7`P [55Ч(rSk~N~;W"Ɍ'Oǥ$>VjoJ!ƎG K|! 29[#&YGT;hh lnO/tm-õn.ki06yHbY}hf 4G#\DZ2 8K1qAAb==~9Qqf24p= ǽ^ 0Tjedq2U 3J:2WZӎS?+aY4"P(S]YX"`0( `mfes}\?=xPiݓzY8{:]Dr2 4Mg.BYfd,I'KQ*4[#Jq+=7΄8@-˲6`bH~Ѧ8[VXUi( JKAY٭+9ŲR 4JecVֺpJԶIͩˬ!ˎ{N: >A &Sf1M* d(YSOK]" `PYެiHN1Xp(BdX女.B=e|rqn˖6_9[1}\dͧ#ÆTUZCCպ@Tkm5qfs=yaՏneՅkR@> &tZ>R+l^2hE/EʎUvDp;߻wF%4z-1[9JV&~td Ɠh[\} M?t|w*L4ˍOoQ܁㘲(gu?  Zt<~1,`yLceXa?˱쓂RJ-EVkQ~`8>m+HFލ~yzHŽU}ZZ1xhngk/F.TN8ip$||e,ZM"-8!Y+^$Z/WC1>d&:::--M4ML&#IR"$#(-MMF IENDB`bcfg2-1.3.3/doc/reports/ClientDetail.png000066400000000000000000006230341223671746500200400ustar00rootroot00000000000000PNG  IHDR/:iCCPICC ProfileXYPTK;8䜑$$D **H2`TT$E}ommnO_>}:"#C$cmt)N.@?< y/H++sߦaImboO b#ach0Ѩ_:v6z0fGVS}`;`} ;X'RXG",,bX? /67=_ 9#᡻wֆ3^f [ ց8m`pݖﺐUdGZ&i~19eft0n1<PWLa>$9Y#G M`ƠX`af-0āh$9x%pY a׈DGO_$E n/?/Klw1{2i?USIfVfr0JEi4QbAqIJBep/?>k/HTs3GK;ߎOϷC7vǁ""c):HIPdd3YѢͯbyl.>GG&z)\b-9N> ae<@,:X; <}0$}`?8A58 y ``L9:AX"C/$Cr @ yBP8@P!T@%!4Bߡ5AD0!i BaC#Q$D&"QE4#݈q H$RCZ"]hd*2YE !!!Ǒȟ( $a?5F٣|PQTTՀjC&P -4- -VCt}}}=^`0,*Fcqc1y*Lf3bٱX % –aOSUNj\p \ )nNCQI9JSGs :OkR| ~ H  kB!PJHO $2ňzD7b1XO"". jӺ6Ҿ]#IљҥUе=BOC/DCAD_B <$L#yRINA!!C? #Qрї14c/$I }u;)& Ʉ))e]adfcamn^!a͑q<':gg+k. W2iG\?y#˸{yXxyyyny5yxyo~0St(RJeϘ/1:?ߞ?@@ >&B4B*BB' S P٨&$juLVDK$JV(FTE4DJtP !((V!D!$$^%>$P$JHK6INHHKeHK}v.'%(*S'3*(k*!{M\syZyC4o ~ '^*-)(n*)+E+(* *{*W*0XWEꪦP֪U]R=D..]u&554j45)4ǵjk hj՞ i+{UwEOM/EKo؀ޠ!aa႑QQ1̸xĄǤdT4ŴόhfkVn\<ԢblIJ*5ʺ>{d=ltڍڋ8;94:8;:;I;8=tprp8ujzuM-m؝׽߃#=^{.{==ynxYzz6^93[;W7_?P0X8T-8:x%2>d;1B.331<$/'boPxdVxZh1P{LG,Lʼn׌_MpHaoGb $}{ۿo"E'&JNIHLJ7Jo؏ C&0ckܙ陓6eeEgR?T~|Xpߜ2%y>y)=ѓ0 h42&NYSs9_PR}"xyiG`ٱ**WTV==}:zTЩ5F5mµ%1OswFLYg7lq;ڄhkmvk<E˅܋bOPyPa#GW>VzDIǠൡ]Cj=~s_~14l?rmd˙W>>yCzSm;wƕoNOs/Wj}0-E%V?.-䬲6Tyoqmz=aQ)ymlkl;l{;+@z;ñ x1şbG` `^ .)ҁ|3G n! -jm~IIJb{q44Kb?@fēQLhf, =9G g.eQ^E/F`ZH^8@MOĆt+9>yӊʜ*vyj6ZGb4 _cLM=Z~clMg#`jgaxЩ̹ۄ=O[G?, 90/[\ؼ$p9JՁNk}o |;+;'wo_ܻew~v1 ӡg]/.|Uј177oގ;4<>?>xRvrCTiٌO>ΝLyK ߥ{:mnoOv 4#O Y6ZFFl,kezD/aoF`"G#ћaI^d̼ºNpsqGQ3 X f ݥ"EESŗ$eO7,v޷%T+Wc; :3ݽq+v[|];N'?px(q'>M{9 7M|hs/ TB>ACEO;D'MO@oKA%e5S4'f?8dy:ώ`/i+[{'~$)BŒƒBQwb QZR?ʾ{#?0أtMJjZ]424Ӵ+tt^/dLLk泴Jnyjceo0*v$x{k=wsEhGG''zD59b߭Tb{zgÁլl\ռ|kE%'yOWFTT8橛?Ps.9q퓝+oqwYݓѡ"5FO54<"e镔?m7h76g=F8x C1!шsH Y|DPhA8nB#L/VaqT}wFe8#&jbZ3stpϐHiFkd)&nRfv*VA֋lZlC~[圪\i܂܏yxEx_Qrt/ 5 R婛"wEyoK I^,}Il\)rB#ʇT&ūG  ҊN=wU`cL5176+3X䱲>`b{.A‘8Kk[{Gj6gޫ~y!FaiEbTMJVr5u9]sLY &8}W>}lPxDOdJrՑjS-u3 :MZ/^molwVB{BODDQ_PG_QG߈sh Ҭǖ")+W֤wm(ob7Zs Y[""1T@#Psh":9Ec#} C4x_0G"ޢդ{MBREap2\d4\Ȣ򙵆͕}3K#Oo _00JʼnI\R2 F1JuUj꡻4iqk>g2p5\=z<㼕}I~kA+!%a#e.Ǩv'&J''r>M:f΃Pš'G]+9_9a\zlB2jZZg54^lknop UW&MvvzsDWN^;7=5l>{<eҫQ7oߌLorX3=9v.u/_/-cQzҙN?~.(\5\}Sgᵉu ō+sΛ7"osnmپ1?BD]oY^y6 ů@Է¿|#2p^., pHYs   IDATxkeu_{}u~O{3 @H(qYɉ;q+'?\q>_RRl#Y$ZD @y =}>c Apf0[{>뜞?mmmAAAAѣI4M( C=}߽Zc(>    Zѱ1T$fn+RV%    "ZkTh zl6=SJ1Zk8AAAAqB "fVJe)AxGDJ4MEAAA^Zh4DAAA^@kkkF@q_,}    ܋(DQ4??̙jZ0뛞~[o_?|{DAAAı?ۻC  V'N!   ½HE?|=r0CwrRk'_]\\CAAA{8_z5jPΣ.zEtzCs666DAAA^su}E#UF&PB75{qDZ    ܫ,_(t({ dy=>0"fVJAAAA,G`>)V)%    .%,T"    2ll`@H VaD    ܫDʷ Eҏxyt!՘AAAA{bXXPj0bF X ϽADww(    ܊&@Mc&E+3hϷA;܅C   $Phn !NPO7!    X-!de ?3AAAA9ijHla,`Έ,R0fguR7~x_XHAAAA>ڀ )5Hc _zQ7KAAAMfEB_gR( pYc Rֺ1Hk- AAAAP`>c23![g`?WXfq"Qq%qIb5)Zkaق5)(yJi| \.BlNB t   ]%Uh*%xG0N=}f?G3CtT˕嵥fl6[QIj 3saok<43dox1`"CAAA2LcPG%"#Gp+?73"DRb=P<!je#6+g^yS/v٫r(y-4K*c "h"`l@+N 0 bX'smeD Z+!ool??30m*i"ve|;wN^t'qG   w֊AYۍ(po +Ϳz?'^5J9|R)@`"0@@C>|a\-[ EhFhЊ0hO8[*+nZTz浇¯~f'&  ?‚RHbĀAlovo Nڨ/.<K'߸ P.@+(2RAXx'{Jr+^wCeAD`26ŕFkm֏V7UT(R c2>|3R 7+7d]g/1& :NT[~;Ր.έov-U)5wGAA6 IB!ᦱqLVA;"pJkj~{s`ើ*  BM;]] ̍4q `b0.!]pAz3kKj Z@YB/ϽOKй$?K;N I~'o~et&.7cɦi[at6\B%h/- sǰ*?[ ZI&`vƓ7k'"dK8v)\AشTK>AAΑa@ѢMH>|%i'Og?%`wBȒ,63@;K߻<6 +ݾ,wuw$ٙblΕw.4lY޻UwƜ;,<:I4IvEavʝJY&]e{rAAΤ6%jW-,ُvlj#8lj8qAAAw,R[jkaH RvsGhw_g95< !e0S<Ɂb>aՌZql%)VN0DPn]CQn C1@0K@ D) _?=}+8w +'0t+/]^[_fuGQ~t{u};|YwjqgO1z}}}=cuP( \\. ](z^oZZ98rOOٲ2OD@Ȥ8ݫ x(npkvvbMNphf'8!#{uqNVmnnigX,|>oݮvVjfjYk|.|NpS0?Qb_*3}K݇¥|Ɲw  pϢ pX8Z@ LJಠMH}"vm/g^E.D2) ; &;:Ẇ#ff64ȵa #)TIAm0{d*{˰~peE~T_1w.ʅyl F 2O[߬#GӌQYvjs\&mgY{şEQN=S(qLD.Uv ؑlI9&c'(͛7 Bww||ޥ,$Iш(jYՊАR*S0tM3JVNT*aP;:Y!S˜5rۛ9:V6jX, LMM9$;"kkF8$i6Jeqq1Ib!N".wMkښ0 sW(|DlmGq;ZQitJAI!@) C(y+AAnMfE)5(H-Qa X~/C1  pM{ (Yi t! R1A1Y`d -OT򽩢V\1;Xh VQj֚Qާ{Z.&Z><ƿÿJMw/<}t`v$p.}<տZ-gڪ.VZrvf2D'dQR\'I8```dd\./jjZ׫jш8SZr\,$InfBwZ8Bhy}}}eBKPKZyf$Z8ꑅ4+ NrRjhhhxxxtt4 Ci6jl68Ύ~T* zy^WWQev d̵ᮩ+ͦmowW> b1|!'rcv;FU7Qֶz2J\X"gx+AA.Æ}ƦefB $%"J?<#{&|ozC`{O. h%8OYBH- 31X#IS8K(h5)V20ڀ p ͮ7 2ƴ⟌?y >!5ڠ!JOt?G^'|@ng(|&b0 {{{;X[[tJZu:Ki;9V:;f i}ݗk׮]|ƍJeg&p5rbX.3999===1˗.]Z]]rq;Ey1hEQ>rj5wIa J%"r*@Vʑ)\ΙEQroKKKǎ"ZvʕW^v͛Zͩf]-R5888>>wީ={ cn޼y(z{{R$rzxٝiJӴRlnns" ҍVQԌc b<] 垾21 enn^zu~iRYwC`EU   iRYxCчmov45}77=B( O0gOdHwZ[OM잸jR9e&2KVY"ǰVÐ =s=^Ԛ+Fe ) eِ5u愭a2Sf&Eb17|>IϽޕyT)AO1o~_|x߾}sss}}}jƍۛ B;#Z\]}zmm[ٳg{zzRm D\7~# rr@6!c)}kaaP(ٳgppϝ;wܹu׬$V[dm>mݽgϞ{=ijb-+a$L9320Il+8e{rd68{ -0@4؍w9Z*B{䑃YC޶N<3hk$y~d. Wd቉^|y~~~mm;;9 k=)I8u)#Z3::zaΝ;q[oh4\D.+ ?f3\FQT\rԩcǎ9rŋN4q;]ccW_}FFFL._宮.1cSjl6岳]?~ɓ'/^永$bsNs;D$7o\XX8w|G}tnnn޽]]].]VQrRw-Lq55NfJtssn?tA*V>:׮-=uZ 98f Fwq ハN<< P.W*avwue7Y   W ,{AJAy\q,yXX~_{Z#BA L(80چcimSlf(UHA)!uP,A1 lETsd_WX5 =c֦Ħ&MDD`#a0Q B Dqޞܟsk'|AD0 r /_uO߮fM\x}w&RwBuZvcǎŹ}c=\hV,"Mӕr|$I= /={vss ;NV\;I@>~s^ySDwwK(g6Wk YT Gr9 ٜՙ\5JPV<^]]}W_~/FQ}2 miqBGG?|fffsd[og^ IDAT3u D C'FI^Zgo\o!5nA5 `F~y;~6FCӿ~xD/_&Ioo/Z%*  p`֊4)y!8k ϭP!P=h!5(q`KG؊K "eĩB &)8f$ eWk3ؐ*h=c+,w{J1tTm@VYVL1"L`N`c{@lY[&"ZWO>i+X`0Jьz澙Vu0!ة"qr8:o+%h4/^?}iZ՜98v'tn`Jn6ccc2ƍ=ܱcZ0̚%3%oq~<͛71r9IuĎLJpbƎ5u!qBR T'mJ<̌Rɓ/ӧo޼AWdqfn(O~֗/_^XXWJEQ侕Mf$q)̼ggGGG5کlZ @[ ˰6EbŨ5{x}3\Je``*,/TAAGĬBW⡨ix@衵. .+G 9<AuQcnWM?:_0)hMa,+0 H"bfЌHu+LѮB1Ij8 H &Q`/6g{ 0k0ľUֲa)fҎKSo3*o\f x>H(Fw!j_o L=]9U}< rٕ;vluuZ~sɓ냃 :'fpD9UZ=X!T-aH+jP;X8ReknolV#MNNiϜ=[VDAAXi(B8 G(&pQ/z}n k}xS;óSkQL % d2CH 4` @ QBX1xcݹ(ި)C[02R!h104z6JBԤ8uB`"$ Qt{?M1%BU (b[,r|~kk|T*MMMϻ6Iyn(h)^zoϟ d5܌Ow.q/f& ZƾYk|M7'e0 [xPvYDvQNV͛7C=455l6zg}vuu5ntdƮdͶ̦*1s\6ƬӛO>/AP}?ےՌh8HgU:|n?K//}MZvZEaFQ͋uƦ7F#N;Ryq2 J9>B)(f N[F Fv{{zXW/򩏎U*==NdCAA>lR`YnB" h 6$"Z__E7[hg>+5u_)a0X3l D3h@aHJNyMc `d^!7kV]M8l & 0+!6PVD(R3h%"`ui;z~*NKbpy/: [R===.yɮgj\'Xd<$I aqĉxࡇ~zq:Bgp͛7{zzfgg=я~O^p!|SK}/JbT*uww nVZ:_Fvg;(իW~bcMOO[Jehhi".83.GQV3IWO]lQ۷ovvh<766z{{ p5,M486v\1v{kkksshdmS+'Z-Q.}o4/b>򗿼o߾VuU4z%1yO܏ Ks{&'? ޸c kmV0qPb߃P 6EX&*%)BWQ?:;T*NMۭJu-   bXXPj)WC1HRG_gϟC# bF_ *EQb X)0\Ye edH"@D`k'3]eۑrAY<=ޝ[[kYVe-``]3G\VȂ Hl@{jftZly.#]o}>znlAwU>0Y##r~xnn.3btv$I͛7WWW]jՉd҃r\Z}7>2Fᦺ nRֺRi:99}ԩzŋ.IΎ2]>;==o߾ɡr\*2%KKKW\pµkתժR}gfV#iy.\O _<\.flghx'pټerJSoo3diy^Ekkk??|,Jaed۷o|||``; 켢(\ZZzŋ/_\Nr瞦k+ ~3v WTcfc|VJ9?qWo$IRYZZz뭷xׯc BgGf')q_|Rvww;KHYECFQCFGGoܸO_|9 B@DeC]lFQ߿#G͍FQ pa8666>>裏޸qN:k jv;8y{ppng~j5'&&<{_~nwr^:t}+83:::11cݸqٳO>w\^nl䔬R /LMM۷СC/^lZNqD.ިjiԵKX@wZm Z D7~|vˣCC2O/|un"JPAkXx|aq '.3tEnIv)   =4 (Z4ע  q֎ ]x͋ (R<>)О|.1\1xeb 7RVSLd,%LL0*~fX?JSfV̖-Y ZDZ) LX^Sal" lT]e [K "^}zl-4۰d%(zWO?f'>X)m5o8RT*iFlع{߿gϞ?p-\ޞn'.\__VFQZ4<<<==j^z7|S)U..%nD455#<###ƘZEFp-9|/N阘^x'NEQUŻ7Zرc'>qcǎmnnfԍ8\$Ir܁>nw Zv=== 7P(r9t8R8c=O|P(q\ݡ^Q]]]Bsnn?'ժOr'ilnn^t^s_YXXXZZu5Jqp8Duk+ 7_uhOt$MZxfFّ}0IULS!'ag}#_"d cAcf+>{C3s'fg/]4'e]EDAA fcc’lqxco܏KKKǏrnk&t6蜊599ww]svᾸ׻ 'sݓ;v lA;}.ggg/9r$onn;wneeutEwVuʕ~GFF:499|ebb駟~+q5/ٱ⳵ow}a޸qcppi.Ƌ욢id!^?z:-C0CPEOz!Ͻ}˭vWd-3ӳ~xXW_C秧ƩS8. F-;SiZ=ztzzz޽O<{je:]?3gZZ:)F^=o|rH RFW$3Q {#?77ѮN}fҊ{nכ ׯW[oGѯma~QwNa(|ͳ0ܛ ~0wϨ   |h60 e&P2ShwHpI1kgPx SR0@)=Bel*Z8E``@Er:C":Qþ@ZM2^-[KZQ>IDNb& D CR %0,[X`Kl fTŚJ_9+A' ,kϿƑCÃؙuOm/ߵD}Wk7 N&yօ <=իW=l.SΑ.c4h]={XkO:uuA)[W/| Gi4gΜqFwwP數U R$VF(wSVݱvmJrܹ~xddR\pk״#pdkl6!iǏxgfkfLtkkkfffrr^nLlU7<>zLLLw'Vz|+++NZOLL|tsdUKP憆nܸ네,n7Wխ+0ޞkъS&yzqqqtdsTISftjxc}᾽b^En\Lk VίpO13yY!h  M{:l}{sֽ~EG{zG4z [ ``$ #@p~ $?ȶd,<{"DѸA5Ej! km!4&j v6Z""QI㶷h.@ʻϜ TRˆ~\Mr@y?`C]F$ Sb R@R%@BH`Q^L7q σ&h AFο~_ gGpHM{p,K":p#8$ 6a+++oy=w\,.aTq̙V[o}Q%89@KKK??-n;77'裋/V*^O5,`^){{E  X嗟 -$l IDAT퍻333J)?e_^wܵM gRE; L5jřS>vw (P@ (G )MEpWu:߿uw)yc^ DhTn# 4n؀YPDKG&@1Sb̜00]T"h= eERq$|DM"6 HwH_imj9MY3gQ[fS/!lS"l1vw0 ڎW߼>55؍'$D(#pSnzgT*)TA΢3 xA:(.\@Dkkk;H+Ye&r|Zr/LDnߟVµb}?OpD)2%)}ܹ355uٯ~ݻ~|2j:޽{ڵJ"‡ZrIpSԐNnehp;W*!.ڄ (; Ws{~QhlQ BYC{HꐔFaW!՘ R?D@#`wH=e-, K̀@l{EY %$@qVL& K}`ʀ8d554j8m`5x%ugw.^XZt9pBAH<%rG*JR""+O4o޼y=P;H /\t߿E(P\Y9 ''@,3V۷nҗtwywޑ5cputt099tNkb aNN"۔gr9WZ;w۷OGĈd~~ _B^_____m4jZ\G#T^1Fv|ߗ. vȍ)I"+S^q;1b*@c`g C;JMTvCV@)0$**a +A9{p(P@ (i0A*Q-ኙ7XJ=n|h PZy{d kk1I-$[ԒwAa[0+Cd  ˷ކ+ . 2,"tamKH3.PgR`dH 0 s2`@Z0fc-']`jy7\aFZGkX0|U/|sgȮ=>Nۏ ob8!p) { B^b棣#QRıulͥi~xx(ucdb|DK ed.58ÕRr'IԔeY\\Wl6Q^t)~_=bT0usGOT z%d.oݻwEkdj9瞻|2mnnFI:8'W ' 3'I;N׿>??/yEQRjnnn81E2h (mw@R&DP humPGa%䡊oA.Dl&i☙%٥`ZKɭ:2Z7 (P@ QbM PP"XaowAb$ 0Z5%^y b.3$C:*)PIqd aq]b1L +!pLOiE)ȲdHBϊD$@4>AJrܹ^G"I xqbY aPib,OHH(P@ (Pك#bV%x)xPU;i m!)n Nȹszr_?Zx]4nn]i$Ѩj%N 49|ZZN[|:r<vvvv7j8T*B9 ',BEk=33sYnݲNNN2cNPERK*!,̼`0ǭ2qK "#E  #R07C:~ـ5ǽ. Ux>DJs j%/Iao(P@ (PGXi(B (gKp`pkk,PCk%TJUKڬg<)(=)"X@R1i&DS%˞Ğ؉-`Ҁ&N,H1X D36 qGl@^L̊l5amlmjkĤncq 1Ű)j7:UD<ࠇۯ@$^VNJDva\zU)KU,{'ʊ :yFucF CWZ\ː^O)h4Tk>+yt~\­d^@Y9T֍qIOPp8F.㋔#;OGb%B$!aNLLLLL$I8MhT*fffzeAɯL;DK瑤`ax}XvtRY[TK?Lj# ċi|ttD"!q[NǙʌDSTK>P (@Vj|ӋQT $Q:)P@ (Pڃ_+ 4 M}6ǸZzð3Qť,OVKڷfRajMJ3֬qAJdRP`2J,HCpH$E|cG5IdX*[K0$ق)PD MA`3ҽE[Rx&50Jݬ J`-A |Z&=3"r y-:;A0_19!,..+jovӑ WƏF#fp+233=113DQrpp ~Ťɩpx||\E/FSAITU$ /*< R*DƱǣ z*r 8#?yw"`0i4n&_ݩT*R}o<-r|\t/D @Y;>%$NU44.% ?j<(N/JB婙>HAj*ٙ;<ѭ\VrFl#)$.$'<9dT@ z~3OS:*qEQ} ̵Zmvv@$D$7\Ĭ{g:B '}8qA$N!yK2,5^oHD (J.9A""}[.<qzay7`Z @$Z֢Yͺbt(7[ (P@*aaAe|ȘeĀAL Wdi?j0Aɇ@{P Rf+=EEhjoT,U$FT,RԽz*&h}_,yMO@wtI A h$b$x,#_)S[pח]W*%%0]<`l֎[o R[܀ tmbxHS$0;|7zVnG^2'Yծ\DZ"޸qΝ;IH-e1R\rW_}饗>88G\&# 8JRPy2I8jvMDGGGP)y|ӱRKAYwMnV}Gp|Y7" i#')#(*an'擟URF|DK'ױ̅|)BuNbPDEZ<]0KJ{R|{zkaY[ɋ p4)g[GjARvNtgflRw~*P@ (P3&e Ʃ2)gMG>qt꣛w?C 5)<JP/t}JCkh RPdG0e̵&fS&GS (P@ a@ѦsCFg2GhPg}ý` HH2~%qE0V4$ĜU?plP"1P fI |J*s֌i Q 0r" #j㶲V4>2w{oη0;ubʮ崂@T"ׯߺuU+P "("@\\\xoY|FJ%_@D0*0/NBYXU*AuiYB9私[w"{s4%tPp<ժ1Ν;\.?ya.//?siaaZHWa8 ]Dj`ժ "==+vNE{}JI jnjZrG+q_.)Z +'8ܨ0"T[[KK ]|[Vrщڵ/^83[\JAm(P@ (1X[Bja6FhLCFݣͽ[hB{S)2ƞr (  ʃ J4ȄE6A̴8̻ZP<J)˜K9R M*$ l R i6:'iN]%D*XCU%G鍻;}<*.z|(3~_x|xd)b.`fqF,..^|ŋv[rxxxĄtuRVE70"ZiۭVK2wL~<1-lXJRRV< H3Pd1Y |3`wd E6f $~8:qQ'C׮];<3Fj-wDϏ,#h4-IJzauUy{oJޘQٗVb vaΞb*P@ (PB A nhTS̨xF}t @rLGs! PٯVjJA?[MU4uy0`*$b&~c͒;$+jԪ͒x"4aK{?1t&cfAXq€ąj:>Ϸa?`IaqKZeqZY5.< r?3ϗ|qFHe+<=\:z"+(v;;;.\|RRY[[;::**1du1hw]yV~e+jg5890XID,V3/{A.^$nNln'p'}뭷(M|UjeV"\=j2rG`ggX{ٙ!{w~]@+!M1Q ~K}p䃈s(P@ (yG )ME>;C`?W2NN/gA~4#:vQ3J<3y%_u8Bv- X#WE1xԝȫwZ;o"w# <"$B%I2A+ ^`zƯ}:vP+ HRDj%p &{0kBF (P@JMU  U )z~(e<eq-pɡ~ـv*ˋZjE  D?7xyuި2ljDjmZj-l̯d="DŢ245Aijzk[H ,<9ٰ{a;8:tOT;i|de~6I`(vegQҹs|+/_^]] ^vAxH&HE@q2# g8Qo;'#`mW>%x2:iNʟo(  F4 573_~ea_KjL#򾋱|dwG^3!O1| ^D bk"yz>y|9pB gRRVG#i 'svnaOJƤF{כ׮]=ԛf7z[ Zx1JЮ}ڏ_{eqjo{7͉MƱ^T@ (P@g+=J fj1QcJZMـa=T',DOuLۿOF' ˘oW.^|~vAwrTbfYQ@ (P@`H[F373=|?Sp*ʼn6MM ,1 "O= {u K~E&MlS0lj5TLulFY1aQ=Ra5&Z6FFkV,OOۼto`4CmOv$CA)'N)vww~F!)%nl.///..^zunn?{qc"?岴x]ti0ܹsGƆaHDjT* -e=S4 NKZN@ .@ ~]SR 7u0ǁ\x.?V~^)&o @)i\jQaEQR)GGG. b^(uʯPH:)‡~8 CcT ҢE\O$\̡%!IE&p8WNgp8VkyyYN'N~ܹfDGQzrY)U.EDZJ0iH!FQF^oooiy0(gZ I^#w$o9섂9#O^ʑ8[h1[T?8X?w߃Q*A8([̴KsK0GܸiYZZrFT"}@ (P@Rb-p@zaQZιQĥהA%JQWggg_ ̬H12I1L؎=\3@qr1(T ACy2 N)%1ZcIIW$NJV//Du lO# y3(J~MNV d8VU4' q hii饗^:>>':p1??h4J`00 $)JJ%qUEQP\]O/%t~~~vvqJn^yRu4o_}:l5x XA9\/|׿tEw{ݍ{n߾ݨ֖dJ%n (P@>W`%j^QlkMرPqpFPBj827)8 ]ڔ0(VmΘ@8'F[Us%U/yZc%<Rb<[yȢ@Aˈz&5@ icj4'^|e-JCJV NH }ɯ# Ν;v[ [y„#ܖ5Mq0s^?::#O٬j{{{y]qw:ɛX?qaAJ%M X]]}:Jv](.*ց4Vc s}|3KAݍ;o;AxR "sǮ~yy驻>uX;39U._h7 (P@ |"  ۚ‚ۆIt),Ykv9Mp@}y?;_mM:vic #mhuI>v/Z)wC$ΗbȟݻFC"K.,CF(JBgf9==AODkh4  :v `l6'''(8d>OY$J@z}rr2ׯCY z؛CDϟj]ܩss˩YCr=,ƔǬZ6ƚԤ006KR$>c &'ZJF흿8!U/v|F L4.'DŽR&8=Q $s||̓f8JsGߟj;;;z='''ŷ22+5'&&0f3I)Ee#1LOOAp~nZ<1ԼKgϞLap0zb1Fr JmgmeFRJNl6eO9&ch4T*SSSQn Lֺ;*$BIVWW'&&ͦprZ~ߍ+SH;Xi9|tt4ќzZw|ݓdWo1==:>>#2'Vv•kQɯ<,8[W<"<37ٍ LDK.^xtttpp ItljY$I$>CDtxx( 5gRh4$xrLY^+lQqSljIkѐlZӻD$JC[[[sss7oޔ>"[U2i C1'&'G}BnDQd ϯ\ruo-qi J ^OŕvoܽGaaaAt7B-8n*嗫@ (P@x)- 8`L[)@&;왜Lk]|`zi| ?\wnv9Ѵp`#(&3+y ͡ r1> fKDuGa<#"Up Q0i$ID0GLJynw_&8  Mm )JHpA]:TҢiѬDNpȳ۷o?ϟO(N ~? Ãg}hlnnnmm S#Bn?ji4x*ErgR[Ǽ(ql{{{on4333VK2A)1a~tYJדM9rʭ,jrR>~yU,aQM2$ @E DΌ D4y||n;ibR;Z:ujaa֭[nwĥ";3`0XYY<rv߿ҥ[o<cf$%AW^͟7q>1xt}G=}ON/ݺ}R.AK$ںc 'Ƒ+X<<<<<<<<<<8}ge_kKNI lErYZmDś i8+4h\5xR`2c@yBj2e!88PW'\YnC b m)h ,+o$ӃG\]ǥa Sd7Dġ,S[c:/L% JCKϓ4w4-aFbel &J`8LG80P 1hHZ0 0aad1~RG4qE[[[ƘJ2ԃ B^xZ^~])T!Q 9a^'!ccc^osssjjԩSے!Z{=/_z~ۍ)P; $1fvv_T*[[[oΎB3o *8oj̻r3ao\_ hV g͆)8?~O=wu㭷&&Z~JR)$DT`2gQADQ7r,g.';<<<<<<<<<<>HQQH(RP!B(V2V'&ZCd J}RC'Y$J*W>f竽V5r"6P&ܰ%wJG[?n?.PQ:Z2XW/ I`t=(a2e;; _g0La5(i4ܹ"k/eDABϞ=n7Q,FT\n6F# r,nW޿%Jr&"T*MOOA0 RT*I=55uԋ/x僃Ufv-N݀Τi$O:s]p޽{ۇ'ŝ.xԔ䒮]zukk~RZvvv$ N&X;#c!eZVkf̙3bZB"#^ׯw݋/NNNK.\ۓw,QŹJR*ܼy͛"qrWn@fZv/?7nӃRP*Me7=/-^~R1["2"&#8.ue"5M^ޗ1 C p( %n1 Q Q"Xzrf3c+k0 )5AL0VP`1P%/ 8QL򨡐g!3^,S >ʟcsl攭€]V[&JC s+; *#VڝtN?c7}bfȐdu؜q`8q mgs P0CQឨh6/_P #n8BYnݻkkk"#@]E]H6Mb^ommLNN;wm~ OturjRժT*cccTE8n6z߿ \.;A٢8^\\ o&3jnHQUH9Q{& X@zɂB`a5(tX(fXgD/\Hd½Ky G*<^3+r `6CI ր-]9&k~aG篘O3-IoبDzxՍŲjĢ6NPppj,+sx fff.\011s8$bƍccc.]RJ}@'Iȫk)GNY'd" THjH߿yp87Iy@z!NRjzzzkkڵkD477}( ԑ9 ݽ|ہ#w^ryaaaaaViDNʵqOLLĥw6(G"#7QN2\B9'xZz)""f荇*aaA242f47, 8})J,.}7CDVbD!Q8DdD* A05!LA B+HnR2r)bI*## *Nǡ(O3e(BrS K,*aj8PF)bQ'fNgg~GORpvY|ky=" @4 .LMMu:;wticLTr=8]+7xCvvvկ.]*w988{rR4N^3s<0lKO!O͐(P@$Ь0wBA&.d顩L4VhXBkZ#1 W_|מ+ő6vȐqjSJwx ۣw$na811133TVnܸ$T:iHA̓kkkcii… FΝ;II(`DHrtp4HsVu̙p(Md[V^+rW#7Ȕ\J!<En+ivJ7KKKW\i4r9ܬFR?%3MSuϟ3q\VESXE24`j|*8 j@_O N*(yNJ=yxxxxxxxxxxc`Pfx٢Iv{GF`lk~lTT4Qdˆ`ADj tBÀ@FjT%jOBy2mkP cDA'-5Bp#TCr@D0Rtx&dmk%kk+۪ћlcj$֔R[5:ƦIҩe;ÒΌ{vy^%6/HP :yy0z:gϓ^ IʳV)O288rZjz]vv;;;fffK"ST7WBjf!*JcccHe._"ℋܐ)˵ZlNNNNLLڒ&#)N X$8Xk(VK 8#G$bА^+nF)$FD)upp믏z4M4i (F1999;;+[YYt:iv]z}rrR]۝Z&%6&{q P+g)) ^Y|,+DX&I>P7(iLdAQN4Ǟ{a)@0 J1B(3"TR%C!̈BUT^¯[~ւAtV7&,]*pGpH FhA!,`.q,GØj:{q"Ma4R4E ) Kac\\Db%8PޱU+ ȫHp1\Ua+Hk$I$wގhaaA&DT.~~ 1dB.i* M_VKֺn~iQ.\ .F\n4JL۫SSS:u{AN*I Y .//OMMZkTrppv`0z{…zl6em:NM1IHr]YYqFN.rAKk=;;X0b fij.Ls= )䂭'l@&C Ӊj9IF=f s9Fˠ-Qd )|g;Ouh; \IAz^`NgooUT*MOOWUp4+6G8D$Zfa7nܸ~4M$Rk]T* QclnnjK|(L>(6q(ⵑ5j1p#6.O#a%pv_:MOO/--9ƇZK+GUJon߾gZ;;;EZrg68e1{Gf>>,! b 61-34p"b$W\kޟ9Y@z"d9@?ݏԡ q‚-s޺ KKکo]ܗ§듂L'-4!w`n028]q'؋p%@5~gΝ[|ۘdeC~myTNy8 i`$p8l6R$;uIQ|qWZﯭ3ޞBr^,v8z$I1NG 333Fy{]n4N "J~oD8p<;ӝ|p8V*[=J7\J"-RWj*: i?<5?8G/]T$YYY3yJqWI,ju||$Uk-˛plc6\/hiE3۷o0 ,F1C`0vNG\-@ VkjjJH"G8'4ֺRĤKqNydy9_wGR퉵n_z#$cccFh G[^nw]5fggeZ>e}\,.r=(kda޽s'vgYboL8(HuyvFIp"4L'O( ~|Mk-R0 ` Vڠddj@3l6ER (}sjzZ%kx0aaH)AT _v珶;VI{$,F\932G81#0)LejD_|mbi}a#8Dpb  ǬG**2xWA.BpM=%q̔T ńQ!! "mrw:EdpJ9v wp>[q $)8Sm>;yaG<ֿCVՉ w7:Q7!zND}5y-`#E9(D@)'LpP sff'?_[~TĀ `Pڀ q6g71C8`hGb  QϾͥZY`s`Q  :h/ä !25O9FNmXs͊$jib ctq̂h"qt'lWe!:Q6ieXRK+JZ;jMɉJjHA._Ý`q28q]hw+>>Lӏbʊ e qV( 4Y &tղ 0 :@ 9g]_R9נw3Pe@:(7vcdRҫ)oNq=QPRȝ!)(P'n&8v~zw]6FrX<<<<<<<<<<<>HD̪$50@BRTGn]— @ R0""!@y$G %9I0yاS$ #"1{}9^Va^ƒ0,%o/;XC@BF',Zse.(5枔|#E-MXtR|W.˴QEG yuu|MEW}R5!rF$A W?¿D((ɿ|":LJqe{dr}9 p AmQH'( :8O}4 ~<<<<<<<<<<<XPPqpLa<%Փ~Ϭްe((HrY h ىP#Ee l}a·R "710KkY"aI/lH(rd1GcM"A)#^.Kx'F*F:n}ıLF Ǚ#PA-"8Ю<>Y8#'y 'y.Yq(4PX<+& (N՝ȩG.ɓm{>{-ej<("5(1)8\˗C`h @Wg]]$ Gc9c Sq$TGnMk1<\E;_+Ct4 r,6;ˉPp#ɆًAS4{+to]<=ˏ a)ݍT\oZfAJ5mRuwm.j |_ѣ(V)@cLGnHX6cO|Jv@a~| Vqa x3x#xwW`ۢ *t"~yאg<]yNyg[-a>mq$]=3 y_Sߛ(iм˘a$`alEp1Q}_/E3HeQj f'JIDdOy@:W{+LAuTMlikNAm`tvtPkl˖`E`ѣm<&6k+A@{O;LR(rLJ;MOdkQ<Gg'@>fi}QkpL`:qx7qtw~c?|Ooǿ5l4[(36hS 뭧KpHlҧ?~K Q/D)0Py73`īYGXrZ60>>Sfc|l wΝn}°3afF1yt(fnġ5 c,O~K]{Y{ZYՖCEƲ"'ji@i/ϕJ|0|ݵH)>0~l D4[)[|ͻ5aZT%fQ4<3?W.g~[wQcTf,XZĈM'(coV(CB( P2ZDPi*"fGD(f!,E #Bye΅U28~+,ȃ@ dD RV(Tmj$QǖS?`bPD<wztu'w^Cj z~À5=FHRs%8\;^{vׯ߾45YFjs#"܍"8)86vKo+Q=7Ƣ0 B9+AJp}xCJqQ%n-vG`p ș"qHo?yARm :GwmyDpq3ɒ%vRλ9xrGQRGlϬ?ڦ\B!,Q )"(@@(r &9FL IIxoL(UҾڂ" HW0tĄJeFaC@VTdIYJ)boR"T&c~g)V̙!ۄ2͈xxxxxxxxxxN4ͺv$I%~E>uC e|5濥 AD@;FC ȊO hX}hM HW-=',at\Rxua{wsݶFGcfI ' c`MjUϪ 0yG$:,* _я~:rrM8  <[EY< Fʥ3y0ř!E.:!c;%`;cʘa&'G2!"H|LP Dža(8X$?^OyK ܋>hC$r)Zus:@,Nl|tH 2[ByX^9yϸ22DL O&)9 }ሱo@;Cȥ~7s= @9/󇺲A34!5 O܃JR[ TO?XcM6>!&gS$Y (XQٱR - ɚf  k 5$HgfɟOQ(zWjiT =:dOqI"Dx~nJzCqYrh8tq#:nQqQurZx%pnpR\ b`HD@Ad)(@: XHx"aḥ'Ep0 !`` XMc2H)((M0Go c;<6= -!BBӄ(D-OCgPt,g0A6HA>0P؇T,*Hthr|p) (ۻ¡$'35%NufW#MaH Ņ_~'BrxxxxxxxxxxxxxxL\f ʞ֞'M(S3FQzŲMOu{?A=BtfAy`IR Na2Et6YIn-'*ٽh+o}?AFC a;2khDjQ"Ia4R wqRiOuӒ$#HTP@I bw愊L0btz )"TlޝU=;qfY{=UݳUOـB Jnۜ7aؓض][6xnˈهIu~H~P{WG $8EDnShL1V&| -])za1cXI g?J tiB.`9s}>%uvD+La wq%H2`Yyޫ'7kK >՞=< DnL Pg (wGۂm ;/ô!m7nNckȀmv3_x)[zͽn!J<53qpc1=64ASxCpx4qKEM?|CrUH [.kP $!-&,nG;yP!'`Rz7e)98l 4ad V)KbYPF5joϔ 1c1c"H)pH5@@G7>4J)gG߾]| S>~Ѷv #q>u7[|n4A Jlö(Ts3F8P %6(l(ъ2 L `0MXf)a*Jbfr!Θ8ADmpfi#) [rΥX gTSJY0Q5B׿oPJRTuPJ^A+)u 4ʶ*|~(3g" rnE[arz/rSh:i]I~ٚ$JHD-R^/eTkȩ$"Hg ^a8'nXT-ps(/政r1%(L-c1c* tҠ$@!|0ZBJծE׫i `ZkPI5H`òa0$IDJPTF(BAn w"Ro8/=DM LXVF0M&,N31-%ݼ i#h%$N? '>wW2%I ҍ8a rp(  !țQŸy= K qbfIiB@sVԠ QZR"#;¾2mz mq5ϐ"h $A4@wO9DW H,(%Rw4)9A()Ƚ@D @E(BAS, h^tChRBPa®4'"!@׈b(ȋl"SnÕm-,B B )4SpvhN=@%) ~8AB/s"d1c}X T:h"! 'h8eۚe4lpɃ7iOׯK!N C2!4wið`X²aY0KdgT:_!ҝ _:C´, ')4aZP^PY"m lҼG AӴ@B %e%l;)9y|`gnad;\Aa 'i,bT-.|Bx#C[iAph/oq"tΦ3Je‰}@BB.P)9E4"]~ 8|Dмc^Ci! "@wBޙ;!M.B o93}K#hI"^l‰27 BS3.Zw8kX/RRNCBc1cnlKIȖ%l &tU^8{nܰac;G_p+W=D"Ai,H x Bր!yFzmC9) %*%FI Jk00$ eF ?f>=m$#t:uvl=ptllYqgjiz*Y| <$iiaOKhi (#ڼfjDOΛBKCqj%"[ dX0Paii VJx#OY)RH9/%(;D(/ɭ3lqlgѸQ 7(|H9{ ʝ! grqTU(ZnB9a7~!,*kID gwPn/RVA 3{:.!hD$ow`Gتw&>0`1c? ga+@N[( bwqB} p8#X)%^=:6oѪY_joVx(+@ DЀf/a{3{$oa3t /a{+S耔 2%*PPEvX alңcrLZ;Uz]kMܢt%ĉ,G+"Q)|fD%𾀄Xm2R7H.ݢ*ڨ{IPE ɰ 0egҎy7Th[Bи{C"^PCpBT$H HAq s$ݰԏ%q+DJ혢) MfZi:QAҳPTzB)qZRi^؈0bv磔Fǔs}ZץA7«&YI (sJ/ 5Э w1czL)[*e@i`IHi؎،8r48CC78aÆU|tźy$L$"X` Ms7 @²D^HpN (1l"~ ׭}G׶MKxKl@>4)*ܰtVϹW܋+Q eTĐgʿY@ Ey C`sJKaT{gU{9c1XKX JR-Y %,@WvR+4HyB;lظ]/_}&ơoۆ;/wH =tʻAPdaw.r :p@}:ѡaFjzzݚUv1c1c5P2,҆`نI6M1q= p8N΄mZmӢzun?~sR(!Ae)>oBvq&(>ew%@*J @E0Ld$eVMI޻[Gt H)m[j9TBYS."ٵO啇=0rھc:E*G%:Vf(:mc1!QН-mUH`P yCsVQffc޴k4Ԕ˂$ٱ"B %*JzS6lo;)Xt-2Scw8AGm(=Y"t}:yȅ6hGZ=+쫿2c1jO!G*`ڰ,t$ :ҦYcjgMos-c-nߓAxw::Pf?MlNhԬQZ۵h)IfVh٪ix;l9hc˺1c1K[ ( :t$t$ bG^tYt]ߧwlޡYFqn;v޶m=rݟ?()i٦m[B* tOZ|?!ONHOi8afM2Z4MkݺYbjNl!m;7C 1c1_H)( @B<6G $7:thӡC;9ys iTIkZ Η %9-=9%%1lRJ)H!mϖ1c1'}4']!t.hpdiZmYvh%!Hu]t],tᮎ9c1cJi$ CRx?f { vV|IUm[)(wH<,c1c1vDJDEGA:5a hZ* O*fc1c")A( A ICH31c1Ah*ȯ~hI%1-YD 1c1ءb6R "蔢iP^`eǁ1c1X݋ dIKf+  ۆeN҃ c1c1-mI TE4,M(wu21c1ءbD6A;mU QD;sp0c1coL)[*e@i`IHidi~c1c1t KAI*$ lXm8c1cKleXʖ6,6  iK2KTc1c1V"@mB RH38c1c1V)[mT-a!K$1c1,!l$Nђӑ胈sq1c1c"P T Q!\%*1c1OF|:|B4C`1c1XeJH @ #@[1c1Ljt"?DY#Q@נR`1c1XeA! OHt@"/Qa1c1X4AC' k~-Q]Tc1c1VY T:h"! 'h1c1⅂YR Han1p0c1c-mI TE4,M(Rn1p0c1c2m"*(larqc1c_JR)JKB H6 K c1c_$RJX6,b`1c1XP2,eKeӆP%%*1c1@R J瀶U!FKB)$1c1ϔ| R)XiBB!(IFc1c1VYB%AI%#c1c1/E*ND' @BYKTc1c1VI tiB6~?d1c1б@P  ~:I0mz¾2Ʀm^?1c1Vt"DY#Q@נŚ[0c1cH '$ lp1c1cAh*ȯ~hxc1c_` N)hH 6xc1c1x !ATd`mX[1c1t e[R0U- bJ[1c1L h $`(b1c1cRTʀ$d@ $<1c1c.a)(I$ m8c1c1Vi6 K҆`نb!-xIFy c1c1/?P9mUH`P xc1c13e+BTJ`ڰP@JsQc1cF @Pt@'hIHAĹ8c1cKS(Q tB*GpVc1c_'H#h>>@С{ "+{[қwL=ao(c1  H@ #@8ںz(&tҥW#og @cfkW}ukB*&ԤԤCػUvCc1; ҉gD]tkn)p0vūܽ}ʽ+~ dڵϺxҠIq4`L_|7Ba89>,C/9c kY7 K*|c xÏʨaWT e1co"%4]4(?! }B 8;y矓/GэRJ̜“o&EU?PFo7Z S8c ,Ny+[T]{q)EA9f1c4ACHO4?4q' IDAT qNeC_'=ӏ!ß]x{i. `5hi`Jp"@|ڙ0rщ.`Ο2Chk.=oϝи6Zԓc1X]tJD@C@O4(/Qgmk%!а!+3PW]g@ Wlv(i3Vƪ)L#wҭ;ݴy^G4g6=3ڮWi5V u3y(E3XZ}7hjmP;Wnc1c_P K*X*[HzvY'tGс-[-?eC[E7j&{ݜ9sX']Vfӏ$cf\~7덳޿۬Ը-[ˀ?hӚ5?aԳ ڲ?an*b@*WD^Rn&>j-MW=1䋛W '(H!>rZSFܽٹ?19#qZo1cA@P$LUdKbyq~#Oz1+wlbgoj}ֳ~ݗG<=wkXu%-ͿdWAV`mՍģ[PѺm:)j}e˖-[6oO7wUzY@yp#/lA>%rn|.ٕ詌aD7o|M>Qz`ia0gK^gyGy;{V߽te-_xU7B|k]D7X 1cCF)[*e@jR²!bEn`Gש?=&=o޾"W~>G VO^.7Ǿou.QQ# =uW>ͅNp)C;>,sg?"˘%VٰeD\Ԝܨ2\<\$w1crx*6v,C.L2fxHaܖ5oy׍dAj?6;9#-s-25ǯy+TZ5!ݸ;4W4]Q!|_π[:MKGUUZ1Z;W{C~`TnPEn|^,,ӣ{4I*X/$Ʉj30c1vhTh H , +vP;?8/ 4~%D y{‡@Ğ/j^Z NPC]97v ٟ\ٔW5{;e|7S~m f3.-n guرdڦ 7M}!=;[}q>[Qk㜩`1`YyA2-; g=<=G{G?O'Ok/^v'ZГyAKg1ba2 KcV-}ֵ(Zf2ivT1Cs<M\ҮUJy3\ @;os9kvR g9 {lC:2iT{~ɻ_|ge_`ƟݧY%Wr ^aV2GJ&vHߖV]U@'׏\2i-O1k"Vִ0ҝ 5=万؞e:STە(n|Fitos}yyw. c1v(ټy΂Cݏ0K"@&k[lZȑ($ULhtdg7XjիS>Ϝ Аqgj3tȌ9]沙zMu#'=9rvld?yH 6k: ᙜc/=ch[kW(9$ @vO ̆@N5gKUG{\ J<39^Ze'l6x||@ZL TJRotCO<B(,"aPR]|%w8i"6G>Vc46O&4G{3sMmoNpJxݫ']2 viUK6l=;KQnj}*}w|5iW\0|bR+;퇝?GgkѨŚLWd?+t}Gdy?~O3#z4db/bQ)HLNDrL<-H&UKH | @o<54u{A#ȌkG䭝 HNk1nW`L@x㸧<;" ʋ}N^RKڅ͠vPW}C6-qaUra kWn]6;7E7o!:PE^n+zY9lFܖNܽy|bX\6w59ЩcӆI5c>9^g׿=@+ wo@\eHw\7zR;nzR~,{5޺ߙg0c[`].@|aʜsN [Qۣ̪6)GӺ棽jˢO5nٲmOe8>ړNu(J- >>`8|SdW= +FfM^԰ƙo??zO0&0jb&=$MPw X+kb4T^M;|a/Ѭі%ȸnj_>ymeБq~[`=ӡR᭡Pu1Rcw%r}݊ 57IҪ/TGЫ޿AOwwB"[C'i ֩0;|ZJA0 Pvu+di{IU9]oЌ>ܭGz<)O z<_ԁ\VfܾEQ%?lWG_ Ѵ^[oj$58A䔲4YS.#(NW\5$x]cݿ̾ VER-E*ND' @BYrPdtl[_ B|V͝xD\ nǢgUܷ{эųn[> @-#,Hݙr }Ӿv.%wc(xx/>k‰/-~}u1ۆ4;ȺH>u@mppZf;`e?L3VnP11ZyK&_6װ?7}whѨYʚ{^F~[[^y;" X9o|_~-7;Q1R^`g~y}3&s0b4ky]]9chASN>y+''L;Y3!}VM|W٭_~'Fzm1UjN*?nkW9|tPgYtcG ~3ffXrC0F{_jź#}e  ] 2.*+?5Lql+^@a>B?>̟y e}jhئ ߍ|4gj^=Sbh:AW6oBO9-/$nfoӺ)@>ӎ輞bZ nr^wG֬F!K2L;LY_9/79x'n^tCo=y^Ǝᴗ[c}ܗyWV.X+M<璛3QMc1vaR^7fڼ}"puW>ͅNp)C;>,,sg?"ӷ|w^fc~ڟ8b[o ۴:}D~*(Dt1;K^an#nCU 1w?V.Yz[5ܺѧwtwe_~d.<={NiD!#fT>W=ؠuz.~p5%?V/^츰$Ma?Uq߾kq>Ig@asCoYP֫e1\lW"gu[)~CÒڝ|?>l%/X'FFE{_bn>  H@ #@TM_{9oREY-,vy-T{b[կГ?cKZHNN'HTPy4ݢ0W_[^0-8/<yA@2QO1#~`ѬW`.UP Ͽ@j4Q۳;D>[qNw*sG]icUQX2S -lϦtٓܿw3g#7['&o>4p2s7biZ۶3C;*\~]QS*d~iMl?,ތk?Hg4,ubY*Q v9w(莑q;9ȴ9~wIƞZ.4Ģ|Ջ#QoM;ѶenZS"|hv2} 2g-i1G iN~..TI/X/DJDEGA:xA&iGSg^;Mw/nwMu܀mHh%n(䏟VG5bh;o~6r)-?}_`ߞA&vkyw.}[z<.oٿ5߽ykTEˈQbdmzlˡn īe3+ jZGuyvs2iG(@^y9:@:N! E~&o&{S;= %l=jmӧw5 0@lMo?nXXTȌg]yӧOQYO^n@JNo9j„ &L8{tN0x_tѼi1S 6T*h=ѷXjz5vD X{mRbW!>V, ?r^G!)_bn>H '$ l|n.]zq?xcV`07wsvtznA~b+Ffn%#q/~yZj[  vd}s 9UPy4k>7.2!.)t m?jT7;Z^t1Ј_0wܽW|_kj*Kߎp 7]EdǢ%5 Dj2"}kJaw]{JrW$_hΊ';?^)pc(u%cÁ+\< `s:dƍo}y'tnӦ}.9~O.~z\℆n؞E;{66vĈOߟܱLbm@{%˖-[9;EYC].~ם3vĈ#Fr]X)K~ī]<i'OϽp;>F``ҹ nXG]ccEʛqmlW9ucRCE^$wmEшpX0d1f[.ݼts0g ,muL'(Y ~#/7鈅0T}U w q͚J l 'Mb-Y@#0ݐLI컨X+^wRT0jᒝ;*|iJZG-5 # үԒ*[<@ {Y4{DV\Ao*%ٿFx@u+/1XX T:h"! 'hsSIFۏ{Ӯ}|+#v7M!,p)Kf<ޜPI|KIz.xvi΍3Tܳ͟!,E5Mu W}r,k4h^mkq{ǪC/$󞼭\|c]][%rƝ' (w'^]9w-dٿ/{[aͷOm_? SOѯ}Saۯ]UJs1>Msl ?,1k|8z &fs @r/Xzgѐ?+{{qIUëQ]jTîhT%f~_#jl Ԗ]zʼidֳv̲EC4#jk|5L%@^$ڜw;;??-_Rx !ATTBcmE@' T(URx]ي,צ,(?OJH {xL-]]jH49iٲjtNO]:Xx~{Ot}vEPy/"6n E;=W}vFulrDoteT,4zߛ}݃JBXжī1?-} +R+1rBr#+X[0D%( .i'El  o[lD+W0J5Dߥpy3JhЦun 8+k墧&|yeuNyFnT:݁팽k).8|Tbƾn~Ϋ IDATP'TuFbjE}GoyfIi!Iミ `}IŪh`-m) SҰm؄@@ YӪ5KNn\s9ڢ6}{D]y͘QNge$aj+Ď7덉K?_6}ɴ ƌQ+}2-_.SI,QWoIk".ԧOj"/x4vtxߗm1Z>@|d4򒇿k/|a]U'ug;3]7ԖFMK4>Bئ ߍ|4gj=& t5xR @i6s7iɨ=GXM@ZҘ_t^_¢3{r[1atpUza,Vkkr5r~|v~@CujV!XvnI~i|{n[H(OE-XL`+@N[( b-VAѠȫy-0/iJæ*7=c8jи{!{YDE *Ymm']4w['eoݕO''4lZqbO+ZtZūӉ;S(:qXOOOm٬cE#+3O=vbgW^vQ1Ԅ 81cJy/xsZ}KZHNN'ǘ"gVŚ?fD/5m-4e w2掅y'G"#ٺhij{㻯{Jvrcwmu:7 {#kj]myeM/\OyvDnx[qɡZ^Pz7K-X7R& $m@΃>Ȼ0?qһgνڷ$qһgݺֺA7hٶ[G4kStī?M#{=tk LNl:`ٹ`1-~x Y7^FZɫ}S?.d296@7_!"k_O*S򀛎TMe.OX8X#q߬lۤȺ_"zD.Sՠ ,Ѷ^O\>(|ƔE\g_Pfڈakk}[ݗ*Zt KAI*$ lXWc1cROz 侳6@\t}aw-\-;[2 ι{V)P嗏f~7a3 /yMGYC]LꝻX57,ӂGq3`,3W*0wӆ*{\w:Oc|+"mEҞ(?4ioLmʠ()+?`g_~7 ֤Eb%+U3CE}R1eeŲ`Ζ_;̉SPהݴ寕=>-˶b}P2,eKeӆPPIFc1c\en?~?'ogFgoN}1O:3a7|@}SUvE~xb;FR)f#1potʂX; @(jKj<'?bCd naMK$U5ʴIGFhh2lOj0G=ܲKVގbjѵ[*p5?_yc/]ۧ&+ch 혛zfT𦝜(z@e+BTJ`ڰP@3{3M@ *X+8TŽ֢UqkqkUZEދ@*ZY*,ɼpa(ڞM=9'7EO?b9uTI.Dj5RX5NpF.ԭ[7B @ ڹvGe*8|¡P"La pغ~,>j1LX+f!c! Q=[B\T@ @ |0QQ C@xi|𵊍/ނ]̕_z$孓#KK*4Q~nUiG @ QShGaCK;%+8 D#)! [s'42@ @((OQ,K8>,iy">yZ__lMaErзsҗ;|9W%U'H/1;>E @ ώ<,4@@42|7 z/>:.p~߿y E) H IB @ &9GS Ӕ!(M/UA7Y?>GAdS |~nPYfggڣS;ɓaaWn=gu'Bţ֦F? nlaћC bMHI}e,UųYϖ\kj eI1b^);ؾOIDU5?6e9}],@ @ |JT()-A@HUlP,˖Eݡ@?|K& x_r]0r$*שK0?R/VZ a+ n>:Gn-Xb~:\}$M U<)hߑJYr-7\agZB@ @ >'N:adjn"TejNp LqPnJ3MlDDDXX؅ ?nwe_PjEA\mϘ91k圅I>S~]d J+I>~ 5֟ ];j+(=t wa-nڡd_e'*%p n@ ዂVbJQV![ 5(5!CUVҥK;w -TsG'(G 8 ~=}ǧx-%H*9U\ΡJ7$(b#Ftjn=kA{6<`NP@yAm @ @X%g^" {5ʨbP*>>>Ϟ=;}III-[4eq@ =$jf@Wi"|>(_MߚN ,ϘoG~ceG% >,1#J' m۶q>)>>>n]Ń{ʭL\%}y' xNZf\>t댌-רς%:b"&gI_:Ѓꡪ[Gd%@ — Kb<)SV,K1VZ-{qۧ (~uҔX6eYyi5Tn\!@j4 ?n )?*hb,M@ @Ra4ee'MO GQjCW}Mظ.Moqh<6Y۸f<œ 6ں,(%cY99_J\vXmGa֭AMP6>u%8'W2Y@ @ (<<X6 >DDZ eL.- +,#"KmsM.^ @`XQRDӻ͎K=\JN@ @ qԩ$×Ws+'T2t ˟2p;I -1{PnݾHd-Y5j KIKGX;X;{G@,Ȓ& @ @ |ɨ(f v`"YB]T7- M @ g ˣ)MQiJZ~^TF1v- @ @ BAai S6,(JpDjQAŀea Vp( ^ˣL\RNݶw2\£|58\s>g`a$.60,, H @ 7xϰ,KA(P,)\+8 0<_-뉉[9r.,٤*}6 \g72EyuFE=ni[HVhý KC"@ @ |:T4Qe9C)QKh羹-6y#Oqʄh@giJf\lFO!)=СYDU;o\ YBOIYڡV |UO3w`ѩ}•{D>xN 3yP_],fĵ 1|а*v h9|7`A $5wʒb&RaU׷eM?/>>%֩S}Y^t;jciURzb;eVY- $5WV_Z&la"}}5OJ6r\3xWnN'8~@F%Ө:oE [9l8DHZע^91*rkGk}wyAMך;ӱԍ㼢}ϹN\"AkcyTѣlu2ܙ~ @oۥD{P(Qlq~^Ё(y^iJ#{ څ蛉) rU[4RYYԒDxrA5k |IӘO ܼj^x}@ JS"EK|le)8 VZK.ܹ344h(Dzg垛YɥE|0zh\F>(IGBj nc_u(AKA#;cav?:8h ޽+\f۫C)+f+l&st=ZTBd;;- E]wy'նj~B/"<W^ܬ,𮻼"Qa:ͅ9KMxh !Qs&'TjM$'8OE4ojĹ/4_v>P&s=_EDaZ] Nd3DSܹvfߪJ$]"@BQQ,͂O )X|DM#@pC ߿ٳ{1lذutիW^=o/^s\_nX҄<ƟN7(Տ,h ytsW(5)h$ 9 IDAT]>-zq?ō;uU]@dקv hBی8m]*+jغ=1G\6/5Ӗ/ԑM&3FҪߠ>u],(ٻQwq}/L@ 7*@ KS  b JA+777B77ϟ7J=~WL~ B] \,t)3`B:e~` ?Jþ漕8TqȜ;{]Z]$Mf)R {=m0nsBӠmkj_dˋns:L(X70)V!V?`] !=_ɟ8ˣlu Zo }sy~>{v\ jޟ??+-SW2Sr])B? &{@ |&X,0T ¢, j5TJMRxsvv4*|iBZϞ2[%Z;uDO]: Ep}D'|k>ԭfi|є#FtjӜ_7^]$V$TE Josw/\xիghB\_4t&dYZܫ)PP(+s-_p y䔷,+ mS2隳2;/RifRZ;W@0,i bKGK'kZmeec_(=Oqj)FW].귴+爧økX鯓f* E U]̸%,Ktp".!E.lx]F R%ۺs'+;<=1!]%)d.WDB7(yƥ緡XB꭮B)ĥ_JRMC>P.Hs6my>d ^e_y?w;~ĵ, eG\Y݋6UZlHy !?l`TN@'UJgD 7BBF%k8 eWS ~^uW5g^5X%&ǁKN: aRm3g[e܀4دN*^DnXo$6Ɲe9Iz&CP~Ȟ=e֮uӞ/97kqWʗQES29;䩎4A3~HV*LRy/,K./m*jPAV+Wʻj(^DnX<}oQ1sJ}Uob]9 Z5#Z s 2;qəE\P>ut,1S?&H>WZs/HOбve˖͜9ŋ˲Eg9fuHQT>S8?>}ZF >R/LBwΝunٲSNfveT@A4Rͺ0XRP(]ұ8غuСC_Ӥ͛7[~< 3+] ij @Ri΋*.gs멃g={HZd^=JFmU׼~:(Q9ޗ+`kZL̸+7s Yj{8)dn*钎U\O5Pv-T{SucLE"kwf[6}X>S"3 zz3MrrHlk`_zmwRYw4K'OhDgw.>4!36?g&yR_nr5+SR@#pK}M ~vnLWUC!p[. XCd }xRR@MnAqnYsUdJ[G)FC;KJpvןL@.+X.l}a?3\$MI^Q.NʍJfc /y9Cfֺ n8<ď0_JګqK)B:7T@( D jG36x^KK߮|58\R^'v#,,^,S:7EӛW?_+SʯoaibŊão߾ji>&bY1^.ym{vRgJ1LhKt 6^lkt'q^uÊ9ry0Se96دo߾_72G*#ͼ'͠oibH61 `B-KF%3̞N_)!Ścs:UqSkZHRdƦI33[lx3cs-?kYq'Xn~&]Nj$n=+K8ʲ^T>[j7,js|}JgTUYU^<8h9)uyď3_P Yrrx^?cӘG0ɏ_pO*<=~̣WTҸR#^aS{֤۷[cKxhYrɓmllhi½sT̫sv,"R~ɓ/^)5 > 4J J3fDV &m}'f?^xךut{U;ZpۜpsڠO[cO~hq}\,(31AeYG8ʕ =^/dQf%k\Y' *Wb[9 MZ0<885EA]q{V0=۳`s!LzKuSE7QrH.u*[ >?˰Xjq𒽣}-:v0*XWH#z99OyiWٻ΃Lz1!u{NZ1SSM4(gW2Ho0v#wi֕. GeN|CQ->'ӣ! %bך:Ճ0߸-S{ީDŽٲw1Q;L3>.[wfq-(|_Ui.ĬV:eYnj>/ScЦ!>5Pۈg?nGsǏVv.q /X4u<|#w5,IЅc{4h*js3{/TaZ<nԛmmܝ/}cWr#|agoNvٵЭk¹?tog~Э{o$w ˋ/^?6U<5ȽKoj.ٱeI.M%&uu/R'gny)=ɞӺBңd¾m>S[W)Gznj[l=sVUvqan|BŨԋ'N_L< %3%oƩޜ3buQc0q~ҹlUr&귬)2#ӪgVXq9ea ow'%V!kו͘1cSU Ԍ**BeX&btZwq҄5nϝv6rUwWF}3yzÆsu՜B;""v^̋9Uwܢ# t;LXBgvX-}u% [y!C-}]lry irF5;To9[Bg]"Aً&<:kk=uُ]ISbLkI9{;+WxʭS\Џc +qoChX_;N>s)^Ds2h΍̵[[[sq^SsC(~[C"kNZZڃM*St+T)B˂ WKdaFT XUp͈1#SOݚjX&vi2j١230B$w z:Kfe`m#*T?TQQr EѸTXƒgfĀP`_<^sZ,xi&cEyjgzoW QH/P.{qp.BR"iu'Z'i%bt0j㾯:@أ圂C9ߘ zkQηi@W5q24W`rkrϜo.r. {wyo پq.9N7kⰷlz.DҴTpHcKےq9/1O'z607#̷@ ~I~{ V ">aˮ:@?l*{ ?ݽ9㈗++bNV5bq1I]$kqӱ?|cmfݻ9]l;M,]Ɠ+?QT6:+G+u<#P&k7#2yzV%|SO\%##O!BHd,"1eFFBVXzu9h/PlYo9W`z)){W%~(;wQsSRvO{̖53L4vF)\Sǽ5`N=_?JzSv@Z|=m\7S}}ʙa=˄Uu՛s+lfk܎iȟXϲ/_|Y[[}`լ{eY *@/eaO /]QT&4/Ubڱjf Tsq)D|E_d4<eok{'lsy3"UGh.Ľ,;CQc}ٽ)`&S(KWlKU0̒%,pi6ѰH4B?ԢKu += )uuhˑ3k&ERdQ> ~͕#0ʟ2Y 8Yla.Sf|:..;|igXzǣ!$ k}o__ }CM(?n圅80 Q]Q{ٚlcw M4WϦnN'8~@F%Ө:= x;s52yG6[.J@Ӵ.$[yX~*{P{ t;^tӴ2 %NT/BV~J/I^k.۶m6mѸa8*MyT@P| |T RZSp9tI6,ȄUF@mr]Hy5a p\HZ-^)9*-ӫVj| K c qY^~i>$??2u\͚y6=/#\ڽɚtP]UfE7O8ϱwP A*vDb<*K<2b)p\w 9WOeYùU5R~\-rK_ Z(tQNt^$ʇv*p8=ODw IDATE޽lUM!Ig~ QQOCdž x;6:~י-KRfnu^HWjjX6|zM ə;ݚU{D_}FqdY> 75 C 8U(yF|7,ox?)Q^+5tXw=P:Efbc~8y #~En@>wvJ&稳d))[AGrntH`ո{(c"3>*Kquˣ/n][/4e 76\Ln_8֗+m]7y7jU:;Tk0r?qg-dEnZj@%{}9Lr4HI)ʗ6ƥ݅JhenӉx t ^xs=“zȉsڭ^HcT;dk[8&sĤ((E„~/qsAYl]J,s bPc#v<ʴfS>V5 %vFY OZ&k米zR( dv3@A!]>1PGAwvl~VveJ=R_w.>Y]:bP] #n?*Fu.]ŋCCCGݮ&#&(TH>W`E-%@zmwtJX[>V~Sg0&Yӯ/WsPXq.*|XD|dkc@oaZ`vys5V/O 1pr̔k4jҸ f5kf"~>waXi9BV_{ւžU4Ht0L \s[$ka ~anݠe%Fۚٻ= w,[W!LXS2BZ>>V5_qۿ=w۵r/O[*= #ugzV Hjkv _egW66`b@/jϏܾHd<4C󀞕_))-[psj)KHм +WG$(:gGFSp7ͷ>quleeae,Ry/ˣ\?ƸyzC g$fXTí #f`?"vz8ΞeO.<ߟ9^ԞGLP^Ze깴JE]fe{HJُf;5߲ Hs5׶"Ή#ß0:zշeSkIDQ kޖ[kWGŊ-`Q_U[,MYN/"m` "!}reI',ަQ2M6D|ݹK*%0pLmgynn?-uMN;Rd7s93TэK!}A;K[eS=M[/kR/b!xuϚ*6@K;Awklgceo+pC7yo|vf<ì*^!)9=8vʲ\ =̕OTu0&Qk)|ĀwQ}EV@FRӶ⃵jSK={y{y%(NgiP{d<@ZN/Vzв؋v}%ǥMf- զ 7h4jOZ0<8k}dT#LiW^seS7,㴦En@gfp4׈մ̺ujɛ:>K OD.ɅyNkwPӚg玟{CQ(Svmwg1V+C:z|FvǥqziA_Sw=>vt^2mZұM+%bac(Q <3ٱȆL]UK_x_ljtknhg;)۬YG7L k;=bu@ǓV mVF$OINq~׊=(y .&7LvO^Ԟ</AFk@OM 7~DOSꕁ~ pk͝,RܹAEBygWK-衳 )5%B 3} g0ځ]đ'/o1x~vVyeip"[~1![6Vd-{}jXP%?{? V[ĮQň%*hlX%bSTL&Flި1*(G~أ;i-[(ĉ f1ZDQF@,ҽӝ]95p.u}AC1q^d Dt-=n>#ۖ ? a^fChMa3Bqy:Lz%5I2xG)2LP1)gf iJO6wɮXgϒ.]_ܕHFgafSJ pn8I3?6iUm {κ'O?V ZpQ} |i&$V*0nIXOv(\֮SŋOTݓ0z-M`9FIn:KQAhrOkA[q&I6u\"RҦQY{1]7 s=Puwmyr,1oV#3_ﬞ("wcj~7"ec[TT_ڔӆՆLKz=1}w쳜ɫ"~׈. ouG*VWBw[t.Ro]V'K[4wnb""HRZv4)t!zz<3Q8A'z`qBQG|YKVVj~GrerǪ>y2hxzzN6mܹk &m+_۝a Z̢&1"͜ŌDd"Xҟ;xU?2.0G< jurw9leK.[8[{jgt_n_`n%wu*#pI5V)2l\@'"˝']glvX@ mk]عv،VxV׸XI֜?#MBskɖ5I'y#KgkU.`mӱ3}.7Ln "nrR:i]&ץVG+S;Xt遌M>nHfRІR^}/8~XgXcJ5]L89שDc*ϦJroj6sh/;sQdxB JI>3n" [vn1~r ݡ%τžjo_3۶~_kmQ0HU\&_݂ӝZwF:pݩw8[ɧʤ}=e1pQ.E1f=t("/ѩыi%>R"aAlhY$@sf9DEg!j_3f\8)2>^e3tM=,șGRƮiyo76ߵݗ릶+҇WL=X@ZnGơ+xsk˶gTө$gP֑ \.N8NTV t}7~=k>Tγݱ*O>]'Oݻ'\`L۞6zs,,"p! pd0h˃n9Z &"⅁JjτFGMGFd91mvuލfեD193ŅGދxjd}o|TӤr){5JO{/F\պ5yu xjc7mNٹya0(J WW2Q?\Q hA 'YәA*LruS%>*<8sτ'q,M4`l h4:os[T9QBD/Ng42Sa&Oͤ=T> H/Dcի*J&TB ~^2eXD~ ȑcsR[Лlh͛7+Wߌ?ydvL>}ʔ)67Lz>11l65jyڿe+JD-WcYI54.(7??7x/JO:.nܑ٥_h_BB ܩD%@)_ǫ|ngly]։SSj܋k܋6*L&g};4I+0groFM˚KkgIOꌎv\ɝJT/~E&ߏXU+Dܽ/\3MH=Ȣ.ZnӨTبjd}Lb`ȴͽa}nOT*m";Y8dz0m$W32b9+RWlrpftElH.PH?}۟.G7k֬G>|xsRJ!}Ep&l`bȼbg>nt'xPWkƽM籟U*`|v;Q^StBSm ~[?;~@s _GnٛՎm]!֨d[Z= :)?3_ɫh}ױ; kVJ#gLgMUwN|SK]Y*&S:Ây|ا/ hki^Y|'t˖12?|44qv:޿B zTMW[*Sȧ1a.L(A}8o(eBlaa!f[WB߉Ndѽ'7aޛ%W-[KKϷ2[uiKXVm8M"O.u[uFA1훖MtQ5`e)ϲ \n,!(#?۰^TM8… ۷|VC lyŸYe?2\7,q7nXhQzQ. "0Y`EpaPBrN8?[շA^;Y39wPkw7wdbJAo(4*cUB;7 8(oFϖ:euVtޯk|QBV[n\0:OC^C=zR /4 :WjZuɿVp*yoVTeD$Rjzyt#rco֬qRnyͥ1:Nɒm{}93 0 28%=(G{ݦM8p ^q3fMVW|4MkLx-S9\+λOU.# *rHv郠@Qy]sJ2@tPd!,zw3_^ns oMw`0pF\'kK!ŋ/^}sTR3zUKjk~E{OagggD&DE930E+8zh哣|||Ο?>UDo4fr1d8Ը*G` ?trkd#|T*T>̕wm !գGv}嗟|IA;3okt 瘔R29^ƃ )Gddd ҭPÇ_}c2Udh>!B!3ggn!$A93h dP2(AewI%K|)FB!B!؈1c pJi dc=F4vB!B!u1Q9x + pga}U"=m7gٗm KE~H~8L4,Gm9˃2B!B!3908CT0829AU8Lw5Ue OTn߾Qgx́û>a \i[9æB#-(RwĴ{ B!$*&gDc/'0ȹS <𪆨jU@M#+/iaԴ[G{V2~~@5t !#By-10"dLsJJ 8x7Bw>3뚊&n'$ӝ]_@]M y^<e=z{)c[j]Nmڶ5m],p=R{珞9vMX.LsgEJ]NaG>rztd$%jhyv!B!BYaEabd-;WJ ,!ĵYv2`*+6X2좦#v(S<`'Tu|]/DZ7i*<sy991|JP/l-սbZ9LwO\<3Ȉ B!BH> Z̢&1"͜ŌDd"XRw| p>_rtܰyӛs0E]1eәR׾k?+PR^-J[L?߽9Tݫ{Еj73 VU+[UETpdh wNܴ5 c B!B+a``-hBZ,_ K0:xdL ;O-ءAN bi14pۍgtYF%S%xPן3z@M; zǛFx*( ;Ei?$tfci wu]%B!B^Qh f@HI2cLawɫ[ t/Ul0k0ktPKͻIu10FxfH^Ө4g-!B!BҒ 0o D-0KR|l:N輩h}`QwDk ީc賘58)G*+\gjӠae=݋Vf!B!BohfX "Q8Y4#@>QVLĕ-<{aɫc+J^3{Cq!r[/*P?`2*y.B!BɎHE0f}.8a x,* !Q/]ny@ ΐI:+/;~ӧމBB8;oP?W7/WoD!B!Q. "0Y`EpaU8T;)pQ/\S'rXZcCG9DǗT +yW*4}ky\Avpskw̾ k—s?n=WCgY_N-*]~;evy h,l:1b&Xo2]lRkϺ2l=ϮmMgo¡K΄BțLdJ^< CaQA@IKf~[aXI{vhВNͺg|RI6UJKktF yl׼M 12!t49bDI{t-;K&kAGTg!s8:waB¢,"PD B1b؈#FT;ރ- DWF(U7̘tQ1+gg +mKӮ~G ջ Z,Ƈfno3^ZnR~b/It;naB!o2A1g Ar0NYR`#{pp?y7}e1Ӽ ;>; aҼP*;E-5.i]uG*VŋSSOy ̞U{( ^墂O89QSWSnE iѷ3J*h跖MT>cӁ[*]j[\kM=II /_ *Q5ʖ-?~jÈERf脿{R'XOiL 177\CvѾ/D=x c:xY|>\ޱtقDJ9اN2HgMg<[:G!wA93h dP20$Ro-{5KZ R-tQX(z_ߴhҼK^TPH+i~eZagF<F㤀y֭kpn1~jO BT* <,U=/~݊fx5{YrĨ||lҿ.yM]kuKwoO{X]ǧp6a~㏛{8&ҠL"ݭ?3"_]+v듶锎QDvKC1ݰ/@x9}&|QѧcbkCW`9g^ ON?VBy{1&cL94DEG22@)CbW}zQF#M&+>4=]#Qyl:ݳ8B-*+I32PQ-& mz/j`%DIP\ ݤ[LOH؏?^K7ZX>C: ^~YPPZ+#5vWo_~۹`t!g|׭f%2&ޣ{-.ع hThؠm3 =\ݷ6hm?[8T-wƣ?}]/5US|:whoJZJ%K?I(5yl? [|p/S-mYt7lz3ЇqϐN2ܣ9\kyj!v)O#?|R{dcͮB"բѦ嵎 mgMQE'WvsO #hFB. (\5LkN.qcǑ֣o/ EdNZz~PKԯΥ/Gˋ0!kf&r<82`p`pdr(8-W< r:y_J=ޅ%ӳų𢝺IY۠9Қ׏lK> eK_jiO9&1~蚩ӫ}~|}ãN?W5Eq)jǥ;VϦ##*:Dvk.VT(Y6aҷ{o[Qbۻ$!ɮe]SwXopl:^HeI-g 6 0ء:2s\}uHkiPPGWݘCS={/m`|`` u:Z oZ;omZ,]aY٦E?0vxۖJ:JV=#]Îf? { kww XuQS[B<n陪!SOCן h˪s쾡r<B!/E92p+1(I>;(!ت 14m=G(nkEO3?2MfZc) uZe˖GUJ]_Rn?FYfOIs\:w:%j֑" 0^ҡՇu빧ohJ7Qڮkrh]~u*~f0}6iRlc&@Ӿvm6NbCss9F\!)ĉ f1ZDQF@,Mbԃx{ZXc^`~:D.}JϐJ hfй܈CSGC.]( (Q) 'com/ӓҮ Z몈4,d}2({0pf <> ATڦ IDATqt_@gZxw4)1cvJ˭~o׷ɓš=YR?9W$JϋfP9jKqj2Ì17|qk rRB!1Y00XDcC@`4! sd IхS N_c0|nJS眠ē<9Į導*e"U  Ic+NkSd6`MnK Ҳۨ4cHt\\5#atn2/IK;u&RX+-ҙ>;uۖt޷]/w(yh1gx9ʆW*B!&hDf#-dM{ o}^Ljx&r_[5K&M}?Ye_33Ub=7*v¹VMzVw x7vF)E!BfOu:?P"5<|:kˣ;XV3f;?l\so'}膟 7"/Ӧ^;&m^~j=|\v'3.w@3% {I1?YS{}klq(/4Jdw(/!Bғ 0o %M3,I %}$DU:mUe[]{￷N:/W^zPBЎ_5Rj*Q,GQ׏9:/ Q F%eTDKԉq~ɖ2A4 b_]pwӮh%ScyDI-똷ӲE~wHufMe\܌<5aK3"E&80rD!,"ͰE-F,p@iF 8d|8.f|=K~ah9ŧ{]驕=lByQ."QE0& ̈`{[oߴS {R>sꕽ8gqi.ϦBYxR*<<.&e_MۡkZ}BUոnU9ffh舰sG~Y3]2/Lgw]Sר֬P\ț'*SVk@^qWoG-)>WeY\Sŧ.KU%>y}˂YǞٲѓ#tYIv~9!rlt۱2.;yy׌/m=~!r̯}v9n )S3`Rpl&eL/ [}=fݫ] eY: " ( I^3eMM>盱mbZO[} YL_m:z#S?w?9hNt9)>ٽr|wqn15!UfגH3JO6cAbR@Vk{Iϋ,պv`i}-|?JQi63_w5Y KKVv~++R@? ml:gr.LձH{̃ZVxjLfd!m5̔%s#M"؇ǡL>kZ&3nse(s,T9 !c<D.`<8 33Diy܍}wn\*++]i4ZjmZWT-:{^ѣt6U6^SkR;-LLh/DΟnDLw2obΝ7PۣGߏW(yϾwۺxzrωM)?=C]4Ź:Rpuf2`APj]c7FDCT82:U_ FuQkmD?"[ʤ=pj <*!czzSD-OKe\-Bߛ_%?Lf:=Uٖ&'dUxYK+6<^=!IA/yr]aB??W/%\CvUNfҀؑ:={N>^,^6Io8w*o,@@# pw=Jm(5^d51?Fr]MaYT(Mzw~G2+|J{~՗#B!$_0&cL94DEG22@)CbRrѠcǎٿil W`]+n%*ƣe>c0={ޤ}Z״L=)_zԬi'n_{LQږ%G7 -A?. @kߵԜ#`o&c?t)yë &j#VoW;B[D g '@&pL lX!!! 8qbvniYãL:Cw-QpYfwv&MhϏŒW8'-@d{oUG[FJw,rdAuƘy5&!y cs9x$.!*ݻw6m۷o6lu{SsP92蔧Gdb-(Py%F@4$k5Swɘd+n=.30)y~lhl4B_tpL^ՠp 3>6%t6!"9).[?n^GTWlo ōi֛}tYLSS#)>N9s?nMMoQY ԪCI6ụih}`QwDk 61 G{ckhGYb+{ؼ*fٳC%#JvnMׂny)<< MB!-U!@3 "b"X,0'M'XCT"##+Tne >|hsj [i\Z $ixfDL'\ t<7+@MKs(WH{/,:^t̽Lr>u: Mp{uT0G-=o\xwgT !B!qL XE82MHLJzY=8ܹs5{yAӦMmCUS7iiĬ, rwrԁtXc4:)w<ѿY)䚪f% uGj@nt#}=dEYcꔱB!BHE F l,`D Ĥ3냂̙uֹsk֬M=&vIHL&ETJm:e6=K^oq;R::x *}7)Z}ۇp+!ë.\Ke^3XEZ_3 cdPV%;[N!B!<Q` JA3`X47nƤx{{hiۄ~m]`0GE=ǒŗ KvWS_m]<0G1]KVEg^p ?[iiMV_h0tq7ףI_HN$"/ѩ i>dvo-v)o9PP8S_+NȤPT )wߕ:wA]۱-JOG]m5>; aҼn{UY8)-&]9**B!BD A:N/3D Gp8cs)Ky^H+DZZpIyGK33y1YˈX Xym:5cقQ"[J s?*4J@w/JA!B!$ZDDAE ,0#JDA.)(E1z&Y\`P*2'mu Hxb4@T(^ yD5B-fƴ{zxt+!BH~{䭱~1=\J)-Ęؚ[8;Yjs>+!*yKVy^C w&":\E &&V̡f4/JZG ='}L:Ph׭Q;66shvWcŚF#DuayaB!3A1?{{@"8nUpցhuYwUg:.*NDZ}UAb% @@m'\rg$bHX, V 1 a} pB&́+W0ޑ6e.Z( ]ͨ{ZwVK%ŵN pa}xj{lyIN~hUtp w;7m;qeӆtQU !>3`XFİ@H(%PR:㮈AMԟ8'~Pw_cǒ[M!iK&?"y*@(wW$Y2/&xl!)MZuZeAH,XM7=i.tIC!{1ax CTİa!A x#2BH>{]tMX|i)RӞ?;#GXWԧҫT/XFMaQ\|~-t;HxqZrv.+W=yq˨B%"~ϱN!YXb``^K X(A)>AdbR|1 R\u~>\7|طj3;> -lD)!mɺxyȾy}>qq^.8N= />9tBȿ/b@$1XF* pBK CNhlQ3%&_=iQB)NFYݤ7;vD%Գ]zw2s7p[-EjáksP*)Qwb?y m/jEQHoټfHaji~f#pzAث;w)#SFnݻz9aa0[WﯻUJٴyUPy~CFh(ɘ3"%\/WTi׳iy39WM8wƽ$(*{\8[͂ȷJˎ?O\KdLM{vέqKX~rusǎM*oеvHc_NV 2SQ=&yYdm vS=gu^o!nن=SOBy+:@2 ČBDq2 PpB[ٺqA{Pv/}*[gggzJ'u=sCD{kP(kd|mb̻sEof]Z%JC=,n0f.(IFop"׷r`lǾp@_wQѾ7'mߣݚ/S:nqӅ~O=7X1qwGsؘW@Y0M(Rs4xs{IyG`OGҹO ՙͦu?4dEu탭]?ߔjM[7uc2W_`ע?!wvGHq;~X580Z,zW/c]4[ HlMC/|}}ْ*>7EGҊ*?BCZ[go .l)u ٳgcz@# IDAT˫nT;_dzಢ47*\}*>[)g2ֹrKT]q[3'󉛽lܥ8/ 0Ns*`d6brjăKQŽqfOplM` #]5l۷>4N-D7;w9»ay!]&99v޸K1{g43C<,Ȏg0& P8#Q%KL vҥcՅofD:{#{e:F U1K J9йXe} X0dgshBX^q|<xQI|.t]^*+ʻf(W%czSkW q |M渐:vx _=͹su zpOCľ},rgkSa!+=:F @?(#mݷOZEy;: v\yOe e./$hl>]yz=?⇷{Էe+s[Xy4˂3_Yn ?>02O fKo"֙2?SI0]ŀ SLytEg@[_&5ԼN\f =O! i0y o>dpܩF;懋 ͚~Tɷ.fXkwIiT8l(II eߎO_894Dl6FlvYu8⩬MI{ݩQT&(+KB>gfŲ Z_?ia&A^H]BqC-^wYbݼWwa&/mXrfԃ.LPj=O1I 0^۶_`nGŁ{ TmJn/Ri4| –1$6 F854k ~c~غԋ%<20>MC~"f{{B!rX Q"!R&N:ErRt̽7NH{mp#b7@KgRzLBR׆_Y H'tew\SӞ>3.zybMANv X5ztn޼qZt猝SSm\d}r+vX3L&Mţ{~=~kKlcF`ª3'O'Qܚ4jȵL~G\ {GF-:6`‹[ ;g ca\><-jcS9gϋ1eۃmqe"҆7^fT ωFxq~u"a,dC55SJ0l]яq-DЯKc~ k`zaƲzlY^7!U D ^(+ޜ>fX4xTy%B,cUFj!/3/l\^6OCpg̼c:1gJtmzdHKqy EX4 XtoǓB'ګ,x@N9aϽ)%S?IoMTjslmȱE`HtOSc~VFŇ ب‹dϜןB!cY11 f Fe1l$(e.ǠABCC9;W_}U|l05[%dbpI{N}mMY-`jm l|2zZ=f7nܸqk^x@کSN>07쵗Βxzjh0کS~\"=X:=W4vUcGhҬkx}T*,qBYC%"_&6y>}߷^mO/8[8ak1,X !v8 EpD +e!f!ܠי3gUy…֭[aAgbV4XCz9b|jCe7=j܆KuӦGβUT,LkȠ5,K:ո]zpRձBYkR2{nD_uL d,TiU}ƇJD,2pjd`ļ(Lֹc˷S8帖9CX; E0S3۲3T[0&Xv5$ ݗ μ4Fj1fE۸/fF6g+XrFXMTRu!_, rd#=[QmDBd!ҥDa3klRʞ3*a 35׊&v48r%xĶCW^4"vrO@ByKz xˈ(Đ1C$$$ԨQ#5jCqE㽛eLbJgmezCVh}+ufnXb!+ȿ7~«6QO;gȖ+`q @=Csu2n?ٱ*%V:/&jM_Iص-hMwlLi[NZ:Gbg%$3β70^bFo!PƝ'6;Ɵf[GNռE-:v2" ;{+@S0_O%?nV;/|g RB!3L" b@&cKchhh(R.Bz:/9>kXH93l6` ;.oSvy~6+ +cQce&Ab+>䵳Νtҟ %|ᶇ79VU궫R]Ц>OKSdRR{}"+Z&)FLͧM/%wfpL|JV]+ a},Jj^ǿRkDPI{x5'iSZQ6uGɯ4DT25 =|FcZ}I~Ld;xv6yLϧE$r\^OX;:Z'YVIPDŹZOm+jDQEQ´E06QɌsTtm,?6_X%ӹX+YWtU'BSx"1X2R (IF7o;5(\/Ӵ镜>ٗZH{x1ւγ,ҽu2N7y潡sEeyrr?U582f3Y:oʡwU pv!E$v30fs<*矜G¹_S6Զ3{gϥb? ~O!|@t 8e e 7ޒح֭[ϝ;6}7t~VKKI8`TЭ jZ5j5Nq`Gth\uVvIŶO lsL}[>5״2oޮK|%MOoae C,rDEk7o$޻1nyEV&} um>X>eč2&O}eRqQ|?JrAHQJ)kw:ȿuto?[;*KNןBbyp8:>G9z;m۾DžWhz/Gʕs j4|Z:Jr$PɻZZa?U˦H°Ɠe\ŽQ=|ԔKͽ}<c$'xQTؔ9wEk>w)aaQQQ\k !b=>+iڔsuM"Jދ uBK,1 0/e` %,TpC`u"@l;[;>}9 /չU;"!jsE;^sscs Nm:k~;~qwE"Uo "!b=>d,p&!"Db [e"@ )QU渻"JxԾK} 4p^WSbBH>d0f̎y~޽s{TӺHToS gT-_a &Ч>^:=%B!`,@(DLl IDAT)182y /䢓J!S!4ԧc'ک߸q'x̊( `ń>FTWO!a mb(B!RJ<8p`ṭ z=tZC>ѸIN=>sG? χ$2CO:{k/3vNMzqs)*Aȟ^AޤkG y?5Td܄!.6Pxtϯ'r O9^?q@rrr6~="lZޒj'_=iQ€>8O1!79h)߼_r{#wdtڙUW9HlQ;Ɩu gn߸؃dEe vQ?h_I,8cn-:hGuM>K~*ݻ/0#ޖ51s.EܻϕZԤw.UEl8pj}bXƮaA29(׷l^}OPIi~6%b*^/;`}nY{&y- )'.̼By]QdOO>kn"23]]ed4˼wk%0DDQ'V;:u_^}]}Re9˨7s릮u ̽4'ޟj]7zwtid&-5:gVO~{ntY?>%h"ĆvO=KlɭVZ7I99MSFe=Գ˒\\Wv)7jb>s[}cn'mߣ6>&e@uf+^.(;>9ksnxjx[/0`Ĝ)uu~h$,=[sJ<:Pִh9/m5(,cƯޛNgC̆9UAB>tzuq/V(cãeiIE%*`m7: ___33gΜ :{dj_t2 1ZU_9 2m{0mFa$,?7z- /B`ViBt%K̜Կ!3N쟌F cPt?tv!mc @+0N'?4R5j\ڲv7 ~𚳭[nǮ]tةzg{9FӖ, 623<<=9k c -',c0difm߾MJլ;E!Wx^Ӄc=9%>AjԨQjϛ6m*\>* Gz>D4hO&Z]v Q.<ʑ:`hryNmHd^eY =IE t&ӡAas'AWi2|[D]} :tѽwMKL `Uӷ5.^5rc"6Gx %݈NSU/[ѷuy-,u<H gҼ]HȾ[.~"vv9>6 FK2T|1`흃{(5ɅU)]MexwwtRsʀsZųB&xI>&dqz)[Lli(ַf[[ͧ*[}3Q#>d',wBSN :m/Lww̝t;_B1,8x8p!Y4h3fΜ٣G/ij&P%hB$"&)JC1ӷV`uRGw굻/8NJ͖z;uP @+'K`,W6nz,cbZ  )=f!*Z~04ph@[>L~Ros_wjTE.d6ʊeܥ ˅w'lnzl<`'bGSR"vUa*X6nqF,C%UGmx*8/*P+[Ѝţen}x'usLrض] @_7F#|Ke ۳- j]17Nj.sWh0^1 Ѷ\Bį6Sjcc!o_1 >5!qE&!]ܿϹ°MFGu6q}_+_p1<~ ݗ4/ 9z4YbҁNBs &8/~ORxvzu@|֪·,&_yMxcq:v%B"2oך6R m)^f(zחs)"n+zS,NB!)`aTQ {=uqyJ=!!F6֨Qɓ'Ԧ,?aF7FrgbIe\juN/\ɖe63X,D Qt0nC=ɇO:컥=q#l!CbMy(q5(ձWmHߕEV&Wɣ_8uFlJSviۼ%px6z}+D&0eBU˪ՌՕs &m ٣1zza)OZT(T1EWܨn5t c?]T1 AK,^>|g) =j0mfGcAUVLGe9aX/-Œ<ۙg^ M⸙5jAd!%<^r<3Z=t$ A+chhhxl=\D8۔3f0SKUƓb e> %FyR/ggE1&ZZ +88:Q:lTI-[s@>TɿBAU8q#yn٫hm!oe~SO0йz7H'{'O )Isah,[6׮}#/Uְ.@MrxltFD߸m]KKJ.u5F8Sq8j<=lUa[v Frh;c@@l~7j_<ϽYovbF7MiA}wB,Ïfл Yݖ@Rg9Uힶ/ie'2'E9_h4EU/Z 5HiN_z.TppTanOwgYW+hIڭ+~`[&^ߜoBy0E,D TRbȌ;K,m۶}-K̞FRu ~fTO<=;&`*P '߃ k>'((Y2)=wgOOΝzt3!:N:2#4P<>P><ުV;o2:lT|\뤸7~[{'wSGB49gAӬqkZiSuéթO#;MxbC'g_zvz?z#;{~j6Pl51CRѻi*̮c~(!>ᵳS*U7&6!Eq)K&/*e8UO+HMÔakʜ!2hJ|p0¿Ye-(qꑛvf+\ס }Y>KŅۿST?7o<&o#}8WRRgm ;hE"iFXlVن-Jz3>T"eS#͋rt'2lrk.0|t*(ďp@cϜpXXӈ3#vg{IXM [ P'.l ^3[3 C"vx^W#ls*gQ2zIEy,~L5YS?;; ":{ό/?92eܪ˦cKpkNY=lq!cC,76/Q+F,0/o!cxV؁2E*8!:j|U96F:}uy() DPIi n$b¼sȖ֥4=EG29gri6l݉!pRtQ&Ie B\Vzu똯Z [=gY.͍Ca\B# a?-P_g+@R͒8r;df\ ҷn 8 شrlT͸ZYGm~4nMFeFmxA%[\\&f2"x]!#Yv&^2#E)0](YCsu+{ҋˤTG .9EEV]Xu;z2__vmm~S{ u糾>i X'1>vF<܅:^&O,!/b@$1XF*ƟbI̟GTlм1NlSpRf>IIע⟤RGG^ĿxVd22e2?7/UZd*eR O[,CԲgUlP; ߽.p(R,ذbݨDhXa%b&Q'v#X""b,?Q@vwG3ñ;;7@ >'(J@Q [TA פ*#Bl"`B)lܚ&6Rt@eQ&`NMe¼L{ސUĿŞBsY5&k5Bōw/|m0JBbSm&D ?A-`?^;v#^_h~21F Z$%Csvװ([⦴Q.zv}傼SRRb^ݹxYml*;jgtDڜ@Y^3B|6޹N@ 𙡢<@ B xHPf &rxHgZٰҮn3u^>X3ҝzmc@+jg'=h>wJk_ev+fL>uN~KGj{@/&}էL"$aX^ H4GywQB>OZ?.ŧVoU]@ȫ_-K9u%Z9䷃آy͆-@8{&ߝ'{-Y<^HGplv nSi!!_2ݣ;WXsQ"is-`<5%>=]PVv2+ζ_[=? :ܨ$eHخ݇XK:_۩{N"%E#,?R$r3d%&) iyWǪ ,O{6C6Qf\X{&bGSg&$-*V86:LptK/tYGS9qQ)N< zfù[ PSc`9 ~>h B^%%"y)8Z^Y}j8EM* _vOpL<,O_7x+fo[U*<^sNek6wn<ȿs M[W[6% ׅ.n^بeBv%K B"R;0Ht Tӆ?V5bYVz|1ѣf9`!n%@/Y/}OJy1&눤srhv ZTQ 'e~>%%ȹ]o;[WV-yVbYH|47Q|*Si/s6ZAwݵ{8*kWv%Uȧf>Lv*O)&{%2C;M Z,Kcu׻vtI˟lyb]Ydڇҝܼyvgfl=<_'Xj_rC6V*ވ%El.$/wF'KZcmu"Ghd6cL-ѕi[qV2Dsnb9ӭ16sF16%K*@ P( ʚ!!}f>X:wf+*!%=9 Lm=idgZWl]W i~2Ebo|4?9FĺЙ^̧b= ӑ'̽Ñ.XRkl8nS7* J1(SV{xUās>J>WQRI&%5:lq_.1hÓLZZELހfy<[>GZmɐtnfABN~Tߞt)n;׏0zrQ$3@b1ٟ /4@ٿq',O-_VGk5{3 <)%ɩHC96-V]\\*0 /,mtm.5/\ |O1W;ѬDZ#fuq L;0̽ͨEcX%\)c@  jded;p&y ,i#_3)3ֵ4nȢ;k!Z^@jͤgxY/ygЎ+ڳE˪.n )MjNW}bZ\eܥ ӫޜk]ŧDZ Z@-J!Tm C!@ɛN_dž^|.]B0rAVf\K ۨtY߹Y gv/b_e(CvR*c:#|KMnSJ!6ɓ|1FzګyInlgH2$ l߰\\93q k;rđV9N:7U*_YQ$xY%OW䜍'ĝ۰Njʭ7Mlk8w'ܣnj]Wma5-*iyvq۾Z@;Voh 9ɑF_DQ-/ 4JzH]&}10e݉ezfuk\yG7)@0@ ZAdiV![ *W9\F 7b1jCuPTUM蘼)вjO=mhـȳ nc[6qo0_dꟲSMܢ(ٕ$^.N}յ[[!3_]Q}k $5߽;d2GߍMi1':Zi/ZvQd1@[z=L%n'{B37 Z&5.`~e @y{\lZuZ B|Q7 \qgKh5S^ؕ.L^50w-eNowK(p57O$zݸGA{ %^YA6?2oĎrGK)a^˺vSA[ Rp)S>RR7)@0R 5x5J# P";זmR}yVRo_k.H|MbAD"@;Bn+L(do:Msħ$}:Cf[=X }u#F1Ls~'#YwHsn5{!sbw績 /G,i8`T۫D#΅'-sٲEZt\fZ 'C7l KL ;}Nhgz[-jؙکDŽSӐ/CsS?Bklp}[z@ӿ߱uz \v-, sF-[׻(}=x3)))UFR~穛I{}&~O rnuܕn^rDkkqWG'ѰQʱl0jVH~lTIoi)}a 5OgdVFۓX׬UE" i&QV-b D"G.Ph3Z||jnR`K J0+1q'sSx%= >r _$Q\fW޽;l}djuᘳϩfւbT@ a4(i *4h]NFKbɅuT!*RiyTN ;x55kGg/9,I^}#-4 HOK)!Bvń/vR2ڳyY's3 Bkc&-e}'ͲNܳ(UAstĆ_{1Ҕ Mr<%'D& ;C+k勽JkϢoNvoZ]w6 \9Yj䅛y .LCS.(t-Ec@/^/ ,3r̚9@ @ZkwZycwxE ,}}avM0.; xt, >Kge^2AX `DyK?ڸWOoUCEIxj2gCFܯ#5#S;\| Ν;<}RЯ$ܙTɍٽvu+ц~Z6Yu-kk#kͬąVcu"Yrskr3^Ӎ}-~7 @ Iܴ{vvex=IfAᬦ>B~\r0wy ɵ |_A$1%(0[Aa 8`g[f6=}%0_tpU=x{i;2bnh8Mš)#ePK >Gv>1X/yߢCR*7vmauQ `$/$=|WF|{[zo5+Ɋy'qSX=;<Jto1!~.갸v+̞vJb<;\Zp<6&%&Y ~,[5,TP`K}T̉ Ugu}yЄuM9[AyjbS=3\z\v{a0 _9Ín脴(v 4ak('L ޢ+H_q80,TFH?;\ 3r>,mgв/N8[X2;hsK1n|<ʉ2OM9U0)Fp#io"ب'|h% _Mw.33;ɍhUmwu( WMx~l7c#^@v`؏kQcG MN؎u {}fdGb*FStuHIq,sP}j:6>by|zMxv;侠k*]k/(>Ť 8jJ< [wqIÞΡ[_[ue3h$  EvqRf`[GNZvjjk5 Wp{s.B,0xJ^nZ)_f߯v{wZ@;5iO}vyakW oU9VJ%z9o} ݵoICcܭN;0Ԣt&Osu=5#df P$3rU)8̒ܥ wpʈ5vbZm|ļ#VB21OŮ+.׸< nhF"VLQMmb9!ڵ}S 2mD_c{-ܩ"Ncɮy=\D64k$ړm]|!a]=RKB4^*۹o%\݄[;Csgx^cbߛmC0VORg ?IsB|lPk`D96V~% 'Lx$JDk :clUl5XDvQORFAaF81bqRTJOԅE,gH2'%.Jxv7\߸j2Zw}%C>]۹t(H}+̵}O5AaUnϬk.M&5zV`fPýdw]ϫ>,&.=!x?icO.{Tlio)x EVʓWp)@ WvsY.{ 3_=s8e-Xup\b Lw8$6QbPN4w@؝Lr Ws=p&"NJs*Oͧ,9MA(DA(jsᐐrڳq^^􏊪t_~ JeLH\~22"|*mn vcylfd&g)DB+ {iybt&:5UvwcF+ }SJĵMZy +V:8y)9mގ04t89+*^:'$)`$+"tZ!>hٺ h "nY?W/;Tg#p5ޱy5Xms? IDATW2I?`jG[c2rB]n~V}3e5 qhވGTi*ރG aUߊZgxݢ~yaCr8jSQh8%:2%Z<5(ǽ}r=j":*Jw q3S.fɠohhaooc\D2o-X|7ݫc JVUҦ}k~> ؓ}Ȩ0O-ɈkŬwX]uOi4YY:)ɊT: rs)2sռi ZQhYԩAb$ ryP-% v'l-JKA(:J bر4KĒC+lb/zRA.*g疳"Ƶ(ҳU,<-7L6;ʎ%P#0ڟl(z}+] ^>[܃u s 7ḓTcJ9w^\yxw*'tϯ5zl'}9zݜ٧cՈiwNsfcȓoVk qLpfX#Ta!MN|w\BdM{A3K;W">}$qwp, uT=x kz<9xiE)ְ-(5\00owi̵8ʠKf]'lcL9Gis rcX`j2N(>@{. Pc"CST^ֱm9\hUg[5m7<Ę^ӉEm!mVʼi$ ZhcJi,T_䛔@ (ֺy|!(>x<PB1oQKu;:+zMw}tBCTe};|, o>=j[w>a/笐i\Ӻsغ4/̹ U7vh5 ,"q 0bNĘO:GkՅGG 3@Xgf=k7N 83 NXhbT rφ1?ޞAfmP}}=T4ka ppmvIeJ-r-Td5-o.abmK󷲭 Dy:Fw_>`}K K3"{'Xe9)5%oYYжEo[x6)O,IMU#vYtDXKNp)œn5o)õR<~9ɥ87)@(ϣX" @SG%|Ei7^5QwY,.?Y#rqh\ lsmj\9;{p:^Pj\ӳK/\9ci` sqg듈?.ǡ# 4 [>7cުA)qx51|XKlJ3 + \)Ы6Q)CC\2Ѡd`y蛷4ßƖ6?xw*+6]psIss/}X3 =A1S֬ tU M8MjezJaMW_ԉF7H,((ܺ՜iǼaV-8Q)MEuPD-*X C@N(8nH Ih@w:h\i7=Cɛ>wa*6:+~_cy6'ghhh{E>yA+ L ^x'^Y_Pm5Cn:?'ª ѡ{Lר-#1N & _^Szg<:;AӭfM;%2`8Q)M濜pI (P#` E+ZeG5h},;7@.Ӈκ J~ tSbƒ~_Aɛ MAz|h_vo[1 51c/SMfvN 0Ji4*8V 0|J _9*L>!8Fݫ~^>mW\o i=$xބ ?wJc@hkaj?v#.=Qa߳Z:q9%1~taС o\鿎@򻙻Jb<?8'o<z%͞(#ٿ7Kh=/#8=i(`+\9wwUI:)y377guQޝ37J7,>9u]Fw3R0evJՐ?-ўr{ҩ}xae|֮.i1&^fFuk9.xyzV вWs,jbs*1:ь$ZOXmѡ_ }}ϻV30ja^iT}5( <1b" |>{lQ!HLx_c~ r֔W͏Ϭ֍uM.>~;Vp jai# msW<_a;lQE?!-k7e'ڱ] \fPopȉc/nk'5{S-:}rMkw ï9׆lk3_Ki @)[2 }}z m:\Ε,j}NzY{`ڈY8n0.iIZ;=vvŐzzi^5OmusaiD&{n[raK4s-EE\u|!W^ι*@ɛNx߿ֺX]4Tn-muj8|* ؜1: K 3;ftя<*j}ʌn5g1/gƭ¥2y75V WРAh*:K ;]|l)A.`Dvv2itf"r[;:qqMV|tHZkᓃ8R.OOI}'ϡbαa.6!%Dn ioS>("Lf+.l,'!2ߦ+r/+_w\J. O{ EFεZOuvwVTxwg&'$`DExNjB UdgbVbs(N,iN>X/׌n5c13b JgP/'%ħG =|VHB[l_ZPRHm@‚P$$&d3GGG!8$#@oCXǘj9j|./,5,,_{{{|pd%< KNHgM{zDž ?dfT{J@ @ |V0f>MAE@3NFq}6W nu-lӒe`M@ @P1`h*SMF2{*5TMQ,Z h\.ӫUFKo%QxɧN^x!@ f*i5T TjB X!V![ 6HاȹG@'K5'u=/-*~?H@ @ 0(92UB SUpoP~1ݻm-| EܕO\zظ1cZiHx|6U4x`aحߴ=d"(ݿvs7c2XܑKr󅴍p@gif0y9x>>t썈)T[; յ(ՃQ1 9ulW˒؊^ 8q#{ W\ z;APޝ=`1>hwOvW3^y˦bD:u/g^_4圵pΨY;^XR5O1(Q,?Q5&,C!S X;qʨQj֬YZ~۵k9W p5e7;mce P}zkڍ=!.M.;·LwoV[b׫NShUžBȷIe!ncIn]; ]*^=n8ɍV@s*AG%y=4eeG1V>f$ܪbh7O51vӮ֪im#K ]ܚͧQˎJrAC +% l7j=M |BB_<|0SZp|Ǖ+WWޭ[#={tuu~:,kƖZ(.Hը&입ZY_ "2rtȤ QX==3[_eSJK[VAP^c-:>C{gС^`ķgV^wPʡ[MXśw? \B @ E (Jݢ"%>X:] Ě5kYׯ9!sw3.6PTUMx"SJ͹P3r\RKN`P#F_iRҲe:_^ҢcҢcRRSRcbbR-8kK lcqɜ@ @ bx|(x0" V@1`F>8CBB>{C3hUI*+4RPzJ~ÿCH[~&T6%!"MYg#e}yL*Jm~}韫׮] "Eb՟;::@ @ ϣ4D|r}'w \lY@@ϟ٬N'W4iEC{[qa::*upXw5C;E_ʭXGR"*6&k&]-^YRQe"dzl/QY~жUdB@4hǷNe6D.Ǵ[A(G-95ź?k3gΞ5"w,g /{Eܕ;7lDD׫SU9qPѽcG|dB̡]NAֶ=llKJ &m?SjQ[l0\ JKǧR;Q@ FFbhdԴBSڷo_mrq[tNK)F$Ps8X]N~l?1Ҍˋ+ټ~:h9rLiY:~SNv3^ \X4^ NEÍnD (v ͡ 7A| Ov"ȋ#U 珟ŧɄ#QUe_MɾE:*iD4烢AL|I6hCBS@ F@xTqXRP($8[TJ]焜ӨQ+6h*M)ivam9[t_ v=<?~V}O>~(`Z^U˳DyNDӺG@dB 6+ɵOnV:9 ʟn&>j2Ͽv9`c9\pM}ڏc@ Ra4(@i*5hJ dEN6~r23 "19oObp(%JWH&6&{*==CVV"UZezzJrr6 ;V6OyLOd&q #co/r&eP @YnI vroon搣7  ֳ\TbK$iZh(un(لV~g YP n VolQ!BٮK.=9|ϩq+]+^ 5`kr֠2koJp IDAToEDX$Utu-uYK PR*P*(5RE bTX$=yv$kC&Hȟx@f56M;Io7"΅kŸ>|˺vnҨezwtXDv?j C:(<[zÈ4E@(, '?vB,/y!iYÖ-wlRNfl2hC& o.w!ia[U-?/= ;;{DTx&Lӂdt_ymi'ͶW[nj>8@"lqcuy6r^4z]8 esjce | RGs>8\UZ`eJNK*vDA @ )(w(8>?uC2AXmUIK֙n;1h/\*5fE"1_r9 vWŧKfv`Gɛ\I'Ľ0AѪdb$pB&Q#M}fQs=P0gϦh;ndD96R \a$)d [sxˀ~Oʏ5NN[Pظ78N iC yOB<<xx`mxPʈxj= ԔاQ.=-{hd\rn84QEq8zi$lY@9Lo)l*$=O9AMJy;Y1G]B )@(>D@L@ 1bk[Gk[GF-1#^v`l·'O#+Ҥs6]}uij[^d_ӧ$._&ސxg0UcМ*>coMRP7d-%ԯ({ v U{K@ E (axbv<bs"g"{V[ῃmᛚzdR~6"1hٌݿ oFYCE 9|Vr IY:{'|0oHUK'IxN١޻Ne@ 1 6`DbC:OYqZg[Z$ڻ8׬V_ Aɟ oܳoјB”c4i +/E!Mnyb8hRVyhBn*nv&1F6H+S)}ׅ=BMItX@  G|x4!D@bUp(_l_eĹ`J*֣H\TK,1@ ??ZcJgA[)E) 4rOdZD1{SHT"yuv_ cHL~!%/$7ː!u' ab y_ielJ)v#BbS0Ȯ&WQADkzvڢEVj2҄O]ټ)*+|j΅EFLLHHx~ʑۏ&_6]SVn,Py/bFpK3cWS~ NLH|s{,HTcbCs o29jdB|rfr7MjmJ-!|?$MK6rrҹiuΐ)Yzs y?dY.IL~](5*ZD@ Or<4hP*Na`h@JIZpb=gǖNy҉?pB,vꉢcKL 9Rw K'OލN~FR+Ԍ<3 2ot:BI쬡*;$λN5ȯ0YKwcssx;L& 2$cn)xJ]%B!?JL$!"QLG KFÇ߽{ǧA.HπӁ/xg c6۪n9%Տ$-{}\+8~7&L)iΊM_<, ! Tn*j77u~;<5͍E"%'Y#<\C2n-kFZB!#Id&I\DM0,sk%8===Vzر~mժUES!ȴ->]B۷*r\ԵSnݧ$2v䟇}\{hQR:teh`}s=zkqTѺG!Z*DUri!B^G IaM0J0zY`ˆ ̳0'8`N:ٳgk֬y^%M'tr3Ц'%ꓒ iE$򸕔 6,yw 3&B){ U(A!וx,I`LgHp2IybHp>|ZjY ~~~ǏoժUau:1GKV oߙSub™?VZ\sVq={ hwRE!<_ub5\72B!$ $1 FY'/iΝ;EVg骺>.9;5j8{{q8ȠOWy Bk,7)A!חL<t" 1PA^\@1$8¬6^vhF|_Stɝjom0c,B!5|qqB!ObRV P4`/A,ׂ¼>>>7nطo_֖]vݾ}u2;) ?ٍ<u%t="K!B!Q,U&xp8d)2zA>}ڵko^reBe <{MVi7Oq@vBӼUjPƟNim3!B!ҙ$c9vB!B!2&0dLs  rxeErP +B!B![$,= &KWw !B!BdF0H&(0HMHXfi!`~=9ZNX6Jqw Z@.J2&HNk*|&By & & XIga4!`yG, <ŏ/;w*"hc^˘ͫC!gQxr*Ŀ $lk4P߰gլbU?\Z6>~R/3E&B!d%I"DfD=2L]dB^ ]旌ڱp߽ 0}KYI0Sl ɟ1bD}}hQ q: W1=}G,=Ze7<@QǓB!0JDn "$DM0&Sfb$!?N1W{O,EEQyPIWtJ՝^LϊQ~c{[mRd2sL-wƏZVrŜmԨ%E!GB2G&B!$0&%Mz,p0AaD<JpB^4 j$Վm} acٴ@Tw3dMdh߽O[]4Fe=[|w3ߠ_RO62!B1% ,O N&a!I0_5VKq=*%L;?48H]ׂ-Fz`l侣GOz٘Dܯm=r^w_%Utp$hOA{n$}Sšisߎ<Qڕ}9;{`ܭ#Zݶs)2_yF3<(\Ng닣m 6h/?[_eSU`Ǝ㯄LpjSh:Wr$F۶us''q5n*흚ڵKY\nr=Lika~A%m=wr //Rn wnB^ڃV"B! #ǙxfD c" .wH9r41N/-qF^[zsm}G~܁,?U,% @~#rt#%bQCv]V!?F *3CXurs?`y @܂h;s܎]m 3?ZsU[j|{mK5'^ZMN>`&M1SS[L<?cNYųV]% ٲ8Ĝ:}\sX~8[_uAލ֜<t-Ӊ eeQ71wKL\>{l, cɃyg |R/߮s|Ҹ=*g9J1Pt> S B!HLRJJ8181%(+0X2kzl-+k;[.n5d\Ck]a=6hJ+ɗ J[6r19{RBJ$D=PuZ㒟;^79 TvfMJ9>{|rNoV:lw*}W_s[)vFl)v`kXݵY~{~g?x9qb"iΒpX}B/,)RvXͿU1gy>퇳űogQÆS r5Ι]\2%=ן)֟T,dz3,7%`\™#q.aߌ~?}Qeqod1/$)"~`1,XVȄB!L83A0N=WyXEER]¥[VOu}x7$Wo8eY~S= ȯs !%Yy^&  ]&5?XIyșN4H)z0rwH{->ma|oɐ~l*5=KL)r X; 񡅖w={Bg$2*[l=x=ӡ={N_NW{Tթ֚S 7Bw::z8|zy%A[:]N2۶Jt:{~w[:CHV}IJ>?iڋF|UȆ(<&]]g IDAT =x83i{gXiOM/pY=uh{2#7;l9+Ϛ˛7pxϦV`H^**U㮳vs?f U?=8?b璣sF*+I7-P !B$'A L8W{mGLl+NH3[@ٛgsfۍUa[{̌q6Ц6MX,L䂒o-<7pjU36oi\߈o*n)xl?\1*L'{ 'f r9JC4:gFF•]=i^t33^`&ȄB!x pr|ݻ>>>EjWτv;萎ą,A6K#U6i Ƅ)1 Yѣ}K;x*$d]zVs3=jЖp=+QT3$^6q~sK5 BJZ&alhIB5O( d_!V(ߢIeRf]xxUlc:?iR;:q= qSN*+|vB[WF9e;$}x#"8.)pݿe .h~B3g^8)IA٨C~?˗e% @[%Ɯ5n,OO{KIÂ:"2!Bk'Af%d z z@L&3+G``L0uԞ={<@k Bv]iyRo> ie7ۿ1N$ F@{a,yws& N2W"pUN#gewxR*JR bXTBۻuF*^x+Ӑw׉G5-fXz3anlYe=kOwgXb™m۳p(+Jל{PGb{'c>{OG]1}[<Ɔ.3ݾͷ,oZ0υt~!(B!5$3A2EAzlF0@2+ÇWVSNY["""?ުU,?k(!Pl=xK i5±aw/Shϴz=LFbCb 1*v$Eą>^/3(ByF% ~֧2#Mgy Z 8DŽ[2Ic;z{Ws]uAJAa@+.]%:shÂLx9Mbh盓6OyA`Г~¾>9qɨהsj6-&vҐ~zgXM |Z W7E_o )mY\IOzZ H:YK]ةbv3ٸs*nhZq+=2!B & & XIg9 V===ܹSO`>ס d!oX_-O[ [Y K !/.lcɉf'3lܟWVhOP^?<-DfQ.)woTc^RD,8w,<֎uТ+Rp_>aF͇+ċ@Ygp >aڷQצ5 ^;q8F,y^rݫ9{9gur6O߭ܩսr(ddB!HI$=$^d0 0bvbv ׮]PBmT}DmԱ549jy0# Ѳ2;s½GkWy׋ !/\!H$Ju>3:c/5WaϹebE~,Ԕ koO^E/ߦ͚5kֱCOf-v_j@_>G[p-ݘNV0u@ji=Zv`2?=ڕs?5bS'V,i=zWF5xHrFrGesSW]pȄB!!Id&" "IN#`2e6{=_k޲k׮۷onڷa֔K5>A]Go  7L0eA6Wl K+T*<))r:C lR ިigI߻ɏzhTjg&l'T`%s޽a. k+Fʮ=q"ͣ^Uet Yȓݩe|xc⓵\.WZ Vi< +!TrJU G }5=HN:NTܜafJ6u.tӥ&?2N]K[! ٹI_ЅL!B$0&%Mz,p0AaDѰųLի tiOOk׮ݾ}{ʕֽ;4Pk -<2tCvg.0VUJ pv?4.}p~4u␢ҟC~z(.^לutI]C]Š7kV)gT䶕ptrs+x]b[oζB~mB\(*7קĵה׼ȄB!D<$0 N&a!I0-eb׮]d7ll\ݜ|ƉtC.N}@YHOz;lHM7_O#cBA%[+vq۩zNwιZ7leK !ZO>vfgi_ۥzAB!B^ I$d$F`It"Rh6mL!V[<4lӦwTå:|ųt͔S!O}jc^˘ͫ[gLJ3gL{!Ԋ,Y2Pק ?4[akV97QJ-b™U>]9 T+1vԑ%dݿFTVGA 2jU !B^$$0&%Mz,p0AaDoY x?xh煌 [Ln,(>>EB!BH&IP$L0ʒ$81@_IpFt?3;1`nӱY5 72|B`w>GSPK^k$`9v{xdphKUNY ms@5WϗWczB]v73 o6yM#Zݶs)2_[Km[7[ y09+]%ޑ_~9Wj+G36rѣ'\xlڽzry^wuO3 bVn9Llq#u`E?|`)פınfPoH GF2@]wOyА??w+QodVPBoϊ+rUS/>?+o4cD2ջre!eDފ-k8򬿡/7ɗʏB! p8Tcpb( KP2ggwuuu)r[:jBVqD?9Ψ)=5w@e{zzғg{]gV##V[#yϦe;=j2Qw[-2¾q%CowF߯sN 21L3Pϵ:&|iu>?w@=_s)G-2{0_(ŕ+¸ K2d]]O<ytٳ&K6zhޫoH72`ޫR9f8ƺS׮zVw. x[n-5rgN->ϻ/)y.^ h:} ܺ=A\цaGm;[ TS3ll?LsoU̅$<֘NjכS̺ћ <53^OB!% 'j%8===Vzر~mժUE}xFl7q̡Hj}V61\ZMCG 3a[@]0uWQi:l*Х<vJ7q;uԺ[)aW.Rw}AJi>x7rLz)KxVª뽤$z\%;̞rC@)k<F}HRj9?wzWogQÆS =+O\3J mHs W5m:ߜEa {<6`l}VJ㟒x%ϝR%$ųwF܂X5aXuW<"T%u@we{;o3uj_s;̳ީWfEi[]R㎝fvoԢ;-D!bƘ1I)*2s 2,#qjժee7EDD?~UV{F3E [b™?VZ\[B̜,I'ОY%t.ׇT:bk$Cƚ^2{- >i^6X 'W,|vXa4 _O%ՎGY<nYWT;i'+yeE[Iܙʍ"ovD7a}(|WY9d~lSx#ǬY=I*h?;Q)<8y}02{r<8˘9{b(5`^l !B $'A L8/愄OOOw)BUJ+P( h'ԚKw52kR3s m?}WUE2'jǶ k6$B ~6zx鐐'Go{FxUϳFʫ)p{B^-z+$q IDATjHE]}tn̉; ]b@xRozPPoXuD_6cq;.-Kʿ"ҏ~ A rLew'ZM{ky[A!"x8A\9&9x {1$8¬6^vhFZj#ga?oC4br,ݴw?B[.A?gX\x e-o[ x|nyԎ^\4͖j iC܉3P K <Τ4d/gm]֕T9:+ÎmU'.!l6; @ZQ~x7<8-6Gp?vfN\̍8x\8#}yKQ5:@4m==dxfCSڶ) BH11&0dLs  rө>>>}|}}[vu֭[=8{T𽬻G&?{UͯC; R o(=!$kqC;yμNuq?4PY'brf}NqІP3m>H3*+1`L֕=J]JMI,zC=T<S:=&N,;>>Enx`ȓy<0\smK‡f2#i١82>͐{lTY*Te !R & p,$x -͊bc޽~܁㽜kTPA_f[ovPI1rj <}爞;A:UW~#tn.o<B9ҥ\8[̕/Nv^nN5-L 4cF[FZvcʰڣx/6lJl׮eŰtq P%k=YPie폛cn),{O/} ci V!{ Ϛ{AS7g W.kxw̺_Fs7/kaX|sk|sTR[x(rlrƑbN8@иS1]a~ve*N SreCKsB!<'d%I"DfD=2L]dFp<3ƓG>8\yu9g;N['Ô} 8:k se[e~X;&WF_ի4MsuͿ~7clm}?ż73[d5.aZU G9wBG64 _w[~֦E~RH ׺Cyh@0.1W\t:5e;~fTw^KCq8avI>:MJ oy]>u6N kVf\GW!5ӥF,99Yʃ-"͆->UӰ |lK#~'Yʵ^>}f=m1E>EuFOGg6 ﷢@| ?&òoMk Z?G(_2ʎ:6SF_YyWwg')Ok^k|]hf'{swZZ;NfG3}_~/w$蕣Y2̽ia}g`}rߡ} ,ri)ˈ9s>pRގaQ -B! odFD&^80"WwNj"TeT*ʒݰÒ d+DMrl[g7smmĮt*+YԪ!`y ||Y|s%|gVplٺ=tF=sPo%+Y@h6tsϻ?y|g4L?wWj78C+M`y?2%ְSnܪi=ޖLxm!sne_[pX5,Y-\"D{ϯ\k0sUme]K$ @zC4=).>>Y @i h\5+(F!W!Ow^O5g;VP>4!^X TWT.{BkAf\"RP+@vsS,n^p+)rONnUl|kWה/B!F3Љ 2@ CEA{\EPJpB!B!䒘 p8Tcpb( KP2gP B!B!\1CAxǁAؠ!B!BJ.sHԀ\R2( QB!B!%c2)*2s ,RB!B!%IA L)*B!B)$c/ΕcrC2UTRΆ1tnaw^K7[#^B!B! 1S󜂇B!e&6^ppcɬ9Q<;9X#oΉá.ըb{BajtdqwB!BlR B3RL0,^EEQy BH9 B!b+ (`D3aL,ͨ!B!BJ. c&3Ii"&iFK=mzz*G{WQ#FA펚냃OjN=|нFܺeϩ[}ϛ-$hEHDDDDDDtiS HCninCב nd\IF:|;߷6eƱ0>mx6uCn_LF(VXf`uvɸ٭pKf{e{cXX7xݻg)!6:f54;Vn_fCJ!% B8MOzX"@nRslNG'Ȫ'r0ec G opsVz8 ~_xqI=Ǜß2v0Wn82)4gΜzwDjRQh P*h؀FQ)z@I[룻iX;Z-"Ήm<`VR;vk y.I:LABR7r16l -va,<8QyoՒ}E,aDDDDDDD$*P5(E Xauw[uStOY+tᨕ˧>\]JB{K~|?.^\1yaV o"A[Rθ&_(~cղ5sfBG^__Nh@D49[njپ}v9nޗ_/@jܑ0B""""""K0 `JET B!d}**Ԉqv{u:f] x{lUZ^viO\@#g7p (V, i .y0ub&!ms8I"5jgptzG ( q̐& Yno5&8"tz9,Yv(i!j9QnR:j MLY`L2G޼fi}eXrÒO9r"392v6WǕDDDDDDD̈́[B"P`0,km #ZlW.y l5#/"DDDDDDDj@Rw0 %܆kY@Y. """"""`fK !@=,m )P*0D"""""""ICZ$L)%\HpH$DDDDDDD܊b" p h#`n/)dC¢P@ Z \)Gߝ"@Z5{d 쑶r>:哲m΢ )Rvrٛ=sY]( !zO"""""uߌ]'MLݾk(ϏyJHR*6 4`Ӑzk]98"|v_qM1yn۟z̆m.N$[RD-}lSȈ.tckq`ƽVmVET^:6ET3hQN-B@ U ,t-au~Bj9iA|˓|r+=OuG]ɗ/]תCWYI~d٪]ǕRڸ׷zM=q q/g1z_G~9{[o3qҥ?l?ȸ}GקY߾ϲ/W ptIo_`?em;pཷ04q=GNa᧟lWz4ȧ!~Ǎ5KPEס8Qk1KRc)Έ*q1пK:_޴͍%9O3X-j{h{R8z&?ܺzETl0f%)/PbJDDDAE+~p'̺qqs".:@ՠ"*` dwUT=xkXh;FyeL#eÒQč}˾-seV?;rs%o\s 3Ý0qwrO KItË?=?9'n[xgr".ÖNfʺ9֍iw '7Q-2o+8)o-IZw5m}S>f/?}SV IDAT1#oŷ F5 + ,~ ~}_/zcnmzӠ}tkS~2n]+i[vityȓIsf2uY_=%>Mo4ܴfw7r'K FǦdEoz"[>WQj\;c}?MZOaz8p=eg15٩SN:ɯ섫|a ^ ?7ޥt²lX2m\u16da5D)tO"a„pn3EB0 ]jكc5&;Pؗ=%nF ;5}ClگCcxp/yr FߖA- Um9Ng;n]Ɲ%*Z]?Rh=x>Gazxr˖-Vb[ey1Qpq0(!3L ݅ъNkB p] rMUZid$WC}bR%(pYV@I ut0Xa螃 HqPwmFLG2E@š'}`U*UKQ'/ *F#gWv̗3rH_)ZRrtYW u[whߵ{m(n=b_=%zTg?׿MPtRi*53jH 䔔LgWs6MJÔRTM S#dptv;,r`^a{dmW{zTPJmJ9 1eֳ2j|FMc_T>T[bŽ͘gF|!:?f?O3ۃ#e7lUذΉ{7fqc c~}lʽM9ߓ{4({W_PѾGr~ê_ک9oňb)}˳ Ǹ޸o\}^v>#iPΙӏ{ϯ/ Dj⛣߼k\.szڃyMqgm!ca};7͚vxǺ;-Y>kss‛rXmux>wvO= s|;N{s 7{Lf-!Mi(0MH56]Bt@ybƜrAY>›(DI0?37 8O 'F.Ë*5q-\ S HCnu- 0`s#Ϻpg:l̘a7KdSlQw'>qQmzj*푮!{Btz_@3%9׿{uf ?6}F뮰[w|]f>xޅ +ݡCVluB^_{k:e?~1e|;hX"̟7,{13-9ſ$@@DDDDH| )= |n̘qnBjFfK !V=,m ){-m޾e6+#_}VӰ!'m/p'4Iץs߼ىej>p{#k4m +x7ܟ{ڦY& 1 "Q`ަx©2s9t3k;Y-Uql+`u/LFKd -hlUVcyoeE;5bl0sFΔ4uw֣ߟqg#;˄{z= B]~]_/\?|a/az u~ޥ?{q. \:t_}/l:-}a.씂jެٕq@Fm{2iYPSjg H[?sۢrR§+ex1Q[UkqK4E"ՔR ,-NMplf[(z;#-m_|gѫ'2f!ӱa6;N~uӝ¡<1bl5kv#^}$Ϻ>;Ptԟ?|4yq\FܯvNqa^֨;vrv SߐMWWJݕ|lߚUA/g\ш7 yNt7ݴç~[ϱ Zg1N󔚫# DvP@U`s׸K+geƶ|[?>bRƦ5eTխ%Z%[F -Ӛ;zO8: "w3hN}Oӆ_G)N)odC6gr.-oSڿ+qбUfM[<|jAm7(q6߷iί'G5K(x@DDD( hX4[d+N RfwN_5['lW]4YFos6j)7PH.JMڸWO6{X1j!Du4 i׶ܢj_l;.ʨ_!'.""" Bwdl0U( j`0<_m+x"Ԉn2 qxzª)VNZX0Ldw⊜]Ο1ccjN4o=%>tZwm|>utΙemvlǼ8n:\@̓_W)6vB.""" bCY^ >׉g~i2*&L))%t nu rxhǴ%vxJzFi`l.BT*δIIg2\V+tJ\|ZO9|(X-ѵjF~pyxis|/MuddX{0LJ?s49EwZ6@X"#D\.t'bb8e3lJ9uOU@/+r8uGlחrf=r Ң"J}Ț5kz=WndHeƟffZhDa#'קO ,"kG*lu5tklFLefQQK0GYFk y1QA-˥/ܸ q̐& Yno5&8(xIiRꐪ)6a 0ud3(%`ge_! I<^ DDDD!̈́[B"P`&%&܀adW `եN+cK !@=,m )y!*DDDDDDD!-Yp.n$Kd8MsQ""""""" ~nE1T8M 4h@^N """"""" ^R E@@."!*DDDDDDDL"TUEEQ-*-;/C@@ T@$`&`jLpQB !\BlrKC;*:"""""RQHh P*hEA:&8. [WtDDDDDDBT )`Qj*ªVX@&8(xT&"UŦf*ى ADDDDDDD+T0aBM "K .o5&8(xinHm\annd %Vc @ a<`.d8/) SJR5&La,0s&e""""""" ^ 4EL$K8M ȮVAQ٘xhGE@DDDDt)ߺC|!u7 Ӏ[m躖D K>PXV༔iz p8D4E"˔Rned tN2JDDDDDDDϭ(*): (!jLpQBH8$,  pQ!""""""eZ ,,jP(P4hى &8(xRU "P 6VcV(6 4`]$ """"""" fn!U f """"""" ~RUjPE XaCT /7`@DTLXT2;qW국Qk#[)0':W;qˤ,4PE„ 6%2EaVcpo:G̿bD4w]yʚG[P Sw+Y.hBfW uߌ]'\x u7>?b)!م* `H@ o<.VcTw/?N׵cݓ׫P&M~u 3HUT͓L´Z=5]PbN7v&a)6lkjl]D5kխc_D5s 4L)uHp0: $DׁW\f+݌W5}oFX){vp'{9c ؗp늈/UT򀕴-;w^.0ADDti#'8m> n\1K/]\ti&ӄdXnn@j\&r<NIN{ˊ'q̋Ul/B6_^G;KҽG"""*Wj bRe0 %܆# p0Ȟd ˂*m3o DeGFz0cdT.+p^J8MOzX"@&det>01 2d p:L׈ IDATU3Vshm!yV3 lBC箏>r8p^<^-[z%9:UQj7&̎U)dr/罵ǣU nxNq] پk\;xG;Y՜7Nljvŕ:r8r<w{J9^DMw#莍}g0yLgev[?Ypɚ+ ۺCݻwmӼFLzĶb.]o|mp{FsYw"""" BnE1T8M 4h@~G̥l~õ?۷_8^d]~fFnJ |7s_gϾ]'4 ]'ZT g=K)EvC?>R5O0_b)]ܶx[]8}3w嚗un[>?$JRi]b?v꠵fxb%톕wN\Kn7p=O5D0iw;cݒ60 o/)wIOG1bD,CDDDEm jfdjrJJ3n2D!aQ(h -KxF0Atى:vJk9\f[3KS T.Lzy ot9Ad5zM9Vc]3hèfaE ]h_0Ug,gJ*LoٟE+C~e7ݧ7[( |#%m8[YVrTB$H!TMp_1Q0fcGu˪AճK^y mQM=OVË2t~}^NߜGx_7O?ݻԛ 8v;Хgd2Éq4= B]~]_/\?CCg˘Vvi/[st(јnyOkRӀ xwHR"8$UE(5aU+T { 윮өS֭[nݺCXa3gwrsr ݢng63lQ&1Cgu|6dv iQE}x_r^sMQrL@bSa3aPUhُkك貓oCVE`9dQl(;I/p@(UwsGm~0gw"Vl}:>)7rHSk6we˖wUFw 9 {}(*&L))%t [ w yge7j;}O&%U0ODN$gK`?P{ۛ|\}3_K݃'$/'q俳;k8ѯI<6>x[nͺtq2Re"{NDDD;qau)hinHm\annd %DX ӓ6EK@ؾ@J ڸ0);lCۄMIetGjI?g/$Ϻiƞi (IS[c٧ɾfiwmuEŝq;V{qakF1C_[04oڶsMX6w=kZNѺҾ[xƥUn4OjVײ7e9jy$te&nmi{>\L}ŸdpgK `H@ o<.` ˏ/N;v;wvW@>oi{}0JV3}<-Ms'n_ @pFdlco/Ϩ6fU#Z젩L\:t* dx!gT39W#"" v%Qڑ#-HXgD9YY)ins&`AKDJwQ!L8Ce ջ'b1"Y[ʘo +gwsEײ:EHyΰC*qh벏yD)_Zc>QdDF;'i,v (NS T"l!=8 ت錩ڲ]c-\qn&]o۟7޿$7i/FBZMZ(=o\׮{<>1khzӠ)s+涶M4Қ3b0}N\PйJjݡhv/voaLYE[1/RJMUy蟽}).bV/!iv%?Ong/: .ZhG漺`4Y-ZhѢ%sO l!9@&)& 7L݀#{?!",&1-uy9|(X-ѵjF(gOqHR#όJ?s49EwZ6@X"#DT #==CׅDXLLcYFL;yt]/ v8ڄϾ]'=LFjs ""JEČy7^ht{ KrBB*U0ADD׽uEQۆݡLL=ZV _=Iݚ֍`(^V_ 5 .QgѱauFGG[V&8(H)bX6 ozp8R\.բFGGj&DDDDDDDlpMӄZ6ժi4LpQPZBQ0LTtDDDDDDDDBxx~UzBtxhGE@DDDDDTa Lo]!U$Q!""""""J """"""" """"""J """"""" """"""J """"""" """"""JO L~1x?n_'C@#/ OPu(kg8myeFF^ L +]gj?AFD$>a i4\孾Rf .nl'R)3ٷ2mbd)_~Z:I5xs;ֻP]J#v}74XeF|/%oR0KW6^iKڏcDR܌u%"|PBlwB Lj s^bq0۝6GRH`UC5atrt|Rl 4DED>RQ4:3D3<ӯ1arLceo^`HFmgy9lp jq UT))ïXH\Ip'аNѕ>䆜lYDK #"#\Xr"&fq6;X][ue{.CM+rmSc{~i 9gﳴ-XtE ܌!拵 u<%."rdvc[8)|LP& lױ>#X+#DD\L[DAZW;uk #c1i*3]k4.?$8AmA$K7`;dAV[|&7uki2i3Je+v2L$s3454W$W:F,Q,CJK2Fad}; ĉ2,8sYϡdȕ7*a*c" ('hrd@ U!!ߔTˇƵ&\ѵ#\gh L:@x [L$>^ a@l[լCxc9zT|{ɭ]K L Hۗbԥ(H.wO(6-_p{ӱ!vG!]ۏ~@A &G;JFH c:STT/FmB#2҃۶e⋖dq&~u!s@ivї:t'+۝uaYIz@ȍnʛcG!R2ebK"7X.ST9ͤAS#čKS c<Y՗.l2+-\'"bUudNGP$Sn q/D.\ JU9C2@l'Dd|gЗ@ØVY7F^#6< fjFrڦkY}oQ֬Mײ6]|)3@ "]ʡnZ@)R_0yW4OdzYEY\S"䑎=ǗeKt+ߕ]H4zUHC/_L>ո}zsUWϰfpx  W9{MN"Z.jն^eU:&UP[сGbM11oj69s9^`#2 %dׂI0 RE;/5yF1sFE$"9 «:VXj6cH)raLdJE‹ (8pU6ٰ0:a.>^XSȹYzEF{wbX8NVsq˲JRPraX,fl6%STʀ'l+%WInLQR9 ]ȔĒX8@Åجg7H/"q ` IDAT=$8!~ {HpC=E}>JK3=$8!~ {HpC=$8!~ {HpC&Ő}%LTEGG; YWy&Ep5*Rү, j[4Ӱ4DHpo=pal Dmӱ] vҽ_{t ឡA(98fD4eٞl. whePér][ܬu؞'>pz ,$8Ȕșk.Y=5ۙC+V1-MFe7Vji`Ku!ڵk|uh@UfZqVCZp  " Dk =/''W==4ou~o2MI涗(-sW\S|Hd:Lр\7.|ő<Q{<_Mne}l*NЈA;2=}Z{Qѩڡ }c#f&%ToK. 0m<|κ~U:R$ct#C7 hJ,o=VLJ<-&Ådz~AV~V.Ҧ#DŽ[2wdi>_ͨPd<5ysmeC07YCe[}Q1׹CgFD%ڹcڵuMJk{}Tk7%.C]7n?- e]pCϑ$'HwMa +֭q}y+3eןJ41|SQ/(4[y+SjE{S'ݶ6ٰq{:\}w+Fޢ獃;1ZUd׉7ua N~SPܨ-sw2'PPvzg=nܝb/8?Q%^)'/l4$h-=XgoN4%(ּwb|A"f2dnkב CU#-}otbա֩=(W-`QX;%o y-N!׻~5Y?1c#L}ցeb=]0DD%'?w0|/-v<7/m+!g#uZ$0)=nWUONYr2l+̐[*W7coK~-*§<͞zHt=p}~{6/[3y"|߷X8NVsq˲JRPraX,fl6Q"뻠wR]9;(ӧw{+ޞ6amGHjBHQt:a0mʍtXqN⹙^0q!1 }z$AlY Ly?eőVC3wpRF%K-{d@.mX/2O]כk^MQʉH6`{*&y)"2r*/JR0/N~Ӻm{ޜ}[;uڱ~"g$ʼn7Q5#.|Z^sŚ|-l+iYTyč:~hܭo@D_=#U n/n`=lwu$"CnhRJFwԋk߶~w)clި{w9*?YxOf}oշMS>I1.Om$"Kn{|m.bXtK?0]kN*-oưOQLw7mg}ň9ྜྷy67/m"mksW~3Ә >}(kCJ9ݸxDD;7rSOxnf,]K?HDz}t͡ {5Y54,a,1s& oaQ0地.G;iX"[_GFٴvZbo kq"QÍ8zȍwM˜K7z=[^ӦS\{ן"4-%N;OQ-)DKK-/ IA[oN-IݵURpR@mْ"~ݰY;$o_}2k6>MA͌D!Fk};UD@T*|jAuz(굱]G"4)~Dnd}aT+&%opn[Rguvv@5T6֠Dшk_e]HVND|؇vĝSѧ"EktMAD"boί59Шa ԁu}`ܥH1O&􊲟g~4z? A-LD-5VqoZ["畚=/meFɻϗٯ<5(":}qUQxApgm۷sOF"m17USV "سjv%:N7ʼnF ϋ/Zy 0AɩGÈjꉨ'H)U.z"w6?s8)Cn6dX ?겤W0}vڬht ̱]ct&K i:}d^B"ju(@>{*mY'=%u)??׹ڜh̐/qG[2)KᐱZy?ExyIRvI<~<ɎeW~gI>Ǽ%y}qֹ[;쁧(\\0>㮕I<ϛ̤;=o.`}z,tɒ%o7U4}!CJ.!"2dʳ{"(q #LMv?׬27DZSԀio|^!_fյy}v{kZi5hРc70"*rDṭ~:f[U~(]-5㰦7>Fyq̽sQҁ4ZـiKo8^b&@z0͒qJ^hRyiIN/M+&"& &HE*5:EK?֒m&z ¤AAgA`l{ s +^_?‹90 9iWcj[4sUy}i~£mUvFX;%D4kbz}(B8wcxzsv~k'mHHH`Y8Zqq,*JB!abfٌ.*PMM7M򟍤?WC]*5hc7W3vn9עm+n {?‰(ATbbܗ8 ]pE*04ғޜbn Pm?RM)CW!ÅKa7׮cސ Ёl0K`5-iԔz@VRi?ե#nUf#{a"B nzFNؔ$Oo #A-{];jU} -;L5U'*$8G{Dÿq6AԚ‰j4=$8!~ {HpC7%w @}B{HpC=L KKwbw P'~]T!~ {HpC=$8);;xpֶD0^oV{/>*۸¤ܳh'|vPߐEaї"!%fEz2YCq&! T߄EMLoQQ܃""i5` P4% dfcjaݵ@`+n /+-0-ᦤIljXP"S $8?x9 ymX}_U*¬'YA$/=[" WNY[C#+-MV|>ȠsKNԌڭq\囤D:%tm&s7rD$2h?眃EUl4^"/j7gzĄqDFm,u=boZAXQ<}9ҫԌU_Omiݍ7ؒߧsgeOl3HJ6ukH^}IVB|I@(ⱙi"Zp~U,ɼ0&.ZT)s,ԜQ <CUUn/,~7w.M)Q(;ʝWW F-놴AW21῰ܽ?y"Q~/K.ݿC/m=4o>5i+aA Sli)ՆSKd]Qy5t& -8eaĒ2}oYls9.V)˳ͮ% _wQMr6zҲPO&,Un9DG'|F?nQA-RTj__WL+vx[:Q\zPM.8׎_9-zәmή*:Mw߼#j IDATt-"ԆQ_}ӗTIog3:E_Ӌ3hb4ƀ.AG$oh*sX12/(Y';NTInxTĹW=>W "b@H؆iD]7zrsl DT 40+[7up?xkT1OdDX8۷5xfYؤ~ %q0#EI2ZGk=,4>7Щz؞DԬ ]T73q8Д4g7Vt 2{o.e<(+~{r3ӊgunx_u}0S;sKCD}1y)o&"2!)pZƢ 9_ޠAA Yk,LjQ^]uE/w+TE[EGjMrghר,.Laaq#J?xsvx]Yuױ^U)FW]Hdbb{f9봢' q) ?ɱ"""1d\|^hDUB]K5 +? ٶ#R\IXIX"~'o^ZwDD2^'/3 2TjfYA^' rQͯZ#%c2J"UA|j{ b`dE24=bPRޠ$LA>Ju/-n^o8?>aaʃZuwb&!!eYj5qDZ,T* \.gab6f3_`ASF-kWzd₉ .qk(T%]#W >Hh ꞊*'e6ߔި5L~fQ$8aѩР. }2zh)۪o+Z_C=$8!~ {HpSww%w @}B{HpC=$8!~ {HpC=$8!~ {HpC4BBΞ9 ,KD21pwPИ!ؙRtF{pv1}[(g\up&1*1Fl^9juf羛!5X#NN=^:@_1nV2vf$e~+ULgߛJO vf;ݻbhN=@yȉҧ=)a Ə_ )nC~V`{2qd[{H -Ek4UhɌF>791*cw?ò FCVT^DDqns,5s936r^]==l:CJmEܔn.3wBayCW'Sum$FS?KXk$5$8̸^6Rw欹f[R&>bőL3_yh'H߸aݩ ȁMu>Q{Lo7sy cC^?>e]o '>/IO.{o?QD$3:!O'?DDD,;t*Ժ1 })qruRMue&?p ł5gt 7MNa C?|Gܟ^TGhr J^s_lT$}tI5Mi5W֏# 9v~=j: 4P(_4@f:3wt۽hvlk vu3A4;xdR[=O'ggA_MAjuXDU-a"r)é叼h{IdJ;ׁ2?x]sWDE4-!<5'hg'n&GzwY][k7;։aD9{ޞwʮCF۾wL)[3*.\^t;U:S@gpp]9,R"5e2"sˌ6Sg_7YdЎ=i/u@ޞD6GooQIF$5>-,^xWmY=V,jGtsr=gWDTVuhZ.oRfkmÂmdvh17<}=]@P֎ƉCk/S_~&={1߳c>H{׏VYl3ycGmjjNn#EH=,Dd 7q|0JϞ S˒):2!Ӿڒ;oOb-pגI(C t[Ǥ[4qL@LY;D5 DDۖ|sS? m@D$pt2Ѯ׃JyF7VqwlKǑL\E> +tr%rKDd2J7ay>烐{䢒*,:4\|7#??bpw=VK.+Fs>T~~g[2d`xqyi-Dͯ/L{,A.t~rۓXc/a1FFRZ|RphOÆ'vnO8T'Ŕg&Oݧuljk޻ı'cu\ ,^"}i]l[Liq]6n[%EK4)lOЂi jgVhhEjˏ4DlyCYrM{?UFDI+͟t[gOzkǵ﹆xi!a?">$!lUH!s3j5Nx#0g՟it/IA%cźÇܵ~ݲ_P`O!T)5%7}x֚Q`tSGf?z oŒ+d]=s5S\k'/<}Lwl#,_|~XUβ=5=8ϻ+@8ꭄ=ݗq,bʇ;tp( [HZ!q l]݌Y_LD$k>lkޛ32qcdz~dwQtP=U"$ϮW~es?vN nrȊm&$ 㗭]p/H{$) OO7owʇ׊lRCl7gS#ڄ37M]j휮ɚ>ߚ "AleQ0ДyFe9"Q  ap\ &<\Cu\]z}nqXeB q3MH<,.';㸀h'GeX<ӮEM8yA~IFba-7KHHP*,˪T*e9>U(r\& fd2! QBB\.f7l RP( 0D$b1d""Ʉ ֧ XS@hX (Hp@eMdX,a aL&l&"$8!శڰ "ɤ###HP lma brk XZZqFA"A"##333M&ZVSDQ4L&y\0 T*SRRz}@@˲UFLv5<< h^pAt:VCCC;wܩS'ZmX@$( EjLTrT*J%˲JV hB(---)).Z `^ZR}8ӷC""L0mX۔3:פ5d2qD$V@"QA.aYf:E1@9h ӗ-[/w:NJ Zp@ebjD/X,* L&*G9w]xرcm| "2 ׯ=]~}YY l)\r^m6iơ7 @CѢE BaLb,P(J%qօeeeh~ {HpCp"//y$ z^T*ʀFV !^U"Hd[h9h"9&AEE˖l},4x.#==Wϰ,аMD ?2'ZYT"*yYXCdm2yIQ2sLJV i_)y>9΋BC`*N=?q#W]#µJV>_Nqqۆp>g65j- 2 DD%֟xxQWV!)?4un] h{ۄ/k[w% '4.n1S}ԯfyyRˀW~(}^Uvoun?ss{)9DdpID2& m~ iZ "×<(RDF!۔GJI"l7mg~@kvۨRmW&|?i៎4n]dxn'l ;,Z Q9鉂[&5.;XZA9 ><+=y? "@ꦺr m~  "_q8N1+z157qj"?q#">BtƖJhиfI>y3G;-'Cm+֮-RA{ÔO<oޢ*D A'}IIO^~#h{ ߧexDo&^cPӨ*F32ZZn"N,l(eߍ И'N(*  GQi}-vS'`͆߇}.ՌunY+5pl_1[ūfKc ddRz.ʻҌţqdWS{|V8,K}"3$i4ct@{Gtwͨg R)+O++oݮZtq@AQ6Bŷ{fȞh/ ~>F3)A$w ҇džH8^N>Nt;ID !K{'?TZf t߽nYN~~qqqGNlmmSTh444й@e$-dwKiO*77Gk@v،rRt7q tb@6C)pwK@Yzƀ^5qZ[:q[ɿ[*mqgBR^25]ktgKչ2ToG;X$W&}L% xQTFPV_<O^?Df0cjGKq hIDAT-7  5̖:uG/Z2Hްa:k]-"Ue۔+ Ȫ>ln4ʽJtQpT04*ri<9^7!DJs7&nIj1Ѱ5 !nCBB,--ɋ6-*444s5Woi}Qf^v{!> 'MiVuܴ:2ҥ> -N90lhC5owf({P^<1)l| +C"u@#}i/WNlqC9VWp׳ i@/rN vq‘%~wѦnpLTr;6&vAtk8( ٶ>+U~Xj$ՈP+{uyAEq>MResq!v<ã1dkʜo?7хsqSi9칦f0cP8w|-`OT%T MZ8b=UF P'G(U(s^F=MGbGNyǙŶ ,Fh$k"n~IZPҨStZ[5{xH}7U vyO <ᗗ cx]]N6m=`7`=LLŖ"#"Fxvm]Cx}F5h2{|00pjśŧ{!oʦ/\|Zј QYw$uGwR2s?gG=paj.{646#'7 y?;h/I4j3Vr:"`Je)\K?;gд+uaaa&MsQگޕ> З|aq..H$3uy|u\*~a,<,~8_ !D&]ë{o9s%Ҩq 뾚$|wn=6Z_tI˷ gB*!W }KSTO&M\;`7gAS^3Ԟ\|ө`9Lma1:glRј* UCM(j{DQɷU|bPGMF~i:J`Ί>FCM%酒Fmә"rf=h+8F f)X}HCC^quupssCDPJࠡ1dknwy1G֡ {֒aۯ_hp&Ɔ17KML@ ]:#9Lm9AO^θq'>RҧM._M( גpYvNnI  FgBjMjT" |2ՊM3lڀdzk#3$%Wv/=AD/cU$F$oX1cV qJĬ#4e)g|.7a[d*NgrcKr S>]؟B1c&. MEQOOOE EQzu,F-Whl񘚹juCi#}n 5 b߹4y`>˩xZ +@7?xJ֗^OYIꈢ Z| g[}ۓʣXշ|5PpvK#C2ĉҏrRVRnU,pzX8>(ԨGXilbAl5EhyPP-F!}tzBږ3+7yO0/FIQQˇ!U Q Ai0Z|= '9fד]I70;yLpyۮcq07nUwrDdNZ&,_( gۂ'H ҾZGrkm[/w42&k7N-;6p.zb IueL͓/(IN D{TօTZA<6&*TKe,tynry4w4ԈALKQ͡X}&v]wGOOkKCC,CpFCCv!r9-RXV˦;ČJ!CP^|15 g gwu6^~dݡC>,ޞUgeR!W~yvJ nc1P"_?yI<){`>' C vtRxH,GIra%\c㷮I*T<릦%$#K7YHo%v[;XT7g~##p[hjG 4*r1Pؕoէ=f. M;@.ՉD"L aXL&bar\,=PyGIENDB`bcfg2-1.3.3/doc/reports/CommonProblems.png000066400000000000000000005011321223671746500204250ustar00rootroot00000000000000PNG  IHDR/:iCCPICC ProfileXYPTK;8䜑$$D **H2`TT$E}ommnO_>}:"#C$cmt)N.@?< y/H++sߦaImboO b#ach0Ѩ_:v6z0fGVS}`;`} ;X'RXG",,bX? /67=_ 9#᡻wֆ3^f [ ց8m`pݖﺐUdGZ&i~19eft0n1<PWLa>$9Y#G M`ƠX`af-0āh$9x%pY a׈DGO_$E n/?/Klw1{2i?USIfVfr0JEi4QbAqIJBep/?>k/HTs3GK;ߎOϷC7vǁ""c):HIPdd3YѢͯbyl.>GG&z)\b-9N> ae<@,:X; <}0$}`?8A58 y ``L9:AX"C/$Cr @ yBP8@P!T@%!4Bߡ5AD0!i BaC#Q$D&"QE4#݈q H$RCZ"]hd*2YE !!!Ǒȟ( $a?5F٣|PQTTՀjC&P -4- -VCt}}}=^`0,*Fcqc1y*Lf3bٱX % –aOSUNj\p \ )nNCQI9JSGs :OkR| ~ H  kB!PJHO $2ňzD7b1XO"". jӺ6Ҿ]#IљҥUе=BOC/DCAD_B <$L#yRINA!!C? #Qрї14c/$I }u;)& Ʉ))e]adfcamn^!a͑q<':gg+k. W2iG\?y#˸{yXxyyyny5yxyo~0St(RJeϘ/1:?ߞ?@@ >&B4B*BB' S P٨&$juLVDK$JV(FTE4DJtP !((V!D!$$^%>$P$JHK6INHHKeHK}v.'%(*S'3*(k*!{M\syZyC4o ~ '^*-)(n*)+E+(* *{*W*0XWEꪦP֪U]R=D..]u&554j45)4ǵjk hj՞ i+{UwEOM/EKo؀ޠ!aa႑QQ1̸xĄǤdT4ŴόhfkVn\<ԢblIJ*5ʺ>{d=ltڍڋ8;94:8;:;I;8=tprp8ujzuM-m؝׽߃#=^{.{==ynxYzz6^93[;W7_?P0X8T-8:x%2>d;1B.331<$/'boPxdVxZh1P{LG,Lʼn׌_MpHaoGb $}{ۿo"E'&JNIHLJ7Jo؏ C&0ckܙ陓6eeEgR?T~|Xpߜ2%y>y)=ѓ0 h42&NYSs9_PR}"xyiG`ٱ**WTV==}:zTЩ5F5mµ%1OswFLYg7lq;ڄhkmvk<E˅܋bOPyPa#GW>VzDIǠൡ]Cj=~s_~14l?rmd˙W>>yCzSm;wƕoNOs/Wj}0-E%V?.-䬲6Tyoqmz=aQ)ymlkl;l{;+@z;ñ x1şbG` `^ .)ҁ|3G n! -jm~IIJb{q44Kb?@fēQLhf, =9G g.eQ^E/F`ZH^8@MOĆt+9>yӊʜ*vyj6ZGb4 _cLM=Z~clMg#`jgaxЩ̹ۄ=O[G?, 90/[\ؼ$p9JՁNk}o |;+;'wo_ܻew~v1 ӡg]/.|Uј177oގ;4<>?>xRvrCTiٌO>ΝLyK ߥ{:mnoOv 4#O Y6ZFFl,kezD/aoF`"G#ћaI^d̼ºNpsqGQ3 X f ݥ"EESŗ$eO7,v޷%T+Wc; :3ݽq+v[|];N'?px(q'>M{9 7M|hs/ TB>ACEO;D'MO@oKA%e5S4'f?8dy:ώ`/i+[{'~$)BŒƒBQwb QZR?ʾ{#?0أtMJjZ]424Ӵ+tt^/dLLk泴Jnyjceo0*v$x{k=wsEhGG''zD59b߭Tb{zgÁլl\ռ|kE%'yOWFTT8橛?Ps.9q퓝+oqwYݓѡ"5FO54<"e镔?m7h76g=F8x C1!шsH Y|DPhA8nB#L/VaqT}wFe8#&jbZ3stpϐHiFkd)&nRfv*VA֋lZlC~[圪\i܂܏yxEx_Qrt/ 5 R婛"wEyoK I^,}Il\)rB#ʇT&ūG  ҊN=wU`cL5176+3X䱲>`b{.A‘8Kk[{Gj6gޫ~y!FaiEbTMJVr5u9]sLY &8}W>}lPxDOdJrՑjS-u3 :MZ/^molwVB{BODDQ_PG_QG߈sh Ҭǖ")+W֤wm(ob7Zs Y[""1T@#Psh":9Ec#} C4x_0G"ޢդ{MBREap2\d4\Ȣ򙵆͕}3K#Oo _00JʼnI\R2 F1JuUj꡻4iqk>g2p5\=z<㼕}I~kA+!%a#e.Ǩv'&J''r>M:f΃Pš'G]+9_9a\zlB2jZZg54^lknop UW&MvvzsDWN^;7=5l>{<eҫQ7oߌLorX3=9v.u/_/-cQzҙN?~.(\5\}Sgᵉu ō+sΛ7"osnmپ1?BD]oY^y6 ů@Է¿|#2p^., pHYs   IDATxi%Wv?73^իWU] hp`8;9X2)˲,)m* EY!Lr!9$g f`4zѨFwU/oor[zS `0@7V/ޚydVG9 AAAAQ/@AAA"    ="p   p#    ="p   p#    ="p   p#    ="p   p#    ="p   p#    ="p   p#    ="p   p#    ="p   p#    =AD.>   𾹿C=݅    ^    G-pX^    %G;z0ۺ?Me"%EAAA>x[޽z彨"g   })E>;tcD "qľ!   gWÖfr@|t Pk#1;qUqD"pSd*TJnk 7޾EAAAUaftrm}U٭TVm? #T:M2d3P, FJB2Lp   ǓF`f[9BDQQjiy.߾]^VV7˻zwZ( ##DE O9J$zl:MzB>3671P|9υQ 1ZH)%AAAA8@~:DQTԚ~kK[B ( }@@`Bd`4tW* gN?$ӎIR h!   ]>8VGhο.8ݗ/U/\G84(P3`?`4"3BB6LJOД8q %   |Ε+7ҙW._rn 9L@!v; ϪR7 pdSpN/WK?zʧOG>251_tT4=@=ʷy_9;Dw0iorwd`  ]\.Wٗ^|kނK0 rK6*?4)4)dH&JDZ#>Z>Zm@Axw8#sŁ >~逈cIw0?UAAACk qە9wKg/-\]\3F)8 B=/> Y{b~rl2v)Jyi)^B"þ6uֻAn~nʵNTmzGpIY:!`Ņ_Og=zl3k0َ{(5kʁ۾4`rxy9yx^sc⢘{}ƻw%e z@=wwޖS)AAx Vh4ׯ|G.v4\B!GA)14 `.rxt8֛̥ T&<;TDd8P @floW/VwUz+[5IG b=xfD@,no^y{y뗿M]ŇO6ɿeF*(piXO}wSt?!"q>66LndGahwХ'XŸ3ιw*bÑ`AAAqv~oWg^ρRpx<6bv0%p`S rl D`@`+cSJs'Zk6f7o`t@kh@=E''XLJ7v?{0wߣ; Zkm6 c?ks]&qrn6q+='s]8p㣈-B$~;VOkx+.kɝ Ħz]JAAA8 Z81x?~G/߂s |H#‘_8uS3dB+*f=Dq q`Xڄ F;D`b"&b)"CRM5+m_z~^"* 0#sE4" ?DCON w}{@~pN: H;)^aºY~Pн0 m(bC!Z{B,ܼ[ш;bZk* Iw]|+N]#>v屴ADJb!怅PAA qJ]֟=\j$]dR ? EA3ePN%\i;ATm3`@"E DLb0Հ7*֮92ft (H.O<`w?I6v4HA<:jy?V<]謺aT65 ΙcNcu8\7qL(k0 ZVlw J"d2y^"at2Qٝڟ۵a?Ӿw>xqnUwǼ>~(0t: ƇܭUT*H$8t_Uꖱ e#~iv}*8V%!AAtpE)l ~•[{戤׃u\D Y̍ʱCOLeӎZ1D.b6RPL ClqD`VL hf؀ c\RϤn3Q<Q@# O_f}w7zM lM!6,E+G,t5bqzC cn4;;;A8d2L** LeFh4v^ Booy}<~-4`#ld+Jq5h[&Lk_vǮ?~aX׫jExfl6N Xzj1&NJT*8a#i'{Gbi#k >Kcf{RbzI0Vq<{w_bAALfnS??}ʕH%pLUp. 7<}ht0N&SdSPV#%@3U1rUNAijjj 'yay!כM=aTC}{ki[KזQi  a_O~3λrۻAl|u=ZY7`Vy^z6tRBLST6773L>/t:Hiw;J6rf}kZZlvpp< qɤhrauJ%"J[~'CZfY(Ţ1&ywU1bin*Zf JU+&jQY# VU.+JlyJGwa{m̻Kgwww5(N%ɔSiu"6tN=\Xf^$9 iCAAS>[,`um~[. B6 (8 R09.`vĉX)F uR^@0& (&0+ar; 4AA(0k8 e"Hozϗz3%lWp<ʅnTE~ԩ#wMm t0N&ݝ7qKݶnѰigVӓJ/0Vpv6lk% JB`XkZѨjf3Nqt:S(lOOOTz޶_d2jĦjT\/8Nq]T* nuj8AX#i\D2nGGG$(ͦ>vww[V=r===Lfxx~[T===3~pubu{aآ$kAb'Ll6J%ә`"kmN';f[oZ~wkF0zzrTq#oW(  Ǔ@ ߾?_h@Eƒ!$\ ӏ}p!A-?4z"FdT: C32O{zst|V`lB& faۛ3VjinEϟH _Gp42.kڣ8zq$mt(V|wuTH&b{ (텅Zfu8ӶO-R$EjEGIӎDQt֭ťrlq38RT6- CCCӓhFGGSTV^ kX .tX,RJR `d2988Ȫq)GpYv.鬭%əq[Roܸq[nmnnV*znUJ)k?tPzsssqqq}}b$㉭2_s0 EQT.j'?=}htd4J* r[A`@ %޾ eU7o\^[+w`{r{wAAAw|Giuˋͧ~X6tJB9{` ONH>ciD:w\2"9b&iCF"0Ɓ&'Iuy.HQoںiA e k20LZs̤"1xd]'NivRSqu] I@>/1o~O?Qm[ܶ\V=쳗.]"l6k=w+a#d2@TZ__T*[[[[[[6"H\U񶋉&㬯3Cz{{<+˗/_^XXqFѰ.N؏ݸ-=p]9yÇjNcfg`p[8ٟ]rW)e;ޕP-IĆk||oa+>, l L&3;;;44400+++;;;YI<wt2CCC:~ɓ'GFFz{{766VWWJR.ZӨ2%vmCS b@뛕kWFnvhװ˜ut'=ӓb‰|q`MvK+k[[ccV)V;_   |uo>oLIDp].\3c_>5q[JfEL9d 4`ÄXTGcP!&'s uPGA1{X3Bb&F"V̬#PrHElʝ bO'ɑ٤̈́+P<D"x SigsTs{xŐ`s{݋/>쳎 Hnu]sssG]\\\^^tL ЪVZѓ'OX__p›ol6mD*d2&rWwո_I,7nܸtRT:wC='ׯommY$qۥ+/|̙S&'N/bOOq cLVkZB.,//?ŋׯ_ذsXl l6=9}" ͕Ϗ=çO;tPOOBV}ߖtmHwe2SEjN=p2.oʭ7kh0@sT'?ggJydWwd2/wY   s><#NVV6oOp@ L&ql?ıå^ߘ9&""BP (bh!( ɾ\6h#LFM+4B0ဉ2(hBh@(Ab?5 9 D!R p_ 7Ҹɂ yӄR^?ƘFQo߾}ܹl6;77O~zؘ}t+SwÖEDQQ(?>88ٳgϜ9sʕjjp[}OAs'y?~~~^z'>NLLu@4ZXH$z{{>d#BυnjjsssǏwgkk_~_R; ";$Hojmٳg}/| 333>իW;u;/cu^ D''Fq~-?pkbbZ%*  p! DoȦP\8.<EKW. H)EJ!`#L#p@c3X8Πɞ@!* x[H _D eX1y"DL`a@`1LD c1`Fzr_?:1o`RH($S-_V7ѯ*q{t*X!J##ޯRf|/ǎ;|pEz @yBat:V566f+S~;wneen[YN~{\y,Ax뭷*͛7/><@2Z 0 c׉;b))v;  bJnإZi666(zgffR/^(3T*Nwwwϟ?NsTTZ^^m208 }7PXcLSJ w^H$@n]7bB́/Yy}}}Ƙ_>7;;̫d2.ɹS ܍w 5WOǎ}5*am5mnnjSNMMMZ~{V"ɺE3s̅BAkSOj}k###=؅ 5؍5 qn=ϫT*Q;ȅGU\Z|zOڰ)߳ofF"a|XWw'FƠիW[΂   Sa!ի'߼t uH"\L ?xlP_ TKJE 2 G PĤ )`@ 6It(畲iAi6bCfB63 7ڍ8Ҋ) "f-SH2  y3*&D аdzxfoEplƨ6~G|?g`< !$vHC B76N$D8`ph&)@f2=miHfVPL("M2T:CÆbr`BfE1P 8 1afD L|n߼y*M^IureumnZg/ JyH5m0 E&p(! "vgoB*BOPi_eg&v\NRDdgE}JAA{ͽ8l sF_ "?42lYa4a2dB|@D`cL&dɤ 4,Nu󩍠uB0 `1 T%4 ]ѕF5,׃h3W>v~lF9o2oF%VGHR<\luw{ka6JeSN$,DJjkvI!7M;&VV2)QMNNK.=ׯ_Irؗanb8==}Bb&kkk7nܸvڭ[jR~})o/Z|;V[$v|VJY>)[oa\^[[{7_{۷ok3LwGl'fA,..b%$!i}ߏĉKKKoOww[Nb+ccc<'9gϸ4c]6v^?tʕ˗/7t:mFVrgΜ:|'_n b llh4jکi'+g(gp\0OhgV6_Z\7o&Fi% XʅkgoJ߷KKl&(bfl v^ (`Ɲ J5 1DC3kf0 ( \"fb[b eHW 幟.ZȺHҕn|9ó/658,\.EQdGlؽ_*;6::sٖ 6oZfW՝bX)$6}1fmmmxxxzzn R*LQ]BmJthjj{'FFFzJl6mKlTJđ#GΜ9s…mmJxT*s=z3̱cΝ;WVx;  FTرcԧ~V=tvvv vә.JKKKvhk&IRrTť.A3L& OѰ%d2ӓKR&񜛛{.\PuXIZ.,,+'+++kkkF 똨RbF"w( E{&Qu13': vjAG5y3L١pxX\E 5h+7pbfVӱb\UD4AAAsϛ{o}ݚgυ rIoiPdH A=448BC%KT糳 Cj:??a;bZ٬Eݾq[o###'N׿>113ܾ}ؚx_V}׏9ߟL&r`ȁ)*qFRu o6lc_aۣ6l4؟K0R~K_̌KKK׮]̜L&=ϳFfs5":qLT* /BR:HĮ3HAoΞ>}ZXQ ߼{oFl=TɓSgO1[׮/.;O)C 1&LzË/]\@t!T?^X##nכ  ppOE_ٗRix\\#PRa(EQT`G.&Ff(ä v G""9`ÎuQ':䢔N2Q; 6l"hHGQkU )ͺB"VA)`-a0c1"6 X2B fp5"qP̖>Oކ#6whFZ'I1EQөFիJĉ'NXYYVƘd2i8)tZ(b[b{:;vl```}}+++\nZ8q_C=n/_mk4z{{٬>SEf\T*.\~'={gnݺe+`߷b`^v7>7>>n.'ޘw-Qcò}ҥt:m+v#݊=X&بZ-ijjq˗/_x1SA8alԩS_򗧧K d2F%Si={vzzСC?hqNrۏvuu7xFGGzV:U:lF$s~qkeDP (ǎinӨ_f{14Zkok'=9=ۻXނSJ!yesT)Jva JQAAAsOh dH U{R@.T&*E 1)pPeA SDy/M("Á]"0fΫ%bcQTJ'fh5HtD!>HE|o0 eM9 U6:#^b尩E 0|/Љ:yo[b7y5l;+iۼswwڵkN>}ԩ7o={m-ͫ#z7lƨvǶ2\t۶׃R*"xk%SSS_Wzfo,--+q @D\Jvۖ~V}Z.y䑑r|ڵjE?j [C5w?~+״ZVEdx嗗L8;>FaLLLJJlQO:hccҥKLLL=`2 c vîLF5kmi9~QNr=o kl]u:d@/BB0@[I Sͦ>;Ep]88 AgfFe=w  d t'!;h#˕v=<<<55kY[#Z'F2&&&z{{WWW/^h4Blhccc_qܹݡ!`um1RxKoooƘ'O>}z{{?n6VU'bcc'O,J/_Uj%*/"ɠP(.l'=.p*5Rq;2&X-'wٳFc||<( f l CCC[[[J*g?=??dluQ*.}ixy A b^ޞH6Ꮽ v/> N|`Gڬخ~??~ԩSD=44dUxy ;$޼yT*MNN~\^^~7o֮_\\\]]rv&XAxw;8llUQ-..Ǐ߾}hQl(ZTy\zvwTy8P[} y AlyCCCLfqqƍarqSNimߓD"asբn#\׵hGFF677o޼?׮]{뭷(teaf[R.wlG؞\ܩA:J!ЌL)VkȰ=LFؗRvtz}{kk}-?34-(B3@"_DT2L+SAAA>e#N\0함xBC>t2ҦLLL mFw+E01eubψܷ̪^g!gHCaєWARmdCd%dچ" $4$=2 ͛߷ zT/ҥKqONNŖyqJra2l6޽{O}S/ʚ1^`0>>>^XXlcXC=j;Id30"z;wݻwOKĈd~~W^iZz@Z-J"S]y#i}X[[[[[Z.[ydaaj1%Idezʭt8:AQ`xbd5I !+;FeGQ$I?~}p(P@ ($8l1s7}h $tÃqeTJ\w` \@ bg"5d]PX%Ji]6wB{ hKD@J ä)`pː"r5@1 ` 2 9c 1 m` )̚YfJX$c4Q-Zq 0<UJz~<SǩzC#%|Vbx@qVjSSS|||,J$I8\eGGGRW[v@!˗/KD_^^(f#Ks]J4M}ߟ,??h4Q^t`)^O=bbU0uGOllO=꒰wJD7޽{Fj9… /_&(&''mby]Dt(̜bkۭV7ߜ1F+?kQI3s JQyj$i Y={vrrGQT,L>r|ܹn{-Pi*Ob7ƖE`uF$~drr+W KIZ-+JpXFI|./>eg-M`GV(M loouwET:|vqZ-9\.z槝gNQD!: +TS]2KR*h"JF$iJ0j:mp/X#lrp>aPN8dֲh>"!U@ (P@?.N>:!teB[G@|-to`XܹsZloo/x;&"뇇Nj,4M(j6moo !* gffVWW+nכ6ƈvJ-$TT*J(vww]m4O=Qb\\U*%81 /Qzr=o$IR.r=>>"#F*̜={۷1l٢1x[j~I%,^&IIAdzA(0}0su}0T*YQOXnZ庾H% |i$KF (P@x@&6TE5%`Ӌb$ 04bG3HHW})Jnw(RP@TFnFqDy@ (P@x>ǰsED| G-ժ_i?%5tRA8+r@JdR@ A3ke,ȁ41H$E< cG!flŏ/\b2f&&_y0-1*3OgZkf\My+2 Mj$)FAxsv3.'4HvzvC^Kv-yc ;&u8oFRy;n%;ÖQ1x㍙ %cC&''IVN>3iZT\MӴK󄂔J|Sp$DJȘIG~DX] ~?33S;NM෻S.ggg v^OObd#"? !dC%PVd_u IdUQR4W/aLFɏ5ʹ4NR (O F$ oQpF# b | Q)=(Y (P@ qJ)Ea>ozYPbVi6Rq9(4L b4!VIA)1RiţJK.`ؤ3"&ɓzOjQ[C`bXb v D23 MÔPGÒWj װa&6 1hf6 .)) 75Y乵R/ͶzQ%QTE<~<ٵn+ i|m:J)Oݘ?K/}F05 ÓD B4T*μQZxn]몐.☐K0g,It!~Q)wwwk'g"te jI$zy^4j#\3WYi<۲u %rb֎)8qcHCV 9dMmdX( lrEEv??E?D4HA5hzq7[ (P@EwajׇB)x.[6ḑRVQ"E훣POy1.nu"za c>&(s+XA!?H1Fu.zeߩ)ވG)d4O!hSa3 1†kT a4qp`@Wn? R5*>\Q+hs: q8 ExHv^ȳ\>њ縤FILChH0!?ka(L-7+ފD$C/CΆk@2@=c52sh 8 6S)q !<|TsavID *W ѽ7oJh}a#NxrrrwDT*ʫzʕ~Z-)weV6~R!aJFhk8$jtA P2KF<վxUV[p9, FqܹgݻwrvJ^O:&8dZh$Ię r,T S!dxش, #p*y>#2pʲ,(daj/^0՜xw t G< '`8 gg.aN42|?(P@ (P3dݹs^E7 σ> 0mxK GAwЀ܀Ck= 8-R2#tʃ,)aOIC_@Zd辁Q ;_yO-NMVO"8xd`9]v۶2|Rf(MbaETŋWWWUV=ϋ\. HF*ShADrYbPmZ$,!=G9V`:lTj~G2J^__ R,pyy… YaaRHWac/D^jvJut:GGGm'wڇb?eȉ$@RiZ|5Ml8/r8JX9qŊGF q+$YZZpxޯwGREDhU_?fBaww?1<55E(P@ (P@pG[[wPQ C‘ nh>Г2]P@ re4 7A L+ !3сyW C)eSb)A .Aid<4@  NCIS8EQ0 Jv{<xL/'U\Qf<{@'xx/Z-85f[H R0s"ߐ(!r @9A^g?i]9rڄ҄e^:77' $6%?yr<z}% Djͦd٭yGc$&cggG"AP.]ו H"" ,S~XbR"]Zz%~[~ J:p \F'Dťzu!7nҴT*َ9C+T@ (P@?<Öm{p RK^(I $ao$_0 T)rc`A4RpXw F#_"8`4 @L)1d|fQ@%:CB$t6#IHq09E)8 PPq(( QG.ek R e\.70~uJ".n+qFŋ[[[vhu?ibtf=H wYsگ[Ir8g9Mp#*7[-J<9H(9јkƎ&9joL9WH$R cxgwwW928E!~D#%coHq~vz3oy{CT8 1 s޽̓Y (P@>AxU 4Xr {A DžA?S?2u⿵^2FHѩTF02 #HWТOUqZic?KF $2ԫ?\bnw~({8~H<sX <1H!Qwk CKF|$>̲Aґ;3$Sc5x$z*3GQTT宰evxH4M777˕5y| CFUd_B јn%{[k @B3T (P@x.уu % 嘎Q΅(/jT~lԕ ݣaf!@ fcZiﺊ(β0NO_ƿ1A CC  s&W*9G & &&n'H""Y+yg}xx\ƣ%i(Kplg-;mGO8;/_T.ܹs||\*$TZclj'<QuYyk8&W֬9jIr,Ǜa^4aQXͼx<[:{K[U|CDbooߎV7WOʊ^"s%;;Bm0 [n?2U~}yTHr@&gB a%}?5*fOBCJiJhA4^W2NV/whiG4j K*4O"872y|-c'c 1Z~7@~S{vGc;W"+6o"wc <"$BեiEQߏdey˯@9o_1\ i8F5K^4Y߻SUYJ`7 (P@ d9Rtv)x 3!÷HE|SW./Wu(2Ilb1Ȫ8F;lJ^aX yb69\ڝ;'_AdXŢ2a6Y~05=j_R0H5 <9a)ôx|2 ),ggg'&&ly|6M~8vfgҹSk}ڵ~/_^[[ VvAx5W'L˗DbSeg!;<1Vo򓑏XO:ND2Ce-#9]=AI6"I`@kvg^|$կ-TL(TA!//÷$FlS@ (P@=N輐a )C3Ȁܡ-?;ɹש3 IDATfʕjlHiM3m HR4™zpXk8J;d5f#q:L)t[ޥT'3r*fNicdFGv\$fi2ka6l1&J2UqA7 䜬Cfp(IY־9== ^DT.3g(sεk׶@nv[[[okǑPIyA 'O$I5"[0z~ #Bx k_9g8̜$Iq]' " @jfѳO8O7}q$Iy3r!adVkcUy>bk@N*"PǢ_Z~O*vyDV$: zrS_?I~_֍7ZA6OF0 Øo7._~at޽~93*؍ (P@>qx>9q!G)>-]@p]*r|8'ژ,&0lL #~:^s_Y*A/uAx>Ge@ke` =lIPC~\KS+Sz3+RL`f)"fm`#}R41$6tOW88K!v*z J\g T ΌZgtiBUjKKZ4_r>f Fp4Z1r-oqhyӄQfr@٬jYɓ}జ6_MDKKKf> zA4 \.#ʕ_8zjTEyD>\hRiv^'8TXKp.~$I$B?r( $!"^'}k8}|RBLNNE(_JyEVN!b,666$40WL84<㓓f_ ?ֻװ "LT:4 14J>Z?_NټݻJuiiIf^.F!(P@ (PCp8 5}j/6?3W܌_d8R/3aH)P1pFZU@Wø@J ˾Jt(P0Gcδpp,K6J/_(ʹ˛;B_Va8ainx)K8Ilˉgǘ _Mk) 6ckN%!˲;wA0;;rxx(G"M\.7` evEFCjrJp <55E,YX!~Q%T*y+xS ɯc `}}j1au+O0rݲF!Nfjbi4ju?sKvxƪ!!b~fTit"!҃eZŌZ;@&8 >\uov9[kN> r5'Cw!Lv'нO"8ƴRfcS 8_!{u,sё1ƆeA֖^/MSq~??[$Irrr;wZ(8d>Z$J@eZmrr2Ik׮ Y zԛCD󫫫2Cqlܩus1==:99#2c}V+#D\*i*֞Rwttjժ$Z%80̙3.\݊lP2enb8B|lddq< RIh#9%oW9MBH΋+v̙U*?WAӅQ bw^\ yw\.Y^ڐ?Q@ (P'Ǣ9"ky<G_,u:9_i4u+e҂Yd$Y +@"P`$Yn93̙ "mXsF^QgW .*#5<$80 ucW WǓy!z )\.f911a{MLuFqܹ=ѐ2Ւc_[*ex.Zp%GGG+,VDtxx4| 6AYg!8N ːkp iF3??l6wvv0 89s|(,5!#lȶ)GgvvT* <}Y:ն3I;~F3W.]w̿x7!JQ P m=SiWʧ^sW|ۻIRF6Z"EO(P@ (PcQQ>><<A$kZcgr9MSrY$"zċJ%;%>-~Ắ$uYf9==GDɋ8D7777;;{M,)B`U!0i# )h2821MrD\DZֺ\ 3Wƭmx&niHC v7|}Uzݺu[Dw#Ԓy֏c7ȮR~ (P@ (8S.`zi|e '9voǚsma60DQ&լe@^FhV_$8frR]tv irNڅ+2 \!j:4͘hƐȘI)u` !:}$ɰh'^B+_uqG`렵vv3W}cYdYV.}ߗ8?T vd󎏏X/%-*'r޽Fh4>^ZZ:1fssVyA7zte-c 1]] 0ƀgUR65‘)M3 3'bH؂#9JLY[W9vbUjKvk+Hd0ԢA >)JKpIm:TҢu0dYl6ѬĈg{{{W^=|$(> CQU#9%uaaI۽rJ^FfD[VgffRҰV5ȓ;cN;;j$4!fpgggϞ]]]1 Iߔ##npҥjz}=~L%g$6tG''Z5?(o^{w( B8Cuf_xzi:llFTZ Z%4Mblm-N[l (P@ (Cp(ղ`Y0\DU!}Urt1-vj{[FZf0Hi`WT Lq,J6!C3`R(۽;vahhj@G| fΌɌδfcX<)HM?֏{8RGDF f@ v|.`ćARK2g/S,*RIA[sk8Кk0tR0NA[lr><'iѵ*!n#Fҗ/8v7&'[Q  ޛXWb7⽗/}nv7{asHQ졚ɱFȒF<h``/? 00 6@C4H&{ʪά%+o~{_ˬxnܸI;qc{(Zg:}+_uѐ71 2E@^kv $DqAp'0LCQ{e }ؚ_.6ۓMl Z@^'wJGY_ xkZ9ԱΑuJߦ5!%$0=(0(αu;{˻ݯav+ n0x|p)p[3o 1()$qvvԩSV+rmPJ5, ^y˗/DImbQ)bֺZ T^2{ffŋJ~Y]]e4(LYkkkEQ<ǎ{'Μ9o^~}wwWKޥ>̌䒮+UJ ?~FQلVtep8j2vj?CbZJ*#^_}G~gΜ9}խ- .P^pϋ<'FWD+Hpvcyxxh|w{m(l `cb2v{vvg7ZHRwGd|<"̒@F¸I} ow&2_؊!E; r`up"p3/|S{sljac< V\ V9vA!&tY]b^P8(R0\'VNM:_k@ĎB-.`Dq f YR!P!8u=bDv#B$R߿tښ@u@XQ,..tuW!@VY__ؘ>}^B*d֒jY։'&''ƶRj v_|4((-vV-///..vݗ_~UUu %>gSիW_z}˭VW_vFC)%zx"z^w񥥥I;_CCCBi]v7߼zjRQ9:T1H R펍///.~ J? @/䉓ɑqaW!ȶ.`dDSvivvv]&I?QBBBBBBBBBB] 8ɥApJ}cqPekYKpVUyaH^!Ąe ["ϑ”@HP¢9P bnZ|[C t;0P>B98 Y6!C-[?¢R 5Z-ޢۻvDcH ܸGn;;;ۭVK,$s Il6vww]666&yaa믽ZV$VgBDfZj_>===;;;333??Uc666666^z,FQtRjN>̗/_^__wh,Z˥<.]ZYYqSòJ Ƙ1c̹s笵gϞ}ꩧ#4UZpmllZ-I*8 zvtݮܗH2ܜdJHX=aΎvrtϯf_w1@hֱϾ=Ϝ4U9i~O=N`c1&MHHHHHHHHHHxq7N84k-L`P((kRABh+`r&Y 9+MXE!s `,,4< 샜)ו8`'q#p({(K/*:ߐ2Ƣt(0u`Xsfb*͈TD 9~*!=3wvzdНTvfffΜ9+WQ(|hfsjjjee%˲SN9s/]$%t< -H^KDY IadD!D|[ɲ9'AcccgΜkۗ.]rIhI[Q̑!Uks)g $HQccc?8]rd#Axf$E1:v5p$JrȂGC;zj\'Y5!pߕ4CS͡sװA-  OUL[u$;y/=t =-ƔntIHHHHHHHHHH;!GƏ:̲vO uGsb#q0zA pp4@ 4`C#zF"̅DfpELʾ }ISUIJJpLpX 6DÎ {l%iF55ZultnFIg#9%!!!!!!!!!![Tp T8A)ҙK?S/Q Y ?JCgF@RgDePCJ)+rj t ^S@)  '5& X CKM48Ơpst'#=D y= N6t^#/n~@$q#˲'N4ͭsε홙"FQ'R3ښ48qę3gFGG/^Y\$ˈH D~FJј|衇z4K+rX#edJK %y n*f7v^mnn;wsĉzjtttuuuggGSnGr$YU28˲4jŇ~X\՚ͦv7J0sY3P՝=34vBwwꮞ/!!!!!!!!!!^-U?*;6=oF14d@sn*Z\.6:҆hX]M 늆Iu'ZJwH(AWP+ԳQ!H[t+ WvoU<$D#l{2䘜aYȹ¹!k-s]:m5vak gkZ9BLzs?91$zh|n'Y^Mǃ֏ɡl4077gz‚y@A!Atn֋1رcsssyʕ"%SPUp y7'O6Mћ\xq|||xxX.-ҀYy|05Q%sCCCn/_/XXX8yƆDDQL-/G.1::*ڍ,WWWGGGc 8rUǡlltpvESIٸwGϠts [}xّ{_Xr͡_7xA:j54jkr29t_fjW Z#={J!m"b(. ]nx(sP(4> |@1;!VzjCv;)ږ45awEa]ǔVڐLtrǔ( %E>gY@lHjD5&.*oQ4%ahhhbbbfffddD.ﯭj9Il4"߈Xʆ$;`rrr… /_>u#<233#}^www1կ76 4?錏7MitK.C45jGL@ػqyxej^z+W<䓓nWETh0|dIu%Ŏ$y>==t1˗/llliَmwFY{tbg`SS 춟_^h6s>>BJ";|CPegScO7gQi8z‚{Ʀ>UB:ؖo\1`ft|schZMnCg>tbCanLjJBBBBBBBBBB»w::9@z"4[@1P#uh  p`Z2j|;p|)/| >):`e8yyآv/'^;Kf>r-c h4:΅ U9ͻqcʲzn(^7>>.ErThţy+WZ鬭moo3֖BHV9z[[[v( kޞ@FGGFvꦉJ^w:_~Y&` IDATٙr䋽^ohhBZ{ZǗK[644$uzl6Mѹh,;nz" bjn^ڢ8 L:#dJ9\/I'|Yi*b& (~BFpM#l}[(KhFa 458G*ju +M\/ûYĊ XvFEeR 9ZɡLE.x`0k}* HXo/M|?89/=44Tʊb )c'$6͉jTc1o"DQe'N`n{…NeȈ,FGGI2XWޞZݮ&''gff$~IqzBcebإV77'ծq"c2^羕ʑHA8SZ->;p$&&&bȡB9T|#)țJkr- OpOq  2>~[~{j(,@ N:(`,HC '? {س gm?hĨQ t0sYSԆsG؃mV$HfXL͎~GO>$_@VNJoۮ"WB4dG5hJmieX ɥKHneTRc"E%q5 /:8 H.4AdՕ߽r*S#b2s|YHD,8d n"9" 9bŧ;HHHHHHHHHHHx(8yh={PP} (iP@a+8ĺB>'V!*&:[{'ks̝^7SPC:JXww~:58)&V^!< :BFmڈb ok+V#%=}v;#Hv\U*Јd[0Vˑ2ȲL (舲y^%/ߍ}I\: 7fd(dʀƘ1nuP.7vc`RҫД g()IPTTW[ T8 m߻voP9, > !%A9KWs(PdA PPK!(Drx7>ş"Ey ]on=x|bdF"FMv֑,/__ÞCٓ^-$dtp&G :`<)a{Ui \7/|{LuGU15֥w7Urc:@gnՄTX1\bM~BD.JjT'DEtLJue{`HDC","7 CtxAly[D$J?8-xjb=Pn.҉6/-C7P9zBA-*Wܟmup$?Cn_}}]Dm$$$$$$$$$$q6O|/}^y 0 c0$8/VE&?Ņ&,^i?]8]WO7tiV&7`,olQa,g v> $Wy&7 g j.WLqI)Dv$ȅ@&OD)|͕a{f1ut7o7oW >ȃ@ D RN(TPITE4T %⁸ݑzV>8!!!!!!!!!!]{Kpv?7^zu,2FN0"8fπ8 f[.*6m=t\SW?pxd,J+>فҪ\(EvְJ$Flo9q Y G`5[JY)Ƣc&W~gfPw׮XE^GÍO`d3燛702N׶ ]>9 5Cnf} ԛ>`&o{|aGGoEp7wZ.1Bmߵ%CGd)[yܢE=t4AӁ!")+j)jSq4,Z5H_C]@< &"P >FR $!]-HA{O)@H&"&RPPb fhRrEB=P EDdZK) ̅"R":,|Jrh`7 R &VJ đ)޺HP :#&$$$$$$$$${qo WzxkPD-LAO#C)d]h6wηA@ )qP-WBir,<ϕ_eg 'sMr">wÆaJݰƠ׫5>OxYy~}a-71oAgG'8 M,}MZH_VjО8x[Bov:'[bߡ9>CwP qݲeVj1搷N 3ar`+R=?3)#az$?;T8 ebJm] RcDD<:C5(׈k5RФD5R a=if-PZ)N ӡBZA Х""ȈX"bf ń Z1 MNAiZ| {.1DФRbȄ3~XkELxYN5~p?Ƙ_/\:=8:JAŌDh*"zRi W^JnYխr՜&EY* !}\C(ky)Vr臌Zp$q!k4/??qb֗7l9L;%}5jZ=Zz#]X!3#P?ZV5`s4jz"ɴF]utmyDp2'Y|b'-K zH_)hU4pՁaEDuhH)5 P#9FP#%5RVhT}3ڂ44 ! b(.†CV9E2RrZ)ELJT3?³TU OH1a(IsC e?G R& h/>%C[@8l 6F^ÿ'e?w&Wzdeemv23>)0a--0%L%#6LKa`JoQlgsW~S2o3z#@rEp|&ȑbFͿt[O3Q1PSuG\T +m-^Rp/ nġ F{jp@Pg BmC N3,2, Z$cc:X7V*.!Uߊ0l|UaXn$$$$$$$$$$$$B屳~>v~nY&_!'?+7daH/Xsӌ !P+H~*Zi]r՝aߢcI3WXJ`Ko=Yni-] (2 ༈-] ؁-9cQq2 }@A:q;>](PPN[CpJ w`a&8 3)Gj11P q!@> k@U iTZCWHEq灯28D§2hM~9 '?2AGCepPyo˞7}&d(· $T'A >??/^A[*5V`%tx+J0X(ARk܃B%ij.>Rpec\|}l~9 Oh@PH3U\98^a\ݰl@Y>/~XP( >7wb(4#rX%ESUe , a[|ȠZTgTqA+{L}  Rq|0" P s4a<]azh"ᩪ TX Έ0,A3@paA|G4&#DTHAQ84UG;]oЄЁnbJHHHHHHHHHHxSfo9S fCK@"=|a`XT$ܑਟ̟eLS^kmŵvvo93uP 90U\(KX!8JcryiG?ٟa!zn9S 9'>cW_Kr&|/F}#I5-Ɠ-'tφS[-g ` Tp 802l` g|GMZ Gϼ,ˌ1YvW<oEh *H  !#˲}~{G`߷p6CiKg0s l cJp>py(ܗ=zp.7-a %‹5!܊S\1 Xndg_^LA ?xg,:<ťWV1=FZ:MȔ`%eƳ H% A3*k3` g+ 042P(K:*<))GO|? zՈD]!D^لs.Ks;M>`V}mML7?,Nۛ|<鿦4#t wVT?R9chh`J^,$!p,CkD2(!:MwJ.L.Ep+Q8H:P7Q@p׌c 1#zUO~,~w쵝.z PӾTrSP:XFQuX%  RSF <}6A+kW>!C4)aLGE ^fRր(J8i6zC?sp)Lo9<#4 RM: y yRBlzeI#5XRoR>xEPW%3Q`?W%Th;FS4b4!#8"VP& d+@PD@gY~#9)DE,4, n(%0bR BZ rЕ5PFB1`/BܤZ ;*!!!!!!!!!!^#8Bs_<#s5XF` \$3 ʡ5(J̗kDP(-92_  SL(Jt 8 -QZؐi,zN?O=$CY)ZZ``VpN4fQFPV 5Ꮄ*ZA7X&&g2 \A a i*et/C%KHB_V:ЀRH'd,200I0N9 +"Lz ) #79H胚R!dh"a4AA2p !h(%LN1 -.EMQ ~@X"bfVA!,G#@s&$$$$$$$$$$0!0jg~'>Y{߀a4Ҿ}( 1(q!ae`*r/p( a i8JH 18@(YsM5\9+nx( 4cۜ##KCgvT$x@J",OpABk4A0t(6%8Pc&xH4~+" R#'R@p&(h:ȄBW.fʔ"B*F#MQ$ a'p*J3>_9Bfu]JS! VPzQ}j_Y_їאQ' א3P ӣ8}ىWneJ :CFބ"6$8iBpޅ6Bs81:plgζ_u4Ey|y/|'NO#S^jysՉ;:@Vq2gdѸRw:^Y9rFW~@W摉 np}WKk{"/UP~zvb薓g&2|i}](vɳ' K+ 'ue^"bbIJ!u CȭgRi@5Hk@b5C>W&^QP#W $w% c *ERC=uB#xBPvD<գiWC;<mI %܍W&& " Ɏ%CPYDs$$$$$$$$$$K9Zq_7wϽ|JEM9sxSY5t hZ>CU^sH7L aq|'f]y+0J eO?4ԣ'>~>w_FB+3Iၖ1A!-҅8a}M:=0 ga,ih=ڨ?O<`IRBBBBBBBBBBBBBJp \o~җwzk-^ylg=j0@ x!`6B0‚sE;+X|{33㓓qmpBBBBBBBBBBBBB;pITg4ɯ}ۯ{sR9676hf@JXc&D3%13,[m%޻}X^ok<<O.9gZ-bj-Mw5s+w08~6nƍnvδ-x  =C-}3{ղWڵz7^̎` S:VHVE! ک8绱dzȔc COyŏ>{psvr,s9AԆ*F;? _>}`?%= n_oBBBBBBBBBB}b`*pmsoƛWϭ][tͫ;ۻ@'qPu32ͩىN;CO?r|yaf9dGL)!!!!!!!!!!!!!!»iYҊ`KG-VW\vus}c{ck~[t;nQƖZ%TW FVMm j|!d@;ճO>.0[ulٲc 8nΞq$\((C+xFFkRW%8x( ʎA<(\!q`!|aDw@ ;ų^iǍ؀_zٯZ伻r%C4t:A:?ȡ!Z CPlC׮ B!B-OcF9%?Γwn1$[?B: 5~V1- \(w|% ^ 3xcS"B{4חm} vMSKP}Ĺw|+KHQؒN,U=Wz  f]?wf%?ơ@!8(Nz{ LvݫsoGfmA=y[:TD-^CP)%V9~V75@[P'%E /'nT`1ƧtLn};!BV7xθ[!:"4xmwK(z@ɠ@˕/ˢ<ߞjy鱓cϯk;CcR-F6Ƥ6$6 RpyEheoG"d1tL[_3s :eb`T}}سvS{̍ @ ' 2ϳt/HTLYq9t\k-xZVdeK*S-~`?`XSoeZi{8Jljdܾ%@\4}l?]g<|̍iمm6y;9=FVϬVb-<e>|f:#'2gK\³ ط|\k-*>jo9SnAY:oJR?<TXJ Ο5N>Vm .x׉ЇvۤnMT5BE5f=/tZ7ۆJvHI"oZpG?$ȵNmSqJu¹<̇ҌȚ{Ed{Vc1\\RJ.ٺzR`QK:a % 50t & ti)r|;Z*: o[HkPUcók)cw4[.q,<:+ɼVϿ2aG* %wT! JUȵ =u^YpOq~Y_Ym E S.b6Wj>WD*- : Um<*Qz\m )dԄqBU/U@&n$oP6//%ko8>"ՠ 8… :p$OB [j5Ys.]œL e.a/᭸27us<1+ W-%uozL?7]hTFշ F[8HDn=ͫbdtíOϽ\|ڗ?':Vft=nO;#S+] }K{t4wZ\K (bvm5ץ*J$⅍ f8u]ef+!J3wit zcHX2--cCGX2*Պ?VW*<_G7;:om[|ߞ6)դn;%zB|[n{Aޝw5BV]T]"|rc9uSjsoeh{5K^^Z0 Ĕ؅Q7s?S5}@MJ/>/(TF :twhSt\iqޛkYؐ%kIeZ2xw"qLyVNWgח[>'LXO?t'm)|gqlivvD 8gsxa-)g>>ji׾L[}5'[-2x¶I=Z:?,vVO6la*~E>mWvCn {B)tSk'G:ok  ܌nKo*x6m.Z<'dMBja;M^{7i_z%i)+R'X e<P8@I5y-#G2n,Y4 ,$jY޺lZ ,˪}>o_,MsDL7OvvJ] ~-P<=Շ|٬BS-8m([y}Csej^bRG66|XӁw :*,`#9$3 1S7B˛u^WvK/oO{|GK9GJ2/\|GtI XqZ"di<۷5~gl|Nat&l-^}=`}RXR?_WhiUV>{-ʇlbYvB—.c5WwytCWG Э2fgE-+"YNKkꁵWd yKǖ&*y;~^:C燴JVa4e;q@+nұ-W1KQPK%1Z?R] <'w?Utw:koKt주.Bb%ʇ$T ?1NPwGٕ_g#/}kHB1 J/)X}>5qd~/|Q5hV~yҞ(4TFuЕ[i.ލˮ2bɹ\F>;O%DqpFJBT +Vg8 hȋUmG-~>-e7aT$FUwmnK9s:Q0r33UTxサyѩsN?^[z?]+ 629zV/!χk ]/\c8@FAIRkޜ~_Euqׇʲ'{ KF)qH"nY{}$RmgوoG>*|ĮK3|!lȷ]suoF|V:p2?gS0\M8ϗ)8D/UGL'~+l5x '_ IDAT=?^x:@Z.,};?"`FDҭLFΚެSGlp=৳緍M+#:J7>sCsCo#W"lfdknԠ@yur[㚟k\%,rOԟ jxM9ek6FZ QB@əw암#? Bv#C3S{\Q]**w HM}@6mOۺd^'*REZt֫u[G]=v"{=5lXW]Υwgc{+O6kЭ)C݄LJQŲCh56zҗ| \C1G%n,\5Ԟ:`*\@TT2?ƕ!P'|yqMu[j]f[׷JZ ZV@>"3w5$,gѯ(:q)TȋO' $Fp63*#5^?.pgs2FvmJgoFqZ<|&,;4'L+TզWRG:G{8aq^]Z1EXԸlX l6A:wʚ >KLA&-npyO fxoޤ9KA08Uo& gQjgq_`L薭lojV|aG8IT ~ø|PA܃qAoo@ɨg&fM _GMiPm_/n#Q 3WQ 1DآyMU/WIWѫu\7'E7(<; u0C%Ù6U JrR)JV!SJ(j[^\; .TYtd⫈/CE)|OK0 ;qS`6MT7XVޛ[_1֝<<&s?*XN%<>Gocy~]*&?vܭg XGM@kC.(hۭ&.J/4}ئh рKTox .>So;MәLPR"HuaP#wHҚv4dݽDž=ߐI%PBGEb$zhqo|-ޡ8>"$2]jwpp?/x_q[ERMz(kƈ)Nxp+*l}q* iY(fgg'G>JP%kjR<97oA*$ﴕÚ$$gm4{ x\;`T `+LX>rd'K-ɈySWA>S?u,c&CDuF^ϜtʑϱjeRhTL 'n[Prvŋ { tEa-ݡsGtT{>ALkEfXJE3X ^m=l&! ^=Mp:}}Z%U K@?=ס4"oQ$ y{jtX0{_^)d!vm^S2e@ ìR֌ĤArw_&ԣ1+"dNYF5aٹ/hqOÓ`P6~‹'hS>.l'&]Tkc*\St 9&|EN;׷}dYʣ]tv[1tj&|A -MP,R TG߇q R9|=S*ԔS&)YL(* ( *yΩVWVmcX70w7._}x/_73DO_Mfv{uFѻ \IPq Ξs.ǝ+3yPBb}}-;̡T˺qt>A2U$ok徾s4YcW=w JOZYH}!2@ q3d]sTl" QO ܚ۽bZEo8cL)\`6 7,%R3Xe~=F7{[|<5KTA>0AjƵ>Kw!S{cW7cؑYçl  ?ɭ'vXu%I~{i9O)|uߪ[QIl.a|weG  =JIF5jx7 {X`ayjh/+R]ɓEC6?ion%툌HIEՠs+Yo_st]*Ps 69D(u﷘[Ij1ԣm"koO\]DOY?J {TY0a ae lʦyd+0iIX x(l&9^dM$Wad/R4 R˔QM;S~”.G!4,9}/}[Z78 0 2/9rt[>C3־ ?#gJaQٿ]/rWٿs}ϗ|] PsjoY}Ǯދ~yuiLlJPJʂP\|v&a0B@YoQbtQ1i~:W 'u0ggKʫzLAh.kҹ1=;/1yC:.+,Օ]h9Slҹ+s PoOY ݈ ȴ6q0ʢ}bU_:UӋ mVvnZ?7֤fixkk@{3nruk 6@M0%r9+Am;[Px?X/<#j ?6Z\``˥ G/KN:t} r ` q3":b9З0d %UϺZ)-B_{SaFyj$Wz/M"2@U r~_ՓiMɌ9}4J^ZX> MhB^rbh`Xu\+z$n ΋k ^-AJ?'*^zJJԑ9NJPUoFcovLN1-jz'V)` в4mlvf~> ==IJ(#AjdxszDxeCH1F;վH"fIODTsZߨKT@NOWvJ}+wْ1xh@JX>VQx29;|a[e;(+ݰ :mB|Mg"B ;+aJOf{ (9Wjx~Q<vNB6[68_6Jzt^B>WXsznT᩹ b`^ ERњ0Z ~˗^3$u)-2Amڱ'NRWvs"@Ȫ3N^7h׬J%͠dGI>\~XCkS[Ϫ\& e+5bO::\ֆ@ޘUn(md%%kI ^ sTh|珽L!&|ANye{Tb7h$ڕ@f*ByQo͚&\ |mXƺS&ei8:eɪdy َW7橫<{Z"[evsm#}ٻ;B5uU;~qs/1 `o е+tYuAAς:P5|= ڪA4{V,=^]`y=$;fFM+k=u3{6 |qC]z67d|:~'FPx6-, X^d|ߚ;RGwK_}+WtZ/^O۵qmJi3ϭYNx.2 Pt(GNlcQIK8ͱUS'$0# P-oX.9}|~;9ѥϝ&ݳN2dҰ울4:$c~fjX'HU,#4>7 n93px5ǻ-EoK\ϥ5ʆCR^$N @}/wma.Ѱ=&,+ m'hy';“y^pUIiS;yu(Q`{ ~ TiݠR^ zO>q\>bi_A㛨"PyZ [ԥU=O3JRmlLkoj@QDn߾Or8zƐ zV$,F.-?5㕦&jTaZKVzsuZ6 Rfd [2hƦ W~ŇD֝]*4=yY*OagRT1QK `[V˹dqwgM2ER"N@V-{ Yn;t_rg9͝ fPV|=ƫ3]ܽ™ vTTiӀlj dF5j̋S{JT'a 'I5Ƅy \LTzgٴ-$gIչ_z4C7z}ԟś Ӟ5[:S㳨 }2|mMˑ^[iaߗn),@Uu٠-< z<8-);VTp)]Yc*Nf `FI|6NΣ'f&"֤ddb.S"dK7AL(%?#Sylv1lE2EX;A4L(*le# pU7?'< +++Od@t|*bq]9R9| ^Ƶ=~ *jUNxW6$DOضw+=iKK}۷oYxrZ`urmȓGxm>yxn)*\z$U4Y]w{s+%X:-8BUAaΫܼW^ ͰNj0jD!U[ϿW,I1{݌l^\pĊ0׌?,[?ul(f\Y0*gM2Edޚa@=c5[ʬQ:Spɷ+.p{gčnXopAengT)w%HҚ p4 vp_6O }To^W IDATڞ(wwcO @z`uj6!._z#.y.i|v5XWB*I!m0Kr$JF7t{ޞ 0_ʿ\W0]:Hw2׎Z@_вbML57*_`XUZty: 5EUV!U$]G\5!o<8C?WP)KIwĶ&~Aw 3Ieo$=BA,á|prW\ G{bGuFT!/)%vC'o /.U:`j1u5*\gm:G  7(MsP]tqvlυ[nP!!T[|#:u)R۷=xSd([X4A<dZi{8RO?쥂Pe<6;3? :]fp9 2PTճ&JEtiCE-Ŭen\|*Zݏ6 SR9Eں 4e~I{n^w]>EޞyqMѦsN^T45f{O!}ٜ",YF7(ǤaBUak^Sa#%<Ӛ+},et,mk1k2)T$X,SQqd6t̛C KěW>ZsnfS( Y+njyX0Bz0 `0_!BezPKr&1\y@ޘ22IQMfIws(>B&%2ߺ}em`l*XujԴm1*B!BH " ȏQ$P萐O~vz!/>euR{kP}X 5 nsyJ]<G=(71O B!Pmb"Vh@c4c|+N^zjvrvZA kضc+K(f+D4 VjX?vN Pb{G?K¬O/="\WÂjYE@!BUG>cmÉ,K!SFW08 'S}. ]A!B! 8v#=+Sm%/E7?|a}H/K5+/vU.7/X`_r@u_F1a7+AB&@ǔe^}-+&P彷o_%dKN$ -MZXw:gW[cK![0$q9el tFװ 9hwpsխʑG!B!!Mm!tJ69[iM%vP\D?]*=ll;ޜcz UP&skW9W4#}d?^P(4>{yHqK6qz s2]BB!BHcQ0;w9sݻ'A'q0@h+[.\"ֺ߸bmGt*Mz]l ϭ|^cg-sΦr)=GC8bjͻg`t!B!κxyy9884iÇ'N8tJNqX?7J\Yy̺prj`ӮY&j +L0r6{nL6c֭ל 0|O^}P7qY͠6_ Ҳĸs9|wA{Er:O_xOY >a'`cKo&Oo[C'k}0:®!B!ĉǍ3f̘ɓ'ߗ啊d<ocvt[Yb'@Dg _$ O趛!_'UZFFqAVJz|W19|,T!{ շ17`~=xg"Ş ӟn!B!A ;w4k֬% xzI>Eu#=$[ q7bPQ)f n!mt~1d7=?ڛ kZVjBw>(oQK^@&zNV)kB!BP-86m499YĴ:Z`۞>xa*oXxkiYUsc\qtcbl١kWgy dk}B reD!B!4Oరx޽{W-a-]V&V1wf6z8Bc-ݝ8_\4h{z%Ijvy>y~!@; mB!B!T-8\\\_.^˗^zOǖBIoKԬ\L?xɷ@ut ˦{./Bm~ B!B߁EСC'O~Yfbcc|rU(~}T櫜o]PPP2h*8B!B8وϏR}.ZgmSNխ2%w.Zw.Ip: 'R+GuK )` _1uQ"B!BN}*эDp".;䃧oǺ,tQ\tEݢI|8N IXQB!B!Ti|`6MT7Xqd7xc|0;yxLҴR8AעR)Ʀq ^vaN'sWr({4 v1r:(iAfp+,|c![0$q9ڥgs꧗F}-%;%04iaݱ]m+maW^kDbAg.ĦeDCrHlf5ժ]NNvKOr Y&z7yon8'6,=c+] $ZJ5*uX\B!~X@ 2l9'x6|O&8 Ĕ|i3~]j|6RNOv92ȥO?'CH=D;Ǟђip,t&R3 Ӟ_}h[o94nD Pɇ9$9/O]oe p/NMs*Rm:AhY:ԫݒːi&.B4+W*_!uقΝ;...Ֆ(F@3D䓸dƺYvi~]:ٯAL$ ^^۫;="w=iwo0N}msSW= KR|CjW},=2LI6ZEi6Y;sg ңMispBs/‹Z2ze ?;v|1T"ot]?DVh1`a-M8y|xĉC)Q&\33N S+!4, %`;ю[[0 af=]hnjNG,忦;YF?L;: ,0:ufߪo6>uGԘN^.RDa= {r~hh9q'pR.lD@w5hp{k1i#dׄ݅8vu5?/L@ǜ%/tZ7ע]<} s1rٙIv@HdVFXDq ?/&_:!6nlzfXCu 422"$+0K:t,tJW1/DS(ƍ:99yxx}q u8qb^~}GQ#K '㍧Q…Oׇu3"<|TAL !@pӹЂq).O{>n:=6wT:o^6ɪ5̈q%ݚxA4u6*KXQ[S@R|AVv>1C mN_&^!n鶟5hɰQeŦWo’|2/.G7&lwVa&tMLmwr571lwqQ_{rC Ă]+b-DkѨE_b+Xb/X(J5nq\'Ny3}SII$cqqkvK8{A%raKO>ʊ$I$ Pd@zp;:T?O!}U֬Yc  RtJAqʕZj)Э[7niXQAAQ Nsgmg> ;klh~mVסFߊȌ ig\. ɾڿXw]+,x]nų,1h@_Ozǐ4FoC5?6^}rC#)BmL "K6o9ڞ=T?obvϯ?D==F^:wR3ty ] ZP,1ηJ{ɷq-7]á:mKǻ{moL;fhƎj9#cPw(pOUPW^+&?<DD3ݹŜ s~lƅ?|8 zjİv˗/4-h/Ϧ*JQ(?_SE)}TF Ě5k6BroMI[M rٜ2fmLNNR-gioz1QSi"zsy?v.n˗f+` %,HxV~3+*"PD!]]]n(OIT zEC~5d`Sz(?oBtɟVZiUTIOAX)GŊ߾}ۮ]B_4cX*0}޽}& zU|nr"He]BOR`xTiz5&c՟0E\QSS9Ov@"N# cKVlˏ]~Ν;'[9zeC`occ2?j~ >RQbnl[v<[1zEfQ_kq:,G&h;scܳfozwn[!),>+ѩG5-|f xOT+ ֍r DDN»ZBdꟚdxƭ|]n}0_\A\rkoXP~/I-[vK.Y[[! HS r}?[nLʙ3g>~ضmB)^}mo3q'8JЉ~4,[s:m,V{Я SHJ#E$R:F8zV87M=n)k ªA; (#9L{"r|/Ϥ 'DMz!>L0jŊ843)c/س4$fvq noVx290עIpdǠ!}Gs\ҢNEY MKC̸ '#^Zdh(;q*gؓC ˪K-R\\v4gذP`4c8!H@SI9:a{Ge$I2cb`BGQ~SɯtУ̣V]v͜9SMNBa ԣGlA)VJP``Ç-[v˗>})zFcN.n@&w=vwT=6lBwZWVIyyaVOaIT ݮcU0Qj3nz(pIe^|hݵ|Uw9ԵQ+'W"T{:ZT?E aUe:|DN\c73:7p\ A |ٝ똳3wW] Ս4hiQDB>fJỺtr$bF-fY\W8Q oî>ixyB]*F]g ̄w2KaJJ}Q_EՕǐʏ%&iӘիW?\O R}׮]>>>ADg` hޭM6GnbqɥG2"(#3+1~+1. aH$dd=npSs 8y&dJ22<Ի2222>E'UJ̌'h1zxi"QVRs< #`E-hVBF:~ IDATkAG;g U7MmV_EV<LÉC?jGޭm5Y%5W>9|m꘳P3 @sU̥7(s3< A%''ʂ.tmվjkMGpP~MP~M֭[oiAAJQ=)Jt-=/zrc>m{[.պX~P |5ύ Ȥ͓o;!_Uc8{';pNOwIVOWmC[,H`'&`TE# B=VT1Ԡ6npj ;hIYIMdvXn8i6,A5 @}@BVK]  IpKg7pq:)s3H".APNsS8&W[L܃{Ϩkch gTUOټ:u1~G -?h<bccW^rqƩՉjA)N12-zs4N͈f]hkN+Լ{͂#apl:!*3j]eJHwn11LꡭDi|bhɿ$Iooin t;MnP`vGpFoL[\n4c{VU^G[Z{Tsƙv[ 뒀])s8D:VN w#6U5MһQ VUڸ${ldj;/#5י`S\n E/z.-+b EE<O_jp[Նr:Dχ\eZԯQ(?o*U}pŋsjYY}p  HUp}fO4y_33@XZ 5L+5W"ߊkT=Dowt}W=Sˊe8l>Wge^Wo~΄L麃5:)|{~Y mBjc';IqCīyELfony|'ظ$_o.8㧲CgA8`W8+쉕'-'Y|X7ӈg_DIϯYܳF8eWsK]z{ziSu)<ȔiQC2ڑ[I=:wg< >^@7Ӻ5J.-je;bЎA)Eʭ|'v pXRJQ|{} h9]/\.dY.wjj$EX$\|fGC}!Ӂ{~jNR"vj8v m扎 a,K` cVf"T~hBNPd2X 15 wճUuKaʱciO#7KvS&iq> 37\_RgQ (VX ( N Xx/b;tk*w>ffqrrF(9϶RE9%/eS9WE N* D Y@s,$D\.h@o(@ӧOy<xfffl1h"8G4GQ~CUڵu F׻u/^`Z6D" srrRiNNNA)p g!daaWrrâpݬ NcyNNl) S~%u TcH=(?ʏJ~2Z=ڵ^:qDRMF cAQL&cA)5 w56kvt"%\ʏjWz'Oi֣ܱRx RAo1Wm9V!_cl%ʏ [[vimyP%JmAALE9TpHއ~H&[:Cr=a=xRK'C"bZV-Z4ʖtX;`Μ9<t80v(y<(\Jի5k*A):PssmdPtM~Rq&RpBALLӿ(;̜9ۻm۶$I2n8J[(ABgH$Lf' eCA) ʭD^tƒXXdWdnv<#ϩ5d!0y JŊ׮]b ///&V(骘"ʏE_I'Ç}zݍڪ;8Vm̭݇=Nkt>sH[^::oRyN NfAٰaÆ J[AA) =xӂ#US u󗆱)d#] Q!@-x}͐} sa$ŕ}f[EAAAD3wʕC]zX[9r e GǡsMs/^Ʀ@&h;hri`f`MM;׿<|͡$Xh7ȦÖ^bќ3'6\I    Eͭz7oܷo_```+7oYo\1#_kS#)iT.S۷͆cD1r[ʦs~ՙR{[Ͽb(   |㔎ȑ#.\8tE 0`%/X YrE̓X>>W(n{@sᅫ0&>HXSPh78\t    W\UVN)ݺusuuqF K"tmɞ2 H/ŚӄfhAAAALH)(8>}TF Ě5kŕ0ZǤ2Dր    JAQb߫%}RJ%/L )   bRJAuyeʙ3g>~ضmےDaۻI [5w2   DDQ =z{jժۏ??"IRE+<<9ˁIN w#ZDAAA)ڢz׮]ׯ_iӦMiQ5 'ڀ/Kstn EdQAAAeVxH{R Z"y>CUWE AT.  A\-$Wi.9(\{V̿N|>so4뱬P@% pJ{OR 0    !h.m7vf_?_A(!Zp   H1R{g1i"eEf@AAAB-lQ)Wo_k\հE< -tR!   HyFztLDCakˏ    .PQ|:!1Ж=;Y`   RAGõrtrtq)m9AAA"   RA AAۄi%PAQ AA䛂iTŋG~:666;;[$kMxUVusskР;BM CRprhYi  ;hʺp±cX,Vʕ&L lvyW4-HRS+W$I>}ufaaQ{ RtJNv;w"ޙe_WKFUctlPnI-%&*  Rͽ֭[|ܹsׯm`zٳ'Mӯ^ $K@&|.5x̹sK}kX$l rAv]aKgB.=ONL{gRͽz5t3!H z|_]i姾5ʘLIoߎ`S3CYGI^8*" +5?=x xNnNxV$y>ƈ[jh($7]qW.YpjRF6P. {/w MӹݻvZ׮]Xlhn@' #ϰzZyT{ʸQʯؤp8''ۇ֩SYfEAUp? B v 8ssm0ȭ[=:?ޚ6sU$O\&o%Q^.WaCrn ܘt+Ũ̹ ־ 6|Hg`In ~} sa$Eh@$-?fRiK 2Y$EѤHꩧ,!y˄IqGe Cy2ˆ _ e3nurx?[H^.#Lw͛ۛ1;Sd2= ;H;{By lǾB͛:t}"Q 7F@s֭*G)'\ܶ$)xw3<_?Q-tؠUүm}7nz+Dza= *]ǴV.`]*@nG+sqPRHF'(ujnzk~횎jӯ dWi[V")a#cŝ+aJw +/v\wZZסf 2i3m^/#ƱhHH@ V9ڐk7R0%&C68|Gk[-L#z'!aOoYZ$IϬJ*񡡡{&I8(V' 6wt Z8>[}`NPP?NN7\u9ƀWQqS۷͆cD1r«Z'k/ʗۿd67=$~{D_σlмbK5=͊uϳDeSo:PscW>82&s fb;;!Xp@&%xT.]2I+C8Z(L&ȸu떷9aqJIR; E w;fUqۺYLm&4 +5lbӊ.hZFJRLrd2ڵkwЁf ũDqicSyHp/ ُ ğĠX:W>AhQY1sJEEAJA<Cf;R|~9IN1K& =V] ڱйXU:qn_(nkMtܧ>$ mL%m:ݦVyOl2w2;|gOD2U䉮xnCD}sX@2A!?U%߱U7v` ɪK0vP}ן`~;3a`UA}1Hn/qIZ:ҟ^>x8<&@:jq]k‰G X\gOwXp R|[ p ~TŐfMeS/2\HB߹7RŋbIRDr/A0;S_Ψg/vQ|4"4=%şGU(-H$BP"p8|ġ)Ao }Lܮ.=2q _,rPp"y9+jJ#oEgdF%'ʜUj$/eSn5N}*i;{Uݪ:U.+<<pz*>ȿH)? B\A%]=dh1-У"#V5o$h ]cQ?@َ3@QAG-#ao~[S29kif1zWC"un IDATnT_V7f{*~_|q0 8202c̴lA3Vi1/[jHy&m܁\ nmzLq8jĜh1qɥ^Pw]]?Rn>3A]zW^L_ԏjy@.dQ\7oEE/4d F**h3jve 8TZZӧO[n]`N|#((H(֯_‚QjX,X,&DZm|`Xsh&ݿRg@# ;1ѸPwgU?.yh2:7W*HRinnnnnL&D999ϟ?gX$InAC󾍔u5NT͕)|pʬxV~3C wm,!k/lXx|?IpuΨg Ȱ`|Lo-`ض3xbxSۙ:cybcwدN@-|'̭oRZfoE^Q7'Aa{n_2"d>CFFM + ֍r DDN»ZBdꟚdMQhZ"ֹ!zй ~. 8hQCxg ,>"_#I]~ENBT` *hd;w:)q ?j~~I^b[Xa/N*4e=$3sʭ9JP6eY\Nw;ZP Y;W/컴k|_Vp+)'y1 폼Яnل+ɲkԨfY,oiYL"0A$|qh>.@ˀk[U999!d(I{..ͯZ$Ot_SE" JP*1crh6:] 4R6\Zq72iřO RflkjBݧt1\Gw'VEဃ BQͶ{mVV*8~((* *?IP$ wK]Ij{bM#b?_~|wܹX[{EǠcǹ, g 4oUZjժ ,"_~HkG.B0>k[l- ^mM O&*VG<>3wA pXj=HP|y 6F+PTs!lgꖊP?jQ[_1*Xx kal~}cO23$eOs&HS _ 9O[/cn7&-z &t=S!V˨vow?]>X"P4jwqMrrO*!~[ υ:2|g& -7ݣ [!|ytVYgu4o wnL y;=wF55[8)$I e"C&X,+++T=b. 7J*tyuzI37<Г/#ݰw*VM Uv~Sիw/@'" !H®>V\O2kϚ4f_-Z]>F-Pyи̑P8پIkZBhn,NGK ='Ehpaԭ@v0Ѡ!>޸IarvQ+H0|@X>'vIͻe_6QEA-ҘJ6~Ƚf<9jM;ү5'HaL͸kXzS]?3sê|˵Tb1͈+DQѻU:hkz Y;W΃?'ᣵ`:F]ѧ~!>_"+*ts%L&r檪 R^uHH"uA4 MxP˻{+{9~-+5?UoLG^@íICbk7O^eݴ5(R,Ϟ;#>k7o `Gy% 27ܜ䠂A4ǹ,˓'9qܝ&_zf6~ <3 [ظ{%U[ -7CSR+襁ouSV Bۃ@TّCsdktMφ|7o-Շ u TٳHz˺sm_3re]N^^>ft5 f\  U,&@b6gcC*4Gxyc^m3m ^_ӹM7Az023#kX{4_b W{.LDQ:E,❫VEwwi@] _#fÒ|вqw7̐H$< u`FrD2/珍gInq5 \jo:ʳ= Я{s܍$W>ٖGk1%=qeo 1;@ GFզiӮqga'4eq_fEd26p$ s "P c8>gwPÅ6xc7A2ǿcm?5wwbIRG.9v>5`ힷީ,7O7u&`Eu{ד(n \׼9 ,+M=b_)< 2o[?oqe> ^rY߲fJқ`]Mܳ`mgX~F,_QN[IrU'Z-~{ ۀV)NP >hF_ÊSZtCّhf4;Ǹ#? Y;0: U7PV<ˉ'fϸ`KQO`T_2Z_s$ّf.΀;5 $,yI@m |hOsQ WY;Ll0=ᙵ_'7A%HA3MOI!$Z"or9-cEs C6`'c2՗~L&ՆbwHYb1؋u\3U]g cT4(܁r'M[B^dNg6"zR"JL1&qZ*nnGP C(H  E"Y*/h7ACi!P wH+8ܰzTGǡnh ?h*^zhpJ,7"3_+M<%b?iѼKE ITXYr]g)n g~oiD,cl&kݷ2I$$`W8+쉕'-'Y|ZY<VBG'oMoc .0=lߵ;êӔ7R˦ǟ8DfVsK]z{ziW s3&$Ƽy~鿓w>myQ,PjTZ 멪䧻K K[w&S딟og, Q;(H!x皊i40J\PTj7RARxIOD-H+ArgB;@m+LATQ\4Pjdj@2#!MՀPQmaitߕ(TXp*2 \S( ZtbaVRVX }>۾7筗Fgd|ci)V*GT~vw-bhSˊe8l>Z<\$NdvNe~W*jn5ULH<mOOe˿܍f:x:6%a -p$iffw^H$8*+EQ W!WTy'U&SE JFm8[ J$ "V~ [~-?޾>D  pCUx,K5E,b;;;3< Co*ͧĥ݉JLHHxڎf|Fߢ:d:Ib>??dƏ ԾWCy,`?08^`,umPıN:+?5G3fLҽmӷ^+jJ-f[iʺ+b<8н p8cPý]HOJ'$iaaaooLJPbz/ڭ/2,9{ԢΈ(b RU(cX,8\DQKwey<-TjT[cDЭC;0uσJ&en|`%{C%o>d*Rvn@jjw-,,P wE mQAȳ܈8%⼖UR@u=ђpye"L)7f{0ځ>|R5t|d:U*#D7|Z?߹I ;YV<~i'_j˻^)6~Ʀh o_M}!I~`QD5ukä{dj5w:)} mшցF̔AH4>goIj2vZoNn/P+!j S<,k;ks"|tt$s]8yj˺홎ӐV\g~?ḘG4:sE.$I;88x.fڠKm3uvu83'crycJ%B )"ˊ;埱S\<4%_F2)jN8!Ţ(AZ|;`cC_#7ΪKUV$Ɇz)4NE40#6ӭSX/VY=Բ}h0l[W:XيABƗ3<}=\u?qϪݼKsaWFu8Jb>Am̓ HbQ.^fdd|DMFIv| q8NN.gM.>,»gd .JOHcn) )i"cg'h1D)BH$Z99ٕP}"IM)G.B JLKJ S 4kEso޼imm|faJtb8ۍOnڐ2,###22͛Br8fXpy%˜lڌg%XڎEW]ǪyD))<-όRBؘ!z/#|'U7DNJJJ6mfNql*ee2T*J"())ĉunnn|>-8T.EA#|_LWrr2!?Hu|1ޞ[OVe JiE˵u*cQȔLz mM9b%uKѻY 4kul]\R AWTݻW^ѣVq(+ffVnk.uBOE 4'jA K?v%k05 8]*B0$$$;;^z*U277G A7P B 6LȯsۚHIaRw e`Xǰ 8]bd?;aL QF#.*J+HFժU֖bA Tp A&ERFʵdt\A!UXC*>}3fxUV%f|r˖-Fruu-:>c\@(3 M111vJHHRJݺu=<L(~˗=zmnn@ hРAZjԨhnnA;+8!/O%%&D[7)AAA _VHӈxs|j 2ՙM=8%XةKC@A( ʕ+[ZZ:88TRǏ?NHHxCrsse2Yii̬bŊLgUdmmrvArrv|!!gϞuJ #wn1('y&̆;LrcW*9$ÎsYxAAdggGEEYYpEIOO.\Y$y<ͶpppQFRRRRRRZZZvvX,d]p8@ YYY1qK[FAҤ+8nh7aǗuwP=Af?4*¹} )3l>^sYHٱ@ DMy9%  ]t)mL\ gggWJ윜XK7 BiannX,EQh X22HDf׭ޝĴN5j&*"![ -lY /۳Ex'YKևxϚg{Ejf]G]]pcC] OxQF01$d3z "&Lԗu{uȪq܄-o>9~ʛ24sN^6g5B!~~(¦XBU1;ݹswU~ҼYk6uuUPoHU0_J!zZkC7śH2= BӥְS9$h2mh;QtlsNGڦ9kn_Vۯ~x,Zy4M6P BTHM\c?5Z#66V g/>/St|`(9Ig ͚vMPFTɢf6^ݙkiګJchԼd2J5:;$Krg7cPLJPP|3W'w՟w$\= =މ& #;*:!B!jZaJ~~~vڷopth/\_; :Ri҅H\[R/Ͽ%nֿh9'a%;|+QVs군ܟCV]/~zڷn3 ;*‘SDN;ó画ﳏhPHCLfnFzܵKu!B!B)k7o- àAf1fsS* qjZDie2k[Aa`{k^a쟉uƠ;S:@:9.hI@\ s fؾ-,6R!B!P8=z… Ν~ÇAa6=vS, 6a; Fg$0%3Bf=wbwn~SS2 F&^{"KwI)L2= ƽͣ:ˍVfR|ӫ{Gu%[B!B53 ;v,:::''^ܢR,Plp B!B!< p|ܼܯUS~FFnC!B!;D4)$:D/PZz&z Zk@Qbπ ɇ`uCNy+׮liRHtDC#B!BMK pnNU%]}~UP+[(sMju|^@YmB5\БJhŁY0 :%B!B4!*Զ|>{>}12OFCB›|Zm$qqck:6 QB!B!QpP8=hݜ >b8?cSF {!ǯ\yQT^'Omߞ&i'#]V{B!B1w׷-\镪J:z*y}3wuyW|jVfXhuuzF"0:>: ۩|Ơ)SXl=p wpD}2(C7jn d4FXUnMNi-bآi;18iO* B!BzQl./Z{LU̘ˁwM_8<<<cePpT{:/ j |jbyWܢȪv-3?4j0@bhǁcHLo`j #(эqn,=Uc+ x}W܆~Ģ}]Ɲ,5̫=J!B!TH 9m1xզ.֫'OduaM_ܚzxxL>x{Yf;v^wBr40h/T>3BX\I=\nC60 V¢" T! `CưS.hn?AS{r W-䀱8e;eeuG !B!/lMkwWd4lP)O൓討~pkzΝ:0lذ8w4. d{o1}&]KdRrؙ0 ?/b`RKU{P1I@Ɔ5UY]Msj[o9b>} ͍M kB!Rom KDl[e9?"!j!߮];۷ήd%с -L1*fd JŻP8j6[y]~DxrMS1kDl OKSzGOEGGN+F4g׼vo^ڎ>aش<ɾi3GreZ}د 8$jدFMR)-KNStB!B?Fp۟>}͛Æ #|aРA^α@U>Ӿ-YNy S ޜBA/; dPs=wd:5OƐ/dPv.\f}q$RN A4/jzQ xpc/׺K{6ׂ*| UDr^oZ-:~ Hx B!G" ѣ.\=w>\m~K=j":{ΚbF&^{"KjR}cﭔ.dl vI`JVrrTJI<T*qn%6&UWXUG h \φ~@Զ| VZ|1RB!Fرc999u-t'Yn*\V9(771d&ѭ S74_A/{pvέ(.0')⁍!iwǜCFN024DɆ6ͥuEUS143Uтq[o /5!Y:U-#k٤]/Hj![) *UaSJNGJhrmp,2][?fʔ] 8{B!RHcNY1)IyYk9?tɄ$W]/%U\i˜V랮׉e#-͖uM<'U+u~iF)*8PO.<[ym!r| j4 MM(a qzgw @y},>q_M_2)Neh;`ƸjAe%/_ ܪ=}Ǚ2&p^˖Ң sR*D7/'JT"!×7ϝwE9!B4`E>C\ϭ8f'R~;Iyg1X,S(2)m6Y@ :M7SF! t6 M/\$V7]Tc\E-r@@nߜ *:PmG8uTE(|P ͮF֕\SU6VLE^9i3y:*P CG,nXow~W"fdKMTZ,ǡM9xseՔQEogb~dbE RثrpKݨF BG@)7{%4Gݐt:2gMuiƨ60]*JS j{ЏL@HƟ{'f0烊 9ʹ6(݋פy+2c:GndulPu^vtHVT @7HQ/kэ཈~Cf 6ɻz 03.YuD4!.L[/p:"%꣔/vf ~ mȑR#_mH-;*4 PU4Rg_hYNMgnY^H|J2$ !B5ñW:yJbk.?h2-1 vqޛ^HU cGO pvV=ecb*(Zэx=uދgo}rzU5ˍ],xsSꊮJO'RL TnegweqL5AYK4snT=ࢮ\sn!Pw]bs*"ק㬵v_opme+mn2ʊ^j]_8|ve }^K2cSB!ШZz#F@(i$tO(M60&*>.eAUx^Aų|sA˘}7F2ɚMM8$ݻAG!h&"Q Ʒy?6l>%Vs=C{ i[ ⡤YBpjdW3dS)t9k]A=-XY_[}B!7@ M=}M"Pz|62?1]R4}dmid7_ j܀˔YP\_ IDATh]*ԭD$kDM'+v0k_Z)B!@"^CYH .'gȯI2EN\%ʵDy|֧ruLEdzpkb꺐s9^B!PBE a@@iYyZe^cހ = S~d}%;xE58R=lڵk {+:qMZc!>+P(TVQȗ_%cVmO6fc+aV,Is#B!T_B9$v MMLLLLD7E$zݷ}sy4*Pc(I"[$.t7͂?\P떴h@]w9ό=m$&PL*;{.=E}S=N E(Dg_[} qqwn_{}:k!B{H5Vn񓱹e$+ـlTܢ?H-KqcxܤB,LWɅ9L7W˖Lד\⚱NYgy'd6Q)6pȗEL&87i%#=79Ŗ}}S=V8Q;&+ͽן׺V+8:h}IY*tֆ}m%k'VZB!$~8D!$e9_uRv}u*~P)E{ϣCN{HHڻrފ7r&VPYU-D˷Ƞ-UW=d!g{%PħV3z=S-F/L9=$;>k-m?ё$ڈsU{?4 !BHL@Ѫ8O(M qFXppl.O^!n { Gm͑oWɋ);m[u}phܜܳk o A|ȝB &#SCϋ: s{hm&V R54?+JU1h^MrOi<槢BV)tF]Fe(ےO)d,o>C\?c~.,r(*Fahb*L'Q}p\.PZڭ1!O+zzVx ;~AuTui1"qZd:jD7zu5mTl f/]%$Z~5 gUsJt]=n#`Wu0w1J%fHEWk uMUOUt]zW:RT-m:%@!N1nU [VW|sjVɏ}#7nBEQI9])/f|"0`XY?+-@頝{o$raKeotnvl蹋 o2&:9tU/2jΝJrMFPzM<}}c!ILrヒ@*Ab9#B!y4!*Z:'Iw%,cW*yJ`+gn7oON?XaU1׃#g["xϲ=Mj.sط!zpť;yҧA-l"B"$4f;w׺z{sKWrBxj^ T&PAS ԣ|͋zdPv4m'7LxiFzU@Xox މf 2S_}!mʟ#NX/Y9#@56v]~F !BL8<<<q*o@6 L30q8pXӒ-%~m|dq 4qD}]}qv':uT=m 2(U8d~¸{_ٚVG7v3B!BM8<<J]APMzmG5+dg=ԼHN(V20?xTZ !,H|.`!B!;wtAaÆ%&&޿.TӫITK'Ry1=Je$+g a^vH:nt-">Jbg^erB!B?ڵYؾ}WaMwB o[UAMcbdEP=+V!G`o| @iFhqQ1T!B!Fp,|]]m?wu$}Gpޝ@lZh'L&vY6rreTWUx]%>]UEM~r_B!B~\ۧ߼yS$$$Ç SYK7^J5{Gvףѡ+$v)䈷_e k%)tpB!BHF8zY:t>>|92Rd{Vzl&!QS-mk,o;U/V/y'|U,00!jxiё3dsm%ձ!B!B!=8ǎ'HP 9^tqg0')[Yzbr913z:Ui4_{ =p7O?mZ!B!y5f6:IQ$mlQ){%+$8v]=ï= JZJL/B!Bh=8ڊ/{~bFDjV6@Ҫ()"5_@/++Z!sڽO>6'9P'p䌞2Hz`!B!F݆Q2] C6=+LXq#VWάc2?XP45fͩU8b%W>Qd(WAAAP>bwf!-}.$wS+_Z/*ThPO(TCn B!BJC࠶SUUXv%Xw\Pڔ8FϤ4abN-oE$HRdS+Z-tݜ) TA+bw=E~c!B!;]|漻YEޙӔE'[? P&4]`L|r9]qⵥ=^nV!j5F8Om B!!0IFYiQ~HؓǼx,% [,{ ;݇RT``} .T8Ω,>Ejf]G]]D(}rʕ7e@55y2fؙwC"_偮jnZaSG-^//2]e>te\O q n#1Uٳj(%\"\nE^͎ =w1청2$ rԳ[q hϨ[j?Wp`4֫| &BZqܽ H\N'=l{Zw2as8⅀؃.Ӓ(aM}BI 4`2V{k|J=e;`U+arq=ZeJTx YdWU’=Q݅! ^^0INFzW^.@{\_(",&" 1qS|%]׽%l,7$??r(Ryۖ9k+[yy5'bBp-,t1||ܴMڌǛMj)xM95~@WgJ-ԼﻉOwMBJf1qL [$kW@tQ?aDPN=8n?zOU?V)H2zOܶށYtEZ]|G̶sϤ d%k VIky'6[n:0tYmSq x<%X [4mt'4-QUN C(p?k6u =Y9~C*YM5ʬYƣ?|rw̟}}KŏnOi#˸nٙ&Q8֍&v}nj͹-đnb[7LdjN {142duh\o ֧WB r*mg~tGrsƗ%C0Y/B!E^KS2q ۥ\+cbo|P|G߻]Y^ϷjM7n`9A@Ca5lU+nQde;ήz+\)) ZD@Aض`+\;L\,7. W {w]Zw>^,$o- \u5ȹ-suKVOc`ڳyADBDi!'-Ku/Y9"̓2t8K 2' W;G!z;5q52έZ$Q`=[&Ϫ{U8zB)}k0U'mwGqߋ4z@DE@!`"m^Mid@Ir6^`Zܪή (@@}&ܶKbW=h0fҗqk*n >3BX\p W-䀱8e;eyuN5مd>AܽtDe̗SQ"g=ԼHeP PDU谳S.hnc4ڀw9ITjCE#Vxj^TSRI 5%;Ԇ \_sېU\^Ƀ <V\]Z3TN`g޻q2((,1hҢ_g i_+jV:TNf΃Ǐߦ/#3SC޷Б.zI>*:8||?K9:&C<.fLҔGNE%fa9鷵ӍB?:AURэ+mqN%`TG)9_]Msj[o9b>} ͍M WU,\mA6(+14 C=d@ZX5J9,rx9Y@}#P&%FFkD#V - '^O=eQqa kEa}yQ5R{}qw`k']*.u3F_}qk~-)}>[lWY"?b3%R4ӄAW\E1֪O+(#Ǔ8m}yZq”>nGIQFs=^Wwdv6wCяIvVؼ EWoW>+qg5Ou(J֔v?ԀQOij]zUІ}RR/Kcsdz pd0A%.J]!;ddEEEE O%)~69;5KST*_F"S fdfdggdff fw7}r-.p7vIȫjw07Zm9yV&JѥF(JD(xyS( ZٷSD޿XPdȡqbuY*O뷄! 7?ϐYpw.TwUÙR)Pkl!X@w nj9$m Q; ?rGqIrs" BwMSyުqtU&};ڡIQ9aRKedA2 IcT @S Ga<{UwUUE]] T(iEoY5?P&PL콆tiWˋ]ng臂W*!VwMYG4*K*omsp>.eLt7Q{(4Gz~^b~~_ILTH<"<{\NJJU:AuЍO3ysoy}ðAw_~$v<|v"ޛbhgh-E<&3-tמ0ŃBUrYDy<!dP%{pIF')|ƚu})y "1LM-\(PG':rt ';YNr'E>JȬts[y[Hjɏ%edAo4Zx 4){T^V rވg)w64plq* \EoD\٥6N,PtbYUTYͤ׼᳘O,)MMFs]S9"K]Ǡ2.U{OZ{:vYaY) .w=YW3-R_J%Jͤu$$l#- œL.h:D *NU{e_䐜.4ͩ0,ni5殮!ɥDέj8UL1E9+fF' ]9H@K, _3sh]t KnnB Nf<P9-`vb[e$GBxΊ8 qan-o.Wyk-'XoA,Itϴϖm π W<XM@Taʹe4;)3k;W +}Y(.h82aUêRK漴3'zsҀ]wG_Ǭ[$5˻gAHӫU U͞@ҩPMAPIvߴf"~va:ODuzMzkz&o| 34Y6U'=toL=4pAPPUm/Cy $V98wcZzFUHnj:=q{}/f G\#h]qtO$A [2%wv/!zi휝3mwn|I_FB!/*8QedeeW"SoQUL%ԨQPB!B*8>D? |/2!:<j԰CUzT.Z7 ߞK3 %%!B!U1`#{nn^4wXK"*JPdxJB!B!D_qPZ|A#[UF>q8'g)]jPB!B!V|+8Ro,v>hd@j5؞C8wcK+LE]B!B!߳ZzԐS&VԾ̠]|nd4`#/{<}#[T3ɘI7ߞC߉HU>Sv0pLmf!ˆ_s`d__mL'\ډCx~e*՝ʊ5e-N5oCǰr8ĠҦ++?x;I 껺O)%?R 4mFOB/\X knzN-5hԠ#B!M`T*UQ ^sz8M&7;&*wš\FoլO_ޖCd?Z_rc@>WwԐ#~+9hwe29jo4^JOzz|H5 )͡VrkKjSmє+NWE8L Z8]j}N27?RFE] B!BOt],p0o^|9&G\ (5dPܟoQƀپd']~Nugq&X{v@>Jv͞:@=;A eBSIf5^u^#k9g,5׭S!M^ :;|u'(v{ZYvp2ury[&|}zpvy1K'v ]ڸ9M2^'ei|(5>}zu(&bXT3,ޅ{ًн[uFOet}2fWDxؐ( Y{I1;P Kӝadi0N<'I yc 4{A 3יbޚ03qRk'^'lw[*B|v˜n:yvdu^OJr(];;)QfnQ&H܄pL[4Š!`R^$u9;1_EG4B!?EЂήSN)]vuիW۶mk"*9=PD^ 77(2z 2,)j0l2` uCd 2{aHa55N |hzꛏuQDi/mz^>f|2ypU J*{ pT!Q(^3kvw丬a5oQžj5+BYww|b*J@$jmW( HtB9@'>՛C}moϥ@MP& !+,ɝWBCK8dƪQ>}zhl25XAvQ-հA[[?d͍1!`EP /ܾG<4/I >L} r:}W!* Ini+M Zz.wfˤg 9,\;YfGuu5^Wki6wfvweW,2gVM'VV*C\ \4g^СCҺI(KB!$'sBK.=t`vI>]ܒKn_^ V ˕[wSx2;UW:8&! z_xѾ}{"(T <פ -N,={N(@PZ-y7;ʃB!V.Z9پBνzekkނ0&76L,&?Zlb#zAH۹s͛7^xm۶|-/}fx y&'Nl{dS)Oz_^ɸda}p͕ψ'zc`jyur_HSa1Yk3tkKWR& *N3). ;fwK#¬Cټ /85.G(w1ȊɏV ME3bEnw?>WuGjޕ!K$QL+֍[k&a~39f*YMvoP5el鐏F nգq+;6G/|"6ENuYWs1@hdР.W~NNme~'"U}WI[eke#/{<}#[Tk-{w{doϡCD$Ԫsz; ? 8}Fx TFv`uvvgyĠJuasgSrB!_}%͚N_oE6ېʺ1N\lݣq?1l]ZWBxwv,òQ}&|EKԷ!CcX9Du!LeڵkW@@@TTE31zk)Y8ѕ[Բ4bX$Iu1L\ma*ցzNElee6>pKMo(/u֪hFpÏ0!rsJOm*=rϱQpi mnÖl٢vyU$DݹsE>~XOϬVZֳfضX<ˀAN]RLQ-/%c 7/:|62fLym2ޝKy?76Ȯ|桞N{iDyڔ$G@F9<ʶuy^p{l~mު-ڷuNuJ7M4vOD7*wź;KrKfm=֧fR0X@7gu9Cn^[rs뼞{Ks:cL(헆-jB)Ae9S LIAXan[q W\Cju_03L?`BMiR wjb^4oczz|y 4ƓꩆR egIFf`z/ 4oSU#v7p *ѹ}eJ%s7?.4->ODL#\an!-&)ͧ]kCLYڗG9ɧsydv;q?~){͹)oiնi@.=;>@i8T0p6=0W+ێ cDSQrܐ!W:0 IDAT'յib>5݂sB\Ǔ%< >ClNw֗ڍ=nH8y!ndUsjZn-kğ}O`{-V ߰3-T1`K^=oC(^9n\YM8Uu:ѤVz킥s4oӰV#9eelg֪JϢl 6҄BпR`+fP]>&Tܼb9UtpV\\GP`aUQ69?溜zc@;}mx)%=sieQgjstpId4r| +BBYNGwXwOu:4hP|;`{LbE;8W՞oUh|V>ϡ0{/*W?rWHP<_˫Tڼ8M]/\猖C^)mc;oҭn]&uksԏ^ӛGM='ع$4e~]1z@OCKԝF4( _넷N^^{^\y4+Nmga?Z uҪyA^[Rw.{+T78B3fשKexZNsqWqq#OTsBlTٰQ֩[^8;EyӪʶc~6Q6m}㞚M=]e ҉*G6nNC̣ hY:S;L?8ݻY7aTFy,sJB!_Hܙ%|( qF:%Z1?HkYǹ[̥j򢾗θp_Yh?S/z=SQ1⼩,XL[}6iuR#iyfDU~klXyr뒙݂e͢BAN6jrB3 ɩ=({鹘tફMT*cY'_srʫ`UHioddܥx R K6]!+;˥$TU|w/qeî{R`M<4anZ$HdR6A"QȒ(Ex($ DQjṶ̑+ÿ5(4Sƈ={uLf~6 V&pK3A8inz&6Eɩ0PD&gL k><|0=ovCͤgӽZ<{|sWCaEj]Mn~2>|t{J0NɍLz@>7kU!/!~Ȝ]?jZ%TT|yxy6d?kj6,k9i+s~AA`  MԜGQiwд4`!UHgdbndb@ 66_,+2ec`QiZZX\ASHѼ*輆 B/b3ORv`EͣM)Íy<<8cmț,=C8du1<>oG+ӝW{G%~Df)M5 ٰ=^{2z 2zyeưl6һG]WB!:U|Fl(ߪ\ѻҀXo߽777сòzӕN=iMGZq`FW6ɦMrW[M!ViԧǐhA/} 7ugT<~g>˭8xuvðt` _P|.pdI"**hܱT7l6{ļNڧP&U/$TA!_}㥾t0f^ƺE?$aN= ʖ >~-Ͻ}Z<ج3Ma_*x;GH1E%j[h3co۹fMT؆3fܯ: hS-Ndf{@rPo]$ͫ'.ѧD6E 7lYb):H g6H#ߩK ʬCҏϵ(β0H&w]]wEH; T͛7p+)Mls R,{}9vVpfLٱA ;!ϽvܻBo||SuJbqi8[%‹.m:'!~KkhkQD]%RϺdAB\H6|n@: YGB#ǤCF-W95iW[TQMk7jԨC:y]Zxμe)ˎǤ> *48Gy E6IzO|7*8!trxo) Qw|W~Lglpg7r\!F=fP+ɫt., SóSLWs60;1]3GxlZ2czf7?msRV.KeQϮq˞~[.r\rfUߎ`$C5 ްh sZ.l|킻Mo_LG{|#Jux.4M!_@QS{21!w3ZI},:q^dL.H^_7^s9OmEs XM œ}`R[]ߥ*eoMcu7ZJ# ͣ+B): !vT\O$כ& ڔKJ;ϧ$dNjH/;ƺRuT-kchvuo994UNy2X‹:q:NL;-XM@q^zز,hS_R1, )Vo|]j+e&K-*PH2ش@q_򓝮~rLVnF~YL)DKdHT*IHs~uæƆŨbW,k}Y~j5 W]mҤR2CC !_Wꫭs~0ZLy[Lj]*iي5ڰB!8)VV*C\(;7%K9,@-\杆Ri>zf$gV( ˰XlIB!Vphں_ƋDḙlO=c %dwM3r7 {nD}|% !B!Y}կ]o߾;wl9U_LjռNxYYܫs{ϋc+X{iA#>˱S'gp~^+!B!Bpss:thN_Ϟ=k׮ZkSיGQQY+u{ԔL&H$ YR%OԐþ)#6vCM awK*uHbڏB ]\ movאOTpik7t֭[W^m۶m~&oF{6Kzuϥ?cmINN:v]Xr֔oAʃK{Vo?ع~Goi)O?˗%LXkңs_ߜ,[?rpj['*ʛ"T~f[ Ր[>ţߒ^o0hS 4n6r?>6Td#|EU_dg7ʡ%ck AP+Wݏ1??֨?ʆ.Yp?8dvEޕcvj=7o 8voS)kFlO[} f_Ȥztm// o7ޟKΚRi!"YNY+8=>1͓s%L.[jiI-V3TVTBem2̦UÖZ^|{h[-ZlyӔ1ީ{3cڔwqR/&|4541~X"@ŐӺJ6ϱ蛒b"c?Z>M;۝;{e՟zI uI#Um=O{KgwފRy`ٗxw^p4V5Jzw͹3N:M{|Ū/_# gqӸ2@XYSB>,$B^tAj)16x_~Lm﹭փIuRQ*UHH/|mfڨ֠AdbJ"XE/ˆKX9TFe+Z4Nȷk{UTNK }G&*cA'_hFJw6FkUeüLl нQӖځE /8ba-]V{s;8r{|TMCɔo]r֔"qa}zvT#;оtե¾3\#C-%0nBȷVpFlw2. !&lbkŶ𹟮J_򰇁O< y>6):)mY֮RZ6.ea)k▱ujݪv4Oe_/ՈaUunPɎ :;|[5qjWXJ<{?dH;p7*7nm.W~ E1zY~RO|w~1yzb{Hz2H^6ƌg pŘeTR*:&o mg@L_org89;|{LZXo{k:9v8ᦄ] yq=gDzox{xJ&ex:e6ͽkw8v9VmiGN Cz9n/w͓דHĠ| /7\SBHnG{:hx6՛(hSq_U7B9SydİF:4ų\/S}WG{~j'/Ʊrjʖ9BTA'jӏi|n|};Oz]{v:CaG^Єi8 +eא\?@i6nNweRmķN@wR?h|r?YmiÉ[^{̩lTk643eLד4pgp,7lPN`߹r>=@UmXOHYٽXB3)Mȣ3:gyv}4neBzW7 o=6Ƕmz[CefFl̴#4s_0 ˮjRmgAloh9ny_Du6;e]ڶ7i2iȚB 'nmԟ+^ߍǦ]".2giBlXaU5MgϓSvtK =E"c;_ܢ %^-FnŅ*8!9c <zORRR?E(Z۲vy#@۷6aۯ'[e6 ʡz׆d 5|Wg4iUR}-74spR" J ;+ I.\|v]׿[~mR?NifKv2SFQnjl]u{fzÆN :R:߇14`D2NϜzqM2Љ=@+GqFgEq^Vu?[ڥ_V .Ԫ>@;ۨӵӥ/ö'BfwrѴv_vު~s/{C|'}YLF[Lm4Ky~\o:y)mzT95>&=ԏ坆ڮÈ}@&կSJ5*(hnjr)y[9յ->]H>(tg[ONPx†4wzT;~̡ro_ ul םtN֥_@YˢWWlG!d׾m`n_ 憮)![~F]jf7D@ aײ[ЎK'lYY$x:RP!$Ã4Hc1onޠ~ % ,ci Hd2Ebb"}i1`Tl"qgn0(;(cw͎>s 0 nLMą-*e ڨ?lyQk+sYImK3l0KOҭj|YkZx ؆S;TRdx~ϟk}eu&w6(ކ @)ζI/o]yNsY5٦A^6ud=Z={ 5`C۰Y:'/x q0ٹB/8lj^ ٘_o<@If6O5|M !߈Oa{PyWx[.2)nÄ՛}xv+r}0MI*8!l5*$ٴ 4-JUwߖ 3(/GQ]ryU{|q6г]&yg}9|Xh{v"\d)~%3Db,%@piLJ(kGn~z)Ȉ0bꃍANfJ&-qb k0Th1LԃO_ߺ R@j_׉K twxޒ71xxdW|{9(IN:y0% 2F:& (1p :c䝯ܷ@mncB o/Y ZquLN.*JTTbǕ{v羻u ȺѱL>O?dӔ2_'yB dO\Y]ti_9}h M_ܬueۚf+Ψn &3s^[aZaXv~JiJ8oj'&F2n<Lj)m*f^d3v8z !KZa*_xrf:ٺ-7:)mLn^Q9浀>vdDzÏ|$1/xndGXwtR8x?6`L`Fz(}󹦄"{&y mL@Zx 8kM.@%QB?M BHYmꄜh_k]w|w{܂/h -f 9cΘg{@r q;yD̨'nO);11>"ByRdOOF15G1fV?Y^ލ7{@^aR[Wn|;s5_^q/ ̲janc5wgt~}j;&i@ƚ8Cyz6הR$?uwudU~˖}AQIo '6>6,,:ʠ׃i Qݽ4,TW. !F/CPJ pNl٥wߊvOޭ=/>(zcd2?"uj")_Ͻi|2nbqcZ)*qD fLVMd9mu?P lv ] r\%|4?.kUʊ]`wjLv00__ ^SBHXH:e/nXܕU3nZ.w2< $OȷVp$9}hEv Ri_osR +({sD$-/////Hnd[~#'L=z:ZB_)onՉ 4oJŵ'c2|ӣdĈ;{9 _v*MT}xσwcxiNyF)*,%${|Z٩ky p!O{?4`DCݻR ׮KξeBN`١._Lӡꪫ>OeϿmWeg{e!_8I+PM +ӽdBJxF<-!Ԏ/* yהЖJK(L.?LɖA?{aɋP4' r})D ~1p{M !GA"$d)1 712L"t/Nrs=Jdž<, rݸy.|/S2})O RMN3>?Qg )j!Kyύuk pyöy֔R8"vߒ 4cg{Hҍ+}eѤ+Yd2>5o~s3)vXQXX؏`ُjFٵU ?)^dPa]GNX/pzN;=^2nߣ8,feS9jw܏=@Su .ߩ!ӹa/?,,кɠAC{9760vwM(J; WZ'2h_YL/yњtݛ췻eR,&>+"mHdnj@oX9`?f+.`Ko7Mfe2 *4i,+HDo ,ҪzK(}>Nd$Ek |y`C_ 慻=~q)ږ, ɗovQp0Q;[ezϜ㝥;*wš܌FڒK]:`5k>3\k _s/H½^CsFyh8/"]/p"'}c[*sJ)7W^põZ~ CȗIdn-2701O$|N?ܲN:WV۸5ɶ)lul~ x̦UH`+,-E{ ą}OmmQOFi޹a!Qc/B>8Dy̎RV֚pHn9(xǷ~ C\5,ܳz۫m2+Ǎ+{rynS4칵OSY]|ɒx,风SpZwbY`gUmGy yzlb5 ҺF|c ҡ>nr bw:.w;tmd}^]-Ai>С6a6N5X<͗-~S55/ !%ۇo4^2־؇Za*ddF4(/{QYq-_~!cJ_*7B!RrMСC;uzYww]vha,PcZ~^U!7siߜJC Gk7LY{l_ظ6i!}SF2dmz@h@GnfTu '#;MP3j>zacY~Va uv_LbtbLJ,`Rf{Kj*A]W)n;/Ių0s_)2J B!*8촵vz֭Wm۶`ˬ2~P3>Psj٦SD^ 77ЋYQoO~\&e;!/P("}W;  6UIeJx;$C{RB3@\Z\:$GB fg\@df#B!߻"QD[[Ȃ/ԤقSV+g/I >,K0)5xGK/ pdI"t|`Aɷ#!BGB[*8,--&x}X10jYrԩeg>K=k9%KzaIW5^*!B!B"pttܷoٳgvѮ]ZIK]/%uO|nDoroSCe!7m(VK ȩRB!B)8y'vy%K8pԩS۶mZwk=W+5WdSoj-HX4S3_tuITwY~M}y'$Z!B!Bk.GGGa v@<zNEףS\?p% ݦ0r:Jx{|eaTySnbL}JF-ońe%oX-M<lX!@ dBs$2>D'~/+_P%>!I*ܠLyEiS&}dxUl!rxG5ѹ @ ~OzGQXidXZ{$}.ELFJB U$"!^ų.?QrJJ d 8A?3XT\_ӺbSv^h=>)ȒJK;扐(e(jutnTd>E޹rܭW˚YYjXN:lS.ˈ{zw}(U[iݺiy^= 4bWȼ?2+{BƒSRH3EvúɧWF>JQ ٣K#χr+7ou;V-UY"~7@ 7}̸q<ί\Wv+feEL {|_j$yuυKߩck>^C$#֌q"V)9ɇoyة,<ƟKaÖR)oC֒s+ ~tNSRHcAi|s]s٪akOSnr1}bko蠝Kbywo+ߝWL?%BQ!4͵5n mn4cu|M ^M-7y7N1e'%gUHٚNk%J6'Lf[)9;۪G%b<l:QN.v.9A)W^53.;b W]HҞTvm( W =\5{a\]?]m޼P$ZrJJ -O @۷ߺbE+'9(u֥Wlj\/!%,& ?Q5l9ndga17?(ef!gpkݺn>frFj@IlZtid s+^F ;ul_0M*hV+gړ+{NzoӸ!\~# V s9\>֊{'eM(.} v>-PAG;#(I͓\&uj6 WaTEZ^ŷi)|1?{^F̍+׮<&&†]S ۫mm@&rgjˈ|(tsAuuU*B&d\ۥ )zwC @r1K7\ /sg߱#O_(nNfYVRPHlO! IDATgQ4k2ݻ^HPҔoAVwx| Zvid%-ՠaز󨍋T etll8uYDA  [y,ДW>cCĽ+GO,7w97G=g2aJxkȦ>==f:2}Ne}62P( |dYΪ{+[MYv>:*.46zw~')7?_sUkze3jqGVj"PN,+/;kѲwhʦz|дfK@xs6`o{z nv2kjVV53Wum[E]}GBch¼3&ޞ>C5" cևͯl0ra\;P-V64 <\w[%%ĸra v?6]OE_)_ӜǂdJ$VI^ %yqq~AZf )LOh;V 7y'fnY\vc?,9S(Oם~2r\lǽngL,E-:qX:%M ޝq@fZ6lXnjU= +:%9hrSn ड़տ>]؂kZ#;\ 4dV/J$ia")pO$7s96\o -6RVfõu>ؿץ#90Oer1LWI\=3 5,=[:'Lž0.$&ϲ/YGg'Ƣ^7 W4rh5/Ɨj.p/wI5{h%8&7xj` =}/ Frp?y(%&D_EoRƾ ?e dX$M eT,$,i J ږ[в-@1}@Q e}d)+s=yApa=()~ L]3p}T|%&\6%ki웴Rv+^ްm m@IUz91j̚O$^O')p13]O1o,|ϧN15<kjRԨ Mdc$P1`|LhSMJh=۔(6٦CvF)dOu4 {G o~<a9,3a7[`]/?x6yu"ԡ8 _w`7Q,o lӝp.(uUQN(؋J>#9k 8itAY^4{>+;ם+jOc.'˖&YklJ _5iIN> \8ݜ63@`",9Ad6 Q}Ú)Q!7|4JdIc=<|!Z.y>fg{7,Ny'3R̎k.)WJӵPPoCVױ'jaK,mǀ͜*9[[{edN<횅;ji4`;}Caei9VLk2ABs>g`Ye Al#KMW:?vO6e'(Q[;j]\DHN Z_ď4PTjڳˠ.*e+K^V%57qmu=oɧoҵ&|Ěn0K.ւ*} -:F,LZ|5?bȱdTF?]*j[uҋ5n6ָnp 7 z5tOLshfKc);c>(՛*^X3oǶp}3OR.XcJ |i>y#Qz^m ?6aIKcwNl#!%e;d$*r1e`X@ M Tq/S_dn%{6u? !$;}$'~*~&H=L1v`yjX*We}h0[Š:-q]YKO[< h4$4qMWV~rը9YI%eUdW7IR,z_]1#׺hٱN;1Ҏʴ*_z"ro!AѢry5onv^eY\j͠o4g|f{K~vvpsrJ8cF>ƒ;Kte`X٧@ 5ɏq=cǵu~Hcj56bwHN Tny5iA'j_I NT/0ׄq|/.o2!d$JLe,b_}wEY?Km_kBǬ  ~3]1ruS7&m3r%|)`vU*;VO>)`8'.?=lbSxԵ>_I6ڸOD" :1{Iu~Wzj F]+_>GGሢnl:vƼCr1L9Z 5sI B{ͫw d>nW}'w+3Gr'= ?~~&8sAfVYR~ʭ ןHR^V o{Zr,o$o??U~w {3vᲭϟv7<<&.v"=}`5w4o,t}DY%?al;kƾ疕<\mT+ۼ* KR~IQ;k7ި/yȔ<6ho3S_OI&(Tw杍wswKwSme|4H(?_;l>{tS(8zKFxMP18*jZq&o|2繩16vhݙ|V.2Gp\R0t(Z!5!n iN{@k.{3] ~O&t 4_7??Rq#Gܸq{rӄ<$ċ~阫ȶ4؍KG6ѥ }MTѭi@~?Gy>>>>>s'W7ͳ\#~iGE+[|0,DFҽ)S|{σaD&SVGLR)eS!'uޯ&#f*}LtksZ4阍[tQH*HD7.r/t^fTXXK;aOXtØbDaSh*g5˶CQ蟻'v&uD"zz{?t\3+wB,^*{;:y.9I%=/ א*) Z^[oyqhCy2v҇HA>Y?z B>56l|\F, h*A9چ 5YcOY 8ȫydyˮsrUرo@9A.9{rr{)~;98<HN L aÆ 2k}9rd%g_4N7v>}w0 /Џtrað<#] 2ғ?DG? J5@&~ u޴{Zkivj2ɹƐ=T&S5rMK=(bpȅ=}[]lM׬YS~-沽9e忆Qd-W?' f6drYT,GZRv|?Jmw\># wmb%Eʌex楉%Bkzx}H*e N" S>1|)2j^4(DE(MYr aB{Ǘ$r2Jh.ԤKJ 2(0x\ hFߛb]o 8Ç*UVZx#M 7 ;ø\@zK.ٗ6f=}޳Yzo#nn,/u?>_`m%`g.16Zۧ:'Oc͜4֛k:>_֞bkEhXGZ+IM깤+t>_4#9++`ׯ_iӦO#~ Oƛs$V1 /P-Vv #\|4Vgm;x?{ң@ P268\\\޾}{EeH@@wZn$??o i~eK7#-:F)݈=hM!tJ*i@oX #_G.JaI@ Ǐk׮#G޻wZj_~]GGGvaċ_=h5U=4DPi"  wN% ӓEBn\NZ@N"ek}p+?H,xFF9-[~l?qwG_U3hҴҏ1'EE;qTBŏt-9Mď4PM,L,~t>~i ,4*ø|5@ zG~G@ @ 'i@ @  (v#M!~ @  uiBB$, @@#@ zL.O~?~#*@ @  @ @ /p@ @!@ @ ’%'&&茔PJN$@=x'ʑ7dxFFf*V6j Epz|Rz~%=RƝZ%Ϯx- RFvU N7.yɼ]ƪ^zjԮbcz\Ž;HQ,4oٲa˺6j)ѣwdu:2oZx?tn]\n{cnKdO>y)^씜/9%%kDUn3Эiޫ /MRbeU{+V:,r#VѵA@ Jc׬|fx>|2ﲗW[kSK<1ѽMꋤ[ӖWoWIDn4;{=w]cV禄3B7W.ƒN`H y2zycS4}U}5(ߜTfoA\Z*jJN% 7?X\}^PN. A.*yxO_uv Cr&=E{G p(%76SXGI\n^"Gb[-w .^2trƀ-={2"Lx҅t2)Ќ঄k:OբJowpgù{*ew!lL9.M(9_rJJ(lq£mz;&i]ZԬmCȺ}kSRrJ*_De 'j!#$sRA2Y]*=:]fX[u])=inߙ Hn>?6o(+s}RzcMSPY?' 88fRѓ=ؔ,z3.霽qWEon$%3;jV|'? E99?HMHJ[V~n۔0I']Ld1MlQe= .?0M;/]c^>yW_ m5vm]P,eq\VuR=~sbU߫οՑ׺*>xs9Z7otC?fHQّ%W">n.yb$:sWBn[gCJ6XT+fOP}p 9-H1Nj+ri%\}>$q_yEjUb[rnRn]m4W"{o"cv! 0j~!pxqIR k:M0koj B7=*FKco<pN!C4)Du\ :r^Ļ0vq7K 3.KŀW@.9tGs{n |/ IDATg`vQhaIl9,ԪT -]:oUz[ɽ3w(@a;FSͥL\a"7[-P¨zW>jP9vS i8~V ,oCع{x| UY^2u6K #鉭r@(j ~Ŭ(6۝BDq*F&~J\&_3h .*TώƽGg֛]{2%rɧ-/(4++ -{q}V2Og'᧌>"4d';Tdys|/'<7Vr=ܩż{m`Q^󍍛.(˫vЭs=Z9diR mqM~v3cZ.,:r׵\ٿ[YG{zTf#i5x ~ӣTc)~旕i7nSZSp )))q"Btp刻:n B;/SuW/6-QyvjʩSN+UmRR 0e7f\{o7ʯ-'w=sK#@uhG.OMt](N~e22RU) $y),O95Dazk'a8<"@(Ja<=B)zeּWʳ8|7+*3zm)\}6x4(%ȃt甾9nd<͊˙&5 ըϟ9¨׏݌ -9z9V [L]y{N]{,^(sTK͛j|ziNʭxP{*PsbfO\q8mPG>.=zؔ%9թ56Z6߿zt8hU54Ƀ}hx[vzRpx$~M~+}IX5^NM4UྣG<| = h΃ӓ'/ƤCnR9Z'JS^F4H7ZxRTm-UX|4"F/DZװiԻY9ĕ{/Fv՝:c&eqjw@zEmK!{ =!.=?++g.HQvzzm3v}.nBVI@mUE]e9s7;:^]kMj:.o8 l pK]{`` &Z/N;$bᮍ/e7۱7Y/76y{r=>gL,|Z\T]7528oEe4[vJ5T)o2DSE[rc 4]x^:U;G54qv >Qu9|r* GxOڼ1%L[ҍmʉugc9&=|*ó>\KS(ą]>2,gUz%L91޹! 涭d|S++P(RRRdK;a:`JPV>[9-8ȑqyI\ Rn`$q'F2NvqξQF!3.:od =BcdZ+$6拏<+J k8;Elr0VJ& kٵ*&B¤z '0n3joO]d|9v1`XcƟtqrs oMߊi>kڨf5†gTFqSZDf8hSsO2ت/Gsj|gB9,Q IT!L/a^-T͚E~'j̇;{͢e>{cZ)2xzoiqcU(K۽}F!l{-qI)gWܻ;XjZ0B*Q u"~-Iqӄl$G-7Fref^ewᔮKE?X1M%7̙.eyt;kإqgWyvld0Ew'bSܯw9S*_2ueCY-s+FncokJ>,n`MCP, 襼y- ˶^qqנV\e.]:Vr/B.Ԕ:dCAE=:wP] +4JN߶+OsQ9@n3;jIzz[?rX6>uHe&xg.F4ќ%K8p{t%4 @.NXHLoYQ6991W.@% 22"8 ϲtW,ʌs86QPNGy{^{%8pwOUJd@\)Jyd4iՐe^+"֎v_[CWR$iۂւOFtUjfvjFCٵ;e몽zT z͢$*" }MջrNC#aw)E!:p^45g3o\RA%_ԛ\NY4Ao]Yrpﲅ>t[q˳6+,DYIr}:?޿ĝ3ձ MOgK Ƣ^7Ō?ּp]wzS9氯E항F9}rB\=SK56٣cV 8Y7%;sŦMF.,se>^ v֟6̆/< ydN7ncw?pן7VYt֡U^Iu3"UYoO2:^wWҫTAzGPs׎};hJo+Β$fel*VauCih$..i30tYrR q+df SPZL:7Fz;%@e4\QY3 ,}}>Bg-_;@oĽ{~Ǘ6`n[MYUm3JiO/n?8Lke^-򵳄#cnh6Л+'_9IGi4s27I0lJԔP7J*A^4]KSUKߌͪ˾3]$2ж66`wY@s Es0yN r#4)bi*˥*7 4A/+N+-e8ؚxЦ?%8,ޥ7B:zGcZ^T"rFd{͕+"f̹g?6 xգr)=y˖4>փ~BU|0qL2ړ&<e+%&AkGFzP,& 2qi؈Ie3eEt~GzA<|KC*&'/Ȇpu(8e&iN%d ?]yM'P ׫} SOE1 8bl~61"``IWc E? {8| bSRjU@IӤ։gdb ͙nmp>S&r-DisU=n΍҃ޢ%&xΝ?ɕ$[tWb fZ|LCA/@uyǪEK?mAh2[94̮kZBM3Cż7ft+:sf.Xx UOK%f Ijz[ [9tT*sz)'ή ׅ \0kow^ˇ~'ה~ 尢b@qaQ|f>ԮdPAxŴM& .Cr ʡ7[-@`» ~Ai;uvQhn_`qgva@(&}EG2|*&|C33z\^oYIWyoS,HPl 蘴aJ0%:&&FTSݸ@H=(s3N;4}Q9$edžڲ̛pLbf{*2#XkutAY@r}-9yfFl,QCAǹ20XGSç];&";Tj TRIG'TKP/VmpL i- yǘI_=cHC5V}DDϸˠYXji~`޲Ī32N^z'1˓[s%ȴu5#i ġj8Vgy[R5KN̪9ȴ֮2G(֖F dS6)tCfit#_Ms ^ 3W"y̞gy{zaFH SOF1 8xɤqlUt{thS 2An#ݞvc:S7o;uϊUuSޛO}@7<(xS+EKJ¯njs\ =KycRiXyۤ9T`L *itȿ=&&(=hҪ۶m[1w!S)o4lkobqz½mx J;>].j>qdljbq£G2m3toF!m5UorfJ,/B0Y,NO ^&bP(VtuS/gw)!2"7)?&?Ms*3oZTFd~r}ErjGHSO֎&߂ȷyyNrMje:ۊ9.HMߑ>sS;$֘5Ic%'>7{!÷ۈQvoϽmALUݫG[&RML7<7F`gmN^Gǝ m̠(f_ esRz{[rE2xf\C+􋳆*UkjdBA3np\'rr}D-ޖF(;crd-^ =ŮYծEoyj䒉iNQ?]sxh;VrNxYr ?={\^()g3]V46GuJ4#t/d״~)ME*7JݞY57mjɥOl]Cy9XD'ҊFJ 2HUn÷<2E"TJqK ؒ}N ,*밹yoreg]&U 9$hχ>Ե+[>u_)ѥfkLwg@eF_[J)ғwj[t) cwyl'?5䈊 5Bd^Tra`igW pkmM:?-^h {?CE]#p}J敕y=أmV}6?ǐ2}UyMQ^w9?~[ThGKV)mW=7)"_yԭE#޵я3.Ik VVG#cvٴ z̏8kN%(h\1!wpUxx/4;"Zҥ'wh,eGϾ;+{fU;vy|qWyE{|5XyF8(lwXG?t?:8sNCQ^d'=7vj?=O-jd/=}ON6?"[ֳ :O[}y#-!NKZٴqƾ:AvDiV6d"<6݊r#Ϋӿj3dmآM];bk>(~^ݺUUU~ 2حVN:"b?>M VMQW5ȝ}?׏~$s[ۏ;cQ?`ǚJ˫Ң]{؋~#7gGGzH޺a3ȯ]>ݡkm]󸫱ϟ݆1=Yy8wFQnN[3MTc_#'~ub&Mӷ]\w흅Ջ*޽WV_lo}ԕ[άN;mK}T׍Ov>oߥhRˆZ\p#z2x̅VSx͉ʢ}>sI!Gt.[>އ۫Gۺ*|c^x~ߧFDD{ K7ҳgϞ={wȯv.~ h~DDeѾG~w]W.G?sˆz։j-\=q%e #fz7=uMM#"=a>O7xԡS':b3V7-cv>oՋ W5dcpnEqdv>l;r:y~D?߻gOzjIt ">'DeCXrFi7oۯ˹e6ey';+Nk27"#>CDɟeu^4nwHv|1#ssxɵށ槞(hѢ"<%u#"NMD< #"{{=zL%u ""q|DT叛JU/_{E/Z<%gɀgTbnx)Ӧ/˯΍tuZqqmoVKo}`}/_>~dnNDֶaDTd>ɎέjE#"bIxtUҠ:sGzEDh]?M%,2{ [l>⣁DDTiܦgGĤ_^y)$<"+#ŷ>[rJUrnجpUZ'FD3^ zwa;7ϫkH=O8~ɯ}೷xQoBq΍]ot͍Vzu^?"bQ럵nQq҈5FFOU`3I㾿E۷/-}SiOZU?" YM}&j5zG _ޔ)&Mxw̨O8}Esv%oQ.Xʢ7]A׷EJIɐ=G;&.sşjLyTu]{5Wl[y =ZCvTYN{\^m~!"_ʣY49XnDDVfZ~>s/Yסۅ=8O]s&dw^FD}Nb5c+]Gvu#diŗ콬n}ћ~3*N%_o8`ʦ|}о{xIW>DDd~q[5fdfro.e㤿Ƈo?}E׾4)#ez8)`oỳ1XZ+.Ö֍(ت{NU%n5!q }FD> zS^tFُ՞X?:P@}Uh66hܴ5i[5hڝuGt;*"s{x|g ωnzSǎ86"bv*䣈٫4/q3]'.!K.+>b>D&9t眷^sĝCF9d؈c[794A'%%{6ӉأU7fDQ=~s9S^DZ1U7(wsoˎh{{s}_y'D̙-/~xˋ/{˯\}olr-w\4lVŽ8*;+wf?>ˈ&i^cx]/8dʼ]*ƥ'=RdjNu P_o 6_NfW=Wv~gٸK>FѢG$/dk~E>ng^gDz((({Ǝgzn]Ũ7=wxT{~""3w_ݭ y;uA?6ɽz/^vBDw[6 7:8c+7䬭o|?6ӤdGqU}=7+b{ˢrW^Sy3' v얲/=|Ҡ"nƌx͘k;IJCT1oSPpzl]sB*R Wv72"WCTmlFZ2#޼Q}8mxe׌~UÈ:OJeܢ{&{τwFDT6q;>DTL GةIu((߼m^5͍NyQPQ1m?v֍(j]ѻgF^{mgޒMY_E,%w)--/--[v1{:c<ڢ;ܪaGDOx[-+NJKڰk;/Iy~㭝*≡o֨>mSV|q=凙P^:gI݈]53ݩQe˦+,-?n}r1{o("`{łȝ6:*/UX Nʉ`ڛWupଡy&5Ȋ#;6Y̙5AVSaFx#$w|cjYmC߼Þy;#b~Gxnh0mΝW,;+6e&b]~|r[v*.8W{Etu÷gמJȝ[2AVr OmcF7V!YZhfS/#? o/emVj^QFX yVƬ׬9h,9ɏoXKn<w{9b:ZC}V~a_ ]qXò1~%enC, ^)|oin0j `gϟv3-&͊ٻ("ltetݰYau:-ʊURifXdQYu}u5i;YlUӯ}7̉3m Mղz,,CZ&z0oMnZqQoGYt̙[WWn~=Nv_~x z 6=AyovGө{t91l~kxFuX}d)xEYk1a=5>}ǭKUyD_,{hfSO~#:Ƕe~QY햾RJ[E^4v\ֈȉ&=?.-CTo*2Ek/34]Oin8Msn~揿~yؗ#";+*oeY~)I͟&bc_y>Ɩ5䱈ʢ7\a-u:7K"ca+}ˏDV/?,S+,sś\<֋Z~8Ճ&ܨcME7lkFGql#09ȕ6/,ȯ|AZ cµIn[ӅLD‚ku:NdF:vߪ3nw*`}X'>̞=sVyQk{r}@Nyq[oܺ~,u=J'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y<H$O'p8 @ y/#^]IENDB`bcfg2-1.3.3/doc/reports/ConfigItem.png000066400000000000000000004452351223671746500175300ustar00rootroot00000000000000PNG  IHDR/:iCCPICC ProfileXYPTK;8䜑$$D **H2`TT$E}ommnO_>}:"#C$cmt)N.@?< y/H++sߦaImboO b#ach0Ѩ_:v6z0fGVS}`;`} ;X'RXG",,bX? /67=_ 9#᡻wֆ3^f [ ց8m`pݖﺐUdGZ&i~19eft0n1<PWLa>$9Y#G M`ƠX`af-0āh$9x%pY a׈DGO_$E n/?/Klw1{2i?USIfVfr0JEi4QbAqIJBep/?>k/HTs3GK;ߎOϷC7vǁ""c):HIPdd3YѢͯbyl.>GG&z)\b-9N> ae<@,:X; <}0$}`?8A58 y ``L9:AX"C/$Cr @ yBP8@P!T@%!4Bߡ5AD0!i BaC#Q$D&"QE4#݈q H$RCZ"]hd*2YE !!!Ǒȟ( $a?5F٣|PQTTՀjC&P -4- -VCt}}}=^`0,*Fcqc1y*Lf3bٱX % –aOSUNj\p \ )nNCQI9JSGs :OkR| ~ H  kB!PJHO $2ňzD7b1XO"". jӺ6Ҿ]#IљҥUе=BOC/DCAD_B <$L#yRINA!!C? #Qрї14c/$I }u;)& Ʉ))e]adfcamn^!a͑q<':gg+k. W2iG\?y#˸{yXxyyyny5yxyo~0St(RJeϘ/1:?ߞ?@@ >&B4B*BB' S P٨&$juLVDK$JV(FTE4DJtP !((V!D!$$^%>$P$JHK6INHHKeHK}v.'%(*S'3*(k*!{M\syZyC4o ~ '^*-)(n*)+E+(* *{*W*0XWEꪦP֪U]R=D..]u&554j45)4ǵjk hj՞ i+{UwEOM/EKo؀ޠ!aa႑QQ1̸xĄǤdT4ŴόhfkVn\<ԢblIJ*5ʺ>{d=ltڍڋ8;94:8;:;I;8=tprp8ujzuM-m؝׽߃#=^{.{==ynxYzz6^93[;W7_?P0X8T-8:x%2>d;1B.331<$/'boPxdVxZh1P{LG,Lʼn׌_MpHaoGb $}{ۿo"E'&JNIHLJ7Jo؏ C&0ckܙ陓6eeEgR?T~|Xpߜ2%y>y)=ѓ0 h42&NYSs9_PR}"xyiG`ٱ**WTV==}:zTЩ5F5mµ%1OswFLYg7lq;ڄhkmvk<E˅܋bOPyPa#GW>VzDIǠൡ]Cj=~s_~14l?rmd˙W>>yCzSm;wƕoNOs/Wj}0-E%V?.-䬲6Tyoqmz=aQ)ymlkl;l{;+@z;ñ x1şbG` `^ .)ҁ|3G n! -jm~IIJb{q44Kb?@fēQLhf, =9G g.eQ^E/F`ZH^8@MOĆt+9>yӊʜ*vyj6ZGb4 _cLM=Z~clMg#`jgaxЩ̹ۄ=O[G?, 90/[\ؼ$p9JՁNk}o |;+;'wo_ܻew~v1 ӡg]/.|Uј177oގ;4<>?>xRvrCTiٌO>ΝLyK ߥ{:mnoOv 4#O Y6ZFFl,kezD/aoF`"G#ћaI^d̼ºNpsqGQ3 X f ݥ"EESŗ$eO7,v޷%T+Wc; :3ݽq+v[|];N'?px(q'>M{9 7M|hs/ TB>ACEO;D'MO@oKA%e5S4'f?8dy:ώ`/i+[{'~$)BŒƒBQwb QZR?ʾ{#?0أtMJjZ]424Ӵ+tt^/dLLk泴Jnyjceo0*v$x{k=wsEhGG''zD59b߭Tb{zgÁլl\ռ|kE%'yOWFTT8橛?Ps.9q퓝+oqwYݓѡ"5FO54<"e镔?m7h76g=F8x C1!шsH Y|DPhA8nB#L/VaqT}wFe8#&jbZ3stpϐHiFkd)&nRfv*VA֋lZlC~[圪\i܂܏yxEx_Qrt/ 5 R婛"wEyoK I^,}Il\)rB#ʇT&ūG  ҊN=wU`cL5176+3X䱲>`b{.A‘8Kk[{Gj6gޫ~y!FaiEbTMJVr5u9]sLY &8}W>}lPxDOdJrՑjS-u3 :MZ/^molwVB{BODDQ_PG_QG߈sh Ҭǖ")+W֤wm(ob7Zs Y[""1T@#Psh":9Ec#} C4x_0G"ޢդ{MBREap2\d4\Ȣ򙵆͕}3K#Oo _00JʼnI\R2 F1JuUj꡻4iqk>g2p5\=z<㼕}I~kA+!%a#e.Ǩv'&J''r>M:f΃Pš'G]+9_9a\zlB2jZZg54^lknop UW&MvvzsDWN^;7=5l>{<eҫQ7oߌLorX3=9v.u/_/-cQzҙN?~.(\5\}Sgᵉu ō+sΛ7"osnmپ1?BD]oY^y6 ů@Է¿|#2p^., pHYs   IDATxi%Wv?73^իWU] hp`8;9X2)˲,)m* EY!Lr!9$g f`4zѨFwU/oor[zS `0@7V/ޚydVG9 AAAAQ/@AAA"    ="p   p#    ="p   p#    ="p   p#    ="p   p#    ="p   p#    ="p   p#    ="p   p#    ="p   p#    =AD.>   𾹿C=݅    ^    G-pX^    %G;z0ۺ?Me"%EAAA>x[޽z彨"g   })E>;tcD "qľ!   gWÖfr@|t Pk#1;qUqD"pSd*TJnk 7޾EAAAUaftrm}U٭TVm? #T:M2d3P, FJB2Lp   ǓF`f[9BDQQjiy.߾]^VV7˻zwZ( ##DE O9J$zl:MzB>3671P|9υQ 1ZH)%AAAA8@~:DQTԚ~kK[B ( }@@`Bd`4tW* gN?$ӎIR h!   ]>8VGhο.8ݗ/U/\G84(P3`?`4"3BB6LJOД8q %   |Ε+7ҙW._rn 9L@!v; ϪR7 pdSpN/WK?zʧOG>251_tT4=@=ʷy_9;Dw0iorwd`  ]\.Wٗ^|kނK0 rK6*?4)4)dH&JDZ#>Z>Zm@Axw8#sŁ >~逈cIw0?UAAACk qە9wKg/-\]\3F)8 B=/> Y{b~rl2v)Jyi)^B"þ6uֻAn~nʵNTmzGpIY:!`Ņ_Og=zl3k0َ{(5kʁ۾4`rxy9yx^sc⢘{}ƻw%e z@=wwޖS)AAx Vh4ׯ|G.v4\B!GA)14 `.rxt8֛̥ T&<;TDd8P @floW/VwUz+[5IG b=xfD@,no^y{y뗿M]ŇO6ɿeF*(piXO}wSt?!"q>66LndGahwХ'XŸ3ιw*bÑ`AAAqv~oWg^ρRpx<6bv0%p`S rl D`@`+cSJs'Zk6f7o`t@kh@=E''XLJ7v?{0wߣ; Zkm6 c?ks]&qrn6q+='s]8p㣈-B$~;VOkx+.kɝ Ħz]JAAA8 Z81x?~G/߂s |H#‘_8uS3dB+*f=Dq q`Xڄ F;D`b"&b)"CRM5+m_z~^"* 0#sE4" ?DCON w}{@~pN: H;)^aºY~Pн0 m(bC!Z{B,ܼ[ш;bZk* Iw]|+N]#>v屴ADJb!怅PAA qJ]֟=\j$]dR ? EA3ePN%\i;ATm3`@"E DLb0Հ7*֮92ft (H.O<`w?I6v4HA<:jy?V<]謺aT65 ΙcNcu8\7qL(k0 ZVlw J"d2y^"at2Qٝڟ۵a?Ӿw>xqnUwǼ>~(0t: ƇܭUT*H$8t_Uꖱ e#~iv}*8V%!AAtpE)l ~•[{戤׃u\D Y̍ʱCOLeӎZ1D.b6RPL ClqD`VL hf؀ c\RϤn3Q<Q@# O_f}w7zM lM!6,E+G,t5bqzC cn4;;;A8d2L** LeFh4v^ Booy}<~-4`#ld+Jq5h[&Lk_vǮ?~aX׫jExfl6N Xzj1&NJT*8a#i'{Gbi#k >Kcf{RbzI0Vq<{w_bAALfnS??}ʕH%pLUp. 7<}ht0N&SdSPV#%@3U1rUNAijjj 'yay!כM=aTC}{ki[KזQi  a_O~3λrۻAl|u=ZY7`Vy^z6tRBLST6773L>/t:Hiw;J6rf}kZZlvpp< qɤhrauJ%"J[~'CZfY(Ţ1&ywU1bin*Zf JU+&jQY# VU.+JlyJGwa{m̻Kgwww5(N%ɔSiu"6tN=\Xf^$9 iCAAS>[,`um~[. B6 (8 R09.`vĉX)F uR^@0& (&0+ar; 4AA(0k8 e"Hozϗz3%lWp<ʅnTE~ԩ#wMm t0N&ݝ7qKݶnѰigVӓJ/0Vpv6lk% JB`XkZѨjf3Nqt:S(lOOOTz޶_d2jĦjT\/8Nq]T* nuj8AX#i\D2nGGG$(ͦ>vww[V=r===Lfxx~[T===3~pubu{aآ$kAb'Ll6J%ә`"kmN';f[oZ~wkF0zzrTq#oW(  Ǔ@ ߾?_h@Eƒ!$\ ӏ}p!A-?4z"FdT: C32O{zst|V`lB& faۛ3VjinEϟH _Gp42.kڣ8zq$mt(V|wuTH&b{ (텅Zfu8ӶO-R$EjEGIӎDQt֭ťrlq38RT6- CCCӓhFGGSTV^ kX .tX,RJR `d2988Ȫq)GpYv.鬭%əq[Roܸq[nmnnV*znUJ)k?tPzsssqqq}}b$㉭2_s0 EQT.j'?=}htd4J* r[A`@ %޾ eU7o\^[+w`{r{wAAAw|Giuˋͧ~X6tJB9{` ONH>ciD:w\2"9b&iCF"0Ɓ&'Iuy.HQoںiA e k20LZs̤"1xd]'NivRSqu] I@>/1o~O?Qm[ܶ\V=쳗.]"l6k=w+a#d2@TZ__T*[[[[[[6"H\U񶋉&㬯3Cz{{<+˗/_^XXqFѰ.N؏ݸ-=p]9yÇjNcfg`p[8ٟ]rW)e;ޕP-IĆk||oa+>, l L&3;;;44400+++;;;YI<wt2CCC:~ɓ'GFFz{{766VWWJR.ZӨ2%vmCS b@뛕kWFnvhװ˜ut'=ӓb‰|q`MvK+k[[ccV)V;_   |uo>oLIDp].\3c_>5q[JfEL9d 4`ÄXTGcP!&'s uPGA1{X3Bb&F"V̬#PrHElʝ bO'ɑ٤̈́+P<D"x SigsTs{xŐ`s{݋/>쳎 Hnu]sssG]\\\^^tL ЪVZѓ'OX__p›ol6mD*d2&rWwո_I,7nܸtRT:wC='ׯommY$qۥ+/|̙S&'N/bOOq cLVkZB.,//?ŋׯ_ذsXl l6=9}" ͕Ϗ=çO;tPOOBV}ߖtmHwe2SEjN=p2.oʭ7kh0@sT'?ggJydWwd2/wY   s><#NVV6oOp@ L&ql?ıå^ߘ9&""BP (bh!( ɾ\6h#LFM+4B0ဉ2(hBh@(Ab?5 9 D!R p_ 7Ҹɂ yӄR^?ƘFQo߾}ܹl6;77O~zؘ}t+SwÖEDQQ(?>88ٳgϜ9sʕjjp[}OAs'y?~~~^z'>NLLu@4ZXH$z{{>d#BυnjjsssǏwgkk_~_R; ";$Hojmٳg}/| 333>իW;u;/cu^ D''Fq~-?pkbbZ%*  p! DoȦP\8.<EKW. H)EJ!`#L#p@c3X8Πɞ@!* x[H _D eX1y"DL`a@`1LD c1`Fzr_?:1o`RH($S-_V7ѯ*q{t*X!J##ޯRf|/ǎ;|pEz @yBat:V566f+S~;wneen[YN~{\y,Ax뭷*͛7/><@2Z 0 c׉;b))v;  bJnإZi666(zgffR/^(3T*Nwwwϟ?NsTTZ^^m208 }7PXcLSJ w^H$@n]7bB́/Yy}}}Ƙ_>7;;̫d2.ɹS ܍w 5WOǎ}5*am5mnnjSNMMMZ~{V"ɺE3s̅BAkSOj}k###=؅ 5؍5 qn=ϫT*Q;ȅGU\Z|zOڰ)߳ofF"a|XWw'FƠիW[΂   Sa!ի'߼t uH"\L ?xlP_ TKJE 2 G PĤ )`@ 6It(畲iAi6bCfB63 7ڍ8Ҋ) "f-SH2  y3*&D аdzxfoEplƨ6~G|?g`< !$vHC B76N$D8`ph&)@f2=miHfVPL("M2T:CÆbr`BfE1P 8 1afD L|n߼y*M^IureumnZg/ JyH5m0 E&p(! "vgoB*BOPi_eg&v\NRDdgE}JAA{ͽ8l sF_ "?42lYa4a2dB|@D`cL&dɤ 4,Nu󩍠uB0 `1 T%4 ]ѕF5,׃h3W>v~lF9o2oF%VGHR<\luw{ka6JeSN$,DJjkvI!7M;&VV2)QMNNK.=ׯ_Irؗanb8==}Bb&kkk7nܸvڭ[jR~})o/Z|;V[$v|VJY>)[oa\^[[{7_{۷ok3LwGl'fA,..b%$!i}ߏĉKKKoOww[Nb+ccc<'9gϸ4c]6v^?tʕ˗/7t:mFVrgΜ:|'_n b llh4jکi'+g(gp\0OhgV6_Z\7o&Fi% XʅkgoJ߷KKl&(bfl v^ (`Ɲ J5 1DC3kf0 ( \"fb[b eHW 幟.ZȺHҕn|9ó/658,\.EQdGlؽ_*;6::sٖ 6oZfW՝bX)$6}1fmmmxxxzzn R*LQ]BmJthjj{'FFFzJl6mKlTJđ#GΜ9s…mmJxT*s=z3̱cΝ;WVx;  FTرcԧ~V=tvvv vә.JKKKvhk&IRrTť.A3L& OѰ%d2ӓKR&񜛛{.\PuXIZ.,,+'+++kkkF 똨RbF"w( E{&Qu13': vjAG5y3L١pxX\E 5h+7pbfVӱb\UD4AAAsϛ{o}ݚgυ rIoiPdH A=448BC%KT糳 Cj:??a;bZ٬Eݾq[o###'N׿>113ܾ}ؚx_V}׏9ߟL&r`ȁ)*qFRu o6lc_aۣ6l4؟K0R~K_̌KKK׮]̜L&=ϳFfs5":qLT* /BR:HĮ3HAoΞ>}ZXQ ߼{oFl=TɓSgO1[׮/.;O)C 1&LzË/]\@t!T?^X##nכ  ppOE_ٗRix\\#PRa(EQT`G.&Ff(ä v G""9`ÎuQ':䢔N2Q; 6l"hHGQkU )ͺB"VA)`-a0c1"6 X2B fp5"qP̖>Oކ#6whFZ'I1EQөFիJĉ'NXYYVƘd2i8)tZ(b[b{:;vl```}}+++\nZ8q_C=n/_mk4z{{٬>SEf\T*.\~'={gnݺe+`߷b`^v7>7>>n.'ޘw-Qcò}ҥt:m+v#݊=X&بZ-ijjq˗/_x1SA8alԩS_򗧧K d2F%Si={vzzСC?hqNrۏvuu7xFGGzV:U:lF$s~qkeDP (ǎinӨ_f{14Zkok'=9=ۻXނSJ!yesT)Jva JQAAAsOh dH U{R@.T&*E 1)pPeA SDy/M("Á]"0fΫ%bcQTJ'fh5HtD!>HE|o0 eM9 U6:#^b尩E 0|/Љ:yo[b7y5l;+iۼswwڵkN>}ԩ7o={m-ͫ#z7lƨvǶ2\t۶׃R*"xk%SSS_Wzfo,--+q @D\Jvۖ~V}Z.y䑑r|ڵjE?j [C5w?~+״ZVEdx嗗L8;>FaLLLJJlQO:hccҥKLLL=`2 c vîLF5kmi9~QNr=o kl]u:d@/BB0@[I Sͦ>;Ep]88 AgfFe=w  d t'!;h#˕v=<<<55kY[#Z'F2&&&z{{WWW/^h4Blhccc_qܹݡ!`um1RxKoooƘ'O>}z{{?n6VU'bcc'O,J/_Uj%*/"ɠP(.l'=.p*5Rq;2&X-'wٳFc||<( f l CCC[[[J*g?=??dluQ*.}ixy A b^ޞH6Ꮽ v/> N|`Gڬخ~??~ԩSD=44dUxy ;$޼yT*MNN~\^^~7o֮_\\\]]rv&XAxw;8llUQ-..Ǐ߾}hQl(ZTy\zvwTy8P[} y AlyCCCLfqqƍarqSNimߓD"asբn#\׵hGFF677o޼?׮]{뭷(teaf[R.wlG؞\ܩA:J!ЌL)VkȰ=LFؗRvtz}{kk}-?34-(B3@"_DT2L+SAAA>e#N\0함xBC>t2ҦLLL mFw+E01eubψܷ̪^g!gHCaєWARmdCd%dچ" $4$=2 ͛߷ zT/ҥKqONNŖyqJra2l6޽{O}S/ʚ1^`0>>>^XXlcXC=j;Id30"z;wݻwOKĈd~~W^iZz@Z-J"S]y#i}X[[[[[Z.[ydaaj1%Idezʭt8:AQ`xbd5I !+;FeGQ$I?~}p(P@ ($8l1s7}h $tÃqeTJ\w` \@ bg"5d]PX%Ji]6wB{ hKD@J ä)`pː"r5@1 ` 2 9c 1 m` )̚YfJX$c4Q-Zq 0<UJz~<SǩzC#%|Vbx@qVjSSS|||,J$I8\eGGGRW[v@!˗/KD_^^(f#Ks]J4M}ߟ,??h4Q^t`)^O=bbU0uGOllO=꒰wJD7޽{Fj9… /_&(&''mby]Dt(̜bkۭV7ߜ1F+?kQI3s JQyj$i Y={vrrGQT,L>r|ܹn{-Pi*Ob7ƖE`uF$~drr+W KIZ-+JpXFI|./>eg-M`GV(M loouwET:|vqZ-9\.z槝gNQD!: +TS]2KR*h"JF$iJ0j:mp/X#lrp>aPN8dֲh>"!U@ (P@?.N>:!teB[G@|-to`XܹsZloo/x;&"뇇Nj,4M(j6moo !* gffVWW+nכ6ƈvJ-$TT*J(vww]m4O=Qb\\U*%81 /Qzr=o$IR.r=>>"#F*̜={۷1l٢1x[j~I%,^&IIAdzA(0}0su}0T*YQOXnZ庾H% |i$KF (P@x@&6TE5%`Ӌb$ 04bG3HHW})Jnw(RP@TFnFqDy@ (P@x>ǰsED| G-ժ_i?%5tRA8+r@JdR@ A3ke,ȁ41H$E< cG!flŏ/\b2f&&_y0-1*3OgZkf\My+2 Mj$)FAxsv3.'4HvzvC^Kv-yc ;&u8oFRy;n%;ÖQ1x㍙ %cC&''IVN>3iZT\MӴK󄂔J|Sp$DJȘIG~DX] ~?33S;NM෻S.ggg v^OObd#"? !dC%PVd_u IdUQR4W/aLFɏ5ʹ4NR (O F$ oQpF# b | Q)=(Y (P@ qJ)Ea>ozYPbVi6Rq9(4L b4!VIA)1RiţJK.`ؤ3"&ɓzOjQ[C`bXb v D23 MÔPGÒWj װa&6 1hf6 .)) 75Y乵R/ͶzQ%QTE<~<ٵn+ i|m:J)Oݘ?K/}F05 ÓD B4T*μQZxn]몐.☐K0g,It!~Q)wwwk'g"te jI$zy^4j#\3WYi<۲u %rb֎)8qcHCV 9dMmdX( lrEEv??E?D4HA5hzq7[ (P@EwajׇB)x.[6ḑRVQ"E훣POy1.nu"za c>&(s+XA!?H1Fu.zeߩ)ވG)d4O!hSa3 1†kT a4qp`@Wn? R5*>\Q+hs: q8 ExHv^ȳ\>њ縤FILChH0!?ka(L-7+ފD$C/CΆk@2@=c52sh 8 6S)q !<|TsavID *W ѽ7oJh}a#NxrrrwDT*ʫzʕ~Z-)weV6~R!aJFhk8$jtA P2KF<վxUV[p9, FqܹgݻwrvJ^O:&8dZh$Ię r,T S!dxش, #p*y>#2pʲ,(daj/^0՜xw t G< '`8 gg.aN42|?(P@ (P3dݹs^E7 σ> 0mxK GAwЀ܀Ck= 8-R2#tʃ,)aOIC_@Zd辁Q ;_yO-NMVO"8xd`9]v۶2|Rf(MbaETŋWWWUV=ϋ\. HF*ShADrYbPmZ$,!=G9V`:lTj~G2J^__ R,pyy… YaaRHWac/D^jvJut:GGGm'wڇb?eȉ$@RiZ|5Ml8/r8JX9qŊGF q+$YZZpxޯwGREDhU_?fBaww?1<55E(P@ (P@pG[[wPQ C‘ nh>Г2]P@ re4 7A L+ !3сyW C)eSb)A .Aid<4@  NCIS8EQ0 Jv{<xL/'U\Qf<{@'xx/Z-85f[H R0s"ߐ(!r @9A^g?i]9rڄ҄e^:77' $6%?yr<z}% Djͦd٭yGc$&cggG"AP.]ו H"" ,S~XbR"]Zz%~[~ J:p \F'Dťzu!7nҴT*َ9C+T@ (P@?<Öm{p RK^(I $ao$_0 T)rc`A4RpXw F#_"8`4 @L)1d|fQ@%:CB$t6#IHq09E)8 PPq(( QG.ek R e\.70~uJ".n+qFŋ[[[vhu?ibtf=H wYsگ[Ir8g9Mp#*7[-J<9H(9јkƎ&9joL9WH$R cxgwwW928E!~D#%coHq~vz3oy{CT8 1 s޽̓Y (P@>AxU 4Xr {A DžA?S?2u⿵^2FHѩTF02 #HWТOUqZic?KF $2ԫ?\bnw~({8~H<sX <1H!Qwk CKF|$>̲Aґ;3$Sc5x$z*3GQTT宰evxH4M777˕5y| CFUd_B јn%{[k @B3T (P@x.уu % 嘎Q΅(/jT~lԕ ݣaf!@ fcZiﺊ(β0NO_ƿ1A CC  s&W*9G & &&n'H""Y+yg}xx\ƣ%i(Kplg-;mGO8;/_T.ܹs||\*$TZclj'<QuYyk8&W֬9jIr,Ǜa^4aQXͼx<[:{K[U|CDbooߎV7WOʊ^"s%;;Bm0 [n?2U~}yTHr@&gB a%}?5*fOBCJiJhA4^W2NV/whiG4j K*4O"872y|-c'c 1Z~7@~S{vGc;W"+6o"wc <"$BեiEQߏdey˯@9o_1\ i8F5K^4Y߻SUYJ`7 (P@ d9Rtv)x 3!÷HE|SW./Wu(2Ilb1Ȫ8F;lJ^aX yb69\ڝ;'_AdXŢ2a6Y~05=j_R0H5 <9a)ôx|2 ),ggg'&&ly|6M~8vfgҹSk}ڵ~/_^[[ VvAx5W'L˗DbSeg!;<1Vo򓑏XO:ND2Ce-#9]=AI6"I`@kvg^|$կ-TL(TA!//÷$FlS@ (P@=N輐a )C3Ȁܡ-?;ɹש3 IDATfʕjlHiM3m HR4™zpXk8J;d5f#q:L)t[ޥT'3r*fNicdFGv\$fi2ka6l1&J2UqA7 䜬Cfp(IY־9== ^DT.3g(sεk׶@nv[[[okǑPIyA 'O$I5"[0z~ #Bx k_9g8̜$Iq]' " @jfѳO8O7}q$Iy3r!adVkcUy>bk@N*"PǢ_Z~O*vyDV$: zrS_?I~_֍7ZA6OF0 Øo7._~at޽~93*؍ (P@>qx>9q!G)>-]@p]*r|8'ژ,&0lL #~:^s_Y*A/uAx>Ge@ke` =lIPC~\KS+Sz3+RL`f)"fm`#}R41$6tOW88K!v*z J\g T ΌZgtiBUjKKZ4_r>f Fp4Z1r-oqhyӄQfr@٬jYɓ}జ6_MDKKKf> zA4 \.#ʕ_8zjTEyD>\hRiv^'8TXKp.~$I$B?r( $!"^'}k8}|RBLNNE(_JyEVN!b,666$40WL84<㓓f_ ?ֻװ "LT:4 14J>Z?_NټݻJuiiIf^.F!(P@ (PCp8 5}j/6?3W܌_d8R/3aH)P1pFZU@Wø@J ˾Jt(P0Gcδpp,K6J/_(ʹ˛;B_Va8ainx)K8Ilˉgǘ _Mk) 6ckN%!˲;wA0;;rxx(G"M\.7` evEFCjrJp <55E,YX!~Q%T*y+xS ɯc `}}j1au+O0rݲF!Nfjbi4ju?sKvxƪ!!b~fTit"!҃eZŌZ;@&8 >\uov9[kN> r5'Cw!Lv'нO"8ƴRfcS 8_!{u,sё1ƆeA֖^/MSq~??[$Irrr;wZ(8d>Z$J@eZmrr2Ik׮ Y zԛCD󫫫2Cqlܩus1==:99#2c}V+#D\*i*֞Rwttjժ$Z%80̙3.\݊lP2enb8B|lddq< RIh#9%oW9MBH΋+v̙U*?WAӅQ bw^\ yw\.Y^ڐ?Q@ (P'Ǣ9"ky<G_,u:9_i4u+e҂Yd$Y +@"P`$Yn93̙ "mXsF^QgW .*#5<$80 ucW WǓy!z )\.f911a{MLuFqܹ=ѐ2Ւc_[*ex.Zp%GGG+,VDtxx4| 6AYg!8N ːkp iF3??l6wvv0 89s|(,5!#lȶ)GgvvT* <}Y:ն3I;~F3W.]w̿x7!JQ P m=SiWʧ^sW|ۻIRF6Z"EO(P@ (PcQQ>><<A$kZcgr9MSrY$"zċJ%;%>-~Ắ$uYf9==GDɋ8D7777;;{M,)B`U!0i# )h2821MrD\DZֺ\ 3Wƭmx&niHC v7|}Uzݺu[Dw#Ԓy֏c7ȮR~ (P@ (8S.`zi|e '9voǚsma60DQ&լe@^FhV_$8frR]tv irNڅ+2 \!j:4͘hƐȘI)u` !:}$ɰh'^B+_uqG`렵vv3W}cYdYV.}ߗ8?T vd󎏏X/%-*'r޽Fh4>^ZZ:1fssVyA7zte-c 1]] 0ƀgUR65‘)M3 3'bH؂#9JLY[W9vbUjKvk+Hd0ԢA >)JKpIm:TҢu0dYl6ѬĈg{{{W^=|$(> CQU#9%uaaI۽rJ^FfD[VgffRҰV5ȓ;cN;;j$4!fpgggϞ]]]1 Iߔ##npҥjz}=~L%g$6tG''Z5?(o^{w( B8Cuf_xzi:llFTZ Z%4Mblm-N[l (P@ (Cp(ղ`Y0\DU!}Urt1-vj{[FZf0Hi`WT Lq,J6!C3`R(۽;vahhj@G| fΌɌδfcX<)HM?֏{8RGDF f@ v|.`ćARK2g/S,*RIA[sk8Кk0tR0NA[lr><'iѵ*!n#Fҗ/8v7&'[Q  ޛXWb7⽗/}nv7{asHQ졚ɱFȒF<h``/? 00 6@C4H&{ʪά%+o~{_ˬxnܸI;qc{(Zg:}+_uѐ71 2E@^kv $DqAp'0LCQ{e }ؚ_.6ۓMl Z@^'wJGY_ xkZ9ԱΑuJߦ5!%$0=(0(αu;{˻ݯav+ n0x|p)p[3o 1()$qvvԩSV+rmPJ5, ^y˗/DImbQ)bֺZ T^2{ffŋJ~Y]]e4(LYkkkEQ<ǎ{'Μ9o^~}wwWKޥ>̌䒮+UJ ?~FQلVtep8j2vj?CbZJ*#^_}G~gΜ9}խ- .P^pϋ<'FWD+Hpvcyxxh|w{m(l `cb2v{vvg7ZHRwGd|<"̒@F¸I} ow&2_؊!E; r`up"p3/|S{sljac< V\ V9vA!&tY]b^P8(R0\'VNM:_k@ĎB-.`Dq f YR!P!8u=bDv#B$R߿tښ@u@XQ,..tuW!@VY__ؘ>}^B*d֒jY։'&''ƶRj v_|4((-vV-///..vݗ_~UUu %>gSիW_z}˭VW_vFC)%zx"z^w񥥥I;_CCCBi]v7߼zjRQ9:T1H R펍///.~ J? @/䉓ɑqaW!ȶ.`dDSvivvv]&I?QBBBBBBBBBB] 8ɥApJ}cqPekYKpVUyaH^!Ąe ["ϑ”@HP¢9P bnZ|[C t;0P>B98 Y6!C-[?¢R 5Z-ޢۻvDcH ܸGn;;;ۭVK,$s Il6vww]666&yaa믽ZV$VgBDfZj_>===;;;333??Uc666666^z,FQtRjN>̗/_^__wh,Z˥<.]ZYYqSòJ Ƙ1c̹s笵gϞ}ꩧ#4UZpmllZ-I*8 zvtݮܗH2ܜdJHX=aΎvrtϯf_w1@hֱϾ=Ϝ4U9i~O=N`c1&MHHHHHHHHHHxq7N84k-L`P((kRABh+`r&Y 9+MXE!s `,,4< 샜)ו8`'q#p({(K/*:ߐ2Ƣt(0u`Xsfb*͈TD 9~*!=3wvzdНTvfffΜ9+WQ(|hfsjjjee%˲SN9s/]$%t< -H^KDY IadD!D|[ɲ9'AcccgΜkۗ.]rIhI[Q̑!Uks)g $HQccc?8]rd#Axf$E1:v5p$JrȂGC;zj\'Y5!pߕ4CS͡sװA-  OUL[u$;y/=t =-ƔntIHHHHHHHHHH;!GƏ:̲vO uGsb#q0zA pp4@ 4`C#zF"̅DfpELʾ }ISUIJJpLpX 6DÎ {l%iF55ZultnFIg#9%!!!!!!!!!![Tp T8A)ҙK?S/Q Y ?JCgF@RgDePCJ)+rj t ^S@)  '5& X CKM48Ơpst'#=D y= N6t^#/n~@$q#˲'N4ͭsε홙"FQ'R3ښ48qę3gFGG/^Y\$ˈH D~FJј|衇z4K+rX#edJK %y n*f7v^mnn;wsĉzjtttuuuggGSnGr$YU28˲4jŇ~X\՚ͦv7J0sY3P՝=34vBwwꮞ/!!!!!!!!!!^-U?*;6=oF14d@sn*Z\.6:҆hX]M 늆Iu'ZJwH(AWP+ԳQ!H[t+ WvoU<$D#l{2䘜aYȹ¹!k-s]:m5vak gkZ9BLzs?91$zh|n'Y^Mǃ֏ɡl4077gz‚y@A!Atn֋1رcsssyʕ"%SPUp y7'O6Mћ\xq|||xxX.-ҀYy|05Q%sCCCn/_/XXX8yƆDDQL-/G.1::*ڍ,WWWGGGc 8rUǡlltpvESIٸwGϠts [}xّ{_Xr͡_7xA:j54jkr29t_fjW Z#={J!m"b(. ]nx(sP(4> |@1;!VzjCv;)ږ45awEa]ǔVڐLtrǔ( %E>gY@lHjD5&.*oQ4%ahhhbbbfffddD.ﯭj9Il4"߈Xʆ$;`rrr… /_>u#<233#}^www1կ76 4?錏7MitK.C45jGL@ػqyxej^z+W<䓓nWETh0|dIu%Ŏ$y>==t1˗/llliَmwFY{tbg`SS 춟_^h6s>>BJ";|CPegScO7gQi8z‚{Ʀ>UB:ؖo\1`ft|schZMnCg>tbCanLjJBBBBBBBBBB»w::9@z"4[@1P#uh  p`Z2j|;p|)/| >):`e8yyآv/'^;Kf>r-c h4:΅ U9ͻqcʲzn(^7>>.ErThţy+WZ鬭moo3֖BHV9z[[[v( kޞ@FGGFvꦉJ^w:_~Y&` IDATٙr䋽^ohhBZ{ZǗK[644$uzl6Mѹh,;nz" bjn^ڢ8 L:#dJ9\/I'|Yi*b& (~BFpM#l}[(KhFa 458G*ju +M\/ûYĊ XvFEeR 9ZɡLE.x`0k}* HXo/M|?89/=44Tʊb )c'$6͉jTc1o"DQe'N`n{…NeȈ,FGGI2XWޞZݮ&''gff$~IqzBcebإV77'ծq"c2^羕ʑHA8SZ->;p$&&&bȡB9T|#)țJkr- OpOq  2>~[~{j(,@ N:(`,HC '? {س gm?hĨQ t0sYSԆsG؃mV$HfXL͎~GO>$_@VNJoۮ"WB4dG5hJmieX ɥKHneTRc"E%q5 /:8 H.4AdՕ߽r*S#b2s|YHD,8d n"9" 9bŧ;HHHHHHHHHHHx(8yh={PP} (iP@a+8ĺB>'V!*&:[{'ks̝^7SPC:JXww~:58)&V^!< :BFmڈb ok+V#%=}v;#Hv\U*Јd[0Vˑ2ȲL (舲y^%/ߍ}I\: 7fd(dʀƘ1nuP.7vc`RҫД g()IPTTW[ T8 m߻voP9, > !%A9KWs(PdA PPK!(Drx7>ş"Ey ]on=x|bdF"FMv֑,/__ÞCٓ^-$dtp&G :`<)a{Ui \7/|{LuGU15֥w7Urc:@gnՄTX1\bM~BD.JjT'DEtLJue{`HDC","7 CtxAly[D$J?8-xjb=Pn.҉6/-C7P9zBA-*Wܟmup$?Cn_}}]Dm$$$$$$$$$$q6O|/}^y 0 c0$8/VE&?Ņ&,^i?]8]WO7tiV&7`,olQa,g v> $Wy&7 g j.WLqI)Dv$ȅ@&OD)|͕a{f1ut7o7oW >ȃ@ D RN(TPITE4T %⁸ݑzV>8!!!!!!!!!!]{Kpv?7^zu,2FN0"8fπ8 f[.*6m=t\SW?pxd,J+>فҪ\(EvְJ$Flo9q Y G`5[JY)Ƣc&W~gfPw׮XE^GÍO`d3燛702N׶ ]>9 5Cnf} ԛ>`&o{|aGGoEp7wZ.1Bmߵ%CGd)[yܢE=t4AӁ!")+j)jSq4,Z5H_C]@< &"P >FR $!]-HA{O)@H&"&RPPb fhRrEB=P EDdZK) ̅"R":,|Jrh`7 R &VJ đ)޺HP :#&$$$$$$$$${qo WzxkPD-LAO#C)d]h6wηA@ )qP-WBir,<ϕ_eg 'sMr">wÆaJݰƠ׫5>OxYy~}a-71oAgG'8 M,}MZH_VjО8x[Bov:'[bߡ9>CwP qݲeVj1搷N 3ar`+R=?3)#az$?;T8 ebJm] RcDD<:C5(׈k5RФD5R a=if-PZ)N ӡBZA Х""ȈX"bf ń Z1 MNAiZ| {.1DФRbȄ3~XkELxYN5~p?Ƙ_/\:=8:JAŌDh*"zRi W^JnYխr՜&EY* !}\C(ky)Vr臌Zp$q!k4/??qb֗7l9L;%}5jZ=Zz#]X!3#P?ZV5`s4jz"ɴF]utmyDp2'Y|b'-K zH_)hU4pՁaEDuhH)5 P#9FP#%5RVhT}3ڂ44 ! b(.†CV9E2RrZ)ELJT3?³TU OH1a(IsC e?G R& h/>%C[@8l 6F^ÿ'e?w&Wzdeemv23>)0a--0%L%#6LKa`JoQlgsW~S2o3z#@rEp|&ȑbFͿt[O3Q1PSuG\T +m-^Rp/ nġ F{jp@Pg BmC N3,2, Z$cc:X7V*.!Uߊ0l|UaXn$$$$$$$$$$$$B屳~>v~nY&_!'?+7daH/Xsӌ !P+H~*Zi]r՝aߢcI3WXJ`Ko=Yni-] (2 ༈-] ؁-9cQq2 }@A:q;>](PPN[CpJ w`a&8 3)Gj11P q!@> k@U iTZCWHEq灯28D§2hM~9 '?2AGCepPyo˞7}&d(· $T'A >??/^A[*5V`%tx+J0X(ARk܃B%ij.>Rpec\|}l~9 Oh@PH3U\98^a\ݰl@Y>/~XP( >7wb(4#rX%ESUe , a[|ȠZTgTqA+{L}  Rq|0" P s4a<]azh"ᩪ TX Έ0,A3@paA|G4&#DTHAQ84UG;]oЄЁnbJHHHHHHHHHHxSfo9S fCK@"=|a`XT$ܑਟ̟eLS^kmŵvvo93uP 90U\(KX!8JcryiG?ٟa!zn9S 9'>cW_Kr&|/F}#I5-Ɠ-'tφS[-g ` Tp 802l` g|GMZ Gϼ,ˌ1YvW<oEh *H  !#˲}~{G`߷p6CiKg0s l cJp>py(ܗ=zp.7-a %‹5!܊S\1 Xndg_^LA ?xg,:<ťWV1=FZ:MȔ`%eƳ H% A3*k3` g+ 042P(K:*<))GO|? zՈD]!D^لs.Ks;M>`V}mML7?,Nۛ|<鿦4#t wVT?R9chh`J^,$!p,CkD2(!:MwJ.L.Ep+Q8H:P7Q@p׌c 1#zUO~,~w쵝.z PӾTrSP:XFQuX%  RSF <}6A+kW>!C4)aLGE ^fRր(J8i6zC?sp)Lo9<#4 RM: y yRBlzeI#5XRoR>xEPW%3Q`?W%Th;FS4b4!#8"VP& d+@PD@gY~#9)DE,4, n(%0bR BZ rЕ5PFB1`/BܤZ ;*!!!!!!!!!!^#8Bs_<#s5XF` \$3 ʡ5(J̗kDP(-92_  SL(Jt 8 -QZؐi,zN?O=$CY)ZZ``VpN4fQFPV 5Ꮄ*ZA7X&&g2 \A a i*et/C%KHB_V:ЀRH'd,200I0N9 +"Lz ) #79H胚R!dh"a4AA2p !h(%LN1 -.EMQ ~@X"bfVA!,G#@s&$$$$$$$$$$0!0jg~'>Y{߀a4Ҿ}( 1(q!ae`*r/p( a i8JH 18@(YsM5\9+nx( 4cۜ##KCgvT$x@J",OpABk4A0t(6%8Pc&xH4~+" R#'R@p&(h:ȄBW.fʔ"B*F#MQ$ a'p*J3>_9Bfu]JS! VPzQ}j_Y_їאQ' א3P ӣ8}ىWneJ :CFބ"6$8iBpޅ6Bs81:plgζ_u4Ey|y/|'NO#S^jysՉ;:@Vq2gdѸRw:^Y9rFW~@W摉 np}WKk{"/UP~zvb薓g&2|i}](vɳ' K+ 'ue^"bbIJ!u CȭgRi@5Hk@b5C>W&^QP#W $w% c *ERC=uB#xBPvD<գiWC;<mI %܍W&& " Ɏ%CPYDs$$$$$$$$$$K9Zq_7wϽ|JEM9sxSY5t hZ>CU^sH7L aq|'f]y+0J eO?4ԣ'>~>w_FB+3Iၖ1A!-҅8a}M:=0 ga,ih=ڨ?O<`IRBBBBBBBBBBBBBJp \o~җwzk-^ylg=j0@ x!`6B0‚sE;+X|{33㓓qmpBBBBBBBBBBBBB;pITg4ɯ}ۯ{sR9676hf@JXc&D3%13,[m%޻}X^ok<<O.9gZ-bj-Mw5s+w08~6nƍnvδ-x  =C-}3{ղWڵz7^̎` S:VHVE! ک8绱dzȔc COyŏ>{psvr,s9AԆ*F;? _>}`?%= n_oBBBBBBBBBB}b`*pmsoƛWϭ][tͫ;ۻ@'qPu32ͩىN;CO?r|yaf9dGL)!!!!!!!!!!!!!!»iYҊ`KG-VW\vus}c{ck~[t;nQƖZ%Tf+t(HNND"b0D ȅ@U1"22R#MҭTo# * K1P|@QxPjb;t@h+.8J9 Q2(2(2(2(2(2(2(2(2(2(2(2(X_t !HJ G?\Yݹ[Pa yp@yp@yp@yp@yp@yp@yp@*w}:*r(@J!8&Qҋo .ۚVd/'$Y=CXc;#$o 1eaji+gajR3 "싌rD"Ԑ/UeӋsBt[%y9D(ˁ]W_]s=W6~h=#vyڌg`MtQ@F@"((l=6[ZD-)>^Yzo`'#h=# هAg$OlѷaW4WǘS(,4"(5(լfiimZL^ͭK1}} "NHx>lQ~\(j+e[Wm3Yr-v "Pkǎ.M>[.=Rm9JD.C9w̮&%݌"()rXdf$ Z&,IUYҴqM8".|Gʽ5^Τ)Ebbb!kMt=a~~~Ds<P:gqJ11_PTҾR >f$OL&"ӊ՜˫LL*}#]ZVh e$%G qW^+RQa*0DǴba{^Os Nz;W_22$9f-=O7iI)iD$昘,*Z^ E)Fwi&"Κ6'%婢"kˏV[b \ŠSwE 7@gĴ (і7 tq:G}T7NsziS'3*`ʗ,OczukSP,7)c"Dy hܡo~+K7zdbnTJ\:Ȍnj"0Sm\Ƙ~!EyyODɠrAy]w[[.>^<+/D7ZS+TnH?8s!e|Q7f#"M+fK]#}mvkzly=ĉA_T)bD7HcTi^Ĝ{iZ(ߒ`b&/i׹>Hu5_nO`kͭw:*:Z 2[nj/=N#z]rbED$ dlmSEwgYNfX0>8nqvkw@t܍Oz]ʻ0_*qu[,Y*PNqof\޶Z=mUZJ=+M]ݣ}JL̝lve|-lE֘\xo6yG60IBYlb!᪌]Pg7Dmꛬy~IH?8uhy_4*uڒUǍWUIczmLkuLDbv̎_~bEDTQg܎ND7"fUK7'=r.{uo]) ;tZ?5n%Ĭ% #*S9w.F7$֮!$ֶ6~*GʊuJV/D$bMq|GNs߰)$'+(wz1}Ƶ "CI}HV Ƕ۪?k"N @ct􇄌nR$b!/ 6p?nZru{}FE2cEF[4D5#)p?Eb{e.{7R֮k4lKMrUmuY|s'LHfW~lmQ!KKWd[6q p>Dq!6-R*oՄ4e2D9L4b?+sGuH#y3IhO>eeE͆DWbM^  K=ʋ5d EPpeY!Nc 17/b@'Y"̌_E wvt.Eݪ.;ESplk#N >ua3nX>0CG \ɮ R:l jTsUҒpo}D/یfM;rKiD/e).K :ⴀc^I o(T4l°]פup[~m\f]Gh?HbJm}u=򒘠y*Ҙ؇w.p]}em']w%Q_Uq8زC"a!rƧo經/Olj2cDdf.gD1%:Z$0}]*Oٵ[ e'_y{=Děg}?}r`7n^(۱W sC̮Ԡ~J4n%Ws'TЄ|ؚi;_+%gggٻbHaڶʬ۲" V? Xc7նC.uC~^UH##"jҪ1Ç~EtWhD-Hq4PݜТn4fE"2Mn{8c -.=SegحCkϋэ'{Y'~4bpB#{^];hSk8%YR:Dp!,`OWkU#YBA%Ʒ>LZݺ}/KD lN1(=:F^TR*tK(׬CE^5F晡Oo]I$7!L8[7_;ѓO<vky26֐%@;) *\ȡQ)$1%&pnbTX񌐙=*{Ԭ٨fZx6+.f>SekMW*zڍn0}*oiڥ %Hkִcωh^>ͿscĆ(^E$6X;v}Opapӫw)l#ǽDw[m$bM?qat̾KVv]Pj_mj*IJkJٲ: sՖ nXT 9U 7fϵܱ6d?:]r^$1]f&dw*=]'Ȋ>Ț3݀[̭{M8+. |v"b 'D$5bnR1`i5Guz'O9"cp>e|pID,;ehPzQPcP!ӘR՘bu rDMٟqC=rLÑm3z7U|VQ=o8>3+[ӸS#gߝRcDį5J 2[/L[g*YpȐFU>UWvB-8es6HDTӼ{w[N. b(Dr"}.G-AncH|>wFxzs y5%C)&m5i;h6f$'~x7WS%ԅ񘈬Ca6 \et]ȼ+P&ҖG6vK;DD(}6埅QʩGD7zΖ߸lyxSm(Ab{nO::uu*A&%z3abZXz|߉{5&/7&/eD qw,rS|ΈWYy:5n' eJ\LR]Ѳ-x*v#!VԊS,v\8c"  26VQ~zTͰoYC;aDw=r DdCТI\Y&hSJ11 ۧ߆'ܱM7xǵ?EFnԠZqLyj7a6Ȧ(x*(D/\"Ԋ>oVy[Qg{x)H< mKiK7#E孌c4YE>>OUm9S б} H8x"=+M8("">^ZJx 2dȐ!ûmt(=""a%G',+a%;)-DT"bSS>ty =zl`wyzۢk!wٕ{4$LnU O.~nZlv\yqY]/cA Pbel4)}m ~AD?Ρ]=Gk%'H̛wQV^v[n!"*?cx?ykX> J-ry OZ_ɚӈ܋?<ӽˋfAUpGcKCDoB:, ߒvX"J~u#X7yFdvӢJy4[L[!($tLrwR"ݦ(ٱ@"sF4VKN歹p-(N%ޤ[QVfrJ_"HS$lDLe?P"bdx{]Bd^@H[No^4NY">r Bnjүk,H^MdX9Q9bt˸f䕄{ewLjk_ޘ(%C?}'"$]aH[wM%Ɂ""j̘4ܮY̗ZGǒ}^{S|َTc_>WTn'58lZ_W ^ٶ0[\о!V$rZ)SDDdӰ3Q(SSqK k vF( 3uĂwdΙz~6}i;<ĺJM5`K>?~/N>;$ލ204Im[o\m7'bu9t*$Ծ?T6L\vWҍd._=..U)Ѳh`c%ѧg?#`V٨eM;(+eٕ6T$@iR\`?/Olj2cDdf.gD1%*t;1b{K= ʝgPML *ხM*8 _*-5*TYDDSvI_=X16jm[ujN *\[Ȓ52v{Ѐu{'Rm}D$VaNFV%ɏ%3y:XG_vqYmzgC <ŚoZ+$olǞ&K˫(;b{'7 ӕ'DTid}0st]-f55kfoWpPl33{/W 8z[/V;3!DDTB~i_^/ zb1c4kY;/WZ|1G"}`V*Mbo TA|*zmzm`0*tGR[VPc5oހ%7;*:6ê_Se"*t]^e76'nuqnj=2l,~Uhh'}}+ '^CP!]/\Q˗IBܧE_9˿,howoJRÊ%!2aWpV3RLKi,G_[A}-{Q!Ƶ; C!9f+ةgQK~>vZ+QR#>hŌD~X028"hC:nŴFnd9 }j״w7V*h1O 7*ܸoIol#e6=,(/W!gW i7bsȸH½?)">?UCI,t:{4JU-?}@ pcfE1 A$H '%L|P٣܉ޭ\&UJDOXv7"W{myED";PVfrrg>?#!3Et!kSzn~GHhD$*7|M⊬W|Aj3H{a02<N9|ݻw޽ BADI}uiMl0mW>6XvCS+n[LDq] kkq4͎<6U }m]NjfV<#k˚ 8ܪDiDC y^J4VK,}ygYV9?mUWlL5ݴmK_ݕI̥ $\Yy6}yT0pUCTZiZ{l?EEyr f\_NqKsӂ")9fZ2oē`v?HqǦS rZ~INy!] joq4DsF9SS985!I( U%;Ej]JЫIj?G'g۬Be^AӏT"c"ی[ wiQdLBTXzry4t+k—4f[9 &6S% (|n]r)l+HJ-;jԠQmBvx,82o0FlL,?rN~ڑ^c2{/zO'DDَӆt/59ŝӷ㤛| K e[YM283"2NJ ҟ3~}f54d([j!hcrt捺̻jWvnt#]_8̿<]idܫ+u_~~ĥ{5qHމ$y!l\!"Ǥ|IHḬխּ[SZyvɊ}(EjJ׹WW#)`PfܪBeSz{FَkZDD`)p \.]V5,X6R\,*7xTG v#?͛'`Xcf +p˙4t1x?YEGk:L:CDvwTB2$zѣG]fx)?YV9rږK52l/= ude(ۈļg-9)GBDo}ҊD\""1)|||||NH6xH; "a{>9AiGCv""FInQ Q=zS5n޼yС} VO4bŝu*roɡqڕyZ;uhԪ-xl"߯WÁ&'?Z9-gѝG1F%S"8qic{{@{}y'1!S٨e⪹W5 =:.%>|؉rG|9UϱT*G9ra/^?;V"L\<(G e|fMӽyeZulIBD3DD&5K#'Ǎdэ䄏>|j)&"I4;W5q-d:(~uWޤ7""nU࿨zp\zv]v/޽{nܸѡCaڵ[uzÐ;מ|0Vÿ3bޠ^ zR;,C.$.n,#Hq';S-e &:N|14٭+HI6z5MJǏk֬VZf\cY<vJ?v-c!UzoZ4,7. ^nM LNxXq^K`vX+RzIQH"o15p"7?M>_Qb|k~uj!@햂Q~]cƊ&>ψcʐڛT*]8i`ZqWb=8 Ž>s{4YEnmqhζy,۵u*\{RnDD"r]66DqQ|bg*}mND 25ԢcVE,&c\M& ÐH$%݆B~FH)3jW/$Ik: str*T4P"wƀ%O;(P#~kGٙ<O+met $l" OaLj3ٲ=`OE #:MMJ1Pa yp@yp@*,AD#o23upSݮ\5?xC’]5*{[ː)m%(ye6!zgeCͪl jTImFU]s1엍1BNz]oI()/(x8pUH##"jҪ1Ç~EtWunξ {O{.CТ/r*55_zKRFJ8VNIJ~s"{#Hkִcωh^>Ke`ύuQsa QE gqM{͐8[47Q_XX # IDAT(|#ᚷv\/Qj"?OzIDj- ǭVAV̵G? aZرkSd 8̜{uױo v3guW}ڝg,&f)S@6Zt` ,JK^]g׺g1B6pPmxHij1)_R>3,yu5o=CvEѶP`#E>N#] _?|&7Z;*t)- rDJb"UfTqM: "Jnё.@r%$$Y~8%"PXe+~&=dE>KL˦lu.4wI1CDLdn6Dxw!ųϾ%ls3kj-\3eagϙE,"W`oo0$II *L;|֫;k  pd]a?W5pu_8xZ.nooѭ7یܭvw$ eAjI Ըex+zGSzn9&N&M›'`Xڙ-$&p̠뵟rՒ"""ѻ.4+hS"zՐ'NFsppjڿ.JQԨ^K_3-6ަB6Xv٦S.n֋۷ ""[R̋^k3j]´bodFhnlٷ#7~2<3-lmu /`үLo䞅Rտ:xr+ޥ&u`΀\uazՏ?ٹYg.XR+ry+c1iՒ5ʋ\ם*϶-=Ub= Jr $uKJAc!63l(L@^Q:WOӅWdgث<t09F&dO\v͍MEX'j%tNNN5jԸyCgX9B³3aFD4b\+Τ\yOrǬ@Vߗ^r5{{r;tp1tKt.G.י_':WbaK>ɫD!9rĹ4ډfmX\OA SHD.WоidN-~qзw8u mڴ47Kw|zuDDj`}' ]ѧ&DɦmimfwU轡e -:ѥ~]b`ǓD)mD꽰kẫmkIdɒI#fɈTg0ᮋDy_cۖ^<* xqӌíJV[#g<Ð!PLc#Gڵ Ǝ~J_;K Fxtmm䏉ҟhSkVc&Sb? .'wLDVFAT޶ݚүTNފ &9W; -WQm갈D$YE:I!XP5 "2&2R-ŔdD1|""G[X8I|}%F4U艊QmH7""Qrү:&b^gVD9 򯆔8sYAn =@ ".KDBwOf8{1 5l:4tߨBVt("po&#cxo |{a_vVz uMZgJ:7QQ\p&F3(yԸ1eCdo6]͍׀i㔉y<VK?trOyY8tV QW֮][ ݻ߻wƍ:tл8V_$'(>}ybS9 "3s ?#)1J17G=:Aӝy&x'g5^{'|}7T1;HKniz6Uih)'8҈rL"^_)CV79GU,/Mkb}%(aEEgJ]~߫c7ڦcwjN?# L@N$j0-[SQvm^JY$A߼q9gߵ+S(9698_sߡ+Odj|Ѫ%*n1O,s9[]"+UD4uIz]݉W\hy꒩ڛ1=y15[fZ텧ii2KP`¡N!ׯ~[A.g%ܾob7ѝk"8e 9Zϣ]z~ 3%ȎCoI$,A f4V$x-CDBň}bX>>vpt dž zVI8aDPըK]g 3]*0.wVͺO`)wAvG:Xvmm6d%?~ȱ?e!Vʟ-- tV^Iwb.M 8>~XfMjՊW}Ym.m߸S:HU%\6jFD#_򅈘85]t. MOZg]S +*nhl5dD{7,~h.z+|F$;LFnYH[A:w{i?==6Dckp{yuj0H#~;# ݐedO:#_ ؿAid ' F OWꢼ`~y?YEPyJPq8_uJv'MSC6K+F,Mj9;sl%a݀-wGԒVS^ .8Cۮ R9"xoj?<;|-D+A*Dy,׹_%9\sØx-|}|G6~%m/F3ʱ*q]v|$`ns +)0=t(æ/cZ#z8d$Cq ; dzyq+T!\6A {`jc؅׵ښ$=/w pم,|捫-կ  dӷOqeaӛ˧1vMލ`HD5 jhxc9eZc '"ſ/rk+/N-G۠f7S$L+ fLv_yr۷o_?jDSYRszPXI  OlK'J ㌔6;.m;G.E]'R}\5ݡ/ <ɣ ?nv ՐY6MI>9ymh{7GkLszWk3M5""FMϚԺ| Kʝ%$ZFIo_L}oV\<&SX^}t)/*5|˔ivj~J9vYׇLRs0huWI?1JbT_/*Ka3ТgZϟ[ʊv1u`sjL=y\󊇮Сj^/S9Mv+uY`5E" ~f]絪IKWTXQŹA ?dz۷oرwޭ]7oSbY~>eL*ix \ĭzr>AcTYf}ƺyC+i?zߺeSn"ec!Y9RK[:+TgDݺgг[v*NA2AU֝+*Pc=G(ҟ\}[S3rfN?.>nS*YTp2o=|ѹ=dowZWܦ)/ ՞VD9 'ݽc0G$:XhyrEҧ.vwvn<3Eg,٢#tϏ[lF?woL aFއ>>&nTv0s"rl[<_e{t]~%_$ 9ɏ/ k?zODD񚾜,ž b>?)̺aM_ b뗏ad #bbu"|~ZB*ټMx6sytO^}/܌䘰Ʃ~,j(m,{/KD$zy"5۰^~svȅ_(&_߻-KcJٷNG8vNNGw/9O>ޚo.)MۨN lXhъ+>SiHp9>_ZXumYK`M wMSv&߶ooٗVWi@S7ƿAÿ,3qdEtCAQgeU3d2V94ŋ4Vcpi ɜMs3fΜ93pٖWgƝ64vbF4M}vn`4e]ok'ܻ 3w^Y\zU摱M)*7ԯ؞}ra al͟ 7$Cj۶}{rxWe܂3¸1) JA*/䤞V97ts48Ej]cRR|C2mޗjOwPփU%R"rܦC+^g^Q5ߠǶTZ둋6lQ]Gnk&{RY$Ml&evJ[piiW/6ֺTj䠶!p=2yKRG"kΙ*+Qv?MD ^mmXzN[MUAn%n-Q_}/}Aдvnk2ڵhwʰcpf/3ip/ d>k\Cu]{n1UzI 34>Hr(<}r(? ױ үFgYΛwJ;n2[ Æ/a/g;/Yw!{>zB6>Z$5jO a'QfڸkRU*V/Rmbo 7׾1ߩn㸴9|&'$,5rɫqA2k+{ ۠|{]֫h̪vVV+vCbrٺ:rڬpm}& r _Dt缉,rr y:ʽ|BD'C o_ݻ?(&HjxnEHY,6i\EZv{C+kc$_-q#򓔟鑡W.aq5K2~^ɖSO O頹zAI9Z^y2MWbʜX޷=sJGYcFOC۪i۶}iӴ6o0urrrrr(L-ah> n󖨎\ytb^N59 bvNr+AEcg "$"g㛾*uUU9yM?]N....oV+d]ɦ'=v339],)|qKT9G*zp,[9CfS/J:1}В3*1*m·ώOyr0ÈMq; >2΍k< Pl9{S#R :^?%`7Mwɔ.2MHDī{7j?0{ܡэ2bh[5mޗ͚ p|&j^~QjdiF<'yמ)B]?Ԑ);Y/n wfn_m~1|a;= Wd]aIΪa8q7xi3hRIfkFtS2|۔; 3e龃/tWYgЗ*Kvz̔{v|18fJL̵cjS-6%1]g.fMI\R΍iF4TyOޏk>B;XDnĄ{㇮KnZS'z׏|;>pV?;L;M>O_cחmm;7"ƓĄ{aQ/ IDAT&p>Iv f~T~qT:KrCgJj?夹gٵh.];{C LDyti{݄]}8qa4ѵJk PpyÂ!Qto*W%Ø sG)24WoXi \z.r;0]oʠ}E:T(ux12lu_vi^$ތ1qET3 Sz.2=aH#g_NdSe=&&d SeжZ¶!v݌ s>5s`eK'2X\s"-dF7bjܥFi/ \!rw:Au7 h^띰`"*lʳuBapi ɼ8V^;v};Iϒ%ZX*02T-<֥+tR0O1Pl>'`c/&$ĖwSb]|74%ō[adgs,yJ}q,3(.]T{pؗ}Wb ؃CwG"nnn5rGE\>"\yl &<ܻz뉹$7y].Ca4zp|w}=8 ~~VK-~*OSODJGLu5"CV{*J7Y哆-aV玌MwXlIDMڥ?1g܋ fx|DL";ȕED95 ܷͯ쨮} l08IUW>=8ƌ8bĈ  2dܸqe_&ɢDDd~1fΖG7}2N}y ""j4h4Iٷ 1-*nȰؘx_bKϞ=]]]/_\%D2Dٚ67#0.C\H’&D&TumW^RRRF#m똔x 9ptt|ƧOV^ S.D`R񉏏?}bˉ'^zձcDz/Lb:4'sOզ5z)UTv1nܸ7nԯ_ӧ^ںuklUnot~3ݣlk^݁]n+TtPsK.%''tСQܿMWс)zhΕd@IsǤkq hTO ǖ0:ḑq2qU)+{a*Sj#׉2nUc] ^kjHIT7(ޭ; ##qq8k X~U TZe1[9U\:>Xct(UT>s/ΆO*6-q˞\m>!*RDdR$.lCnuZ6 r Sktm0Gfd&ef$"u˻XPaQ0-H-"|Vxw>qp>pJ ~P,AJj]XʮG^kqע2RR¡G;MjW"""鋐Aö=Ye 3Mi,@R&Q(mzBD7c%qd*)Re> !ߖVC0; i>.P!#69{JE9wF:Af'ݞѽmC, ;V$Œ$Ti? /q@D|Qƕ֭ϴOo X𷶃]PdC'ҏ@))$-by"WTW6n]^k +;"i#Bdq7_Edu\"b;6lL?zc[rGE\>vW:)?sw|\d .\xŒ6mqLHU:uoœu9$^?q>Afj*98WoֹG &FPbxŗknoSˮ+k~'"vnM,so0([godڥ#gIHkd6%'/lN #~[!OֹI L7O=秸Y|J5rW\iPOcR[Bi3EtΤX3NmzN{x8]@jGw}q9DteS]l/7_Ddhcr{ϩ5 Gs0"0b@)C)ҥI[ymAܐA&.C[,1+Ldaܕ&x|E=3w|z*"WYyQ?؈5 VYEuC+"z|ݵdZ43\Y 1+żɉl"i+~;Xݐ;!Jt$yt MDR I 9I3m+5k(ك)'RUi2'xaILj{qv"AD\)?'Lʾu?9-2]%4秖jӶ[mW'l@+!*<7tt& 1͉(;!Q9-)uE1je4'SN ܹ&PX"qKT/ىE/͈(wM̈́XpMV#3kosҥKgg˶7*:/,˂rjXKҒs%"")s[S$7L@N%K$RųֺK թzveT~t*e|swtl!F/kLFQ|\ned}54fgӼ_N~hPO܅[SƽpKz+׌aenoB޲O],`c䕬(\_9g~},RŴWsq* L(I>?88KFik]Wiu#+Dw9o\ntmaun^S=()zȩg_2Xo^|:ϮuΏuS[zV,橽g㾻ʑ}7Y-\n_-8j=QO|_ոOl>8t?>@6!l.z~A۴ ^1ۚUH`|). ,{%+ʩ-Wnx.=<]k]!l5v1)1صB[G.v-c X!._NPJs"l"{xGOB1xBȺ 7` ʍͯW[. / C=G"9h m)9b>?)& _D~svȅ_EDDBa+OPEmO J:5+RGU6 ?G5lf`+d9ywTPS"q}k⯽W\8Sh]:vQ5ߠs9?k\ ";-g Xx͈Me7AD$e"vc˯~):hFpl8Fԩ)Bewk@DʺTȒ~ o&< ^*XD"*Y/rg䕬N{eS~ԈL7vpggwYha6-ME{gw՛urD$\"}6JY8QM:}Sv5:bذa_4VH–ucɿmIl!jAnY=w\YZ0 YBlK [pip-6,b/}/)`U?oI~D!k̛z$4٣GwDTOl, )KERڸn׽cv΅ +̍.ǏԵe [ٽ$%>2.ޓ<γ5TlR%^<'$$,|7c}UG"xprPK'IU񊧝=r&-Yaϻ>^ cDWGCJ$PQ T)ҧͤ{%f)]⭈9{L"U˧G \WMkR)3yZ RRn:oEŮ/<U45ݭT Sv >8 G}׹>Q680RK 7#7[$""ҁ[b>MNIX,kk^'8D,&WyP̑/ +\<8P-1z)<ӻ`Km $7WtخŕgIBXE_>}y@|lPDS|T)mJ~ixzM.4NtFsF{jwce~yz? PYWXt0mQSH D.Ik*ٽvV{g۠ҞC NJsrrrrr,ADL.WU ^Iܪp\.Wm >6k$gfRoķY佯ޞivNI^2H9W "bH=P ݥ:7 E c>{u\ T)mJ~ih3_ICiS w(2Fa'йlMܫIKSQiq\\:PMg_)r&F84-Y8_l娦G-|`Iu%nը*oظnNX,z'm"X%IGȊED$}iĔ(pl19jj]Mԯ9^U( ;G[c(p@Wn\"IJb ?BrN;H٤=|#`%ES<:Z ޶wS""/?Y1_rߩE unfBڇfvDR j7׍%zvx[v og{W{=G- .` صqqޣsOk 5P>ʰц⇻LJ~f%fOԹ )IXNNj761ވL;,%~P0'CԠf.DMt.WTϗ3͉{hD- tWZc5#PO!VȰע*S*Z70ی, Pj'%WC!2R9F+Ph!A R][SY5"H;(rS)2)U|!פ,IҼӿ8][nÇѹP$yѹQ7}]巬bB*ګgc4ϏkPx|٦f\>sp 9 2+Q5Rŭ)(նVM#AqTO\*fB]rgPɤ6[ YS3qO_Ƙޙ) GU7v kQuO2PXܚs_zL5;2%eLܘ%W(S$2E$*)Wj N'4cMP L~ft3I5;5>¯WӸ%?t\W]5\ k WeRڭQ`0+YI|߼aHUQ9tיUu)!^kf by95PNv3.O "漴+Aa Kz]FtHЦY8YF61aҖwԮIiܩ_`N&Ӧ"Y+rRӣI~V-U=/@!dwG _>""qNXUJL>v%MRMN-~>m쀚7˲ݴC73D^؜䘬Zw'U}ɓܳr} !"FNӛZ')Mz"mFlDoҒs$,]%8z{K/6C$6~%Z51fWfb5*QS2 IDAT)X,"1!&mrQ ea(ypy|YQgnSXSAO'c1 D+5D_:CT)|$KǕ4E59 K_~"s!.%)Xi3md<@DdG*:38yל}(- R?!Oͽ#Af >|ɓ'.۰y388888XvGG1B:z%Ş9rh )â~]"[oꓡ/C qlKv7R~2Btov/CVtѝHǫg8tB{gP)辒&QRZ%mB\K4&n+>777]2nJ Ǻ/ үw֥GˎxӾK}}`B=!xr~w.Z{"FbbbظV,4wbMÐbص(A1O ]E_پ ._+ `%vxckڨCRV56Q4[S֖4GŝlՃ, aHW(uZsנ'w-aeY"juDO~U3OI-AMmj-ΠJiSK[}% N-p)%}/hepQEoF>z"b[{T56۲SRY:+ӱ;'W}2qIW 6frN\omo.:}#m .W Ֆn@9cB$D9fI"f9(", <,6ug ^$1U$$>E"nn*7:W \+Z5#FsV:T;z(!CU;. #%?\ʾ~bױcrjذ1|U*(^ĉYĪթ{+ZhϷpJ>bV^~lw}ٻ䵣~u i[Rc{~iYBWn_ԩsʕ{رÐYH)]]51hΠ/jqY$"J &9N1,LeM?$5.ض~_D©CsnnGv@s<ʶ onH ".C[,1+LdaY6:w'q%"J:g*qDT9;YQcIDV;!PjxxhXQ @T^1cޞ>}zܸq;wxqrMKbmKe0ͪu\K9QMa= #lDE?FdKAtCֽ Nê`,s6f_ Mg]f7󏃔&O%;‚!J;jx&CjBN$}qS!"?pu'*$N:!&\OT _"AD={y˗;tWL,9Tv n3vrFV~$ .fY켦7/kȣMYD}p"+t_GU t%1=F&",qmx"3)NnڋE13=~=ĩJŭDěu妯W=g{QLswӠW('feʴu;/ԫW/))IHd/3&dMHLLP2N/\"z.ut"ضY=ƽݷErr7ô*QQQj>}کS'3a$Ddt"G":n)H"2ܻE qOǰsQLoOl& Q̋E/͈(wM̈́XpeΦAt“I۬X ~.DmhU/QƘ >UЃ'>>ӊ-'NxUǎ΃ՔQ$];Ńj{$[ОȏG⌫oQRӱuw`4'\neW#h])<~'JDTܪDtrժ}gD4T ;v:thٲeX|yhh֭[ wpޞ}][ I8l[@|"r޿_y %xkV,#CDiSd^|"uO<2éu~%Pv Ddւ!lռ%0'AD;wa0>>>F7esاlP(HKϺ5w* ΜdWD$eߚ4L1{&hX ˢe|"t!hYx}րKo䋄B!?'РaGOb"fY ~N Îέ͕'zղG `gXCƤhlM|BDk\] ZF̤a|˃?UbZٸ AAc=!{s`}b ڽ-2`aY4N9[QT8>EFUm~GD 4* +&a1#?R|C?p2wFK= ese/ wUv2ʰ&C kLDVκ(^6E @|"lR}jG.ڰR9JRIًTR!"L2T*-2# l5 4C2SEBr;V6tQU$,5 +\<ʻ&|.9DŴ\kl-;{ !8C*<8C*<8ClRR㟧7U~7i )9B(-;ĥTpeR׆]~#ʼgy ΉID!m!{q,%6~e]1icN\z!۵bg* ?uي' ;q휴<@Gu}2Q~o ٞJd](+Sl҂ =tl \l%JSqkJ$W۴iӦb_N=$'x+we#{abŲM'BF.JR` Um2f;A^6. OD1;?!!LC>yk;VO1%J&T,rc{\{1":8vu%*O:vqB.[ 6O=A­ /޷0#MC}lE"2RN[Dgqp'1mrfٳm] ~z'qu_Wa :W \+Z5#tA$^?q>Afj*98WoֹG ]%Vyk6?Z3fOޕ;ro0([֙godڥ#]^t91DĎmQ<[V<&-XyėtFx_+yӷѬ`5[;&W+sb˓df&^1atCc7x(/S(͓D4zAu^%2Wȴ4GGGg>hO;[S"PgRŽu,6s=w{<nx>ID=Ʒ%",F#s;H8|MqާF e:pKCD$eɣ:Go_""JkfID/Z2UΛkkUv?ȩBD=א'~}vtJennnuԹr޽{waX4٪ZRQzʣl 憄 /218ŲDv?ݣ4FD0gY Lrt׭C ;:VѳtzEi˺Q8N1]$`p^"^'>"g][NE[A3ܞeZJe|Mƌ+{{qܹӤ'a5|OVi5x"_#Fw i3v2C&"wX>@$*r}8vNN9|QFf8睭)1OgC #ad !Te͉Hb(څalϖu ]#ãiddd "ٳ͛7/_ܡCHڂ8""q҉ĤgQ,Dĵs̤[:$_۷?uOeҬL¤ܴ$6v-.K5uɣ{? eڴVɱI0rpխ[WmczLz.16Z$4'DE̷DdVnre[[cɹMˆEfNL~d3Ɲ:wlW ~ ˣHC1**JmӧO;ud³H_܋`H,zinFD׽ lj'WȾ0a%y-c7j9=Zνkk@,XZ1 o0slֹ;/]}y| A&ZT#2yf"3?]q.|'Y-x9J*aI|l<|||O>rĉW^ud?_3 H›Si>jpY\*ڡC&"A7i^0a !s2P-\ YmUN^%Y[ ݛHDm]qI&— _z'Dd&xӧOg=~§ࣩ%(yH>}$ ;v:thٲeX|yhh֭[JTdͼԛ*[5iֲͼ^|"uOd#xs_Q_js޴6zoWIpꒁgp˾|5l[4{GD5viJ 7vI-:Tz:']:mƬ'>^YrFTixw7 "J\4~7:Y"GCfߎuD(T>"ڹs1:AD1w<?v􅈣VMuY,"Jjxg{AB7^m $KH(s_ ~ğ<ˆ!86%G'<ወزn-"K(H [m,b= i2+"oMStBO=4[^GForYg ǤǬao!AfwS¨+FљEΨ$Wza6?#TUVnAmϻ53cye")~+I.-W:kDAvj?{{'tp+j*fP!D:F Vյɖ̑?1=ǷB J:76SHQͳH[OЯ< Q^=ˣ)eBxD p j {;Wmv֍IoPd[{V (8 .>H`c"6^<.pxr%1ݰaȏv,[.L~a'|t&C0+0j: Vc/}/)ݪ7+*^=/[Đ)'"zaOsr"~})^Z fB}i<<,]U3D"!Q.L|g$eUoܲU=iWɃ)/TeNE/tkפC>{M^Q_iPyLs>T"k˶,H~⩐ϲHj€U!,orrHbY[*kX#\r?r"brj }%$UwԘ4ٌN]%h$.tjlg:;s:YHdYi/N:;tmsSQ W6c*8/4>i̫+*lp![ƔU AD<ܪ$:}?3Fԯ jZ$mշw^z_=AysooΛСbO~!vC0BIz71s#qO[t yq8YVqfIs./VIU81 "b=g>˷]U UPnUٹe6&a-bŰquk9;;;vT/He0tMR헇q4n [3ʧIF-Ν_ȻHXܚk5hq*SV]\ߦSs JI/R4]呲ȉgaDV.ׅܲYG;NĐZԴrj;}ngVNU0gڃJ #_J̫2agQ hY*KV! W1C""z}~Uy_Xi!$z|ƕ 7_/CLWt_B> u_+ZE 6|?d+|NssAӹ@d&߾~ӧ# }OzED7߸~MJ)>W6MY|DaXkmtW TnHD %UqK)dp3 Ti `J i\Ftgec h1ݩ^  hj FhY*[pDŅZQZGպբm]mZUUV܈[ 2eȂ! 2sܕܓ3-_j @Nv_y2%^1B!B T>& ,)OXٻ3Y^3Ws 8Vt &ҥR@ip~~/38k&)3Ҷs &2 JStU1ˊ'ۉ~ Mhb] !B!Q BH <{\w.ſBxWeC;$?Ef7FڝCW@G[ ڪ%12]f䷗0$%2F]ܧYJJJPKF]{}F;azI3^mSf<$4 aL?!7koL0o`f(3O/!B!Q BH߻9;.nPXf7kd)`e!;73g%i^:a#< 63pǩo;XJINd f$i/Rs3pٶako2N kG&KNʫ$Oҡ1!B!QA/YyoM aFvM`uKMђ"аٕ18d {G&ːɠ K=G,77d2LVY<B!nXx[MG񁫻w3 G&34 HsrF_&4ۯA HHQJVУ5C!Buv }ׄ0bydI-3d"gf{d<<c#ڍ=cz9B!B!2*'3-o]3d[ZR'5O;0˽+n[NfR E…/+6\gkަB~6ӟ]+;9!2J!BHGVlqńkkk g"o5rq+nt:]Osl asfh3hmnjJ,䟧ye5ƪQ=?ʸuϭLx>˱<Bz\VzL~v02W g]ѽ<Ԗ%{ne~nO?S !B!BjٹaÆ׮]۷oΝ;+D#/z-Mχoo):|tE7:n:/>݆ ]0Lzܿ+a6 28ʑaQ >HݜZxo ,1:.8Ltoew<ޅFM%B!B4Lĉ===U/Ϝ93iҤ]vU(3h梦Am_-5 0i y,SXL,9%Ef@E@ op_ MJ9MtYg(sWkUd< ozx>?7%@B!Bx񢓓S~߾}իݺuXt^?'\7TlX@رBKD&+v/e1P{>./7n:@heF%ΣgC!B!@GBBBF6n8&&tX1bdbJJ(}qu/윛[(χTdvJ,S,.6WȨ)ИӗoB!B!@׵|Gmϗ[0KqiJ@@۾bLT/uQII|ʴ;!Ep>mYKNq":?/!B!R8e'lgΜ_ݻW]]6w||woڊW2CE3/Ņ(R6 ybB!B!L[;wN4)$$˗o߾ݾ}gkwZ3#n6{Tw{gr_9nTKo^v|ͣ`޼G>OڐҬm2 B!Bcjʮ]<<< v:C&]*J{'>ag T9?oڴwf)WaϪhL*2c_>vt ӿ{&PB!Bȇ&Gsx>)lNm8wu GЍu_ߧrf =A:𬔔Jyv匳VB!BH1j{S(x%?f$xC{ˮy*VdƝ޹sY Q^JM|Ӽl3<^<8ޭ;vءK=lMiVWP Yr6TA!B!(ʚ&"3p ̵3ÐM1˴\x_#kEL E%$#ﭰi^QB!aZx[MGN8*pZTsoΝx^U'z k1yb 6BaeI!B!|5@zҕqBFU+I<-!B!BB5 #2*4Rtq~BjB!B!*8cq)1)Y" dP6hٶMeB!B!QG5Y9Y98tB!BȇNK!B!FB!B󨂃B!B!uI Wx-u|YRBlgaSg%B!R+82\gJs[Xۛ^a6j(PSv7CcaakQ[g7׶-g8'oNۭ8)]5oӚB!BHQ? )rݮCtOWgXǟ@`/9zª@Ƣ8!(MFUQrұYPofHI5* o '6 j:B!B!JUP^dd؛UI5U&cgǔW}47wuPGpD){vl`@H"À{nFU.ߧf֛}d8g! H_TZCe3I!B!245'sAyC+'$wP`@d3YB>}^~aUW|:0QrFj`?T&# 451S#D2ɤ;*`j[+ͽT[IQ_Ug>H=NJÇ"2hSYv$\بW=VonH%HE Ok uٍd ţg>͹+xzH$9t/.sy)vN|0AJR0Df%=61eHmRtsW5%INj۰Givhhٙ'/5Y˕՟45h?Mn9hʋ=y:vGtIS2?8^LN6W7ih`B -}sC>`ЄBʣ{jv Q5>~;#rܛ\Z%)yzp"Ov›={Vt F O,I¾#1 J{ب>mK#?CALe+T*?ETB *4 ,^E,9XvɣC#&eڍJT9FL ű?ꤿxwqF= Uٻؙ, yߞ? w D\9с4Ƚ3#I =r"Q``O4OU~a;@;9bn?q"fRעJò,D@! "JmޥPqy½3U1KH9\]κ)f.Ӷ_l,ҋ-{ICeNMyAs}UETBvW gϕJ9J={;͊ iRh`n< ~ohgk|Qa]Mk6iN)e=wσo`h6Kݐ3(;RLߍaJ{򕬋/(]*@R iĨ~P֓F"-T`i1FUz2 Viœџ+֙28% YwʻKOܝ(S8(Fnl' WQ~\wG1#lWFNHY/$_O:JHzP.gcݢFCӯCE=::V?cwT"Dݺg6'@1G\Uɢ4%+x J3?ᄃ}M;i U8P!Tjx3'Zpd2 ϸȳrL ̠/7MgyټC6}7;W1:vuw6.q$WmJcࢩWF&nCsuR){ `"n{[|*qR9ze'iOzjt͸BL߾#i(+ETyg9>ޡy#rdO{l@#{& ue|ze9sCpN3xX~˓[M.x) q|u^wר%*8R]9#QR̬aR}t:ݪy3>7i;;!/ǯW7ϩ&rv .rep;/<`_?8o4xٿ?*O4[]rzu(6~6Ob@6x_4D-h^,&F؅?05ޜ9r {a[&+8oDò,MMn w|rĸSv5.E+ÃL{3gH|fH&?*))f63UG5 ie&LÂz] 6?UUlN߹ol;7}>U|ΙٜW{έS >@J~<`Ϧ٨j7d+^^4e^6F,Omr٪|$7t+sJ UUFyRp6G-ڜ <)M&.Z~vC_a,+m]t%bwfu8]~+Wn޼S IDATyuzRòx<]Oi*捊 !Wˣ~T =8YmRϾIq0:3_YK4+#{3 n kTkz|g;xW<ro|3ZJ]YryJqn-ZOԝ&MLECY?R%n2C[\t8 (52E̔3$_R jǰېݾOk ~WI'[>]~cuՏ)2:I2;YT}=v`ٓv^ @6x_^?%ͩ@npg!tգGp)mԯi5K?eSӪgQQ!y&fa5.`s]%U?g?߭zW%jq):'bs])3:~ߟ[RH^Ծ#ɫNK=5S/L_jWaLq-|\VU+ŝ<^īzT?{\]momW>Y ma&3;8"LNyZ;k9;Pb[zש^/_D>]cK />n @3yo)Usڰrqؒy79M'$lF =kzeB/.*U@ WTV?Ҙ7 `{EJ^} aC5k|sϖJRy~1L p֬soI!ۣW }˅ڽr:k",ݻht{={2ygD'ű]E=DT(A+"=,qU*XI=ʳWr7;^ @a #iYq^GN<`j \o!sNph^-gZjӠRa_#T:xcM 8s Yz$eOn70ֵ4Pe@4&/|V+a8k12 {\ϵS2HT 8r>aBXpR Y77ՏQ{9- Y-Ct3.;ɝ)eػwM&μ^ՌGf]/ܫmcK!RikL ugs6mcvy6M,%%s7Ndf| g-.lnRo~:dI]>[3p`POȹQ9W\6iou{%b)Wݖۚs_ްTa@ @)Xسz,TH<^&[='~3lV1:&]58Úp3:rO yje8܄c3ȊXg/=eW[wF}y N[{VoPYÉ--ǝK)Hg WqrK(][OآȵءX(uZ`a`[sN>.CKN+}xŞZ]L^} |bfRId:y,ȃp)ײE4^a1h 4D[ы_@%E}fsZ~awT_DuLw,W-9IV0BSWp>l`Eu];MI~/'TFܢ|DV_"ٕ3ZY(+^MKol*1m#\J/´WV3CW:70 8kމg / /ty=\JS c7۪ԁg$50o/NS n|34oiWg6?&/?}/'& Ui=IZ7݌n\\jVkRǽkh<0{֫Zq7FꟑV]byO;& NCa_G@7ξ Y9w6t(-vxFm|aip wc>sݳ!cf*&`Ӛ-Ig.<u7'lNy}StIV-MݣLe\@?[5 n̗?}8+m (KC{QTPĦ(*W]ν18~]p"KxBIoB_ߚ~uó[j<͖qٓ}+onuQkx0zPv ?uhAM|<.7wEz苜a==?O8"5%ǧ}(RSLW%E#mQQKWׄ)Ȇdf&.Z4ldb_}Z9{?OMը,({W9/|pI'Gn]d%s +eX/ Aiubp/J/ӇBH%.*zEpה,T"T =*̦]mokdFR2))+ϵUq+7k.fg/fӪsu{3Vc}?|zN*́;ߴ{I=?Tq^[wرC#l9zؚ. w]qs]Bٺy(yU%`b䪵B)ȁ} 9=UbK3bZ;t1:3)o7e?ĸ(y9^[~C/v6M>g=.k$x9Ag9A`V;2)sL2:I7m@ P2zĘl?^0 GfFywM̬Q~ʲ2>;r.?*蓁fA@p^S.N=a|K`w]r3ϤF9w$ 8o}Tptk7 )`E<ٓcػ>K&$%n^c?W>,oXhR7ZXؼ2uۧDJ(hr~ N=Ι$ P4h#,{7pn7z5 Qz<,Ql#%ON]IF˰Iy|x^jF`쉅9A| 35B\U܂pۋY$d(c쥽נ_ 2)1@nDL ܸ{JE)f6c~ԊVH2d240oYM9;!Rfh$;ЗYk&ǾɠX" &NJ^A?_(B)0om?x^)ځGpb{v\ X.#\K^zƍAogO2x qeZu#N og~SsB60֘ ׼!bOefTp&'/ 4C.s$-_hfAyN6Hi#^V,QzsΔL:'V7ᾮ/Wp$w _ #)WqՏ^qN^@\5Xf_TU/T=uǣu7T( 9Lwݮp-@ cQo|_eҳwlZv:n [ޮ$z ,3{W6>NxS尋=͹ʨ6s?d(٥ac'LQ[ny8@O"B!UQR_7N<ߪ}^5}/\vA V4ܞ^*ç{qX]Mߎ-F&@ 0):~#OXuqD*Cz֬(_<,K!0@`I˳#JsCi1"F[~k,W%hK}`{4M*a&EC*Put0)2H%)˄uQYiyF@c;,CΘ_ˌ8>-].31( QQ<"-yo)saoSг(:%|qp(>pTA$['``Lz/Ztuk |Pl1^-^XaŜKl4m}`lkR{,VD" r&mJ-|I@ {^?*0O%mrmB>E dDFFn=ԏvTFmGNuIв3Kz=:WJJB!B!:^!9zª@Ƣ8!(MF5EMjq\Aѓ=rk:.B!B)M]qAk{S٭! Nr}l%[y,h$ms ˷ B!Bȇ>Hn}f=} <#vGk2@R 0Ҵg~?nVܥ_շ[PB!BHIj"!h r'r^o{Qk߀Kqظ2yr'G2_{@OA{n}#>}29WN=}+* Jƣ.eSY?HӨ{(6?I(S~ ?u4 }6ܢ)tI=Iaܣ[V1Iqm&Ue<<c#ڍ=c=Y*aK.pqJ|<,tɭ[G3}pf= x\!B!4RY'[^n4P2^Ӷ1)Q?mfZe}ooo-:WGs\oᠼm9IIk0 J3fӛMWGM.lU էpaD2܍ޛW WNwaúmvk@׍3z{7@kj+Aأ>VK8I?Uh^IMNOBaoM'Zfydzq]4iv^^*6<>#Yߞ7$܈ k@é뷎w!B!45ӂgĉ˖-;vG1iҤrl/xؖ( aB4[B(b}(EhndQ0Ttaoą˫ R]h6|%BJ1sPuϚE aDVn[NoeJ72;ڙ( gQYJ e< fϯXD/%nQ.KP$GI533r`!B>Xq!$2a߹5myo$Q!#"`B!B\ Tp\x3I^6 mI1QM^c6TgA"+ReLI%ټz}e@,jW430,+hP~ot1Ǝ5v1c b%ΣҊP)㨙tt1_|ܭb6 &ƚȵEd%:+b{B!B{5E%!!QFZ 7n:sgG1e'*y:K{yp;A,.m }Z4z$fYЩRѪr$YQ̞+@FdTThʱSQˠ6:Zaٮ̓C%t4gHK.?nj]*u B!BQׯ_Z=zτShhA&ˢu87*&s'#H;]ZQeS2tUBYyE>mYKNqM*tprt'+Wܼy|+!k'M7wQ__?d s.fXWvXB!R袤< IDATq̙%o߾޽;<.C'MwKI'ʐk\ QDA\:5%6)qVv%zPh_^б[ eqh *#r0jck tUZ|yc5 !B!T3ܹСCXz'o^<&`8kֹŤ+@>`wpL[W@cSGsVR+u9Y9tk*+ʾ xPy"G>^GE*W* #bf>|йg[z>tFjy>+2!B!Tk.a<<<[h4uwWt[?$Lqp(>p5ER ,BX98Ty,﫚L}9a6ZPh%`PhSm|A!B!uXB!B!bd7e? !B!R;|0]TkCB5p/;1!B!RPߨmC!B!B]T!B!RQ!B!B< B!BHG,Ȓ"#b3k:B!B!2q7J6dD m%  m*qac^!B!R jgĉgΜ4iҮ]*+Ƹ;A6L,9%Ef@E% yt4`ĤߐWW|B!B5PqE'' }իWuV2~G&eOm=? ^7رBkܽ>Wy9V}B!B!@GBBBF6n8&&od)4-Z}MixR=>YFjn`Bx> #,YA!B!:*8_˗=z@nZĪל5Ҵ>˷}ŊE/uQֶEe!B!BHmǩ"=<<"""Μ9$ ۷ݻw"t - ^cg߽y`ڍ"F _giQ,B!w\%d0T=ЪkV:EmEmZjժVuĉq ΀B Dp|߯^>3sC!V3nڴiĉnnn>|Ɇ *)@,){>% N]rlk:";t!B!B^U56L͛Ϝ9[E5]٢%O.䅹W~_w _ s "Ui?{-$7=:riW_|]MS W(A!B!j,Iar@|@㸣4lI0];m7hf}o*?s4F,!B!򪫁>8-;WfҶkCL(NMf祒)r+6m Ԥ0j ;LdQ_/ s*!B!BòzogqZ,*(&uFaAՅj: B!BLKR,u(po IjT`VŕT%Qʽ?N}Vt jZ<\1BzծW-B!rjB[)rX,L<&Yw]\NdwyglygkUTt]Qrٔo)S~]k3_i/8DLAbtBA&5 xSn^j+ȌMS]GeޟG'IyM\pWET`+]B1>= 7q'-.yFYN̕BNU{qH[iZ_'T+e* :nO=nmcac}X$(`ˇTWxg$IeN.eSJgL+Lm !6B1FfC'ٜ)8= Pw]^Fo柂x\b- (s%10Uvm/i}{8sn0llmTOM3:=hEDX8W [;T;q̸8Dm7Zmhp4^5eůW{p)ÁqSsgI`f͟&dFB^JpBa 2d ͬ\\('y׮ øgJ{啢gIC=g1aL*%?-bs5~h콾oygopac?ZEXj@*"((IpÚ*O^kY΅k xƢ^]6@(} 0NpRt @Z=:7'vdbPЭ[-֎̳GfGW7,k} N܏!xd8koXy}SoKQxƵuZ/K\9nyO隣:y8k{HPŌW Gl_{'aVu}C$z$]}Ztwɿ3&~}< N=0+|(@*)k"-` Lݬv[!FuMcH\3Sf}cL1i;icT[<2N.SHBݓ7H&pb~c?vp[v3)MeݺŇLTsnpWvzrf韐\_y-׎$L/bČW_3@*{ GjvoHս) {=^M7.egbbRӦ~RxVw#=%7ыDN! Qr )XMf}zQie`s ſndm@̴3?393Oo<.G󞻩 ;rKy,Ҕ>Tqd3SҶ~qQo=ǐkf/=ymKA1sp?l/Zf "Kvd2+W\wM>CBߔ69%[>īT`zrxV??/"Au~XT:{H!FB1NJw'T熱{ -vJ68r-X$7۽cl{Zn?.]RwYR#hmq,LVwv{{x *Z{ "snBZpI,59W)X`Jήnm*//B YʤI#7kf|{?&u'aswH1-dZn9 =0"dX1fHdZݡN'8jlmI뼶Um<t;}iuO3g+i8zD稺Pڸ7S>Y55w~[Xf}={03rXz(fCLu}0$|mk.=Rݖ@^s\O&g|[FHN՘日TnʧKjl}3ɹ'j x]iʸC=`ó/;n߻C` Vƴ0ִO9)񷮟VX;vEJhreeE߅WTUͷ}X`x? ZOE6QG>2\\;.ۙcڕ#;>_0R"o E@CO^z r6G+i=pɞ 8'S%)QbzØ`;} ?ئ ~bIS޵l{up^<̱O\ұ6xbZ1:n8z L"oZ_KrpGq?/Y]z@="zQ=~guBxo#ʁ3O'[yq,Rzc&פjM}odp?ع6YQ"t3M+˫fuW[١JVi0)[33483 h;\5tX*V($.YGɜ8*Ŷ$l:2lǫZW Jw}> y2K[I~`mE߃A1GiYzg:ږ$ sJ-5Brі=3(͓.8uͺ0`MܙտLrGڣOVft(Y'~#}\P6RY%;Xω{1r,2w}e092Gu^0iah1'3@M7FS|GOƞPlrg,Z.Lz\+z=&Qu.BOy߭RfN M!{ `eZm8*ca xnW_1wZg>A !TMp(4H4ilɏ^d{Mc߶vʌt^y%8B^˳i]hGvu߸T:sxm 0桳 inV^Ѩ ]:6y4Qw5/8#%py/j-OѕcXA+'`_8 }7. % ʦ2kc~zq.N9ţ 0xZ2iS}j*j7iI…ͣ/ѳPN<߭&f콥LQNM*bCQ>n:gK27 0V Jα=<{1\OomW_W!2@鷾%)I&x@z5?&! +1|>zR.?ԍ ԒYm<t":3vow,{eJVq0T^%3-<9݉ӷg]ȣCIT {+nvIx!EDaŽ!T}뢕8K=c?[j18!It$]/ZB{?+Y5ƅɋIpFt+('Fv_Ҧ2v:8mMI-0kv_.5_}T<)9ԧS\%NZuŴJF}zgy[Z*Y0:5hs" N$=zNZ]3Ao!jބNFJ=efiKYYq~… .|a1j,`y0ʳޯ<9` ` NTu¯i,.Q IDAT]"SX`+6o'C'x8+oso'`./O_x#I0:53[h!é۪/0 0)>B?>XOgvm&X0Q7 SQ4nj1*Ğn%[r{t/{ZZf:x;:Uu 2;U܈_?$rem5|oQ~Vʕ)ΟL ;NXz_B!Ոa(v x9]`֞ w p5eksR˓N'6&}kvs,~\id߂6/~)F8|,H.4W3UrYY>=.%ֱ[@n̘C}V"B*kAՅZj2,3$gkkgݞ1rl zEΒeyc;Gĭe9LL+* λ;<XēV2#N`qv;s9,(S;.~ $h L"0-J\$ wCE&N][C3Eu~ -&x !ۂ1!6o#6t)L}Ϛ -%09Oxz"@%JL-Oq}R *l\yC.(2K6zue"\q!3G`ȯj[px]<#Oj*LЍ,B:YR@F\UuCubXUXBj k1 1šJ7ؚVKGM,qTK%I~Ⱦ#Ѩ""5'f zR$oGXj!RUj qI777Mv@Ξ=۵kmi?|c{x8Uo9Ex1ZvkE1|F*)W oJ($Aӳm$ dsrc+|;9{C!ͭ?ߝ07Aa] u@وECh,&Ԫ-ڂjRcs7q!BS FtXkܸqRRR7jaZS\2o۪NEJwܮYaQ 4*8qUXMBfcVMGiFtEHhܿ՜ecxn_2ITؔD"R%5Xnp B!By;1,֎&M{Z0[[?Q&uzz*rrP vzUmЂ tB!YUQj&wq1XȬBUA 8J$H0B!B}EB!B!DB!ByQB!B!=JpB!B!G B!B!(A!B!%8!B!ڣ!B!B^{&5@e_9p)ÔTVdֲOƦ!B!BjkP<)hi ]P .y=,B!B[}W*c| 4]dg7Wjr|X!B!k$_kƟ7 *!B!BkਘiԾiB /HMƽSN6X1Z.O qYx!&޾T.E3߮Ms΍gs[]]lk҄B!B[OpE5AuŒ4Vv\C? sHO].5ڽG;; \ ~Aaf TKOnt4h]'F䱐}ؖU^_B!B!U 'O:88Vz#G/Id2"a-Ǻpi] C6~Z b?ЎQrINT,kݿhv Үۻvin/ F|?MǙQB!B!%G@@{Æ ϝ;}M6Un;ާqJ|s#m;>?|8ȗ]}<Ԛe5s3ܞ.\GQ0e]$BȲ` 4x_Qk>Ct6<0aNx>ʡvEHD5!B!TR$8Ə߻woÇ'Ny旴;1?- থfz06{RU?Jua,@lZ//=#7G}nD9%8!B!'Otssd7?**ٳ]v5vk ﬽;9O.י J,{'S@v"z6jjnQvi?7:t\^9!B!@#55QF:37nT5w`is6Njg1;ddٽY-cFoB!B!U >޽{%&r,(|fb/58'٧&/tX>HR̹i]ʄB!B*ߥollÇ5s8ɓnݺ_2#?ͤ~\NYM;IDzELFl]=UqvB!B!խf:ݴiĉ###>|ɓ 6TnSr|wpIonWˡ 7n)-t hybuscGYB!BHuab7o|̙d__J-qXyz:5qig{ F| )%_`[gt/9T.|‚Mso. i?{-$7=:r؁%B!BB5廊N7v̚Rs#m}pƨpۖtYoa8+<}܎< K8TGڈx½y Aϖ  صƬ}B!B!@UDTGV8]"2#YzC-oX^jEښ#9|yfq/XZTM: uuy4M;d`,B!B!cX4[HBD$Nγ\Izv ZwuWMGA!BțoW*B@"m:Z.Z/#B!B!pB!B!F B!B!(A!B!%8!B!ڣ!B!B^{ B!BkB!BytWł5!B!Tò~g 6}}].uJύx4lΝ!tP[s'WMa\PJn}4~?Mv7˗/]0Z$. !B!j{E7#Nq*gEUn4[uRӵ/$߻lJ;;ლ< @w-^w i NV–L2B=~˔ ۬Vϧ7]"6i]V|~ەo;Y5x_Q>Ct6<0aNx>ʡ6=AN?nk#cB!B!oW웻IM*>^'tI)xvvI;یY=KVoQ97L(oh/=*;Na`rh}4]ً8BdLi^zFn<=#CZ4>sڀ4>2 Q|ZP0:BB^Ђ5!B!o.^5 ﬽;9O^FX!a(Is`k~4\Τ#ϖe[Ε f~c0bH,ٜXNJ.NnYQw/m"nZ^:ʘi?7:t.P$K0S;ڠBGHȋ.!B!/ X )J$T0m^, @Tq&\8#bȎ,ȄV$ZGXԖh~[ _S@`5Kev.j)ZI&}UDH!B!=)!r+'y5]3Heb:H &.5@ɋ3wRi6snZ+}!DH!B!=^de3yQ;6SwQ|k+Զ0"_O"J*B|>" nl @t,[LFl]=I Fnc| !B!I5 YC8\K[@wBkv]jfnӨ_0BB!B!m%81`<?X=\0Es"O6L&Mz1ߛm yiNNNNnrĪi7Ks]%'_ _>Uy f9mK~ ֯ߧ:*!!B!6{鯨TBnmtȬG0-hqsթ)` 0 ]rnpGM-}/y IDATcޙnϱK;xn_:##$B!BZ [:*=U50[[o*(rrP vVZ} !>cyߥ0''W.gL,,tƻy @ UΈwi"g4mngiSfiRU]]UQB!fEK5F 8J$ɔ3D\q1HjHjU\$u[vpkP> [e[w,!!B!6{o$HX4hdv5Q[Fc#B!B^W#WwY* Cd_Sԭ!B!R)ਐ5znfUE{S22H!kQEۦU0-!B!֢GDMet/B!B!%(A);xेK6'1qk;r[7q~ܾ{ν ۩_>s̩yҧ{#m>s<$݇ p1x8@+"Yƒw\zALL::uѫ–&ByuB [_D!w[oo6t` %=7͜-y`/ Kķv9Kwu-&xTEc@no,w.B!4Ll5tYލ^+n8{̼Sy#D.49wo嗚%O<}4[xh%'4 -\uK,Mz̙3.^ҥAvٔs1i|OB!O.:{oSz ol/G=[LnM)']cVŻƟ>{HHصD ]?гV5Ɠ˥WݿK}{ָ֝S C>zqf[[[m;vjVGM}xId.ߛ4vDRTz=ʭ|}ֳʲa/N+'"r|j'icվo~mEꖈ b.aT.N 2gR2PCE/[F)*GB!UP\<%]h>uYi{;T*Un4#?1۽ƾ%o 뺰^0%M9jO0,O£7܌c#Ts΍Tw+P|@;: u3Rf םRM֞}ߑ6[7#SZ\nUR=2$Uv!MȫzGPR˴;]4 Y.$ =5zEqçuz\澚s,uZs.4 5 _,N 24H&D?$$ Qgx`M=bB1 Q ^v Q}2ЪcG1dlJFdBjYjԎY#v]Z_*r ~@e Տ@6N5yQ9KqSt;hM@lvqsO;VL(r u/ҬP+";S}Yu%T9w {|Ў{.~HZy6}oٱ9݈זt[Yu/ыn?z#Sh3[}~F$^z==$'a7B֙{.灵l%pn񓷈@TDCxލ4@N_&ֶŽ4+֙>W[۾~6,2 {azֽu=;Ԣ6{˶5]8}2ڞ˜V_a:nǾ+k 5sݷHȋKIJw::7ha?ŅGLs8)b>ly6-e>Y%[Φjgd}xo=F9:E`Զ0xM*Qd`?pތ.nJܢr!Bȫ&{JO?98o;g&*6xƉ3Ot~_n8rD׮|qMWBv+Cb9V.*kG;~ݸKk>p^2/rEiƒ8|3lT? c;4dd|0 QE {@4k#BYnc[B:>͌8'o<_2 S~l&P]3EX!Onq;W s㕬8`_1 I /R{g oEi.Ny:-pdD?;\C>4G}kM`RC=5>s;/QJ>J53\_7z'T(B(n ~%W)񠾾`t @TG'-߼7}(A!7F 1b{ `ӶkFԊsFz *bʔST<^u*QA _/y:75vvI FPu=sT x!xGFé˶m* w/g̝ fAګf7f̘1cƌn+;[FaWAAAAA?*俟* nAF/8ۤS31N$ά|H.ɦ)xű؋NtWSKf\UQXw1<'}^KoK~b 3HEc?N^J+@ ח+:٘SrIxg: {7(97&/a4.@_rEeht @pըSN:uD m5K +-O!ڪ=:/N6OJ@UAlEI"CbOwNSh c[6ֵS#{rOB.No#.O6eK]z|gnŦ"(愄P2%x~367FMb+M+?C&vzRt<7XDNC MG*c2KFFJεFޡ,~qx+V_WP߅R)w{\}u-'+4_7kB!WGx <o'5 \5(yW {3z>} ."/2h1h%8qu($>w.+;s,&\ÂWO5ԅƱ~ JY̲Tu7.×\ &&ɝ wn,=] DKq&8/c襇a V?ojtiJv}%TJvz"[m[T*#+h8{-(<=ۅ)#F!J'R&\P$Cρu\)f7}^+G:gˁ2Խ ($Aӳm$w`wf6eP[ZЂ=e/  ( -((,e){/ J6Mۤ<?>%^|N%"zkL57GuC:w ^=^GpY^b󋉻o(=""/npyvK.w~['^ᄐo}w2qw,2)?hS';S3Q*bê;Q:{!*~{ve?3w#4)MmJD_>,RwVMQjq ]Bo-xiM>E[;_xowm|}b}G*̐G'rKDB .gA5퇕b>xQsDA*2.b~`7[Smb餉>dv8NHNe{ʨ=y˺y&N:l?2|/c7}I]W*fSi0󱃷SpLb5xg撴T;JR7u/ɿ0从_]'9\w򒖎GsI5oqz+a,'s.U ;^$ QŴmv <ӀruzI""=/YZD_K3c($yI8z;T3Oq"RXϫ;g?yj3Z+"٢S?3dЎ3:s^\c\O߳|N( zO !"-v?; 9i8-?TụvS/?ݭ_=8A]MxvM.p1b]!onl|2)i1O&sUmv7-޷7v NF&_B}J.-^u7`iVa4!en[гzFDn4nR%ԯȊ.kt^ɘp_ p.~3n$(> ju/]3"n5Ī+PPC-WUCm3'% `[TZUNZ5˓;v}w>U_}%&&26d2Xu>.~_GEDZ_IG {U+UHc5[ 'N7n_XVY8l}& ]Aew ٞ'/ᔖ=b .{ZѦ>Gs 8A xΕ{̉kydhM' rL֤NsuPz}6kb@6>9BrtsP\8lW/ٷw˼>iG RpO`QbU9io{{%q3inRw, nOz$ݒJN7or)q\Q$h o;zl^gmzQ]l-yi@;. )S색wO8/R[pT'ܞC[(>*7j1`%;-LnȋC6o'a}qˏ`w^kg}T-AE7imOij# 퇈'Ф/$j辈w"rLPdF,ӳbv唄:SUpc-0S|5"RٜEؿn[Y%ZDT>oaqN5+)SYӟY3 m=ޘa"U 󇆈b<)I5h}fo>SKL}FO12?&2+"j_aߌǜmp' ?Y,d4GY.^^9V$ [+ _/[[p>& 'p#G(P<x@8p#G(P<x@8p#G(P<x@8pxxV]@@@g]W| aok4޾0pzceN DÒС!""Qfx@DtP#@mF^3{bD$ΫD{{1uKhU6֫-g ꏤ]lY-O}z_Xؕ([L*Rvڶ\$u73uбJ%"~/֨[NjiL}]ap|!4nٸq *0921| IDATn0IoRuhܸic8gjdnj>:xF,ҳSbF"֒w/YOh5d9իNR/-j|vF{z!rCVS]7>Za8zjyUg|KM/G`=&Wf8zj #xߏWnhxY9ӯKDNjr^È7|הYNSω};o'J<[~o?+j|lȯqyFOli5ر@]m߈Kأ\1/'jYEE׋![4h=b \s)dМr&uJ󶋈95{hٜuk""m $*|IohDdɈGǯ^yW7|lM&ti^|Թߦ8v7Ì+l.Μr:)kпVo]8nAJԵuڣSޘzΖ--~@*СYYruQ[<J:4ӝgVH!D$߽9p9M%"w ы'k<ڸi$oܺEMVYҠ%SzD#"W*}zV=*nԾۮiFsT'4.ZSHF4T$q~m:5cyy݈`ąs5uB^}[h/JY1|s밬wHz}[6Hgw'K |kvPTZFe=1%5-xfߩ}_8s=^DTD*_o"u]-@bq+h%VgLJ]S/+KEa{ә/ݸQsՋ;6.6wȥ` M=ߪ; -~ڇZEawzgט"/ZbWI@. h;z[?[M)W{nU{!=fr뿘6ThÄxuȨ򻧞;cwuw/MD*6ݫW`ÚxܲQI®6Ļt%mYouGDny떩iyvnq3\? Gp,R/,n&)X+YxuӼSrlx\'މϡ05O\ {">A&d u5xaqm~|;:9O&DўowM_ۺ/#.E"rԷkzu}^rwo[W#s7_byS=d]r'V#U?urjZIלKMZLD%g\fo6wN}狈Le#:/i\tZJR#ջLTK5)FH'+/ߞV9"λXl|>tၳ$Ktn4Ϩ=s?3Оuxx~IȩUW >9~~u/lq9d ED$2;b9*t,X׮|K'_[l4/(:u3l;53 7~yӃ$t@(T"=wt !_~F@y褴ԸtB:`%ཪ:՚SD6[6_SbiG+O8^j;ߺQQfY] o4E]0ՒwfKjN{:\jdTߺvnJ?|C#׾%޷Λkn0w҅}^Łn49ں eG.e}׆-ڷ+ӣgo\ ޸umڎR;s /W.W)P-"Ү]٭z˷qhg8W=K"wղϥ^__Esjן;E.׷rʙ/4qW%:k߮| ٺkۢLS"{VDMZ= sWf+x}qx xi淾'6>4D 3aŋ\7ĤZz )-j_zpzƈ(I5G=M7Dd yqࠚ'"rRD鈈Xt/{WЈX؍fjø?x@DTMwgWF6^X ,gB}٣ O%53xBga5Ul',m8n]S)}j\7"x sV9;hM;Q.]7>15. >ϭh51؞v>% s"RvҹM{Oؚ~!Y)1 ][We6;xSƌ8 El}Jߵhn未dZr}H7kyŧp}],"ן7N'a\@T؆mΜgּ$^*[:I{$$Yٮm?ZQet DD_ lj}Wn+ٰ × O\dHwvl/%ܼidt&5H8[[$]gHcJjrĜvM-#f 8 ƣ *2^PF+cq(ͩ㣯lv2Zm{/ᔖ=b .{4KxpzW| yXyxcW]f_К)+ VF!R#"o4'ni?UڔH=vVa_J>7*;l (g6^l{#\-Łe q n{՟7.'JwT!@S$ %FT7c5y`;CCDpftόذ)w-"9a=m2D8wUYjdʶ\Ʃ`뤲dZ? *m09^2K2eRkDEZ9iK"2R־X-;4=;e\N\ԸtB_"u^eh4o b،DDt󠲩vT*ɗ(. ߮fN˥)9IU톉ڎ-".rbL,{y6w@OhgP=&fo3J}[ڽ6d7664;/O)zh"";FǓoS8h;IޞwI )|pmfJHv {'{xw^S2X>ţ"TMO~.6G4wߚrNDίm}UT"[hoZ&Lm <4&kHfCǖM8,o~76?%-8\wfkLJwOK'K%"Qg>p~UtkATbj|x%|m ^9\1jI)j]*W0?i\:%rSR.q-?Z‘y,("_#__|դ%47P^+Gܦ=Q'? {?: )~DbED+웵7Z3\ރkTX:KLLXD ̨bX*b+Ն3 m'XЉŒrmEDL{&J:^/x;~J%&澈NDkMKLd!Xu|ED$GxjK>OM,QĽnF<^~^%+%{9ƎMUyhބk+>Mnԗz ߳)T/opP,%j߹k^QηatAAY[YVDD:dj>:d CTp#G(P<x@8p#+ƿn[LF/+m?mRęLw M&sV_0gn1FJ>&Sld=9^/9^[fj z;j>:o}{9)^ZAnFŔ9d{*&KzzC{lt{5L'Mw+e~NTE7׼%nA9Ԍ=k6:)2n@q=߲'/U#|sӠޭŹBjjx :}GKJ&"??6qL׺E\H7DDeΕbqax)F FWig5sGx}~>Y׮]#$޷wʼn߻o:8; :HdփއVpyUoI>!lῼ<۲}zrW {AÁ F_kZ?_Y Irwqsu_ H^ҩ2K9F-mեC8vk7D|bSED-z؜7ζ:`ЃYվ%Թ'"g7ԯmZ jн塗?E"g/}>z_D|s碉mVU['s-zWMp jxD ]ݧH@:u_;j|sQU*m{& CڴQU*㳍g3씡+ݟO:Do}fzmmjۆq|&Y]{ŗDb%a4T=DJPϫ/t֑?'"'EFoɫkEܪ?rȑGDDS9I\W{P'em7'wOҽn*i+rriL޶`=IDATK|ww?s~ !"j*۫ |ir6~ZtgX8"r'{"vemzw~ tfUr!l䞻Nn9e\kFtSq?{gIӻ"^Mo~bKؖg"_IjыN'b _ͷٸq_{׉#NU3\mxbk]ڦ)>v_^x>yj\ o|W:! bbT""47ky/2qֽwVi^-ՔjoC- gaL&HS{l}~hcߜuҽk'op *gk-U n\nst#FKMmn?o%GRwȖn|Cy YDPĽK&e7<'zq|}NĮFN6~:ad}|sρDyԾ Pͱ#ќiP}ëww""~`'=/ vPux㋇\>*'&HDc7?:YQrm}I{3 ߇[c/v-_c}K,̓4z:Tny)J%wkXͶIjS{VkX,[P =2MG7{\OQWl?+ɽ6oyqEdLa-Ygy@Uط;+W5=}8xW; D7ZHUC?~?M%"YK4.\UT|pnwN?@D&,hb8LxߞG]>MDƯ~7ϊ8ȢxF+o4v:~N>gSmV#}Ik?WOh 6݊ѼؤS@lj|-__1R4W ߈=Ji{8ۋxPuB;("Ư͏G8""b 7u{ &R)q܂Z?Y|ww_Wk"Ob >-$t~zѩb>&8x7:qd4I7LPC#R˞A[1,XFD$ww /:E+ջvbAuI?"*SL?zҝ;,ѱTZ =a;֭yG:̗O P}:"#C$cmt)N.@?< y/H++sߦaImboO b#ach0Ѩ_:v6z0fGVS}`;`} ;X'RXG",,bX? /67=_ 9#᡻wֆ3^f [ ց8m`pݖﺐUdGZ&i~19eft0n1<PWLa>$9Y#G M`ƠX`af-0āh$9x%pY a׈DGO_$E n/?/Klw1{2i?USIfVfr0JEi4QbAqIJBep/?>k/HTs3GK;ߎOϷC7vǁ""c):HIPdd3YѢͯbyl.>GG&z)\b-9N> ae<@,:X; <}0$}`?8A58 y ``L9:AX"C/$Cr @ yBP8@P!T@%!4Bߡ5AD0!i BaC#Q$D&"QE4#݈q H$RCZ"]hd*2YE !!!Ǒȟ( $a?5F٣|PQTTՀjC&P -4- -VCt}}}=^`0,*Fcqc1y*Lf3bٱX % –aOSUNj\p \ )nNCQI9JSGs :OkR| ~ H  kB!PJHO $2ňzD7b1XO"". jӺ6Ҿ]#IљҥUе=BOC/DCAD_B <$L#yRINA!!C? #Qрї14c/$I }u;)& Ʉ))e]adfcamn^!a͑q<':gg+k. W2iG\?y#˸{yXxyyyny5yxyo~0St(RJeϘ/1:?ߞ?@@ >&B4B*BB' S P٨&$juLVDK$JV(FTE4DJtP !((V!D!$$^%>$P$JHK6INHHKeHK}v.'%(*S'3*(k*!{M\syZyC4o ~ '^*-)(n*)+E+(* *{*W*0XWEꪦP֪U]R=D..]u&554j45)4ǵjk hj՞ i+{UwEOM/EKo؀ޠ!aa႑QQ1̸xĄǤdT4ŴόhfkVn\<ԢblIJ*5ʺ>{d=ltڍڋ8;94:8;:;I;8=tprp8ujzuM-m؝׽߃#=^{.{==ynxYzz6^93[;W7_?P0X8T-8:x%2>d;1B.331<$/'boPxdVxZh1P{LG,Lʼn׌_MpHaoGb $}{ۿo"E'&JNIHLJ7Jo؏ C&0ckܙ陓6eeEgR?T~|Xpߜ2%y>y)=ѓ0 h42&NYSs9_PR}"xyiG`ٱ**WTV==}:zTЩ5F5mµ%1OswFLYg7lq;ڄhkmvk<E˅܋bOPyPa#GW>VzDIǠൡ]Cj=~s_~14l?rmd˙W>>yCzSm;wƕoNOs/Wj}0-E%V?.-䬲6Tyoqmz=aQ)ymlkl;l{;+@z;ñ x1şbG` `^ .)ҁ|3G n! -jm~IIJb{q44Kb?@fēQLhf, =9G g.eQ^E/F`ZH^8@MOĆt+9>yӊʜ*vyj6ZGb4 _cLM=Z~clMg#`jgaxЩ̹ۄ=O[G?, 90/[\ؼ$p9JՁNk}o |;+;'wo_ܻew~v1 ӡg]/.|Uј177oގ;4<>?>xRvrCTiٌO>ΝLyK ߥ{:mnoOv 4#O Y6ZFFl,kezD/aoF`"G#ћaI^d̼ºNpsqGQ3 X f ݥ"EESŗ$eO7,v޷%T+Wc; :3ݽq+v[|];N'?px(q'>M{9 7M|hs/ TB>ACEO;D'MO@oKA%e5S4'f?8dy:ώ`/i+[{'~$)BŒƒBQwb QZR?ʾ{#?0أtMJjZ]424Ӵ+tt^/dLLk泴Jnyjceo0*v$x{k=wsEhGG''zD59b߭Tb{zgÁլl\ռ|kE%'yOWFTT8橛?Ps.9q퓝+oqwYݓѡ"5FO54<"e镔?m7h76g=F8x C1!шsH Y|DPhA8nB#L/VaqT}wFe8#&jbZ3stpϐHiFkd)&nRfv*VA֋lZlC~[圪\i܂܏yxEx_Qrt/ 5 R婛"wEyoK I^,}Il\)rB#ʇT&ūG  ҊN=wU`cL5176+3X䱲>`b{.A‘8Kk[{Gj6gޫ~y!FaiEbTMJVr5u9]sLY &8}W>}lPxDOdJrՑjS-u3 :MZ/^molwVB{BODDQ_PG_QG߈sh Ҭǖ")+W֤wm(ob7Zs Y[""1T@#Psh":9Ec#} C4x_0G"ޢդ{MBREap2\d4\Ȣ򙵆͕}3K#Oo _00JʼnI\R2 F1JuUj꡻4iqk>g2p5\=z<㼕}I~kA+!%a#e.Ǩv'&J''r>M:f΃Pš'G]+9_9a\zlB2jZZg54^lknop UW&MvvzsDWN^;7=5l>{<eҫQ7oߌLorX3=9v.u/_/-cQzҙN?~.(\5\}Sgᵉu ō+sΛ7"osnmپ1?BD]oY^y6 ů@Է¿|#2p^., pHYs   IDATxkeu_{}u~O{3 @HDRHJ؉#+%?\q>_TRXˑD(`A`0`ݷ}+vE@`(Pn=}9=Uk-ކ    }Ǔ$iiQ{{6DQ}ԫAAAAx֣cccJ$IfVJ(VKAAAAGJL( zlzһc"p   p? "@D̬S x(bP?ZkǓ&''EAAA>ԉg5 bV[!suYxaԩ>8AAAAPr PP># 7DAAA~$q_`&PΡ;D!DQC=\qvssSAAAAG1o ]MwtPl Ae5n"p   pD9cL=5 cF/H   ptO{J|{/|cXRjAAAAޗg_R((@[(QѝCAAAq?JyBBo@S|DCAAAXb }*{=t)ΆR"   K^1,,(V1#,` ^ {p{pAAAAnKA`Z ᦱq ;t =8]=    Ib EKMtg;   _meR K1Z0p0HAAAB<F"5Hcv7Hyx?u㇫؍pAAAá pX8Z@ LJ8ޏG|uDAAAAdQd/MFA!Ej" 5Z)EZO>,r]\06طw=X@ZkURbAAAi |}RP<O RifĚ7OzN_qc r/<|ڙKؤHF=350q_yv_ {3}0UOOW.*X\   p0kE`t`G~;Ӥj:^=N}lD (@@7>k*W8?~+o\Og>~f6heq%.uFm]<=ٸ<[^gNc6CYQݾ❇pCw.v=Ggܥ{yOO|LAA~z+r1Ĉ 48Q7._^|'OyA\VP e  !0؋O) 0Wʂ,` 0`em$I-+f항mbek6P"e}f@W*7op/}<*޼g7N%.USpeݾיeZk;gewDwd,72Ǟp }'g*tjȝeO7{ȖĚ{#  pRؤlp8U&E+Ǡl~8\fnOk~ `ើ*  B?0]] ̍4q `b0.!]pazꭍs+:ѭj Z@Yb[/og?й$?K;N I~'o~et&.7cɦi[at6\B%h/- s΅*?[ ZI&`vƓv7k'"dK8(\AشTG>AAa@ђMHҝ>z%iO̅<9A{hH J9LC/Q[ %-' À5ĚlULD "E5) *I.,u (1\XEbY3GxQ6,W=i_Zk #K~ϴ?,~L/pt @IdgK:W޹0fNӴSPJT ާs1IPdNU9{{dÞSHDZp٬;8'ƘzDZֺP( \.W. @Ez^ZZrl.'SqdRKݷnWvVjfj|?e>'y8)(ǯnlE>%LZ{Z{nλTAAAol8NaAHM{-dh%pYG&p$I>ݸO#"CxN`]+CJm3MiLڋ0\Aݕr؂y6jL l=eX<8ز"?MA^H{pro7F.3ziu"-bB$ͥ'/ll_ұcmyG,s ^nZݹ\.63O{nni) 8&"*;>p J6XDZiRj֭[Bȑ#|>|>NGIf{NhDQEQZڪjbqhhH))a:pTrY*[,٩eV\ tyq+njZ,&''PZ5VNH I4J$Ţ8#Lvrw 7u0^P=OY(jEQjiU*A"'0 PAA{I4A\&נ"`FG%pbKk_W_{naq!)X4˘OrQhB@ ,lcbX1[&{E?Ѝ^c "v 6-^ e"Ԭ7ZzKgк0- #|@y>5J#NQv$a,MVj`{{^Zj5fr؝U2NFq҆Kq$rXժj^VF#NAk绺rX$J^__w m5 jTV/n۞y .IC/A*kyKKK[[[Ih8vGҬ&388ei4J0 ih4׫vٌ8;RP( 777y]]]GQ3 ~Yx2׆h6΂]0(w iM8j7vlFiZۮ6u*r9'cPAA{ [ )(͛}:BKwoܸqJq38r\X,훘s+Wr8"Y w4s(rj {`0 J9 +.Ljŷ0 ]JVvoܸq֭ZTήJ)g?p}1nݺzJEJ)WNY9\=ȏp@OOϾ}|߯T*gϞrʵkVWWstvZn]ՆyD__ѣG<8;;WV766n ;էh/===J)i=L".\o28] B033o߾A7o޼p… 766\lZ n*pAww}8pȑG.---//J%cL܏zqYeJiZz}h`䁱rХZ֭oTzj47t۲\z Ow>_zx<7;8[̏n[˷Ɯ}ZeHNu\=tիW׻{Y$S72RՌ1Gr…SNˍFMrB93[m>l4JڵkgΜ?qıc~ᙙ ._tL#pۥ777_{^zidd^ 277 ]]]c+:ƦfM7jlegpӧO_|yuuaq-HbNv$Inݺx…'O=Ï?\RV(r*.YYjj̔V#T(&||a+X~V5"$fspZ/>>9H#c@.^QaՕݜ?d   |TX_&Wi(Spƽ8aq?o| <R`B1m8fֆ8%J`bXR7 E,PD%`8OuX0=VomLlj҄@DL`h01% Ѐ!Ā JHGf4[?ַ IS'J%8|hN4k4v^j7o8qX,~x'GFF.^066>h=Fpei#G %Ir^z[[[.wU0t*>I|.ɓ'/\ꫯ>䓟g}ʕ"MdG zzzi\v6gufWnT#Gh^{W^yQ):l "rG p[c\?s?pv8O IDAT" ;Lp7tdaWW{yH[n5z 2Q_ݬ)Ѿmn=|W^MmZAAA0kE`t` Dk((߇8Y0,_|qaaӟg?IիWǕRQoeYo?$I\e 3ol 蟼~ߝu4۰V4"32MXD1j~u>zo=pt>vsR XܽEC  p!UJT<< =v#eeQ?W][F!"(n0y̎8 dƲR  c0)b(Va; HQD*$:PkѾ bxֺǿ̰  sJ[Lla-fɞHb6}/l@烀$bt~㥉e`?~gzgΙ`O{G7{(\Fl6oܸkksss>ufWW\=6Iu)Z=::zС Μ9ϾjT* =Ǯ}3';ͤ"}ߥKKKۏ=ܜRڵkJRڈxY$3??r3S}< rٕ8qbmmZ~ _ :'fpD9㵵5fgFFϬK'ylԐP*`ޑ0Uap5j(@;}Eb^;66&&&ΟV}}}lyq  GKJ4SJ#B =8(W^; < hA)љ(&xPY2 p!HA0 H R^ƨr\,YY< FoJ -NHwRH)4`=Uc%`!كcbRj F (?|6tf@1i|r|>}|>_*&''\$Iڃ_ + 4 ]:p).^?;u<!C'u['H@bb┡-Lƒ%bP bKfj*5Ё_+ϋXv)[0+D;YmkZm(i(L #dJڊJi3)&D JIJNF _>8LwRh1*\zO>3n Ə=yw(,pY&iqcG=}j:qtAgwNh4'N_8+JPL+fy@# ^|o~.]r;|p+p)nh,#}p)h\.uѧA8i<禦jZR!v3K3--^\;wɔ;gq >z衩~{JBoKV3 Fl UEhn6L߻{/e7iƍQaEQ6/048I?:v|oUr}X RP ̈&;|b_yӿnW*==NdCAA>jR`YnB" h 6$"_~[G7[hg>;5u_)a0X3l D3h@aHJNyMc `d^!7kV]M8l & 0+!6PVD(Rh%"`}e1~`C U.ݖ;^t(zzz\ֹ=Gլ,N2yI ԩS=#<2::zFtl1θ[nx駟tR.uv MS_*bTv@[VZuv|w`WgQJ Vׯ?b'z+АD\p/f&=죬fGl%M=j(Z]]=xLxlnnAjX:hpkm>%˹pcVڦ8WN&[\.h4|+lZׯ_wJWR#x<;u`rbbW+0ݰkhc' E(=h `S$)EjjLhjgRB)tA C?xDݪT*\];O  +r1Ĉ 4 .u?y>B)x|!fupR%ր ÕX)Y&K6,`(b D(z"0yU[F)[dY(uݹոZ̈V;*`0sX5oe, bXO.X-<e>|t/)(*Ç&kYt\.裏fFCwW$IܼuښK:LzpQ.VoѣG]h4T4:YMZW*4M'&&Ϝ93\|%ٱ2_ˇ{{{<811144T.KR2|yyڵk.]qFZUJ,j$M|>yޥK}فe 3ӌ팶a ? LNiuƖ4M=ϋh}}}jj~_l6R) CLqb\T twwaWE[[[ׯ_|իWۃi@4u`777_}}s### [[["jc7:z4zӧ2P"I0c 9JsX"NԎ](Q Z~t_MqBd5gJ%]   Y6)[($46NIJEktݱ Xύ z")a6cS~b *&ef`]P&4TACCJ| f|{#4VYCk!I)s)h+jM8d@3[fXYJI( 6k=0M}/oc>8SnEbgsp|t&XIT*~7߼y1P(t{dvbիW+JooowwiU4d9mEi͍?W^ ðP(kih6Q:tرcccc.Qka?/^wd#dJ՗^zirrsss/_nZNqD.ިj)̍+XDwZ Z D88w|rf+CC2.z¿znBְfG+<ڗN]:;64oZ;ܒ\R^5AAA>4{iPdE(]w8|߻ys./BkJHcCpbaN ڂW(RHYyL1uC01Q =>蛑b!(MY1[f-ki*xqjy]e%FH3 aÂbRMc(b&v) 6PLl-1 x1sٱ6lZ@!o^>Ǐ2Niy(JR)MS7bcO]e5}>|xttk,u;qiPoooZͦT6"ᩩV/[J2Ev u)q&{'1j(666677k~XwJx^zԩSQrlMV;qġC>O>|ĉ[[[Y<3u#I\.wO~?tiBVnOMMϻB!ˡQq秦xO}SB!.zŖ0/ .wO:UV}8Igkkʕ+訫Q®9&۹\nUl@+x ;&i0='ώIڭV¤l >of;8ý9\]Bd,` tl篿07={dbvfʵkNqb\UD4AAA`69kKH-,h_7=;_]9hA`Rv7i,(,QMvNh)n1@~$鱐'C[=C&{F>abkIA07-Lcè|A" $9 \ L xYB &H!A_ש;̺1|\.禽fHgΩ]]]x'{{{_x}75lۛ[[[q+p2z=11ĉNrN}}ff;緶.\:b:Ţ;Dպv;###sss˿g}͛xռdr[o a8???884=ELQ4z ŇUX:ky̌ 9h;!%`(r?i/]T՘9 Cųhlll\t7777==_._~MdA: ܹs333?x__֖Ɖ~ǽ|t|՛M4Z(`%?>C_xW[%Z[,gfffуc]ӯ (t%lpe7L ܸqc}}}``on  Gg2R(Po0,RN|O?ZRG.O2r/ 4Hq!f0b0CY&fqCN,8%E1[^FuzC>dV(c*EAGJ+*z"*Z$eشU2 0̮%Ǝl‡Z*e26jngZXiE~gptpdcL^o4cccƘ0 ]=GvV/nnn-..nmmmoo[k0tRH{hcRYLq=666><88rRfstvH<^c/;vj={v}}hE1|w4JyԩOӾ?7np_[q]0/]tܹzj||ܹ]\O1?D* 3gӃzt*>jj@S(\T499>{ӧ8v:EY:I$I#8::Zժժӡou'&ɫKX etHR0C)0 `=v کoߪTZQuϵֶzuJu{OO(m,!)0 o:3ý[w sWP  G6`q c RFjk%0E+pck/~<+7+FbPJH@Y  VT+3L- buk=i? $-z$]-$i@2D)!RL òE )afIU)Ӿn)|2,߼o|h{%*nZ׸]p2A>w;/]G_~qgw vBD.v p1FkZ~۷Z{̙7o^J4M❽• LNN~K_:vX8w|wwP數] R$VF(7YVݱvJr…G}tddR\tkϴ"pdkl6!ixwfkfLt{{{zzzbb^k nLlU7=z߿>ٳWԓ4]]]=sz\!Y':ť١^'$eq[6<4/oo\1'pXV/2i|⭳KKK#}MFKRO2ƓxK_*S*JNb"X M V}~ag{u3 DAAhk]Yz9}z"==ӣQC=ɂb0C0HqHq` 8?DAqFd[a~DIKtO{nYdzWݪ:Z":ttG]xRzafiJ(Vx8 f/] IDATdnoXQJ(k#ڽ/>箭7mb9,U`ѨT+ΜS@ (P@6ذ8LH m,CpHQ^E Bvs%(Y(OqUȂ"%X84PDeyR_NG%Yp`-+2#9#m\@zCJ{lS lʚYLw<22[FȜ| `27锬ecՈ sޞ@aEv<杝???gn<_ t G$!D -Rv={vffRH r!_ D)u"Z[[AZJU,3+W^~e"uܜyv'L)I{Νg~_w廮Whtk׮U*>j5K8{rtןp(~FnwyRѐYij3==k]z5I7x9s@Zkq͐!gh劬 wܹqFϟ777}~"v) C}BȋZhX9aZCѸ"Ƣ^W/ʹ:{;niۭVK)$?|Od݅Vo.~]X HҖE@8z7TJ,Q/ (P@ *e4U<#xX&j>Ty|EADkko:))9`F A Df (&:&=SL!&J-abF \3I7R6/G+l-&RcSf2,QBbpP Ƃ%ͰL S(di*Z-?QCo8R4, <-+߽?|t"p5-N0>}foˉ1ݯZkq+HVmwwwzzl[nI…c EoH+l6677xgժ \WpH,q_jil6Ϝ9sʕ~ODNPtz]_.YgYja>5EA\Z][[}v$z=)Z՗_~s֭e= @9y(Op8f˾G:HuߤA-l`Ŭ@`Xb,Ofۓ.)!RJd 0i`hX@P|! ѬQA}h(;wse'Ϲ EB)Otӕv^?R)U*ZIXyJ H~{9B=E^xҥK|(&''Eڼ8%99biZo߾u֗W;󎬉 íNsB_km5n>'sr٦<7ȹܹsn߾}xx(|Dx:"F%_lP岈8TWE zVWWWWW׍1ot=>>^XXhGGGnLIY!T"X믌OͶ<poرWl2Jh!]mVQ pf#Q4_ ʁ?ۅK@ (P@-HqP"l W\yܴƢT$tÇCk(Z%۳% `x_[kHLlA$٢ ؂Y"4dXX6<_mphHd ;$ n[RDt:Ĕ; kEj ̆aa,:0k = UC 3je*8 _+rwg~;Evq~E}u +H _pC_ |܆3"CEfsnn.MC; C˗%Zb0,//#Kp٨yD;I -Wf)7O=ɯ#8G~b'd. '+pqDtpp0==-}s޽+Zw S=˗hsss4MNN(9891P9I~t׿yi͓E( RsssЍ)RFAi{p7a&RAk3텪? (I%U<56p r!b3It(=0?.%Z"OnEfֹBQ@ (P @`H hο~ "#P hԢQ-̻f[p4'U)O)PLLR# S7]eHX )uc@~J(JA%DxV%%y!3;eh`lg%`- ;7߿^,ˏTO&8\l.'GZT9h" jHYy4n8t: Y={vrrr0FFgIdQU*sz>H<IH؍"eyh#`p||<99|ʕ+B + frr@^JpQ -OYG$U>JT*;r… VEQnJ"뙟vM;FQh$DR9ג_O0"wwPc/I^Q 4"HRTKsfxەmR¶( 0%Mcd BB@ (P@D*ECMpRDQ|[hA0&/*n=XRiO@yiO)eJRI+4&bE"(1մ^&DNl4qJd @ 0L0΄$z\a08bfbVleì` kckSd6X$&5uk'=30QQ(MQ ,ypmW"2=~u<קƟ$zZ v|uU"2G peeիJ?p}}]b<(VVVt͝V@ (P@g0 XVDUIdgƧ!8NWRiEaX:0''RYi7ZhoN{G &C%eeRP DDLDFqB,Xb&bRPHRgmU,5C,D13 C㔏XZ-̂e+g2[Zc 3[Qw_JP{M+e߫IIR¸r+v7ήz=IgڃR=7[!(-BS{#bi"1Jg,S)0A%bkzz'O %}e@%&c Jj2R)((1R)clİv@r͔}kښlkG8E$߼q+zp<'•a&?Ap7eZvʕ8E1?ƍwIDjl)#1Jʕ+K/o<2iQ*~='IǡVm":::Oz^ rκ8mBpr;cȺOOH7&>IxY~EQ\P$0Dv8>1*60#*g\:q ǜf.+OJEPu" -"אָVYRzxӕBۣh̨\ H^LH) |?;?=n>R "s?4KD0dW (P@!6)[($<46NI&8k: `DPQ9F&p/ Hg~)Lv`€PJYXAGPi E V!I`SHi=Is-Y$`ְV*x*9Jo988TWqCF}fS5; Ks33bqq/^l;wh'&&k˶@Ze(D!aC,Jds郼 *o r y.ñ wJݻ 9| ]vmnnN$c)d=]*J~>J)+E'8q,JnZBcq发ma"T**y6Ȉ$"¿1($,V)%b٥/^|_{bxZ!M ߃etC=\:ƍie1Sh (P@ (lY 40 1me3 8\ٶsOh,Up䅒@e(B(,$ -Sjm{`A̔)8Wf,Ls1 ,4HgfUDR=$`IJ#EJA)h (wî\ v#5x$@pH&rO߼BNժHzU6͋/nnnv:fh4*7Uw:'TR] Xs;qr8g9Mp#*O7[-I<9d$kƍ&9*7ɇh+G$JtgggG92(D!~DEwq7xg݈( `0;=+/{gwZ Z#M@)X"Iq~_zrt F (P@h60 e&R $Y*h(ʤr >CC%Z12unRFHөTF2#H#ŀj{G)}3}?K@IW_Xn|#oua4(z@<s8 T A R >mnaȈOGV=D$!zn/,,ͽڲj IDATk7nܐ';CNCxʹETzOy⑯Og^8G"".ZL$k(0lt$oq,ԉ:x8vy4UU7m+ ~~dp^F0hIlllT [7{wPƌȾB#6n_ K7w$|3T (P@` h\p F۠"`F 3"8/voxS(c: Q^h~R{P *~n+g`;!8P!3 -l!l^A MVsVmQao _:݋Ɉ(43#=Ɗl&.Tٍ})L 7\*ӶCʪqi0ov+xy;6G.[NYEQ=88\__p˗*Q\PIgq%pv/ Gr<ȳ (#_Q;? 䨙ȱ nyJ$҄Eey r"y(upwbw88[oEQ^o⓯"V+#Z!$]E/aWK~w;Bm !}pQWձqU:&hGp-of},[vM 'kEOź*G^Eӊ؉|3Ip .Ih4 (^Y^;G[7~{ױsZ @"P+s_4Y۽ݨׄYJ`7 (P@  Uh%xGL=|g@pH ;]$@))(sgCoHES7]^V+q U 9G6 ˫+FDaS,$ WkTkasg~}o'A1&'D@( ٦ GJS3_NEb`yɆ <A+oN+8p#+ԴI ~E.; ΝƘׯ+_|jz] _@2A/._ LIpfx<|ĉz!?k#)YǓ׉HsVfwR'3FY@ǿp&q'4nB3Fq<cz-LOBޝ}&1L(T˟+ Soy$F36)P@ (P3#!mPci\?C)Gy!% )c3Ȃ-7d&.N-LUȒI2ԤZxd&kB?0kE2@D=Q1)>-]By::ra(xB9S~y29c8uBi2d(^:{ʥrvg}|&22`0iW/LVJ;'J3gp[@ (P@<"fU%X#4M1ʎZz"IPGI}qTQ'?Ԧ I"VZkaՓ儑u` AהV& }Ek1dIJKS0Jc@!sD\zIJPJ94%kN4^Ksƃ)t'(NdЏ 8MpƘýr< RY>u{.Ba޿aaayy_b$}QE%&H766VWW>h`8cJ g-rU~"rdNG, IGH4/24MáZ}p:|oZr:u4'~?z^+Jr,"jUQN{D 10l4z}{{;MS8A=#jHr<#y+g'yRRF*D}qnʕoܸMcҒc7JD (P@ |+ @hB|r΍".\ (Q %үNV5߸<;?;R&v0`fE ,Ql,lOa"`ܝvA媞菡GRhԟzʃpH ,H%cLJ ƸJ$qĤ_/-U|Y9v'zX@exițIFQ opÍFQ*`$ITT*yr(>ZV."x'xQ/ѧ󳳳+T$^ :&÷z+cɰ,ʂ8H|"JKz.-N9#s)c%R}Mhk+1N뛛F{pqH\KEIKtʋ/j{qg DH "H 勿+nܻwF$3T*yvo(P@ (kE,W gCpXk(6Ǝ5{4o?5W\MhJԦD1@Bls!(=1تb+z3 . +yrF 0$XFPc51:OpLT9svsyc?`zzv[%7aJ4crqYPq5TZT]YY988#Ex qLq\TZp82{45M+ @)0h4E>k)kQʬwUBZ]XXwG:5eXlnn; H~UI(wir$h8&i嶬lSёyfV\|ߏ鬬Hބdĺ< T* oYV FY7wQnV?LcnH[拗Y Jny!|c`]/?v MOݽ[ڙɩr,BQ@ (P)`@QSF" h ΈDpZF)P9P KP B+++?<}~59˕9Mk"T11l7 ؂"ev<"JXc?~F46HCH`Zجi<jkH( OiCCؔN{'GpH/%qCtwm4Yvxxhua2BT:7 ~$ZF`0ֱqf999nE!y' Q4듓q_~}8j"?PkNG~ vrJ%`#l8BV WK5q{;;I.,̋G88䈪BQ@ (P Ű2R>d2b ;|6#Wm L'}a 5NQs*5 GlŪ\NV)6*#xL8 lMRcXXNua5l<:`0 LN^Y_/,rlooZ[<\RJIbStǑM (P@ KA`BCcTaOrm,w:R̩d,EZHo8Muz'N41i"M-IRS*fKd,{dMbLb#g֗qq{HI4a6%9BgA`=|t}?f$z牧Ӯ`{{{qqQ8YkBF j}qV)\ (P@ Hli`c"5H2adZJk;;H3G+4E/~'AZs͉caDU61ldv_^VhƈfI'x1["b$>o% ɮ`/WZI$IR&1&8RfR#xvGZ7E7Qq0 (A4333sssn '!8vwwoݺ555u$In߾-n̮0)pgbf"f0lnnDZ$EB-)D$S-9s;CH 3M` ǟ+Nw_jo4rY=V-#7gxDX&I>P7(iSEQRԑly`X$aCbBj_gjQbXc#!R[mZp s!,cn՚u fzDp~p_(I!s [?Ȱ5c Y@) ٘Noopw0`T-AC1QT#$㶮,r ' )\)ŪԖ8&m'HԢnJ%qHQ@CI2^Ok=>> f].&rConn޾}?|$ߗG~_THw E]@a>쳍FcssskkKr|VMOO+a/sT9(;#N"܂?%vGqc8ۻ}vјiZ ZL 3եgJ~UzoiSnU摋apҥZv=DZD%QdF$6tzsqٯ]; @J!a0?+?Y7߼rgNMMj5]p*iߋu8i@=f Fˠ-QTV,> FH*JԂ,|T࣭ɱz*`q 2l-`4Xa:"5(.\MekZbX)' R@^́ a)85 ,_lm [IAjWwz6F` "@ dn|aQkL˫bam a(n('pfI$pDvI+Jz}}ԩS nv#.ٙS._:;;+W{v8 DLYXXN͛TAGvjyfzn*8En7w,u.hpgϞ>}a׉|J^vh_t{{{ZTm K*pu4Mܹ#]*kZq !{{{atrR޸K_nc P#PW>tЋ`շoW剉 "qDT'n>#ԒKN'61-3w-,R5Z $"B`!gnQ,`B͔~͘{ٞhZ*Rj΢=Ch˟V]o2YM )ܴAdAA{4@2̩ w.S m֩fkws@Ǡ?@91sj7jͱڻ(R b]káp ̜iM(gD/_ RK4cg*Mk^zhLOOݽ{W\!t_v7,JSSSǿܸqcyyYj$I\O0{ffW_T*7o޼w^$r>xjjjuM1 $Nt<`$F. _:Ā#h\ǃ]*͛7\rkN:@@n{K.=zU'y;P4%(Q/̷nkQHM+# >|t/}ەr9~_*$#׈'n81\_q=L~B+eCK4! Q2m 8D7U `pG#Èl$%Sv?4LF%Ph5ڰ-x^Ϻ)NR  nᏤzqBQQW`eM[]U[3C0:#B894)Zc8FQĖd?wh~j ZX -'c+|p@Yke__#-*$In4}矯jZkyN7gIvji(PJU՝Nl6Ϟ=S iqUĕ+Wn޼t&''sOP*/&I"ӧO_pRlll\~]ZH[10s\ZZ֭[kkkcccu KsF($kZ柦iZmZݫT*gΜwmV;YYY)J/^\^^huuU:LNNƞȯq@۫V=ܥKJ۷o߾-u9PJtyi̩So~K]9S1̕ ;++k_[]]]:uJR`(**h#*T8NùXD$ǓFZ&Pc)Az:lJwnXhHFc`a-T_!x#>R:.8` [װ`"!"` yg*iPJDXz;ߠoq Ü7 ,,`k`4QÝna{=ABoFCR18FX)kk&[֘"88(2T*Tzg윇DNOO juwwN \ pll^z= zSSSNޖ &D w.{|իWnNߙV؍$I1/bRz7vvv}U‚1fcc#Ic1Apw3$,//ooot܄(b0z׮]֞={̙3ֶ%u(E!ORiyyҥKƘ;w\vx38MPySfݭ? x7o@0VN8k64Hc}E{o51nT*Jp08& sGD9{  VAe9 w=F(mq3vwڵ1֒}ƺBIuI|NAa-?|eʰ>pa amF LΝ/PY{.#r 8==}vb6Bp(rl4aeq\z.Q6(Rizz:`nǕRRI쩩;w(^|˗/2kqv&Mӵ$IΟ?ԩ{… ><<>)wŋ$tmmի[[[-]jO$dp:62qq )jz^n6gΜ )\~^xqrr^pޞc^/..U*R͛7oypTp2z0Ǵ{pp~tT.oqRl `,,c_|i~7==/ʥ8ق)U%0v+d0n\UQE)qqJғ#8\573՜[Yۅ1@$`H l `"ܰ3/i|GtfXlj`2G < V`bp9Uc3l2!PGjX[Qa|VrtG4Ӏ@$<|Ulj;fh;3՜Wyj|D@ټ|b8vA$Rvw][[<*z>??ODյl<@z}kkkgggrrܹsNny* T4MZVRߗ*BpH q_~mr (e^`0x7Vu]GBʉzxLjEţHRjmll믾b~뭷A\VJHpuuu8>}zaajZ-;;^Jl y޽vB 8;kGpP")J`0l...wsc Q`R```7>|?xnyhkg/Kna}_!m_"l`L?սTlnnʄeO^A1XTB!P Nf&VVs2uu? PR,,?lMp .i5龨ue+M` !&,HSQ`yZ $"ȋ!!D 8̰`6L_$[ b$^A`9PրMɛdmtц,eq8?3l>R 5< XZáA#bxO^[wE `m#Ӣ^~in5QT"(Kz̙zX3 X]]r{&V#Ta@(COHy d3\ʹ \( ւ,LHRXF\J巢"@ Q^.f9dA@P P^B@0?;Th/_DEj†H>R `Qp[>2I'dG<(VzRnnn:ѣK -\iXXXpk׮---I-ٍNQRV7oޔZt:8S3nQr\i&I:Tn'AȩMMM]xl޽{weeebblJfq])PòJ Z뱱171.]rzrH 1;;;v[J9}a@rd yjjjffF2SD%$㏬0Jrj|*6wYG.1:o+_s p1J[A/}ik֮ϮAe S@@Pl$ -LcV&@AȤB R a"8`Lf71"&a -e#MXE!sZ@&L >Ιut%a)X8aCi&U&نEbm όU+EiG j']Wu y0z= DZ g$E1~1pIrdȂ;v¥Riaaa~~nI A 8RD=H}ad^E RNQd^ Ę<<70 XVDi,P%x2!DS0 P'Gn0YGXPK A!(B0â<#b~B"38% X3M]h(g]WHL.8RYTc؜ðiJJlau1YX ;:jiIzF%гgn,U#vp+eTfX;g033s…ׯq<==$F,l^7ntRjuuw8I@^]K=r?!i8EjV%D͛7䤔żI#$(wh766VVVvvvd# ʃz^󻻻W^Z:ujffF>]آmCx \\.,//WUћܹslj595QK#fG/3'"q`T*&''߿o[^^ّ'qxӑS.y4 na:11h4\ a7rQǡ2ƔpQO,w 5(ЅGp 5J7d7';<<<<<<<<<<>(0˜K `M~!x7Lj8*C j% 0 IDAT[gPCJcca8D!G: " A0` G K€hfF\9  L2t|'3(f%p!/[)٘4ՒaEal_0FڐuD@H4)>D)`Z>)҅%Q  xP'pPTǧD]~][[xffFR-7aշ!"+hZn?{3<355%}^ŏF7F6 $ݼy7j*N|u eZpbi8(i͵_}}}oZFcwwWs0H ?0bGQMNN.,,:u* _yWƂ t6)Rnvu=0# ň>=8pp(n{t@hlkcBhL9^-{ jqr(FI8q0B$4G(@gGa J£m@@" C@6ɻP‘A0l2u"- 7,is XQXa1@nKqQĢŐb2gHS MH5iaAd$1h|S 9{\J8OTZm||Lyqq\.jV~033S_AR3 PT677WVVžNG*siZ'\䆤-H\jfsrrrbb֖47Mqn"ZEQ^oZb!8$"]q+4g7O!1"J_}|||vvvffffffooowwwiHWm8PEQ5Y 7wJIӴ듓j5.qV1ً5\XZ9SNL1XF@`\l%c)Xd 'h6IAIf*'Cp$p9?(%L"aQF"E9rB( AE`Fbr~ZStD򦳒 0AMdEHhR38 Fj@5B aݜpd9dd0V#ًi j)N1L^ |"$.LD^YXXE^GcĈᚭ [ADZ$I$ݽ{vE 4!r, N!r!MSihpxx믷Z~???_*v4Mr dp6rT*dڷo^]]F1 rRINJpyyyjjJZ+Z}߹{u1y2a݋Ai) ( @0pFCیIVX>żɎx41dXT@r0&#,Y/X9Ke !nl0`h^xӧGDM J Z\kmt:{{{NG RizzZ:g \1>©$$Z70 4q秦i$Iߗ\kڝ8RIpeeessSk]*FgE @yV!ٵqy+!t _׉hzzzii1>,tZrX8ZV*u=kxE(֒;Iq,>4aaW<<<<<<<<<<<>Xሱo@;C'r^{a 3 $)*~u&l֭ YXZPNE|+캨>\d:m isy-텏;A^ /k/0I{d qEpE˳0L: e<GϜ_nXPm4QPK 0 .w%H$Sa)0ѥFϝS*fO\3=Ń  DJa `ڵ;݁JC~ !/g1ʙM9zaQYlIa,Tk$_kMk!$Ąh)PVQAǻH=wQ:Նk!,cm\dDb}}}mmM8g(&"1D iCå). $T*ȡeXyJeG s] YL=>>h4ęBD~nޱ;illl||ܵu,ZNLLq@2p u"|wۮm\/G ~hw) D!"DJAU}v{ݕ_$Y<2P"E{x7wЏa:(3S02AQS6X55f+Z>?rF|GQU|5eNdH^> u(P*j֧aD#w^Mypܐxv뉂B4G 9Oq@:q3Vӻ{1A($bV%)BOZ|?rmp0* @#92pf(I>ş"Q~ދJ kDf5a)~A"J2:ayXG(tpA6WEg/ji¢3DO+rY*:*%\ȫKo*,up퓪 3*%b'Bq\F(NmhA gR-#GpY8&P`N"p-oG8GQFw}wQ$P>!*b cB#p)k~feO&(+@A'EC"5TFsT5fPN䕆\G-Z- e07g +w>*f8lYZnw Hzi&`GF#ˌ92pHh 0J}yr\;0R1q#f2P,WX<δq݇ jyivɒ>S8p͊FPǢASGg4%'?rsm^l1!(ʝ_aP `a ضYN,EI6ȏ^L R ¤[F?ХY~)[mbVo;&Od|Q\M>Bs䁔Ә;}\)F"G3r\[#|8xq2/{@&ǁ B T)@"&8'6VW$<_?~w}FY(,@Ԍܢ D)0[pP`X XOn|z2G RicRkuoV0p1VyW#…F)J1Zc:2vC:28+/U*e. {Kﰊ KUA;{]PcȻoH? g-dw;ۢo l$qdx5ܟYhDE1,,H[] # f=-C`?ʟ}N(A*5T#d0#%D8/VM"$&ۼ͛!8 ~u\I>Wf R mbH흽];_w hKZ`[c&_cU?x]=<<<<<<<<<{_Boz NErϋT +XF ^ͺ>""AO4cc$ D]e(tw/v} C6S87ɣC)'86s$5Doh6Ќ`y ?_:/Rʪ*2=QTK]Lk|q~T*=[(EJ`K JcN7oM Z~RD(1\¥r8oݺ{E0(23e/"Fl8A{`Dt}*DQnqT"L8PA(1<&RE1 !$fѼ(R(9ʓD,s.EQ_1dAR &bB?@rHnS#*=p" %⑸ӣ#<R^5B{0.Iڳ~-!r7R An)0g L[E}[)! .;Y"7$e-H@Õϯ EH,# ([38X` :#F[JfmSA_,~~쥗/PO׶ï"*!a'lPo ͿVNOO?zX կ%HR#>xokQ'+IƘn_-Y#&_on#8dwu ]#=rmmg%1Cc)Ovs!$CҥZ{ ~""e*0aG@)p( TLJ %E@D@1 ( RJ1,!$8$ q?)'@s@DL+*$# %g$RDDPTRrB)@ _ ydל^%_ Jr=ndr*}a=<<<<<<<<<l,s,A[X2MwI6rTG?ootP l8IYFV12Bx'Y5A@ )q P\mw. ,Ze 6Q)˽?+]45` +Ukr"Gz.r7LCnin 1ʏȇOXsi#pi{'X las^)IӌzDS@AF@>Bgd{f6ZDdJ@MG@ 0( B+11b"VP@LJ{cRFhȨ DP@BpXeC &P*3B  ĵ @HJ2PJP~ԕ4#P7(i,)[7-m?)"" 1" AV|Jn@CkbDڸjy<` FBΗJCCEô߻5:4KJ8IpkRxV]p{8%ifQ1lؗ?t~#eEok aVxIbbF|$'E䳚G oTT|!\d|s^!gkIb]I Qn(炑۞@XO( V!Vm$LvngXXd X3%qo,ʂȌU"rKHb'/E>UZ|nxxxxxxxxxxx1h=0ZU mB??_{ؙ; IDATq,*ȢLQ0BP.x ,]( )rѡ )#UD3wO>? Ejc"V Pn<.D*'iT1 [긠H^<VG";dLњsbqd;C2Hh(l |}ƽ b", y2IAQxxxxxxxxxxx| G} :a@.g_|W% y ?ԕ @ 0 6PyJmWڢgj~0kio?D78̟"A"ʎeha@L5SphXn A?3OOTGѓXRL#2n!{NMj>~YX%#EvS8Cùq{ |+٘pLGsg-@(2J@" "CHA u>^P`zDS -=|-2u }-`O8_RnF*OAQGq(h*y 8"sM)AhL@U`h !B&D!jT#80?w\> d9!"C@"1>bQpGDF0N[Ka@Q=-&Y͕=M,` -)i4w3-i #G 6`..LS?' 3`2cTSi b-d밌]wŀ뮻TUU4UBO7x^'flex B!c10H39ܣP^QNP/nj]t;UK** $tL@ 8u! B~C6K"B7i)98,0`` )aV)Mb%r!fJAw"B!B;Iɽ=}q&@JiҥV-226jW~qQQl‚ԜhʝR XBe:Q;#bgg`H@ iA‰VpXM@0a KÀ.a 嬮"|ёcDk~ACzu0fYc Cأ4 |KrluM =B^JH))y( aڿH)v&+Z8O`iȇx1#,NP"trtRhi]{^$ "]ZM*݂2:/+Ҋ ,ߩةpҧO;Nωt wnN+ST1B!BH2 HCj#9 p0)/h\m}\$  tU`XSZ*L ]ىH$[ßǑe"AN *ްk& `00030aX&pnru^Щ57}a‰a"rpHsp8gus7/ro2%,+s(؃;{*qw:8g`([?9uۙ%U 0C̟E#/E@`!PTL*g 8cgq0@)^*)MNJKӨ1̾\ tdF7?K%(]VPڀ9cSp'/!%7L XB!BM* .4^3xA*0a2DU-6૕-[KBL !Tg.) ݀nAatgoIN2'$F8 Ӏ0LVd  ,7ig"WЮUc%!}J5I9GueU($$!xYTM*w&  *4VEa ]%YbTA~3aۣw+QH `;ܕFY@7: ^<; !܉Y0oZ' N 0( @m X8˘ L{ArWJbq@ev|>pET(Q΁Gpn?Lp @W PpOYK`82@r1&981,saNB!B߇ X`3E)ܫ+aPH7q6ӲE^mauiϞ=aJpxUL\q/tM ^;IFyF%`P˅;;@8Cr&i4C &԰' ]0T١ 4-E5SXzx#@՜Uo +Lpp:-+gF we0sq"Eo `R}Y՝#Wpp(99>@\r}`_l):.M;kL U 5]8GQHwڄ}pq>,?`cj w<`ćvnB!R!B3)%jJ, ;{~j߾ڶm>j]τiB5*PTx4*Ga t No1g> 3eޘ៫bgj)b1ta0a08oԠ. YyRab >y=\)*H'#˄1gNՐ0s!E'^ )>hO/4-Yyˊ=d` ;iȫbܝB63}Upw3F#pz-/} QLvK7H1d3(1$c ˜gqΘ3s;1ŝ>89W11@C!E2sUaS $&(w81p g|93 sX< RKّ !B!T9 iRÐٖMn1ʼU"a}c/qqvMV%kl^p4!8\|0Ca( pʜI(j½]/eh0`{J\< ttݨWƥz ӹ]Fq9kMg kWwEo1/c(:k55-´Rlwccc?`T~L7ń9hc(DO\iVZLeIU UԷzKֹNCή%?eΘd{ Mrkw]Ji#_o|Q '촚n/a)6Jp@0 @snw9)H;Q쬡NBa'~,_kNs3Mw?h";n)+ 6q&;vgVؙà0q;f`@!Bz v'ÒgG-P g 8R !DEEtl׼N|\5~Zc?LcPH$,łGrG;7ay H˝bGLݙ--g:)aXVv޾O[4ig$U^2eݼN'~9qA#44/t%(Ka\tjyKjq;B2 Wa|^?فCE~@+EH)ܠ`N@9 q&9LB:qP{;pҍ8C?MW2`)Hx;vNӎp; Zt7F^ #3%/:&ֳiq8 <;¹&I7"D5?s({ B!TaRZBJR ` tXBأvPʚ5ӿ.?57>SHDzVVT8|[P3vfY p 7[pp8 ',gM z.rNҲ Etmd.wm԰)61Mv`ܪnE.XQAXaJys^ZBʣ[7ΟS:C+llP:(28'oQ.\ޑ|. B!T})!˲=Duӂ 0VX3Uԯ[M;kشܔLL+<Yx[q)MaApLx!$tßz$` >)6V[޶g5mZQnB!B!| Ғ KX0%LK ‚D&NڨaF lVcwCFT_R2m«!Li)7!i98st2@HXR²1dC7z!khҶSmi!,K( 9WYSKL"(k?grP"B S@JιϞKHPg7|l׳=|4!*bDSTplXr*B8JiD(m:dH Ջe@B8>< }k?VWi}y|7?_B!BHE$R zZ">|pWaoiX&klZcwؿ'q=xipwN{X AeAXa"j׈iPf5[$'4߸YGZ [v B!BiL->*z p <pnؓV*hjZMktmsHҡC'O>y:tJƉ\=7Ǘi²,!qMQ5MT%4DF׌^-v|\Z1 T`²s>!B!B'LHJh^HMbpp8l0nҴiæMIKHKJKLIHO醡BJ; (-<,4"<4*:ZTdLldTTx(`RJ)!8gNgl !B!ӂ X8!B!Ru)%uSZ‚)aZ LdIFi !B!B.-% {rP"B S@JB!Bʤ%5!L`D&O B!B!39 *1h*5g7 pB!B!LHJh`GXh \"BST!B!Ru 3Aaи)\6h!Df?b4M>!米{-I;L1ٱIJ.B!TT8S("TA0(A郻{vH6@zjܪU w;>yvdxGѷs3C#e'v<ØC9jtDLD\htD9]wmw-UWZ=A=)Ww}~|ꛃ>V㚟Z݋:߳6sI vT^wmw-U  3AMSg >:+=M+w[L IDAT~Ko&UA!d2p  1@8G pr; Aэ<ΙK/Z.KW' H]Z/2\vg2qϿΤVZ1˿ݼܐ|QmBJ?ht_Eo><= ԣoOvQl?`}GoMhB!3I3@QkqQ< QM<xkEmy"tj_~g)ysJ YOe=2SjEW AlPCcFvv&7 5᎛jVHqtU(ی_jon~%?{m!v7n>OB!o,0ʢU0( 8kC;nXӪ3zӞ}z֧cBPsϝ s>X [{֦w :et6f~`tӨkiر ׿oKy TrZO. G̹Fc]!F=zjVOۮvq@7]6rJmtйK}}SVoIeR{x@{8}B!P.! L!ad: ˂Y p$oxjIHLLܴS<}럍jO0HRuF7%Kf\\L-ixWO{ SHLL^O7jԮӬz3Opۋ}~m1yNLMN>扌Y-)5TkFM,GD{{`zwO۴nAyMwק}_(hݰϳL۠_ڵQ]3^xEQkR5SɣYzɘkc}4RB~ekۡ ;@%3x3wXəD!BV %ΎZ@6Π1K җ?p^Ӄh꿆c4NyJHy]vڵא* ·7'~m-,=Hg; `/:S๬ώkp$OGK.h_?@t~GM)z!t?8"pRxmY9v#̵Ǒ eja|K6Skw{JSoVrc,]TN'/u&AFx-MK,/?|? ~uJG`:Uf!BY!%!!`Z $WL- xeDM ܷi=cn0]K6(X6߭N߾tѻ ~ A}iߦy2Zö4D1'Ў{tBE&U׊o!#w+hȊESϠ.qA'YI_TZ{n %Xu>W5BAz83SMwn9oݯ iKMcԏ-Nlߗ`/~f83"bKs5-p(0ﺏ7sZ2]|Dm}r&J9RԻ~js0C/YS&2yM@̈́SgDQ2=-]f}SsUwm|Κ )ULX"8G k{Y ݜPheo$AUdШhxڍi9DnYz4;Tv zyauLs#ᒦU{%0ϵY)8(:a9_?y߷xxO+;.)$3س&{_:w<ׅs~v ׷ _wwl3Ի㍝fxOkLуNKԼ H]? \~Bm/Ho]TpVVJZ tM0q{qbRMv?jQҐEvixAbZ̃~L/bl}&'TZRq_A*sm^ ħgF6˶8@_sޜ1'ʥE>9ۮ؍ N*|z ! C;s)ToLwxc!g|7K@pa|7iOI,kYzȨ k0Wf(S2d"U22޴cU{|gڝz}E[|tݗs^x{g&`p--f;OiO׌lWY`\b,NzɦTOw2gP't˧X;-9zC5iݣ؁ە/oOcQ7e՟yQsA%:/<ύuhe~'púޓ׶8W)&* &`X0 κ0N%`"3,5,jޓ[wU}_8Ӽ spc@yku<8a-ڐނ%s:dvoT4 *,آv -Wo:F!n,Ľt>}k8,1&^Rw:!Jߩ$s=*چ>)0f}E TBEhܩeT!mbIB=:n w1Z4U=:k9g ' yM*!CyiDҁU+V,\yվrN(^j.AN耱MB/:caoj{K*/3M>3lY0(ȩ1|`Jʁ ׺gBHr;2ziL;Q}AfyvGNVaW=5+4Od}c}YN^~`Oּ}'K_7c]K'}ふy*yVb +ߜ2[dj̐":e+=H%X=~y0sJg(S.Ai2qchCW|>O.uI'YL[O?DfjYBDC`Xl|q 'XW2S_ԕRX *2Ǡ/ phe+S{Ϝ="(-NO^^qr ?G W3xB5.wKk^hr0tZ.7Wk~5W 7f~-9}}l4AΞ\ݽ-ƷOIz_Z.r|D9;=/28|j[~xu9M7*abNŝ(o4~ 3d­{ݩ(?_hof^~Zwi.ePQ2\:}\=13! \tոZ0j2wiRrRث(:tc~=xtĆf"bsRlI*J)^֞߃ziWq^/;0!>sBN.oҴ W=7v_>pG֧8\  h K03W*uJ\F9 V;٨]HkdȚ6xšy^ hH??Tr TCDA_xhտEY}!ouUf2kmOHYԾl -IVʸ@Ih l rg)gNWz8=߹{ԏWʟ:fA 1C(]iT&qoޚ8=6晫&4V)հCmЭ+4%V"`5CMwݽy:=?wK]z-,{ ֈxEnT?_h*4h` 8WrFٸm~{ƈnE'=O@5fŅ;x17=fТe, \vd0jdVzAC{힕{:fe\گn7fo_s=sL*2/fM E&5[eo?i׋gqo-KVo, |RG.zqpՉ׋OY?d?\\P7O_sd|#.8O2(_cgX5vXܥ>᛾atiܺҿ( /[3x Iw>⅊a=k@ >]sU=?#9{x5kҒ2mHAҷ/]…me_ڷi^X0?lKCdb׼gwHgZ!ᄒo 5}_?ް؁T̠`n5b@KO;&&>D{qP*U;wܖr4)S;e†wK+ׯڡr̛ߠoPϠ.qŏ߳pFպ5Êwt+4 vWcԌ{f YݓN,ٟPKdeڳxek{}WUr;5" nx '?'`CK6az}TD{VĕK?]cZuɫܻw>z|}8?z~ApЀvazP[:Kg10H3q@<*BUx;W}?JǿۉEe8΍d[uRCuv|W|Oպ:C?w=_>7檝SI"C/r6O_FXӗgV ??*%Ml<3_Z)o]Lڜ/"ammj>3MnJG\zmwhufgC5j*]5CU7!J62 ]տȝ#9[Mt~%26&|/__JNNN"C e)Z94|~75ݢePG(Sdq#iMaۯK9+7Mh [{Uc+>s\KJ pT[Nu@R{jM{G4y}|bPK/]%YlekR3[7+j[Ŧphjͻ2֔O󃬻u0i)~_*=+^ֶ^E/x![6-e>' x9UOeǗ_7rɉ6ʔA~S{)**9T*UDebs D3w?Mzdh২fI- un]Ua6zpp̨62N^AkK+2jwgXP_{̑N>7Jܴ.Ee.Zyw9PN?Hh^KRV/\|@hۆ΃M*q ܴ版k˯p33h4vs';_ryiM{UuՅDDaSq݉ ܥ"&Wۯɵvzu2EU䣡C@/3HmRohqHzL2hV'1vX5=co'̽{z;^{'o>{7LhޓG^:%`3f{,#J=g̘1c 8M_$̘81Ǐ?!Pݣ׉rcHS@W;Z[dAX۫"˿Xy[)ws9D;zV\Yh!l>V/9 xKKYrU`G7ufϘ(4l'P/(㯇?`G7x׉Κ5so=[$WT0@z"UwYΌwn5Ng2hԀHiAc|?CMz~5]P22''9 +KZQ=,>-k,g HHջ]2/(agѷzEwKתKsˌ|VBU  Guprz IDATQ;ܰxJmtb3ek]\t=*wtZ{㥬eS׺@GZXxg}׆˟Z/94.EnߧlH~dDNG  ǃO1[z=ހIfbjR^DĒY *Dܽ.~-6lЬs}a@ ,^?|LE}AUeR PTpƠZy%/k~%8O|4ɑi1G}8Ȭyϯ(ys^_0'K~ϸA};hk>`%TXYIlȝb{ɽ5G>?*!O'Ulg[ *:sS#pDXh_ 矯Ф%qWS 5Ox]'`QiA2L:`(cFoo_>e[ ̉^5Hbuzt߾վ]_2}KWKm͝?~zy`}œ9XE\W2\*;}ίud ScZZuf&xWs<ҋ˚@>9ӛKe%huf#W@j/|M(}}s̙3gΣӣ,nkZ.ypLi!ݯ~<ǦS Z n}%X_/o.\%/^ӯ~Q |J[N᜛uvm޼moj^S W`LgsMFkbJG/^`+OpJ,LZ#}0_7FLe4n{̣=addOܸd=q"߅Qs輵?]گJ[JJzF蕯M7N*,S?>x 70;/ęAUq&`AHE)ܫ+aP-dɨ7of-ܒ3 ~v,t92Fc1#~?6ے/uM\vYv SK,HzܧGh@f^$pτ>7էD 8H9՜Բ}X ݾK|jMZ֌',r|onɳs{{nn=N>تWVCp_PŤmm嬚KoI}y(=gC!3mXMjqĶ5?ܚ sTHuMQ_uKkacO7ՄGג8gRKꁇ^z "qȞnי՝q3h V Oqjib%?!n}٣;z4,: gj%32\z78'7^n1 ٴ! ^=2 e/uxDp,*H$mr޼۞oRa6[_Ovz &^_bS3Pz3ܹd(K,wk[>[wIjx>9/k/f] KUdY q +j,(GG +2OJ(T%)$L,QSB`Y0Ygi%fd_Fr)_. Qj׏(zaW|}{4rӫK8 @x<qӗXеkV ֮VK~9kpڵV1BJ|kf{9?pLes̷]m1{CoyΗFvsӨl9#>wcܵ<~ڝn]@7+>YO`oȟ]ܬ#7ЯQ-S6/dĆ˻.vK&!򮙈m~W`  ZD[}ype`UG# U`~Ϳ ACܞ=ҕ6>Ml|_A%)nKNAPt ;EypT<_zVޟq6hަX3pagc@)J}}9'gfxuP1sA_mY߶B(8~I5G5y_E;CɼCQ޷?<4J!87-Z;].nK56({jOk{8yX-KwMϏJOm<:`^ gpVgh&IR5Ol~]krSQ.#Ogsx;7P )Ɍξ!tIT<+'08%tpRzݦ\˚u.ޭB+8sgn%?W˴~mW 7<3ITi<ӯ-U׭B}RRWѕz\sYnUyqXJ~d 2B)KOIi ϧҙkILR*+o?ɭ4yi?9I&~٪q)%XTǵ~J+}XbWqH|3,KӺh'sp>oHTC E> ׎ƮI}'Kظ_txު) \Kub.B䉓5jӿ]s^,֢f86{,Zm?v=&|r-P>iy5WY_w?A1[%M` 1+ j8(!Qy,]suWj:кQpuWjXªT+U/_)<\n 2+ȇ\vJEup:hZkԼf僟.ǯҰ~B)꾱~J3tzL86zC=rmO϶eM_ݱLoޟ%ev2CCLI1;h;?‚XWw:T%9\$/>V.sl||tGm1Ǭ#zSfm&_٧,pN|H9Q7w˭hũd]-,_0ۓ{/ԝ$^˹6vkw2;PáeR}ι^`6\CN{'~Gt:}1?ͩ]L3j0=,sS#>(˲$s}h4>=6r;KidE[xŋvwԧ;y41ϺXqht6ytlDUP%o|\b.1eQ=<1_ Un)]*yiY+Fe ƌ)vN3[8v^~т'clWMK$i?}?Pױ͒K]J+jկ&,M-Kp1 MV'ۧ@.<t>E!2e<_=Q>>.4.nb.)}n,\* s0fL[Ho XG=8!BHQ.)q!}|%_ʧ>Rmgf]~>;C2_qu,ۃ^32ԙ}ȲEAv!WBrd/niK'5LD)NQL ;xv-d1)ÍZۀT|7SukaHEdLRypDRʅ0_~=MX??](+ KXs?=V WյM^U~gZ>|3'Qp>q *ѿsڍ5jخocǺ͐T1\ɑ*q `GQ>bh"zBHM~jFhŽByII$[,R3=KUv&Br@. RXFf|sLaNyf=p,CJǂZ++,igRA{F|uuovs>pĊsI W/ߎ2D@Ryqd3߿L@lucɜ>EL$>J $ 4p$?ݰ5c܌ ɝGr1h#B!RڵsP:jΙz+(4bhG/Ip7Y7]fj!ڣr{oTEr0 c 8(D(bZb{oyP%ՈmBt#1ըZLڿq[&T^ɜaw.p66a\BZ~^- [)um[Ueyػ&#<|%<u8hd(Q}dWAjB!By, ZZA6P'y^XfY*FWl2p1,aElu!KؘO}hh\b 0߻ j6`Zo7)l@|yx- 7} /9K)U_Z7{d:} B!B!ŊnH3OJ& 9XzB2hР+"+F&n]pZv/k=j=}RpP89grJ^2XCjЉ?8QaB!B!2[00X9 8+x1H%+ 8pI7y޽{<ص,~a fmý;.3a~ږytG'>6 MG<^ެs7RݳZ!YߧNOX>K-K!}|i5 El!]]36ؾ,!B!bsĹ (IX!1+ jE^ٳ'<Bp'>޿{𩛶TmmnWF/.1L& 5'/]{%Kl+S Gr敭ΐWѫJE"joV܃R&N/ M!B! .dIG|(*Uʲrʷov=32:g_9|xMߢi/U1kv]{cwzn!um0ܒp+m$It+UB!B)BD+,*YaXM&f+$ GppptttW^mժU2Җ-]mkR-ѫ?kj_t[H']Ri:wAK5Q+q9ݬ1]@&u V/!B!RĨQ778"sx(111[nmٴiӭ[Zl|&f} )hkTIV.gFa#Q @ϻ.wl>k669u!B!B2nJTsX8铌**K./H*ݡ "Kg\:`Ddys踷KM1:}zXV9y}B!BHd<Ġ eP*ॄ>gDᬢ`ٲeH[7@073kP&æzdm]hLAGVmvY_:4|cz=s.TFrk>Zqc1Ywr˼&UUj':,!B!3!r(*13hoB\Eƥ1)YX>;-k"Ai[vдAQ2:581aJcz ! D]YqǵwO?EW~wH)vx̑Eƌ ދa !B!WR`T@)J0Ay:BQ0}7}l岿%m`Pwʑ/O0mWe>!LsZ5%\wPjx{c\6:Sɷr3-X IDATKNjC)4e% @Ze[LB!B). \ 2|%P3{p0yaYpFޤO*O:*uq&p MQ]^E. RXFƉ]c&GˁOB!Bɟ[v}cnR*$$Z04 -Ej2_*jFdj/mi/4eq{ؽP&k' B!B!řqA?p7PB eV˙Wy(t?ԄB!BHEcP S *">D8=[/WB!B!b`(B>n/<{T,D*:,!B!⮄!AHQH 9-5p<{<k/B!BH[-\3OJ&` K_:B!B!2[00f 8+OXLf$YҒQB!B!][%M` 1+ j$dB!B!ŁBK,*@$8,VX5-5pB!B!Vnpdb5 ,H*&ܴ.cJ)UB֓vԶ#w,v[Reڶ_h а!ŽB!BR8cFI 7p}1}e9x6fҗjz>\ggRA_uHo¼ 86.]\na%ȄV^ -j B!gr%#s`VX:H0Z{e%(ݨ9v(b>{C 9F=4{/|j}7;ڹP3c'4k|-Lك*JUTݽ{wΜ9 .,@!ϦѣGO:ϯ!Brd<Ġ eP*ॄ>ơ_g*Ęxbѻ# `/ȜL>pB=U׀{~~Vnx-9G>"|s̙0a”J%9csΓ3d咿3i ?OSzjʕ bGfڵkgϞ^w}ץKDE!g܃ xqx0#W}hxi7tXW8s!g"_d bdl ؽ;~Y! U;lYU jI{?5/\'2e LWb~b}.g2A0"UzL]ڝ!u;:8WEmڵBݻ,/պq:e?;q :Rz+||*jNm]ӏh*>ARRiT]3\i>rߍիW{xx0hP W_唏3ir/.Py&o/A(W\```z{A!hgJAT (nO8b_9:9|d}f4p$bZu[5F_?,#z q.|YG\>Wl2pqNzz~԰"1{>Kv[# D{ۜIG7u͸rW.[nx2`lQ#ԯVk{SK9^}]-QrCGC*Yᠥ廆G^SNxߣRsP/.ZKs=5c׌0`eXGiꂌH5Ծ<uATzxxjطJBʕ+(Zmb h6mvs=HB! ni9aaTF_l()FEEo={GFF-S?CG%UvKkb8t攖=ͺ_hR_v?U-XVY=Ÿ/? |*RӺlEr>s0]68}o4x̪}#'n*׵cz2@Ҷ|5Jyw?Y m`e-Tڿ˦1x|f\"J/M٭b'M27AW3_jw^y91ڑW{τۧ w)_DW83@)G׬>A%-_LpuZoi$ŋnOO?ݹs'\$9wwwγW4?O#+aǏO B(q.!* x PPjRZǠA"""*VxyҥǤoa"WldeZv=ӆ!Yn1bUUx'}NhТ]W?~y{ Y+=Z,tׄ0uQ/~⻟N~}\W@!3l^N11|4,\#$:݋Ztt}gQuڏhXʉcARzYK@xxcZqն gk? #t_xUmڠT]R\:v=i0fĬ;k=1X+ձIy krLHVxUiS|.o.ro.wLY)~i?}رdύB)(0@T@BT" 9-={Z7thѢ)bV23.̷7ס_ ƇbOKiv{gо8]̓ٓ]5~ռg#=ے% 6ij%=׹NN~"/,KUEښ)K~jˑv~S"*4 ppT%u&sfxv3CӤ޳lű;w΍6#ѣ|]a%g~w ҧ1_?˼g+2Jѣ,|9 E"飷upS_P'pCG7W@_>~i?aMN:-,q||U~,eEws.]~ w qr|o OS_`KeÇXeƌ%NAL' !e`G"T ~mW *UʲrʷoGn y_˃t TJP6'<`[Bp-qINS~MWG:;6 |D@ic| 7/muܣk]uzӛW/4dN)~ɜԨQٳgwСFB)% E&@VXi #888:::ƫWj*i&?,[u>c;'Ֆman#?TS^`5 F3WWr|57c޷?<4J 5t;\syv®^)鱐mBpy'vݷ#Glpl!iSWwZnrj!n>_NJذ>ۋ֙q.㩑Jwfl0arׇvj,m;fˣ.vQᅷ'$[u>$BU/ y7qsn|~pQ@`RY?j-o;}KW Zu4\j#o^Ldrw}z9'^9wş3?mONOMB  jyU2Yf(8XBuVۖM6ݺue˖MeL: /^%mҧZ@WqO9`Kr2|*-1oHTP.KrёgmܪvXxxիWoݺ<[E7Ohy=&?zC=v/z-M]ÿ'ԯmU'ݵiK~M;O~XQ ^u,ϓbFFsG]/$ +SD ,jWʸ4c \Z4ܞ˵k;RZ3/ї h_lV qu߻ ͘ᘄTEw2l8p5I>4s53[1YfxqFv u#?'ɟ>ֆ3 !y^keO\i_ʥ)~]%{sin98!e[%M` 1+ j$$e˖߿?...222?sdyK/7c{ G7ů7VRMf]ɓ-ZϢW6Qy41^]AYŋzW/%LZ5goת~S||?]Q3J`4M&_ II!Rԗ9d]咱ZܚVݵz)EⱕLj[޵\Ffb(b&66o!^0=^KvE)7~M&I:Y1 X\nXɹ܂⨏k~uM^ZϏzf%u8=~ޑgz|hC 3` w|V4* ,dJVIB$`Cd4[*m35Z) hB;(Rn+q!g&ɖM$Iu\=9g[oʶseݚU6<7boVg˝[Ku+,"<9L ?L|b`ZV+!J[db5j R͐F落~O/7\үUOO}o[eJs'p:(Ru:t#3j&| !q*#o Qe&ם#;SweۅYjmg pS9F%;},qm[LM̾3̳s U~wӺޚ=_~ӖLP*RV_1s>^˥ A9Pļ2K{;'J~j2ډ)Z)2cjJV \.yYo2:$n u)xT2'0b3'e^ d~9ADѩvjB!P1@o CPË/^ ) Ry{k}saKp8#P)5-1\%PjhhȽ]C$^A9qѨOIH )\N9zW fl0iɸ/yĔ>㿻+w ]ttS,aZ?ٗdǑlykGEv|>mwiJ'Oc`ё//934z|d?Z=ߏV,a Yp^xÛ43 TfT=듷Ө܉9zl?ڐv zű;21vZR<<QO_*=8Iф8q[5dN|Th5@ȥ +ǣTh sVk!n{3F3}Li3;J iڳ6'e(yPgof~}wrZ'VAeZqW^0} ,KpCEe P:Rw=gE`s|et'BۧwKY?O+~ jXXɓg̘aҪUaÆٗ%I(f%RY*2O(1(e x)!ggxo_Ock0>rcxT Ny+veQGO\ 㱏ܬTy lY} e;|l e 5Ŝiqkgۇ>m>Ԡ.ߜnFOy a[놪nI>oS4O_*Rck`vdn;'QOSɓe%4?ԮYʲekˇB)8"R ?OAB)(Q`2\흏E|$ 2(ALwIq@q.-kM&g0VT,u &R^'_P(<<?O+~Q峄-A|ZjkݻwذaB0C($)BHee`D&@@   @q^EBf%%sԛ=<|(~wW63gΜsd6b%!BƘ1f9))I!7V0H\` # jj *QOoؠB)?Ν;Ϙ1^zZ (Xl )~/H61v͓'ON2eҥ~~~ B "qXxGI V+,dŰ|mƳjU^錂NM#4;i;VZp{}vaڴijaBy65ٳԺA!(SX.AX%EZj,( '}ؙfqlYz5K !$g~~~ .\paaB!BH0[00X9 8+x1H%+ eoELcc@h&%&Dx\_g=CB!B!sĹ \,$f$RT' n3T ֽmfllu9:|8ʍ3wgH!B!($X8Ē$ t,VXfMOVAZ/k7} Mvm#4k} M.mWv/_[ҮWjhU0=kƵl9 Sz!Ĺ c-_*U}"SV8zP!no;] Ҹ}ۚ&ڶCB!B!Vn*YaXM&E*mڂT3DJGMsvhQt krݘO}hh~Iݖu;ae$oḙ1q&]Yz 7OmA!B!Q)1$oop[iE琻۳gϪUDK᪴3l[d?ad&P|6eFRmGa+~d?bAc{|mJr|igϘ<M~j3!B!VH80[a#(A_蓌4(""bŊ矗.]cYChzDuѫ8O5zϜ нkAG^_1:43 WZ =zXu a*cnu(զK5Zx(!B!R\Y*2O(1(e x)!( 8pI7y޽{ s|d0uP+ĥAb JM B!B!p=8D0@@%-៾J!4pٳ'<>5e7Xqnr[I6ij{JIfLh҈r/znɖ{ɺ8Nhb*G~O\.Ν;&RM`ֻ Z]+l'm؟Oݏߴ?b5>\Lԗ2pĆ}6 XR ~͆Kn}n Ie[j8(w\%jwק<e% E&@VXiɞV/^j_>'QeӪ) \ɞwP`:H j;sOK~ZwA}BoٿMEM~Tʹ&iȾ mOY€ K`[\7DݖGq_oR,aNS@a Wvh#~V1aP -o/ע_[ ó%s&m`}r!^O3|g3-[#4E(#'83]AOmy+oKKs|bch>豋7$g.˹X?,YA%k'ѸxԹv4xDTҽ1q+!JaZfbLjA ӓjQͼmr|U([XNwr:F3 L1) o,x'BrV 7طrBjn{\ :ÑE')ZgƯl<ԝ`IdgxdJ۳ lNt!˹Sƣt}6bg#~?68畐Be``r@`qV*!b0jIK,4p/7oTV i[f: F8J9#755Iϸ;ZHGggzYR|]]gv &OU@ Â0YVP!66^_mw4-`>nP3oõVZ1c?nc M֦n\ޡO^2މK. CrMF밈J1O?Ӂ~[q+!sĹ \,$f$RT&2\fw4|B+T N) M/[A7ߛmvkҵ]DZxDѭ}{ؼϝUzU4=8cӟn;At~=^"ױ'_ĄMpȗ5?F>̶ύu@©={iŽ;|xUԴPRC0\vۧN}Ykj%\ %?cH*oXX?Wneʩ`̊UhLJ;OvnJ>tO`q9$~!S@.2ۅƝؼ)3F[95C^\޼FjܮF-ۻ-\kw)4 .dIGYӓjicO_O{|v͟JwS}ޔm-Ssʹ)6v'uIOʜNޞC? u{sotK5=%+~Ƃ3]!QuP5az;ܞ˩Ǝ45TY`yޚ8s7boڽkfl0wǗ͵y5us$ EAE@ŽUԺP:PjkuVku`Ӷ~[ u+ р! D<ޓs{rs!=$p@O/m"!4b㋿8s= cr/ $xU j=dnɻ_m)zH WifD`m.4ͳ&kؘg):|< (awLzb\#g_~+l OrQ2.NԂ<7"npHM'Ma,1^qil ';%a6I2/=dպ i/̙oj0~͍ Ft霵Q'tH6d̛-׎miʬm\9/ɹ ѷK̚qٝ6i*iq? &שeq}1UD(. ` VcޠՊ2W$zd͑w,sn]jⰰia߱k%gBrmJy-$b+n,TMܷ)<˨զ)Jf-P,|}9 #%$̚mfߌa щo5izV+2p ỞS}MSe8 D= c ]~SE @S4? 4*Mȡ&{^V|`ϺY2KЭz%jBT~j+f\s5Nq'B 껈 Y/||s9kװ/23yo6M+OJ$,AH9QŹ8T-74 sXҤ*56(jmunQC6Fieiy, KeWVgʓ\;RJN BP zy+7B&{sK.ghړ[k-$A>J/G(0"Ah%R{Fr0OfYL"]{]”>py}%F>k} [\sVHɳ꯬6{ɳ b皯ZZoK"d|=:7]78>%k5yyy˲ƻ ]6{~h>P=SNQuMm)^U _n5kѪ_?0IJ$,XPPR )n i‡S6gkGf}Y+Th`%l.)svCo3#N4e 7,MsaޘWkwl1MiVb;i%ݥX]ۻ>tjG)g'Y;3tE>^ui-*[٩zI' 'r ~p:Cv`;CQ^u!+CV].c7eT7^#dOgS{5u[ `m=Ǚ|,:NR(^F? |{J<[KJQY&0mڴSW \bR9_ U$Y91s"_ݲVk7NoCnųD6vw|ҺVm?cU4GfR]oKGJ>]e9FzPnDQ%A8>@\-sGov`C-U> NVͺlBgLh̀۶Jl-pq]sЊ?ȌGOOOBa,QL>.c.s^AD׼ZKaJYD@<7*)R"<+١X׬Vx֘\ob6b@Q@ɯ 9Z,Qѳ%cB0F6RN/Vb4X<5 ӗm77:9vV+c&T;+tX#WBwzbWvcH@S@ VLѐ8\{p螮ɺQs愅MGF3@?FbM# LĽ'>r:g*Jͧ.Y\AZ@qDz<78Xɕ' A֗bxZVJ}~L ,*̯嗈ոr} mN^W;v28F>qעbok)r k[Ti/E]]46]tyP_gRyAn` C"yHi3ݯY◈HS~ԙdjJ?yFod>++LCp8;W΀{2nPulK+PPEPb! &+ma8gp7=n߾cMs{2z+<ߊ~9A,[#2s#ˌHO,s6736?7`Ό^򟮽)-+nkӮRvߴ;󊳮}t]}e3V~׍L9vhlX1P=ȿhhL7W#`Gc.!5ȟ,k۰73~x3!sF]ð+Өrm-'82q Y),5,ج:'_WΖ܌17Sqn2e;֔}`c@98z(~%kc9Rmq"od?a0 +f & 3/6<d_3tK׹kf6&2ɥ%^X ;W>=f0BUjOU,VĠ  QnBD 1Le=-˃C9 9u#K.]:Vac#h.i供o!>80[H|;[*Yiћo!kiG̹M۷E}L~.\*uOY#4m,jBAV˹>9ylcwQWSMӆv<P2ѤLH֗g.^ꄄXV^nuB&8"WWW&؅FTTҍյ,0c:2Ueb)+e)iT-ňerW/2~XYT&+2bYiWIn= kq [;ВZ9=1Y7FΛINqpՏ.*Yyc{CGzj*gV؀On9pBWR54;lPAj5O/ݴ-b7#@ @ Cg@TcPꐑ}P bC :o3-%OiiMa!i=V?` ;㛆>42W Q 1\v۰jpӲGxszK .oں緩緭S H@ @ aYòZBCF MQ]|3 ۍ"u\hSuCQ@i_f;ǑFfAXɕ2-<:Ԗx #Td ֬] Z@ @ 1г`* 0P0 d(m'REc2u_ -@*U+r'>Yt Vk(͐KmIkߠsN7X߻\)W"@  BXg7h :p5 ugZ^2`#].ȍ. r.?}Z[T#uK]-yd LfWD&vX)@ @  (@(@ (C @PdEl @ @=`PDP B@LA(i(@ @ IJϰ3,, Ɉ@ =o}d2@ C֠gc V/0葡Yxp@ @( (P7*送Vޘxp@ @(aY-X!CAπ E`@ @ = 0 z`LN$ Y$傡9^)QsgVyЬ nŤ+._vY:2],+e3 G[~sM,` hod5 rXG(ɤ>^lF `YV D kJ jzC"֝x6жAuF")ή`4EBHR"WWoo?@q>s"k wD׶o5 D"呻iRR))jǧLl_1S͚Ը@FPd#45Z= zzV+\ 葡h_W1>Z!oC7n=ʸC7n=ljXJv57/_g\C/_$tN8^|$tH=111[xOEӦvR1\Ԫ} T6 ۟y s}(O0UF}vDIܭ@gw-m [c /<xEV\Hu;1;)}7?:휯 ܵ76tN)IO 'SPcO,d֦Y!| *R⏓ >uE_~LD+وtEi*0z,\-*׷7c|~=jԨa+>pƳd9$_?0~נ9b+:依w;_Fyp׾72@ Vb|ss٠8bq/}/utɞPP<{x]r鞮{۶m_lޜzrKg|~T#L?*XL-ICYw:"ż HB^ ?p4 YF[6_^ȣ. "{|#@}7kWq 7߆Z8r~tL9s6 ԵKcۮSJ)QK=}_J~FGY>3QXpGi&dXEò,t`a,)AFO2fWbbyvFWH@yx7m#XRxT-/-#7b毦품S;/@@ڥ#^DVFNl`Zܞ;GN;!rin]r ݖbhGBJׯzP%{V,4cRp-_AIeB$-ܳO|L:W@  }ÈP @]FR=0b?1Z-̸γS=G;k-)j/*T`'}Fߟ[-P&q8t~tH9thYSz%4^m=?5YoUX=bh"^;D+r4  B .4﵁Ձo̽)>h݁dE]g?=Hݸ?<ļ:V^ȉŪ/BTșBw7jD˴&N @ EbEW5RۿWJRP{4gjRܽ2:J֭bժ՛5mcKw瞤PUmݴEU?s%(K }h&~ԝT-꘯b:u!NRiZƥ3P궨tL}{41 $N).m…Q c>˜*~qҴ!֑ /\o׉,KXpXFͺ5i݀{F} 5Zw撦P =U@=wy r'-_׸h$?-HL I=B{4wɺzzGvTƼRj]<7]lIW\|tXRHU йeիKTⲵ*Zq=5RQyWh<敖qs.S)G\^?KK@'JRJ5s϶oҿxB++>j{H y6ڂWDa/m`!wlt хV 6T2Yw)Ut;?: 8_}tm{SXF"<_9w59MR]#_ /v0[v];"h}[+=?y;TR)B{ }}ѣ Ck3Vq#Ke-ypן(T.?#< [qKRj*SP pa!\y C9+_V oUu&n'+7P>)~JQ=4품I?(sg(M> @oLŋ/D?n݃FC/xo5]KcI;VSYdڥ!ch!x#tON XŸԐxk,s.pB[6 0KBU (.%hti;ao}ߢk*;y[up…kP\}{xszQɻMɯ; cg+妑hs,O'ɗNߕ]Uij?XCTS\.?{y \6}z/]4]U Q.BV8#.A'ݑ{rןc1¿c>+份0g{ kg߶ ~28Pf6 :MWW<t@ewmW_'~ֻ\vj]%=.Row?N mܷn8e[+`f|uW]%sO2WR 3WKߙb z]zvU?nX[Z'<͎ZYHuds/#&y5 2k[4rϊ%R_""ѺLԶ6O+C!Ef ƍ'"!8 zggRoϮ9M K.B{쒈{T P!ء|S [ʥKY:ug۶ >2$ZMJC:)K~7nuF]?pˁD\QZױ'B'G-1}gAqxwm;`^5j(m-nz2UoxuJBR}o Pwuxyh`_>Ni&vs.A yUVreٴ+R !3G4w;,[n}EhBȏC[x%(?o{ߊ8 (B% $CǗr8نÏR&ijS^@D(MºrkcMo/OGQ![Gu jZphjYL*&ޯZdz*ͧ:Uv0.ڔu듾q IDATB9,蹏`>\QVznɜzT!KN1gݶ ^2n,~0 "J.'Q7:co3#m ĂgYlt >k|>=]oi7:%/Yv7)<|]_A~;w1kelk=.@O/mb|`]C`_e ޮ#r͆S`S)72*c[Tsj7~̓h-2XoDŲ EEDBƷwj_|Y\֭[2 &sa۪R22ӷQR@lk{|z}ɿY4.bKBdEh!u#`8W颪767:3s*/ѳ%R՛z9o[ߕtl˅zdb{Igm4{ک7"αsZ?p7ݧ'B__|/ܰa^vɭ@#(۶Ol\u(kĭTP~p.˴np%v [Zx;fsoD־5cܿߺBz ~r^##3gk|,܏No# $I`_ hs /}hVobWX@~twrP ?g%Ґu7 IyeyWxE>q%rX+jԸ3EU'(L礪>z@Z~oW06mۡʟ X;Օe_Ψkѝ/T'n!ۅz~<7,,E,rz- vm>E<[36i2z~ tfãh`9O5((uPU\w&sV2P`XEAD !$ Bw`Vjnر˗Ϝ9ӲeK^Yв:ޔnr5k͇.yqE>s΂h2r-7M;S] ܚcN4.\c.n3h4=}HJDo)27VzXΘGWm\{8gebN@>Ϋ-u;HpY|Z F[*Fw`ݛb:L$ z_ACܠCRkm Ljmz M H+|R c=ڴ.> hV.e!x׉9\fi'Ңwкt|'>g,D˛ QPC/;A@q3-_tV*[ka:d{kjf羘/@x|*FXuH_* VRz.i ƎwD^MPOE׸_>2 hq"zW'٠ji Fnd5Ov͎/j}\gGK3EsqW-4k؜yq u׋7.>䴜=:M ~z }T1W?930,@gBQ@%lgо|`G#fK=+ȧ:|~txarͧ7tϥ۲orUi{ \<,9?ϢI2>'.̍y0 ~-Ҏb %^ @֠g:6h0葡˂ܔ ʕ{Ǐ;E1ob]αot};nx}aG> \VҸ٭Z'F?Rvkm2|pǎ+'|;iJ$ѳim"ɷvh`@ oduqֻcJԉ͎?Õ:C?LAG)eRbl6 izC@6~_ET V.XyGxʙdsi?zfox>];\s}~w?, ˘X3+?ŝE6vFf̈9&sVzn֐Ьϴ}_ si2狀v=S磨=Ԙ yIvՒ͖1_p r;zW'xu?mO7e+7ajШ^.:76)}|J5wr9 'Ydα xJw{ Vݸ"쇡J]ъp]+,7E@f5Wh!k&blva HtqS/y=rť-BE6ͷ4jE2?#C|CGY1dbGo$-hݳ;.\ 2,A@?ۈXe"m( }qBȩ`v>\=kD/^ɀe j  z e-2 dxp?ou9s۷~+Q:oL4*J@tfțPlW胧JڨdsOuk5QsƇ9\czd:D.`nJFpz!EDi׳ÔK(ImrrqиFܼ.m4rGf̓w>gXE52M3d/]ҧ!Gubc)c(U?&`]p2*O<V@=pu95J~YהBfl=>40K G4q 4Xٝ\˔]<:t o֠A ?׈+B40[fԞVĦ_3sYW/Ǽހ"++栖x*{vlnքT- @'e/՞"+0?|y-6!9J{7״~9<-]x𗁍NC(z` h`^K(0 ڼS7]Ώ<'bl е7 2/Dߐ ʧ ϦӇ|TRA?=Ǿ-u@#O 37^&ª!A=#:O>ؼ x%= 0 z<)}g SO/_<آ󓯌JRA?١JEzJ{;(*M>.7M'F[v/-l7~KU|HN'=:qOcS/{|a]!R°S6PP0nHؚaMkzYN?gZmYԖfLԗΔLc@\ͩP2IOxKG1)_ $]3JJ\]]en_NIHQXV^nő ܊Hj {Add8P~خ7G0Z-H_h-蔯_t ]j=[[PztT$G ́&H Ahvl͊oҵ?|]K|; u"9UKIEBo%uz|[ ܽ?:`O )2׊: [Ç;SGCi <|%UIiYiFE$^`ER7+Ii0鰀ESsd|+Hy %Y*a3?=PC+&s@:˂4}X8g{yҏ m~uFrڽg35⮽ۙ*Fw_ZneM o+Ox=[ ѯL@ݲOS^OC@(^Y\VXsWdPGtX@ >xTsYaXtE @78ǢS*_TF"8Ї6=>!I+)W'=F,vu+1"|JI29gw'n%eFW޿+[3mQ)wp$ sCܢB ‡L1Vb/8|pNF +-Ic)??a.m) pg)]aȀj硡%A  j8&O1@ ) R1>|S'ׯwHzccD<7Jsu @@H$ƀŒb.@ BSb.^)- 5 z}H4` p("@ @ !t4PjVTjHh b ѠH$@ @ BqD x@A IQаHA$L!IMA @ 8BQThnCē[J=D,eR1wݝZZ@ @ +D!Wح%tzhЩ okF<8@ @ GBa.ՋibiI 1@z-k߿;1p% RE  L& (yu^KX)uX"PL aj /eTOfD1JIo\]]hP'$&dRooo'}2^ňK{:$?@ @ @(}"*y!yGPR( ִX,~ WN X$s+WwQ_uC X( wcOhKT5FbIKK(**c])R"w5nwǕ= {@R87*!PdV4Q7'ԥ`K@ @ (.+ mmmy<EQlVitMU\.F-[ SnGG3%5@eG~Z% R ?G FJ#18^"J̐.i@ J?<@ j0Ppj5J#;b2_y3]fӚZ$ywxFu.ɏ@ @ AQ3pڿ~BƭaŻZt_s1Z:Orس BfC~׿xDy+x[wwP(å}f|@ޕǏߏJIK{O9;h7S^>fԚ2&El? ,2ClUuP:{7a`Y$$R~}{;@ @ '?a2mOisݝUy׵|TUA]j@I˶qAQ!?-H ̙ KDCqG`Ok珘3FCµٽjDRR"A7rP3Z{O7L IDAT[&D @ BS.]JLLtss/kes.{ZW9 &\Y5eݒVy篾Om_/pDNTuT]sΟ ֶ~E1m$opCn|@3`rycz̸.ngǀO".}u=(rPQ}8_<`HE0%A']YDjӑKGKI|5oZikG1i@ u&URi@loR&6dC;CySXPHRRSSe֭r͊ /K}tQxrM{J(2㿪mW> ]hQCQ"XO!^BzXuzEmZTBPP NQQHRR>$;;+_4^X$Gx/OB5Jpc>>>ժUvw] lk,ζbl ($YsTȃyΡC5z}On3(7z:bCmS}k&\p |GEZKHҮ_$r?s7ֱ2Fċinv|ՐqG z^P][mD<7Jm}Lz`Mp)>̌}' 6[&it2LPРOhd$w^ŝzL6g%ћ3cbbTϘf4~jV/9va^4t!%IH3d;5/n{=a)9Kꅵ՘i,k(J)m>(p}]+βM+ }8qM*tu`75bwv?^q@q +|<}WPyn8@PS"ZG_ >|…;vla+8 u D-"$){h5""?OsP@T$1X#{t *e٣ M"Y&Gɵ۷67)/:J1.$xm4 (=7ui]AyfJJӐVx_낂;REnoV˦gS*yN)ȷjyऄڻ|ZS~EF7 $So,(1B]V]a!u=,|v986`*ߛ7@EBzb$ OyREӀ!}/MSשڥo)⻶_ Pׁb'nMq|ӜcO!MHzq]qL`PoS*}.+ܮP}nfg٦D6/}}f܁}X+R@4fNk3W8ʸ3'mw~ 6 "p^VuX{ogW6jHLL^5j3wÁTR# i,fR_SBmLciR)G/-DM=lz{2?ػw0P Uq$BΝ,9#_ŀa;~=B'4r۪kQJڝD{raWt-.Bg]{wjQ%($s۱r7iאgVꋛ]~;:Vefrl\ 8yC*M]mܵft /.4ӫw3\*5vUeĿz^qѤnEFyɩW.]8WfldZLy>,bj&Sf΢ܢ23J#)|x'r>n65'g,RFm[k920gvBWw?>its~ ɘ7cn.]+B62 7Dj?Ӻ0d-%lv 8ȟyEgǝh@KO;#>9RiҭVk\?mL8B ޜw W8y>z߂N>%^pD~|J4u}%}CQRXOa޻B]E 5κMHECVhiGeHy|,Nlδ gtH l5w7tre Gkw/<)/s^޹s~we9QrpjA5?ݏj%Y%͛7gϞ9rɷo~gCz}v_uT*b]t#Welpo௛z5hi*dmX÷u^X*bߨ}碑V~wiQG,&,? u;# x_PP)K u;kAR03!u<Ӟ~۵rbt sسcFDnQ{F0-tr'Ghϯy(5$ۚ/vl=pS-n{oIyܞ?5<\_5:oM:*~[\ҖaHlTc R@25bR6iIgOg/_*Yx~ 2"OKHyu3ԘVM&Z$kF7N#^2?'Y}66)`@poۊ/9rI h >>1'.GZ׳P o' ̚WZ)/v=v۷o׬Y˗o߾ݹsgRIo bgM3ع^ . 5׾ϵ!{uS3$ y]&,84ƹU[;N~=aK q@P"ye^U$Bا˘GN{Xs P4g~<#u7`h3_~klsE7~^8fvX2- KOny>|Ue43kƬpFZ6m>ίV(sғkx@+0KF}Iϟg4 AiMzy䧷L BKQ/qZxgU[wԙ"׵ NF[6q06<{4O|̝7c.bx ;3$&:;V]EeۨL]3E%)UԱࡑJAhQ8ERs| UFWR߽IH7NL\8W5 Mʸ681aמoۃzkt5Riy1e.טO k SVv`RE-OCzvtm SN=1<$=.te ΢iy6nezr(@8驠<@O)S2cYϒ3%.((~n۾c)\weoǖ-PYFs[]OTYcb|3 29ֲqTfM>כ~Uw4pCX )w Fi <.Paxz@2eܩun;C!+֖#Bd1< /Ig *dרh"QBکSb.2;vL6@nPtlRE=Mh=GFR B5 E֫ffW]cEIާTJ6wbv[ҵgŊN߾7#mBm{.UbLlzL><ݝFT.v+cmEQ.hgH-Q$qN4.yEK.(P"h S=e}]z+9WP$ҝw^$_ԣ2t+_ڽg\3o/":a\ul(XbZ?ۇ"iM,gTERCQq6m6lT$Xu=1#oEٺXy[Ȅ 8{]&iJۙ_.% ܈𹖟'j6a[XT f(;zrG;|/ID/j^ ]{d)Beg;ٜ=o G,2r"Jnߘ¬UE\yT(`x.LGq\#є2};h/;<{F{@)2b*X##Zx\*=ѷL{.lE) {3|uxݠOPvyj.SNQEE隤8To8 y1J E.&^ݼo4_+,R Asf)j=.(,S\}A ޺/jy ZD<^|yR ;B4]V$u7{k ={:1hB$>3&& tHbknIv[Enj:*HV(]TX^9πX,ynWҌn16ܽ݌ B]^"X.:m?-v:7q+R&WRVsʓ&qs Z`WY%IIL/۱۔"ᩁkEBAD[Va)޼;/ZvGc6om621!W/x0LZÉzR[E:,ӏ\L_g/f[fgvI?^{-~EZٛW(4Gb~e4{۞-Rjvb\X֏'"BT^ oŗ0e3(n( )>dszWȲa'`aͷiTQd:}%c Mp-*v*Z02'>1+1 ,2PF82S f41b ij:z+^y)R(ɏ>ŹPW!rM[&DŽۧns.BΊc+(LCJT(> ~ZTjXmWCMӢ[F*ֹ([L3a*r 7G9sI_-|87LTCAyf^N5`y^C, گ =7 $]v>][ w&k&)|PA컼żvnv%0A%Z(g6.op5gf4  EIaZ?҇, -òe)f ,ڴ8 (6E{lhj,Z×E{8ϯzCX%ˑS^KB5) tiU_;2qymtU=ϔ"noRT[e]qao3oa2fWsaf5zv<'`Te9'gժK=;}lduTe{mɇüW4M@lS2N`TN8S]iԥWo|cdfN)5lOnѰaÆ |6pκ>b^BSNk6tq. tIU"2R0rg}*<ϧg6. h60>9k¾ŬË[aA`P oS|Ⱦ(ĮRsY?EVPd+xW.ok/ȂGP6ִމ 6_֢Uthޅ-UOqp, 2uCr4M=/ ؛WhS#]1>w'fT/ F>O̔J 0Ѯc3hxRBPrɆD& B.tt+GPΔKR 쌚jۻ{T}%m *i{OKbM}1(JJB":C*S\!/Vd}yuF,!$_]fHZ<+ ϋ Mg\HVQ4&~ Rº?ź Pb-;E.3ZT*]EXtP͘y9W'DXX@  ʕ BP |r)(*++KRT*D@(+swՕ͍6NQp3$=YOaJ WW)a=@Q_ys\I(]"k U@$ւG PXy,dϹ:v"CG]d%9mB'j@ @ O7Z-p'KF~kZd @ @ R';C Wڀ58R/ JL#OqY`r1?_#)>,"X "n>&UE]*`w%Q9*!{uGGƥĽܝk{4Kkg?@۫cTؾ?~)Iջn2(bNggb[Z~];7IWHԼڝ]e0/ǎ= h6t}kϙ@ @ 'I@!|=[3%캁^2n09b͜G SgD[&yvPonLyg:XV N8OAI%wvA93nk7PmFx3@ @ R'ʥK]Ħs;ZwvՅi';;#\Jɛ6}͜ BP;6lXEDŽ뫷y‘9ҡJmnI\ͳ)ޖE?spX$L%@ A=zOjծ]ݻw'uw5/ζٺ f=+:~T֣K.H+>wsi4o}+`vA}>XPC\ ~f:I^=(蹶Ƥ3k yP;9thf-׷A46qӮ#6&YS @ @Тd8F_tܙzٱcݻ7_) z^r>Cy(ELYFrTHNIQJH"*Y p,7ѷRCAγ7Vx EikG7Bv7;,CI8J @ AtR͚5sF7t=<<իڵ_zO09P/L$P'kuJ@PR{|ʸh>#L=m>6@lOKo-(.b@ @(SGbbb֨Q#666ڶX4Ps8ܯN^>*/Οfv ,1_P5K >@zdTTod@([,ǹ6(ЭM @ %2vu/_l߾}A1~I%^8 6Z,ܱK튚dnMerBaTZd~ CEuWJ$qy#2VdATܒ6-* )B+EԸT`gX -˓R@\pIF@ p(6i;eզ/o7?MSA[X B<=S=-H[k_@{!%i"ّhte E vhϖ홿B118W^cˈlٲEm;؊6*H~NBXiM|B|ld> ^nDr']Z4?&+$msK,t(+m:uwry#V2B\VFBsקx D~ RCw2c]=z1ZNBbFaWqvmZTh#=.yBBPL=h饙( 9Ғ0  )Rj 67"|ݙPTg=G&K*;ǽ(7n["b7z@Wi; :C"fHKsfe۳Em ݬLJ\g2{|m-Rfv\σ oWE>vOK"OC%l6R=:hXT1קjуoJ}BdXok(J)>d{}a =JF{186 *y;Ng jV ïf"/Wga=;EΓs:K|b\^c}|D\K\ >ϳJ [y'=[f|Pn 6~ݐXae?z:ת$.ަQPUԫV-,bj&Sf΢ܢ23J#)|x'r>n654(}|츳m~~gdSCGb*&znkt':.p慍0y<z-c /W& ?սM>%>[A-~ʎenyuܰQjmCl4T"dk}u^7TK?(yt; j6zkOsntz'ozo =[xarp+?_~$T+Ptsp|¤GFn4#tC2$A-\6ɟy&1aUI} Ts}dஃ=.<>;.-po{7n\|NoDeL[BFI3+n.pi D^:mfDcOwcEA=&p֭M sڮ`Pff̘>}ĉAGI*A7?s,Tuf8t q[;O_5yœsEiQeʝ\iўo QjđI( j5_zZO`F7=7swnϟlYoeo左go-{gi0{6lF) TtVά9(C LK>%+6u?|YBe(`CQ,XOa,?u)6YM(e KE tm*ԉM ~/|] TP8hO*G]d/ԤlqEOl5$֌n,_iͲ` gvݤw k q\JRwNỎ<ˑT6yr&{^!YJ+dG!#X|JlJBFDh u$J :0]yZ|ƶ-0XTvkte>}hwAKk @y |:smDorWnB2fm@6BθX@!-,4mƝ3'JmG҆2_CLJlVJ uڷL5[֮i@Jwo|&"iߧ}CLl-K53k&lxay6E{J.w kK4Pd-?q4ԫwt$"}C't^Ipdf[у{R6n"wΪcZ{ʻޝ[eϨ ;8 %.t &.c'd<7%.n( )#>4xu-1|VufzOs]6 mĒxG[GPp/r;Yzbiz֞X  Bz`@uжq<s_Ny0tR۟9]L4gr~#hdSfmk4 YoHVB@8}E/^^%mq>l org$LfD(9>UP"@WC9BRQwгuMsk6dޗ Á}ЍU@` oG-j'T8g(_L9$iI]ؿolabcQr\\\NۇEKxgwR١[9Ꞟ^{[:w*89{~t۸q Jध >L>OȌf=KΔHSxi}wԌnA=:]۱\J SQ|m?r;xt"#oʣ" Jc `Z?ÇPSxaģ| Q($c7iV# Vd,46hT(b Z0Cs>P+d9?,e;"֔#f^ scM*[go7mI=65 WbSOֱ`+ 8d@(4 p!ǃ8%w5!=tgs)yBzrT5 'MG?t (vߕA,(tfε,xy"vnUެ$f3wuweuGq|p1jXj^4@]PM𐜶lGYQӽ>ߩx"'5ܞo׾)UaLL^f3h>sǧ[9O $ -~Jy`u74+9$>"ȷ"gצD"")Ex J #p}#:ҴKv[NNnw]Y0jQx=}3BGkrfPFIŠVT|.Wu%7Q1 IDAT8:Y0qk>:>QrE@zл"QM};WrHNe{[0W6STaKF&m[P?oS}h.+VrE``@;Bذ(PiӴa"'yDzb~*2߮R ?m5:?e\fgg{oք%k]ө8$T+S,"Q`䝆BṔ:0c0ߞ2]1hȍ}dZ}X^+2cu*\u;toftUso_5K `0A)0kSw# <=[xm~@,e*i_w?ձLެ?/{2dsjzcEb9XXqmZTP>yP 9wWw6EeIO]Z̴ %{Ѱ4 HGDk,@M@B5+;Gݓ'O^p)AAAAAAaJ…Mf]xCN4cu(~gvE9c![-,-:d%(Y1IJ(ABT]fYr @wrb]9)LUlVph^積F%cC:N=ts@Zb>5~1׏z3R&a( )8PJvjX6|jt;#ϒJyr!g/: G-ݦ|ڹMKV/ MzXYJ@XQ6%ɹY!Jo^.GrQ!QgFiQ9՟ 0Y BpӺ0c!- [:w>zmrA>;yxz9FE25_ nX ->Mzfܜ+U-' @e4*V_g[^S r6޻M:yiw5.\8ri| =pͦf>mO\5}RP4mADzəm2fD_7e;xyMǹ,j83}ıS}2x\.l(VT*f>}/.Q$119P^߼s`Þ pЭ377]|y&)hYrw?@TkC y:ِ߳!^<v8) .lF[7omn'urwCYaĿǦy5nz7:v _qcǶ gnj Uze0fw~{Q;MlJע#6]n"Szr;(}¯]לJjȼ|^ EPb!Oæ|s#6iߕt-}ޮcA93Њ`%V7sRе:9>FCM0\tn#܈|@ҕ9^dGyB3;#9RhLLd0FΉUx} ':J.~+Fv9Dž{xM褐gŽhf5d@#4qYq4Rن7s#sU>K?}QH2e뀮˫UT޽~7'Mٸz@ ŊB6^SB..(?do3{?IR{k' g}L W߮y8 ?9P;=|vqG[m ǭ{-ưm*̌kv)v] &mLUm>~q>ބÚ V61A¹/NJ:Bajt?[2н nDoa5f&U#)h"k׆],S_r9 AP̒ PJ؄/dW)]T,fIv@#O^SdwQiѝk* s>5Z~5(E"Uf>qh=tvmV 7eL<ޑ;/֍$ox֮h?_t/4cL_ǷB|7e@GlݰV|ml\=@ b}!W(oS}lyB ZeRƏ}AQgoEK@9+Eo>HܰFk^34`_=[fDtU_ TM*Ve7v}RXex#= ;ἴ @ @С:8x;Vav`+Rw>W=[ .@4j+ Y?rԡ;iε޷5 }?(e){M¢XJE |@ @ (daRf1=!W>Grk w/EFFFGG{)nq}BmOxjU캚K9ԯo͋RD>%ٕ9Zϐ@ @>6MuPU$EgC_12M8&3~o2$CC}E6 ODeD w.<7(p9 uJ)@ SB1A [b>|y뷐ߞf;+{qAd܋|~Q:jWer\ԩPGбWGT7硍zjTu5[\[PoJ[3ΫO:C>8;fwnN/%zOE2΋@ @ %DQmxiV47)Of4Yԇ"h͏[ѝ_9U ߅->ӌ5 :J  %~gQSʳ_;<>uQ3|2-Z6_Ѽ vtcfvcZUa @ A^¯͜NiH҆P<] eqJTT*T|>q\f)(*;;[RTQpE"{Z;E|{bUj(ZFܿO<Ӄ {~zՌNǷ̿Ʒ(S.mX"eM|9_q@ի3G$ePX[`L?yCkiPHӓiii2cVvJe5H;ͨQcPLUxf| hjZXˁd)R5OTt剫F 7,8wasdRf0)SF?;pʾ|:Y gV~RN/fvW)&,ēFo}zaSawÞ:o߱j!U)_$oJ 'g~󹩳3 u3BaV, a%oR^ 3.Q3Z3m}Ƴ{r9qX@(F1Q3225ހѳC@SWK~ȕҫa"=+i~K6~ E^7ku 0IM4Uk_"k^s]!)_d m boϧ2>}@V&Eb>φ2egO^}m##Vjz 10+8:hY"3RX k9B8_@fp;㩠(n' ܄qC$V\r$ N_/]ٹf'Kpz䍷Z=vcNE:0W,˦:0M i. i[u[]3kN^=48>o1",c]zh.pl$8O<>K2GX7WkhK2|˘=a[4c\)F:0Ɉ)aV,tyvnkL3u>A ůsW@:8Ŋ,1)#-pww/ue.IwU2E3Wffrڻ:ٵTzU;JI\soWw1![u+ B7j 2ӲؕjlR6Uմ5K/'\yQN:مc"~֤Of^vwAUM*2_<{/U9WE#Pz%i.\YkVkfgBX?EA!E3/gA^RYY⥯)^}x' >nܶtB, Q)}kDIKj 'v* fH$ Ӻvـϧ aEQؾO!yUG5kN7yeX δN+B\Z5eoN`M(nG]MYQ\xkűїuCgroMJ5jѫOZB.Kz3*"w6oD)R=zүA:ϯDG{UI[l ZjNyJ|rε;Ipx4o״T.]7`9>$撄Bu.:߼y-dĝ!k1uvfӎۗA)cw,]\]c=cwCMNuP @ǟ?w^!za5)ӐLRd4׭[P^5{U'[⾦W.Ǥ K̼2~wSKZ.|slP/ GsZv2O{ڱ3M+/0lYʥ#W浐=64]caJR˽O%S8x3]8\S戦Ƅ;sEڹk~{hYcmƉo" /ۈƮG[ =f>NڬW='~%{>钋g@Q*5}>˘ݾq5ĝ (aFGCo::͢,:@Mۦ8rU$wY=ؙji30ߐ˓qr1ݺ+Z[9+yڱCݥz{mzW]5[Pğ[ܱs9;o߉~sکes/J[O'&撄Bk}rcjyϯx_瑭:o̚r^ӻ!u`߽zRGK_O524J^zYDMy ȓuUW{pYzӓ^wjLM(kf0iRՔ Asw(/\$Qk$_I&N8fX/FmpKf33. D&fզ^4 aڻkð52 ӂM)wrtimUnwVBQPjψ6;-݊i5mpjrǹ x9/l߿ͷlW]oV^J֕ѐոLɴHs)m=&@cLCfόv8z3/z\E׌)DEQؾ'd|oYa.Z)Sfd>ridA\rӶ/oʖGԁ]S@QQ0x)絽j.] 䮾upz&cYj 5x_u/xB@6s1[d&۴Deu|fA=! [t奝1NU?4#حI {L n_8RҞ.}H6o׳G1;h!jHG탚])!:b?hg|F.ߛ5t Lmg7 ^1^yEAG~?>KAz8drp]};:v1onm_kFlu*>:s95vu;W͠YáC *f]Tyݔymf$M= 3N+­$K+5}>}ˊ쌛pg^1ς`WS|k5I*U-9&_d-מZ/تqg@FꩇN C4r_69Xqʸ}τQqr'z*qVnY.ݡUM Oq?g͑ԓ՝XHR_;:Dgtg|Nہy[72#CA98Tm2@yROOR$T13 A|ߡS)]>[2âd-3bvUkzSoܾt6m??-+EWkh2nt)jMdriи\R(^#Mz YLiM,eS#$o#0 .TFڟżF^=#hٌ $՝0E^H=!d_R EvlQjImw^ZϙU)a.F{lysu n7}Cc/u7vl+o/.>um:ܻ_gG{L|3t'wk]nQ$115Q )~.z~[/)T 'q旬ŗ'{ilBaui#OÇGzN1eC y:ِ߳!ʛu`b/ ;POG7vb^@"gkoZL8aK3Y AWKeL/UÎ 2|T@m֘5ن,+ [5}ʃ -lr)RMg_N#:Z6 т3Әɂ~v9XqqO8ƽ̬D.Bai1HB30wU28nfaKh#'|TǗ9len^?͎iͼ|T|n4{u̮|EB' .wbn0rؔWY/uS]J}O$Qo~ QݚyU$ّ܂?c PWF @/I6=` fpį2dqcr@ԫТr2򮔏CzOd-מYؐ,#H(KH3\XbGd&i|}Z4}-ئz<\I}?a3]IqU{V]n15nWx ]QSmh-+Աi…ܜ1JUzLF(8:M&Y~\?1ǣDp/tFF)?[4%k-T҂j`o/D #5e oOl5- oS6aD\<~aByi*pP;>6omWЄ1{@ ~!ptڵkϦc y=a d\9P;=x/;֨Y-nucbxxKGto*5D4c62:zu7/U/Z6RNuʑ& 'Rר)RM+۩۔"鱑E%W$QC=ԸqRG7w4۷ RHo@'= Sy?&>|-q]~8q Tjt'0-]w {}rL;א٦YҦ+/uoᰠnzōM~4(3ਦ4RƄu;Te=*yEA_y lI`lϬIVkhW2y ]vS 8|bX.:YTgءQ@?w _I6ٱi( s@xk<̉ou/ d E}:NѣGQ#53H'גݖ5ꊠܽu;+9Cs҆ 2R((r eU̸)3H(:YuCe_0r֤I'^I9em@ hNჹ>lfύTG\Y'E) UEDDԮ]K.Gzqƍ˗/oߞQ9+ܪ5y2B\`q%~>OٔL5Z(Ed2@7\n-%L4=o>LWz񐊮mN^ "huiva^8("Z"}h Ytbd97ڷ XkhϖxyU,R' _e8G'uYv]FA@MzU'k]s.gNn߰KsNՉcN@k T]М[dܮ-@CZ~R9,_ylg5]ĊY)M~쭷ޕwgizgqX:ۄje6E5oL>Z;7;mޖ+MVk;*;ŖhhCOM}^c 8V|}bVn}V;π {itU.azo2@H Gb@,j')Ѱ -_fGnWR C= *O?IE?`O4 F TkhK2|˘7_'Iu\ߦӂW4L2b.f n::Yb]SPT iu< :Ö[򎢎9fiߗ`teWz0#99f͚kժ8 gKig<|ix޹ʂ|S˲Ear)Y x화d khɫqü :6u88ull85e]4]W3S,9iXP3B1I,WyptB29{5+mʍ ZԔ1oLڹۦ͋]i@nQ5 %GenȪrNΆQ=Iv'dDMûg^ƽ}q1~߬z2 m YC[XN;Y@L~8gY3jh %GhԨQF~ak{pH4>4\[a,jӜߵ2އn}`{6<)ոF< o:Bhz7ήE}@kܗ4W@+\ӊjn|lڨzJ7ZEP>\8φ[eWDFugj&13ӛΠN+jd\*:aԺLA0֔ZoQKUޗvB@ׁ4KhLL !}GD:|<ݖjhSŋ^{pʕ:tϙ!>NP;<٬ќ֣rs<3SGsٚ27 ق|gَmM^Oٕ ~sUt "+ix~LKjU6k]ݤty7*h6vr1=9f̸{)222::Kq^hkJq$!?&&}W݉<=)Mٗɼ#3fK(DowS3DÑJ +e"K!y 9ju:-r4S*PhRU3cqut%)KY+Z%M}/Ur5ϥ{Z "i\݇yH7ڋB$G$q w uT&|NL,>$*\YfJ:¨͋}L(D*b5}ʥ 2Ų[(xo-'4*}-fJ' |~ |>x\.l(VT*N IDATgΜCkhh۷on~Q`~ֽMMI2"\WicosؖD)62!|6,` ڦ>h{lo"/C='U HT'*0;68 rIl_sBQ~1[Y1Fw7KïPLgAr)d* f$&2Y v=׭"䲔Ϯq\ `VV8BgBx#%%x\I&L; }VYEQؾϧlO%Pm30S!++N={3k~۷ogRp29h^q>ԩԭ">z_2^ xxiy,淕 TuKתZJ Gߞ\ٓo՛/<M0"_8^ W 60Ǫן$I$4)@G/x]K8veT.]&1&׀Z|ϣ;/;_D!%҄']=ȉk3$ʗԥ&2\Ǫ{-RSt^I,Bvh઻ZPuư}m!G Pظ/d<3C%kR⻐Tiͦ4O }ԑTZxvvzRw\ᒃvGã!H\^T%-&=bӌMzγ.M0 g }qtiLw(!+[Izy0Z>DmYE/o *]}B(2ՂΝ LEcK 9  A @ eLb86*T2gjTVdvAY!+QU}"P)E@l0/tupP`ˋ$׮p yv 7D&*M=u,9xanTcW:ද Ti>|u\bv{X۟#~y=O 5lMq@ l\vQ)d Wh"21B:I3ojxvdk$BAqŠf772 Sm85#繸{XyHKԊ"5&qVH@(a%n9fW+l첹Lɔ!TŦͫڠA_\N8G1 }f/IOsrr}6bٜ S%鉩b {TFR!7RkwQ̶rI52{>er|~W%kh?2o,*R"*bc>#KŌR@- H&N"+`gHkݽ*>IX/3.YLU92I皔)#>wu tʦ/׭n ?98&4|@ކwll^s&~QpAs;u^X]Ȕ oN5tmQż&]IW)8}+JrN|БݛVl(5&ӿW%`]6xwV<+'anqf#hm: W%khC2~]K1Zab&aVpfuZ27òj~:]/9cik9\{_WҝPD9KB!ët࿭N%ΟڮA1g8hAn\@֞3 ^\?_`[d^}=(3`[f,gXu vsH 4g* ;\6%4D^ i7gnY꤫gs0`9Ko$h,`h8}T1'joSz)sD%2}򊢤} mhCo㲿ou1.L4Vizd\$ ΰN+J`j ~٪v">tΗod6 E[g ع߉ ]jטjeR=zg}vʕ?cϞ=K;mx-/0[9.pwy~)un 7Wa &U#paA FF >IՅ2dk;ܐqRt +(٠,(J)6d1/{ߴlqم-w6mQ25iiE"&nQę:zZO%ǫ8{,o J}u±\gskН[ˠ<[Găs.i}Q_k)R>xmӬA:O.?yEԾRڸۃU+]W)ѣG5K.Ϝ93f̘}Y+}Z5G_CS@2eEXWR#T 9 8Prz74НH -Z#gjl{Ry[>(s_@*_n)٦r2o2enNo0-rwָSv~~4QN~W-їɯ!o$W^OL螷_uhs ]oShGOޘB AڬS 슙g@JhG.#.$W 9xrSp ->v?:,K2oNJF@n9>T8x3]8\S戦Ƅ;sEڹk~{hi8Mewõ6#zso/ /vO nܠf=t͢T} mOdž2eĆ(aFGCn:: ٭铖}S眿ۘ~{pߛ+I~xk@Z^3se411~9 3j+?!J싙3g̘1iԠcOk$[vn^` l#E3/_ώhL8aYPiAɔ;;UiMVnwVBQPjψ6;MObmMFg:qn^wK-|@#[U[նue4dx5|6S@2-2Jk^wHω Ky>%6}!?IRE߰(%l_Ca0e)30ɔ) 3TEbq38Ut',7nB&Oi{7_\u\g wƯϷ+WͿiяOٔZoWX2Xӽ0MGRE+5=3FU5 q TR 38k֬ipVZHͣQmW(_GטAW^g/>WN쟀/ȝjxwQ_8=/!"evPf;; Mn׶ t8tm Z,*6pv[scyGM>diw߅T[se:s95;;/=ޑT494OrAA6, .;%M= ꇕy1(iq7hκ0(JDlh-+3nzJBqfA}[$^;]vbW| `=ڵ%k%rc W7΀yܖc[{@ԁB^A 5!/|4ٳ ┧數7y` Mk&0sAEùzwWml77ȵѶ)D:v;9SRtjK|~骗ݬ|O\\: ZNCԛg8}@lp{[nmjZGUYg{QW I%H!$~wϹ7{޹K6'N10 !58]W~{hg6Gl;GBSB@] NJ(O0SDش(Y4,! v%οu[mt:\J߉-OϹRf2lʷ#޾unύ$#>3P!/ioփ[h2#y74ck`MK}Fl0# Vd*,465l顖Mgtҟ,f.KOD[S|Y%9$UkҐrp`kTIL 삭(Qu Zm}ӏ_V)G``ݻO>ݹsg͕ǏUV'tL:">V=|j_|&9Yi8UQA|cdafc_.?u:B!:R RSTimI?&Д@xH̑agla ,<P;7.&T+@Q.q]&Y ɧ2ͣsd>q5,5KW/sfM𐐼,W) /kt≜(ug%]aS"(%xOQé;l:GxH]x;oQ]CaXO (CK{yɻ !1w?/!/G86%fRloE_3l~xޱ#j<ޙu9{k ¡r޿M,;-IE%Z)h;v1֭[UV}mۖh#;FZpk  #3xܴtm'=Q/<){20F@(j@֓&ygњJT 8xceƕ<<9˷MM!2\9M3hp{1?wpkUvT>{JX ZZ օ-b듩 *: >s]uI+pԮNL~̿O2bY|Y0@zª;" swc{K+9w`J)łA'y _)l-~Jv (:5Y$dNĦf"l4} kz馣^Fwfy6+oMPfU c-aA@\REڵ+444:::00e˖NS7ԩˡiVRs@TŽyPjUStZnA_FTUW;sJpet;1;@lS)KG%ŢTs(jͶuUݷil ndXҭ]'@xo$pnL*9+`RCdG2%d|… z䱁:oBU-- SN)4 O@]IhhYj EbZ?% sz $9>"sŘVdqٴiRw7#?{NgK# ڽ*V(STtjjРAnhޯ*<^=fooǏ:{4ФCz{i΁wmm رEB[jt{} tnժp{.r2,=G,o C6RMڱGC ΎYۓLf=T39ookoUU:=tƥżyӷG<+L@3q"tc/a:2Se5&LC3L 2K.nK),gwg[ < IDATm^AO0n4R~ 8<Ϟ(5Q=)[#{TL]Ǯ~ʳ?LĵѶ&4MCQXOI-C6OYnjTe;^KϹM˝ 7ɋ"H@3kY4,5hoLOZ E27 P8y7h7xަ&j[d.lΎs2]{:1Q ]/\:,oj }ؽTM _3xfZfWcGp*6?3?><]g=Ve~ [}1Rb\[7*$@bsO H}ӌ8X }P($ m*qhW5elkMgQؗK/j8U>]3q%`hssk#Y_C3|77*Yyš<0}Gj?_mw[ ݽ{Y] [ 4W(iS+;%$D'hd7[köo_҆/NMz9͜de^!@Bw{w]2Qq[eK }l J.x_N?HfGWio"֟K.o  [fu偶MqߌC.d^߅(yFZCCQHXO,COeyW(д@ 9 ;B]ON21cX۴ȊQ7^cE_Y*>2ӵgJ,)`+}3 e7"(\rNZ Y`^U+ͼ>׻~| w o6:&V9s/#|4@(l3NLh}A.lʳao감wg 2/ذ[eYr_9IPzv~M?]IBFhx|.a/cx霺W>,#&ir:af׻Tx*p¬Ų{b'ww]WdI'JWw6NmuqUnF1n)+t]8hр}"NiUxD2T>53HJmr6 lp5>~鮰00YvwD=nY,LЮPNs61gg()a{k>o:@Mo|{9 nwrPoSːS:"O] gcZh"b&GϹM˳ LxUUx!붑1fr( Yl[2|۾{پ5p,1')Wy;r$.ilyx{{{hBF[-g¦S8$ 2OkL%!zt(zVn?eI7n:]DWdv:?xdE9Wþ;3miN :2z?I(EY]Ȟ>7 Ŋ]]d2,c? |({@BƵϷ O38i_BsQI>FW|袮e> HYoȶ=3B!O9W8Ppv=P/GMtC* i6[@ukqffۦz5V|QxYKgWeޅh07)o-]ٜ_N. Dü0~ W{yg\׮Wj6؋mYiyTa)7DWP6oTFL9ۏ^wzCEҨ/u2bYC0w{⪱7TA1;KZt' `YDI1Ir@-&$Д@ e R8B!ߨ^fihxR)MjXb/T&EۘDQ|)^@Y\*ɤXlTӼ\̯,(?ʔjkq7<8rID~+JhuLB^Dy B.I|t}m'vvFWP )eXj^/e(ZTޚN]LLD\Q\8rq U+PP( BP |r)(*==]RTbD@ Xm9KD,W*˙DZs.gY\pH̼} I$"vG]{4SE(]~5cėx)$,A lplMWP ZBPC @ B82qc?_"0sGeAiU.umi)^ׯ2Cn_ @h_15@JEm'$MI*sQsdZ?% KdV] PR)?F\}"z?ŗ(6:}%4jltQa'uֳ;};yWP'Hjuh+K slwط-M+mvhS&)ȑgoS=e*nXC @ B8j8evafz+xGpNKmg>,4#q5whHwꅽ3>Ի^\ݿje#ݽ=qu6mte-[$,8I-U*=֏-g12N=1tK!@ T)B/^ܷoߥKԶnm^v>o~ΥIc[W,ɂ}j/\MMgw>dE2EPxcNPҙF2h6p ҉ w8p{,)& xnT׽{ix1 6v@ P:)ÇTիWw޽cǎܥ#a~\YWB _tͫmԵ,"|HAG@UߛBރ1<]XO>f[u5Eܻ랛_UO sᇑz[2لO8,Aޚ2߭~Ph۟Y]]{cdNV&dYQ@ @jÿ˹s~-Z\(w;Hy5B2@ @ L8ݯ]fp˗[KUFr?=$V7tCIGr9jUg*(/D17:N[}'AY(J(LWKj0tS5H#kجI~%'F?OWdtǎ ڻwopp?m۶'ٱǠ!̽3՗e7 O [i )=RKoO k5ߞeVΣ$!LvLovla=c5_ٟC}^PΟ}l_/?6Tu]B=eeW?Ԑ1EJgþ~GX6E[h2dλ"-11iq|jd<Y"fvgզM>{3{!Hqu`e.hxV_w B~9Nfװj1v  ޢ𥸰7.fv?^:b7Lk- 7}{5pǿmQo7Bm۽ :2 we\TP8nڝ@Q+MG \c@Ѻ !ZG;O|Rim8aO!}/7q5*jf2q7t (mϡYc"N#Cܧkg>}7E[h2d{!i^a)Xfewq9b雅U׬^(O-NZCm*DL,-tHk/9B\@8qM=+XT|y 7ӽy 469v4A?S!1fcʫˑ૽8`lktCy#}rg {V(ʭfӚzG)SGWo <4+ ū)m=[uޮq] Ρ<:5|ʾ6tL![ͦl_̴ou=xe@QI"5@/uu#^]Otlz9 |?(oݾgԨ,I|~3W iFײ<ӓ^ܸtғݫl[Yuܤ~EޚG ,0t/sľ8+ѭ{C/[_APU}7Uj̫eJ[*~5jr0!!>|+M mMφE^HJ@ݜh,L*-QͧR u7!K!.:,>ó/_uịUJٞϷP;hQ H,~ 8R2ːS>_6l?=qHxt^"b`qmZT舿d$I4f“>sm"W;_G/ŸoW\Oe,)=6ޚĈ9D79ň&6ڣu1LvU4xPE'߾}ȖSH?w})*C{b2be/KЍ ?{tUs?{3 &~7khYVl mjër70N\bOFV9f#o?NJdѸAQ Y9'NOhW [щLKOT܉NW4=BI.x/T/۷9fͦ5Mۮr{80ȝyGs/om`L(񫤳C:uT]o/޶;' 1Oo̺ Jh~η9o~D/w2h~;7[h2ϻ]^ <>/JRQѐuf- t-i,IYW|Qq':]Aיypx&C*FaHs) 0ݹt|۹qARw훶{w'w˳y+!;;M͜W]5k8f PnO6E#v63ι$*(}Å1ȱ{!קdzzWۖ޾]IȻ-FHe2hb2;c3tճAw^m׿6/*?CAtK&sZ-dRQy}ܭ=_ l=lN=:5qU^cHש5?ϓ1o! yl\" '˨\폽H^\/Cc6MT)uVQrYM-(,N||gË vh.K}kĂ1_g.#0324_ˤm.P]r~Ľmm~Um]+0ɱBBbF{nwDIw:.j΃a}n{GټQzq#{gv@󅏣 Wi۱'oxw^n3W'!ū*n 6< [6%g26#i~ KB-~JQf{rw9EXv=aR{GgݦY(uM8!5 p+tw\E$M}oֲɾ:l5A9mtC4d:~aReZ`9fm?V;i9dD77Q5;&Ѭ8Pimמ]S@|uyMNN;r}lc{p\w~ 8R./H4jѥy#S١ZnhPq@TάfKUpӯ9w|ׯ' sozc.2g8VI+ a]kk|:'Hf?& IDAT$C_<·3ۙny} ~ RRF;뮧]ٷiUp-&X,SDPNҼPo9ߡ=kZk oP&O.kʪqg@ղ5k}w$ja7|UMsQ^"'&z*QkGhPYŜO/#i8 jEY 9A btnm^צߋYhơTGBʀz>*Yb4  γp%ԘW^&}xo {~j6q7opve4r(_ˎzÓm>B]`3fRej2j~zf`C1ln-OhB.u1M"Nܒ3¦'!t#%me‰U=1ҼDv],QY43S@ܝLI)!'% '\)eylZ,YBTi jg:[9bFשPNlywM7/T;3O_AAbP~ R{}wJ/v<"_z̡i̵"Sgۦ@}؄P s-"?n߹RΣѭxt6l[ToϭhrFt$5kH.pdWl1eNyl^Pjyf*/U+Po8x'C3b7}Lp'~n 8 AwEʘKo6Uq#=i#F|ʻIF7oFfa \q5QBک["E/rI?VɫJ}46yyWP% Pͫ12B+iz:(EeD??x"'5Jۙov$\5Sק*}O--~JBZ SM+U'^'9T>/'ZьmءQ zz+41@#"ߧ:0XCZ,Cr5Uj̵M{D8Wj`њw-k:ޜ 7Iwb-j\e%Zi8҈PF~ djizŬ;Rҏɾe=vlUs~B}G.UM4]ٳ߄ջOb!ؑYwfa@֫ТrOtEɛ,?="D,ɒ>Np˜ir]k KCCQHXO.COYݼRsgTjĦf"l4}بmjT/1z4=׊³\|)7yocSmvޚa(yg~U+UPI01iKMGvL12}1x7׳mt-nMws(>w5.#,L{x&uP+lG*fOb$>JŤ3^a*>r9EA+PU᱔lYE0cL*(xhPol?hŇ6+>]r_Y$@C;yY S3`fue?2̈bY^ ѕdE\ ]+h@oKŇN\d].l疐!666%3 Ŀ_z5J#D0{~(}sXxμB{dEe-<Uع }l Jm$+7rA24%yf')B:m7g/u(Y@+@X6E[h2dYwM 2|+f@,3EϱM˻hgS Lq}̰cǎjW,Eg>ӵ^ZIêF<1mvG{*Y“sLZKA@\5 +U*#*:)Yg~]NA)T'F\mua#c#A(LAuirÛ_g5 ͕co}q|wem̟Z{Ƕ\DEOwɚ-zWNv] I{v0=zzl6~?`NPRd"@a` g0A;vB:寯8lƜ˳ܮFzJT9[?zj2F_,Â~ Y,2yx~ʐ}i@Mb;Q{:i(b/f ~tmZ^Ub2Ní#eli"AhlmSYzk/t=E;@o Ķ(j+UBF;II @ۂFxpL22)gҁec2~g7Q)cQs50t ~6kN56>2ک+89FW+zf.o;cǑ B0/m5Gji[p Yھu4,vr%l S]n?=;M\Uhڠє֭[_>3~n)̀#48smv gup Ok[wlGs;])_o#aa^@C&pvF$DZ2MAV6,lE7>7[c536՛HAy+]t /{3. Dj0쟾MCQXO+C˼IɅz "bf`h"+ʋt{eȱA jH7~5C?wk|ӎow Z'3Rw(#j"!Pj~K\5*(,+#X/HۡMڏE*5sL' `N,^ɱIr͹\NEZbb M XlmҴĘRJ 0#jhx2MjXl( 8ml@ jm;-Ϣ02YL ؤfxw7/_"ޢU2%P \rM<կ$дRR0W|xbg\x#e%| J9ٚvK( -~JsڼB l ǟ[lâjn^q +Ϲ\^^VLQ))^lƨDŹXB19wS]&Xz<"V@\+e5{?z|?"1):1#\ݳA@ߖlOH,'XZy(en9~ Y%2S+ԮS2\5C ~(~18UW6PB{{$]x Ws0dӂ6 :wVnݿ'yL'AcokmZ V;#@(Pr p\x1..=000׉8W0ScA "0)\LGv'W=28ϝjHӧ4n>-*JP|=uG<<6гC/z͍[t5@ @ P4Ç۷|<}#vU@|nXt˳AQ/R=SHIkMr)M@e@᡹/5!Q&d5⹈H$A(T:(2>%̭?GoS"˰Df`%E(AŋUVE7t9<<ʕ+-[4w676 2m+>7 P%ye]GG uմ3r0:6מ|ޮ@ @  "pU\b*UrZm2B!5ѻcO ͬn+ŋ_[E}-S'@ [ ~5/_lݺu.RY oqו+m̈"Ҙ-~ӡzYvNq~38@ @ B~)|o޼9}߿ߪUSw>xg YS;QRx}~[߭^Ft@ @ BaS4رcĈnݪZ˗/߿m۶%E sG0 gj;AOC][tMFMMX$A @ BaQdڵ+444:::000{x|pOs#|5, ',Kɛ ͹_zc5B79Tu-7<շLEGl{!tS3f[[ܗjvo{F7۹'b/c!0 vuٸkJ0 ]ٜ;6N@[~R @ @ ˡ\J K"vH?MK\;s'E@ 0@  mllBP(|>q\(JOOWT*la_"qĥ !@  J2 Kd٘I4rьd7"]RRh'{xx1+̦0ِx_KNrrrThzs)䘄D9+Wc(a$=??|nt%7=ijNA[u[[Ji;AE}=7=C|S~uߌFSÂ~ W=d^J+WvFQ 1#qmZTd#jWۥT7wg#6͟|N~_Ƀ<_SX*[jMF B0B&;f^ ^N;4vc{1nrVAu=zڬn{Wrtb~>%6v߾, *'.ӿp,0҇\SHjleę.2b]?y3="-~J`~w O/@|5ca186-O*ߚvT ,SL =^󯽷v̢r ,Pˁ%q 38B !ִAQC70/+mC ͈0o^4D؂=N|RNۤ$bgBJpڎkyjfc>5=9 (ٮtK@ĉZ8(}*)~PCgI')"-~Jb}Xў=Vh^KF{186-/*y rVj1+~PSz:>iM{}nTbgݗsc|q!.1 qՊ?$A |b+Hyy'RW_n7*yzQ$^EᆔoD ʲPvaً)ĕuչgf HyqD۸/e20V4 [oƄ(-<Գpe(J A{YbP? g\w:SI|,L:*<ͰWI$vUZԪ*I|z78S̾g";WkXV)~<⃔s070!!>|+M MφE^HJ@ݜh,L*-QͧR u7!K!.:,>ó/_uịUJdPmk[B\7嶴~q,#p7(2BD!˧}m*~ۤCg~z37Ey(b/f gצIEq~9 jgpLQ؝T]v <-VįݓY>mA+Al6@C:guONPW"14 ]=ކЕ]~8sQ;U/SnB3w^gyפm-ث3ld+֞EȥyGs/om`L(e_%ש떦]o/޶;' <1"Y^wik M̫cywo'SdƲ(BBa}G Za(hÏζMHzriI?wX;.lN?wSיw$;l5 ήSv>꜁LzkYrfgo #{ Y[3=Ofș]W]5kA x\>V_`oaJw ĝD7C;txϚ)TxާF Qyk׮1xFwOcU˘<::_*Y54IÃhjԽ+&OI36hֵgQ72…ˤ%mcKǎ3EfDa.S06/%=8ꌔvu_e&nyx#;wYl(:Ba,7yg(3犰Q,@^~tm*q ~Ư?qL%ʫ@;O*E^I11^9Y-uxk;n ^~b3[}ﭽ}pЌA 0kmxΥ:}`?pyOGZyW ɻ wU5 qr4Bfp%J}A_j~NFrɦ-iɀm4}Sv]~8Raپ}UxR/[oѱvfe8Eֵd&u-sꔌ޽w9sfތKNaJYեB1?|m{L1CI %lGR? G3cFr)\Ĭ+2ǎjg֌rS.(N)\i\3sF.+ 5n3g8}I^$AQq0ފVqRŽWuokq nj[unZWʐ E /̈体{ν]PjO^ N]p~إW养lJL^\K|dYum]d畗2Z{yxΐsotߎ5JK.s6E{–4q53|U.&La3#s`k%$>cC 髂1#i^OQtLZȸD(&fv j\WE1x]g:۽]<+#%E9&5UMO%&xɠ428Eƣt"IT.`LmkgoҢS˃;i> 8ۚn~ެS6^UJ']N0c;ܲ":>ţ  c-)}urŸQ^rg0\+qm*q2 `hSTč|4UĜbanm ʼU(\Cj+BE^m@?;{kI y`{wΉT¡l_c& +rN1PZQ{W# QE1Ehvn1l PzдK,*mr /S @L˃?,{\7!"ƶ27|>;%>7y.=C SZ ܽ -ckr,V#׀(e0>ġR']ʸd7Ζ%q7y1~@îFe]`tĕnD7jخKiubVO Xa6.-_?5}?)"-t|wZ (֕5],fOP-M (EƹipQyD&b?ut*2 -)~G_Ƨwx6'oMPʀn Gك@0Va~.l,oB-wC 1{Pvo.c9HxX e[Nݭ1{}GpnqG<$-ŖI1(Lk}*]o14.b|rXVdwƹiR!*ыftt45[bkmcD{KsT#ٽq(Qᨧ\5y@s_~aBxg='UP܋n;f׽$vۻ' /888)[#mzĶ'c7癧jQe7~7wc'L0 [oEs?~@ c[j(E;߄?qС=/f,$8RƸ}MgteUPd EKAsATX99?lڱ؈:?3o ЭG]T#9Q[+ФΥg^Ru @l%Iq /<~0kI ymA\nɥ]j3lL|Yk_'-vfgS\\EO,owl^A@05Ei+M\u [Ld ՚h|[kl7V?9&&T?-±(BBǧ!,y4IJ3۴ܩzzp1|S@{mo, 24Ee٫TfZԄ; =gpcŌ$j eC!8"s0mgg k;d_עSd2L&jA{5BWXrFqd,95>bC:[2LuЮm7vuEvmt3Ւ!"֭',2LviZ~7=;51n}ЩKnE޼}οMeCW=m߬؛w/9oޢE+,|`s@[ ilKzvZfgsG>@k==R{?6_a$' Yu:V^\\;2d k΃ީ =Sz.M7`UuW* @JL0lpg5eJj0f8դ=\=i9ppi&eZ` x1azSooRԦ6aþƫdfAkk=f(o۪ͯ!fgC:Lyfuj2dyCWFW(y#?+Ē.ۏiޤR?|9>4zrdʴ:5syrlo;Ƥ52ա݇>x.K*vK2Y-*=ۭ:ʪt hYxxqc֏\r4h ask2h^E8y's@P~lZcu$pq&p(=Uڽ"yC˼G_ be:-͢gTn$ƫ5w' qdذٟi> .xvݾJ|>QlN'`+mK[sQ簎x5GxMQ.-hh2pTMYs,w+dE'*$3nlvo!$Fz`T&)\[.9ͮy~;ɐ~K6jQٵ)h} 0IJc ."wYKbJ30FoJ@ ;ob]WA.%'5E:-/9ڰڕgh({мԵH#r'xIlx͝ E:>ű >eȨ>J|۹j,xtL;mpQ]"v۴<0b(HPU;&!#ڭeǶܼ*Ö15E1989} XO-GI UnGE!hv&W^ ܫWבrJYf?̿_J Θ&b pO&10tY*jYSu2w_$w_YW$}m[ǫjkYIuݖf̦?reXn\N[oZg/H[lۼ: f? dNWo0 C n oT_?эX[ mCFJi@Ί* K| 21Vů ޠ] \q׌sh„ i!ja"fs5gEuɀ[-Wo͵徝[8-FY_Bd"Ų2`UsdWAƎF 0'&?#Sn 9f裟}~v~~m,Fu]~Rl` 1 f)ueNH=+ $Az*fQTMR5^znCk ,iG_gJK)$Lxz|*SLyWF0*kv{蕃lZH p'=>0R9D֕ J@'Vp^ՔiIT5% I{ZRTHDVivF j-F {,TR5p(L\P)IJy;uMV}ЀRh[ig8fI(rJ  i+JԺ4REE gq dTD|4w>Z-CZDǨqm(Ƿ{!dʖ|kQ{kYhJ™w{VȮZ) E"H$DBi@)(*##CjBZgk O|dv2X;;-ˈx̧jz(uj1@ԹPN1%@iAQ.\1BW(+coqKז-k5Rim&DL F~ (iOzr\,vv/oeٽJV+W*p9ElgbT ~F뮍T--yG7ک0iKhiix#d=Ph(La[ ]B`($6E[|e2^heIQ\5@9,z}(RkI&CNn:R4~C^K`K@ Ԩ[dٱxTDz?ph_Һ'|Iu?@ |h9t#&[\@8y@ Bn^SH!?u,Vc;CrU)uxѼԳRό?N zՀݨg䷅<|;ԝlZޥC<@ }!7qdڥ:"Ϳ"V\lIKM7E9TZR(LM|5 DR &D4~[lyCi4ɧ{W4p:e_y@@_?22-b@Rjr q>@ ,6t g* kǪ +-o ߼o<^_ߣ{ٍ{&(2ՍVŋkx4o ![nκxh!@D"In@ BྐWX8ZM-a i~{q]NpӴ8:D?j=څ7VVTrE,b%&ޫr\q̈́d57k7cީTiI7|ۼp6E/l˅׮]>!]&b@ @ _b}AUJb$Ǧ2<"֜F7('os#7H!)WO9}1}O,~'h8ʫǾel IDATXohEN @ @)2:cٳBU> ^mV?Kix$CZ4&J<<ܭ-ڤlŧo鄐M[~{ 5޾wB-i/_2W O(X:״iSnω}ZH)kl@ B1qGB**^l$?o :P"q/ij-I H$%E6F%)XKsuR) @ @(DFF BH$D"P(iZ |(jZNQ1B\ǧRTj]&YJH.sGWΌS(Y4MsF78ZH @ nj#8%߇Kp즻u3A)VtXrM 43K*sQs:>Ų e]iq8'kPj8$5*VzTlA @ B TwB8p{Y,1-IBF5=[@ @ >ZH*" {e}\ g{@ @ >H@ ZidQ`Em@ xEm@ @ B^!@ @ <$A @ ყHq\"Ee_`KY]=.Nȁ/?!.jk PlUYXȐEߋMFYf!cxV+-W'Z).m=t]ĻϬI8V+ReJ@\BղHpJw{mL)AyV?Uj5*POJP8oSʐ{/`W޵ EPbA&3KUCVS`B;{(2ٻW)J@"q-Yֳr yj[B'xP{3e 2<ӹ`m<m>,O~~O~ +)ܹh)dM{ 6SWS,| =ᗻzsL=VoKwMM>ll~&NNŇnˍچզB.M)^OoDSjL pSuyjy]̦jn(Ƿ)nehO/`_ޅOtR˟tHW3+r!f80GA+nN#xG~+oJ.]90r ߜ2p3hW'ˉF@/QU5mڴi}/{m֫/ ؆d:S`zҵpE;[i|<4Ԯ^y0 g k+)T~ Fӯ͝]KjڔY]bJO%ҚEFn+SE';[CQ8O+CO缫e/𶪙A|zl+G"pVa+R:pQDO%K*U*/X2w]y!HJNVpj1.w߻b'ez?n;r}׻ܨDX@MqcSz\# ?ܽI(ތ#Mp:7oWhFm{:ˈs4aI˞Tz+0 1"LZ7uJ]Bo~ݒ`S0^u[~~t-q^ )"-t|er|ʸ=kٗ1k3|z(.fBrQM}܁{8+RCTgW4G_u#ٱ=צ7k\33\Z:x۾ -6!Z1Bak".Qnм--)r-;KѺccwP>ueT)O^4#ZI)pn7$JYı%{f~xI|raUfeR{z6nH< 2JxإŬ? 66u̫ii`*WkҸkv iQEQej6tt{v).#} EQN&7:|E׎XA0[ :P#s4:e-?2^lIK?3S:''9b]{g֖٪~mZ³oU, 0 )7ߤԃצr,㑱)%eܺ7n.N*=^"Ż7r v+G)1HU|4OJ~ݘ1IJ[eɸU݊y~y("-t|gr{ʸ]\ƍsu3RG\ϋRpRY[^]4dYJ3%5 U#.Y.}U#Qr%)=.K,ULN1)ru;wm]EB4I#O;QkKTmڠI:O/R?.;|#VZSH_nzH~c>E;wNmzj۫{NNEuvJx / ꎭ< +V5vmK_"Ϻ~Ю[mN+2(L^{ҹ-| jdBn#+|~M|PQyz~~m< 3߰dcqꉝ-sum7__v87 |~ҾMc7UL?RHT-Ҟ]b&z%Ou>5&v{_ ӽunϬYׇa0qF(ZaKerXE&eQ}tw`Hv㕬b >7k0n=7SL) PJD,:Gv}JEv([#-Ҟ,mOҨ@ls۲g Yl5f޵wt)eS%m:l]K1ȝyKGGO5~ndNxc% kkF6*O|ʸݹ^%z./JQQa).wY|@&芹$UgM_I˟hŬ4/ŝ R1 o @lLg ڍ381>xOyAtoΆ,%0i_ִ=ݏjE9E|Ll^2?~={Iw}}XcǞ\¤yӑlt#ޠyk׬T?`"TC:cqvg~3/V]tCުY_e-R*gYэO-uod-xoCRɠI3gf|a/.7a/+߿?PϝuEsooAp156S2#;~ͭ9OpduƇт?D[O +x[6^p(,.{ZP߲x\Ǭ C4rzŋB'UR: CrQ%hT]WitX* 4%X4ɞ36(h^r墐m0Du%niܸc>›a{R'i^j#W1%V}ƞ; a}/cl_1{1?|=7-+Ö0#v*7Wkϭy8Ge7ip:OιjdM6բ։ǷQc?_!7lXeA& is _-f~>a(Ò]ʂz|9k)ܟ+3Ngx*9p|  s;tO\&}_nZꒆ\E+bƬd=oM>st~H3[4>t^@xkӢ(ECgYx5biBۑCYW !$Z4R38J %)' ~vD54+UZȽ"1;_< =)xOlbv2Ņn!olAmN#vOŗPEۖ@.-O}%>lH;{Wj]OGԮ<pMlAmX=?]uηp~ ?԰j⾁49#L@}+4`n>#5Eҷr9OpP 9sevM?Frӱ-XTuـkzy!->\ϐeK ;Tfߏ *ˆ'_EgܙW3_rO_QU@'a{Smv?>|z[^{fK(ʵGlJv^y9Yp>)AyxI} :¦|*C߷YxkUiD*O,U޼P/=ߡjȶ-K[k okPʦK/n(a˪yg@6tɷC+@cɠsjze؋\'$26eR{˥[7,>0il$ZѸjB5xU]zg-q5C63X\o?#@]q93Rl՛Yg=1.->@)nsQ 8x'=mf2ڇB@_Gj]BAi>LK͛@tC &p,p{'8u>k[9{ sSK(hJ/$z| YL&ei$gMp ڴGw.;ߛ6bsd* S͸X\D[xG)͏[֭Ai\uPkH} ?ӏf؁)7dd˵oyy է5?F*~FQ|dj"۟H+=qn]fn+x} | ,] }),o_MpŒd|ꇩ 4l]v`uZ\d&{v1z1=z4))1׆,,5[2 ׼[PgLɜӿp ffZQ>U4[Lf_eijن¡p| PvY)d_N5h 'ZQ>U0/ϱ®J  {t˴R SZ"EPeZo:6-±o6o׮6nrOkqMbVN0N(-{WcHܐF)2t :f< fgT+,-x#`QYd0o]˲?x*;yj.S\bHC~ d=k4Nm_Y3{ n˖tS>t9kM eo96qbdgqQN˽scP4h=ݹzzNh E).=:MN*S']x8gOȲ.yK4jv{ e麉+n԰]7_iyWn[:d\_?EջLlfL=h( Ƿ)eh)+[W(tw4y"F-U 65|.*6X9~Mvpϵ"=->+YTbܸ-歉R6ua.$GE21w#h>hԱ|Zs,ҨQ'E}:i;J SHSό?o z`Ɲ\b¹ ŌT544j5ϥ4hLi=$-B~=&ET~JhF2R9R;kRM8htR'yn0Uo<&hxqVQ[cm n&79 @q2UAtI:{SVx̹Whޛm=Oe.whڣ!,dC)T>}5O0R/_ \KP6oSSV yhzf~cYQ!U=2F#.\|<v!7GoMEQʿ :jE@>!".H6ƆB*vpju ?0' KM7- RiI,y&DorɈͱ 8F5N] R3Ҡ9(#T52g Ze38R'>4Bᯜ|V"G&vJ[L˫RN#='FiV ҥ3t=p$5Bav0#Pg@m#c̄72KLZ}ṡXʵW<Tn^A@05Ei+M\u [LbZwdZNl?/SsemM??-bgCQ8Oq-C.OYniTc've>@.UT Ԏ>2tin1ﲣV-*xvav5!+sK/f,S8W.> )i'^[ݵ/{JWףU-׎Ug+-o {rN/aF5|pf@(8_}R>q/G+rMdUf~9 X+w諛Ffeb5?.>ԚgwS=}tz_*諛'wiq,B֊}Ǯ)_?05f#.>9 IDAT{V{yP)}UpawbW}{>mړfii4Dp߼.mc~aSǍŜ{Z+Lo=4#%{kvE]a4Wnߎ<5wiܽw#ԏnèU$=8?8k)fy4Tu;u "Upįnq5Cop8!þ=>#7~4˝O^kKZݽz>O"w,yy` #93DJmsT8 h+sf@GO_bRm tn&׭g9_,[ ކmȒtޖU̽'kd$G&8b/KTՒfZ>|Xp6%7syq3xGf43ٰS_4pxܿi_"CTQ1Sb@ޡKO *w(IUk@uZ(_r*a +wnPC=  엥~1MX5 E:>Ų 9>eWV~6HrutL;mpQ]I)4Z܃Ѷv(> P@%o[6pǞyo(jW]989} XOGEQ&=/qոQQ38gUc 5j¸!+ﻥT?w1: jsHMվy[w JtkN,_W^ [ugB_1 7# ֣cǎF6nf*oa[ʱyulرC/f-9OtݓЫCLx(Z )  ? lߥH, 𡢯n:\=r1Kʤzd:ɻdE(Y*VGi]d;>fN̬]ʹF Mkd0sk?#tH7wӨ\%쥞 mgopepu5vae nʑN~sĭ4,s)L]!\ԣQzMRUڤaK2tݺumЪ׌4kaD֖dOi6#Yf;=S .SuEڌG: ltC! ${z7bdE77[co)2g5 ?3R:O tVU4]p44vv׼&\g E:>ů J&+pVj.Y!}<0&CZ =0^A(rm<}@&_TO[v5M26M;vЙ]a@%NRwa(а%w>*(Ԭܠyi \}|j֬ffxus|Me Ҡ-!A5'_+×_NN?ի7#FnlY Y))e+ >0 {d's.R_nU6Syeg_R]n4}Y`V3uEoym2h̀^N^nc!Iʺ;>qFFn??G(?|aS?[|#t.`^~B![\>!0Rn^T)J\.7N*]&KUSBP ظ4]VN qfvFPj- \d2QB!Vx+WPR(rH$M͟ݫb-+ ZE[P鄥mE`^R5kVN b!ms]ZP(H,.Sy]Lʃs./_r=l@^ZR +Slp +8O,z?ļ B+[nhQ.ZdZ&%jDddP(DNNN"H$ Bϧ( Vj 8'=,W;\V qvҒ`Υݬ$/dY %yT#b@HKES6*Inqy8)|ގ7M;3X ^gjT.-y'~,+w/Wf<ǂ8ϑ[2,"8v#pRO˿{ygwK7"53̤}y8@G(':ir=Dn@ BྐW1hed>D[ֵ= 'i;L_uj[7G@ ( 9y#/Q!vC~j%V! A B3̡-|_;~BǧXaA +W,Q!Ba @MZ7@ @qp_+4@ @ <$A @ Ⴧ,Q!:,2>ů?GoS,˰Xf E(ސ@ @ >xH@ @  p@ @!@ @ <$A @ Ⴧ8@ @ |cb ,ı6"sT&@ 2@ @ B18@ @ |@ @ Pѽ:(/YKhdU&a3[& BeE^T*7u"aV?*e65³Z0pl(Ƿ)~eȽ+<](-iA&3ӦR*F$sH$n{pg|s U$B'x@B񆗚tb_)9S&q 2ʇۆ~r2FRi1[% Ifo mMR6yotw5w͚!5ٵsBݒ]Թo [[ByĔS@Eѽ>xwҶA0;̦6d ~%o5E[23Pß~Bo(+r!fڦAE~u\z`9(IKO&|S5,ȓ<_#.-A\5xÈ+|\d>z*70 ϼ-yӑltVs5[ҵdCAio^/~Ikvصe8&{ex5l] xI=&'2Ť;ܫ }s*^N E:>Ű 9?eLKJty^W3ϟ";,-ڴ<0DyuR-rܳџI=w%~XXv(@R*jvuη(A ;`e%"1x}r^Z[x:tq~Eϙ{lOA4Ro#;H1Ef%^ejف;(cjKP̋ AʼƵ~gkF) 2}[CQ8Oq,CO缿߭2eP܋t=\qslrݍߎqUevjVSzEwpқRh@_B1˽ǁ:ZRk,C\ ?$J>=qorw@ߡ=fu /ދmYp!q ͚2ZL(<)OWv J}v~ttԫħ JUVoެ[v /=mY鮁lX.rskݾTXeiG/&L b :_ r Rj"T];wJd RgZ֪*=""4WwE|ܿkmoZR$Bn±(BBǧX!ǧ{ޝ*j׽4!s˼(E,5ڴ<;/_(CPAnukޣ֭U[ujvXG[Ѻ*UVuWL EB2^1?9{ϻQӡGj)#oe\kwno"~oO%'k@g߾l5F?.`89%I7sDg^NүgnRIqҼI:/;qTּ!` J7T+uH{n6"m0s+ޓ#鍚;ܕy =o_?&}ˤ ޾S"va9iQ꧀-ۇ6wE2-d!gﵩn BWj72PT~vʡp)O/>tfV&O !g\S L3\yxG>2"|u^GjsW,)Jf\#r~mGm07OWZRWo<<Q@N9-3sA5nhfN3޴{m5/޶{ H7`#Lzt}E =qurAďշDk.5(JǷ);>ϻkM 6̰5Qj lC!~tu]*4?[ཝ{gહ$NXE͝u1-L~y$_[I~Dk@zԞӿyn |rƺs7?6QQ~vD7ڔr~ڜyt~f]~$T+P {peA^_. ]:qˀ/̊ Hm۷VKxH9n|G>ǑwP>bDho^,۸qe{?Fc(cB24M4X9G5< t󧝧 &U.|9 RvuS)'LڐJQ ݼ}ƥyIv?k*@)>}ڴi&L{J&i'nڹq܉rh*7k{\< ebL?qgMn?nQ(aZ4JOofؼ"G}xEAߏ|ңލLMprb 97ݖl9nppͥ[lzuc(Ðиҁ:lJDjQI=M/_4.i|ʖoy܍%p| 2|OYANSVa-,k4;ThS. @? XJWK@f8yQ'%=}4T5fs밉֐rR׻lK~a״Bgkڰ`.%eG:~=a:*'-XƄRtWUːPͦy A 8?B1]6=',[`k"j4ʛK;2mS~VeGzR?oe¿j.XhUì(9Sǥ>Ʈb.jau3n-xP"4[w:.|U:y٤rE@EJgo\&"iҺCCnbFFĚ*2U.Sͬ#ɂ_PFO=d)-^W rAդ|3|TӝT\:q~]]>``?ѭWE }v֒^-+Xw=4/7/=מ=T4Vw`AZb(o[9. >7E-OfQZͲ=(yoe۽XXV[}ʊ;﬛g]١"žsS\yXOBY+yyvy.9u&ZRlXߒ o|0iǘ6Хk#mIs)0]lcg6O.0<5wmь98K:O;*xR(y٤wiMuٶ*[ɓ["OmkidTP@ZT?  ħO$%>}-6˫XhUϺ 3zS&͝ƗH̪SNkf*]>mk2;܁)T%דvOd?BDž1]rY[c/FJLJVS…et8&R5+b@w `^qC?WE=yrʶ>ufJjUhp2ARɟ$g'sKeriT2Ѡ>tjXߋ~UNlytpU>Rh-jR>+i]rGZށb*&ƹy̬B}XVcق)2@+,e>>\4j^{ODO%sZJl$PGm嚽΄SGC|LiM.Hhـfy%ZtpΏQ5 J+T?\<!U±e:o<K!D~ +q2)U)f}zZi?䒝-F[ûȏD JŪ3Z@v h=CU)@Ǟ7t5(w@*U3vG`[uUsȼ2jk^4@Sږ- X+*EOWÏC2w[*.ae4S|5gL1ђcbf *D_Q8,o>S[Q# O~cps:8 |i,ѩ Ž-ؽ$T+c,"hQY[Zb2|^׏yeJU5g꾾~'fS3DH۶PF @gx4 ɘ єoNӷ_pjOԬ?Y )M`eQRzۚˠ~pAQ"dS1I@^sxб.yŎÎ)B{[śʕ@"n6 'P^*Ͼ\'Ull쿪WK2OQ-{@Ŀ )*-t|ۇ֟bʻu y+TcSC+ q6u!lT7DOb{A/ IDAT~6x<{U&(EH-XKXPLA:8LŃ1 Rs)`z a%׺E6oo<ߊ>VCQ+ιuo}@\ hMȿ*&I?y@{ LP7QӦU [1-67sf5=P<x/^$lι6]3jE*>Iv0?݃7 qմ ѦN;k!cOM<hd V>Ij$%ʋVk(JǷqfzʊ%6mQ] a BYыfll,$] ?byyaJ֔-i:q(HV Oq~k] i&kDft2\cP]WX3Pd?Lڭ؈*_3o\$Ч?Kj^ᜏ[c̮ݭ8c*P޸|-&%J1֙t]ʧ pp[3'YWP@_wx\sQrN`^zi1KpB&eXlTeهe6%-+EEV/!Z@:8TQ'O\{Ͳoyp1@ + 9y%ك@ > / %NصjAM`n9y@ ŊBn^C @T*% $pp@ ܼB@ wP%!qp@ ܼ,Q!@ C:8@ @ @ @ <@ @ ; @ CNQ! LV&B"0\b8?Go>tLR ଐ@ @ yH@ @ wA @ ᝇA Gg$MtYHi@ @ @ @ N @ C:8@ @ =8gNvͱf#|,*6`䜩M-ɼCޏZ[6+696.%CA ݽ|d3ِv7}'pww 35T*yFrZzzTU|"]xSZci՜;Ja[@`jvSRQfS3>XV[8ٷv]#g $-ac]bfq괂0R@ B۩%IPb{J| 9*k\+x@:8#O? # PʤNfQAI{14p/{^fY>}KZlzli3>?>>^SGrʼn2ZNX8Y۱䌺@:8?ҷ3(vO=(o6;Q( Tq| g=}y6r;&g6=V@3βN+ S8?G cY爅"Ne_obrӜs;%UܴᛟC j%*sC玉ZȼS&P̋>{o2^c ~-WN_/(M̞o6|ؼ|lRd<7*V`_5jWƹ"EJþ|*)7d[aSQ.o|>dλ2;--Q+ef伈ۊ 坯(#t`ϨW/$1KVwcZyPT$'+kXBB5 po/<"E96N<>v覤r%njf<=+υo}'S2qՊsЃ4ϥ;\;M,iS6Oi_*3kNb>/۱"?.'qu1|#5SHRk\}(e$o(JǷqJ|]zᛘ1>f:a%XfeV<=Ѻ~9bHǨ:ì^O-vv3ܜպEBADŠ=kbql9@p~^U+8gN_},WiX&G%P3XA_"mֲbB<)$fRq[-kpJÇq)eeTQ#Ey)du3צ~,ؿW#_{'_Q%p`XxӘ 7΋? ynqi0qӬћiS[xgrԉ"#oExCA_]VukHҞ\؋_oHPT{?~̥ yS QVz6׭F&UJ3zrMT)tq*Mzg؉RI>ORUSot]y/&=\&?bo J7T+uHtHϷn] 0K!).ٙNto}_ݱx3mg׏ d ?{aw"Soܟ3 f5J<-d6^^MdL> $G\Ӧe5APRsBv;%OJE iffW}kUk]ŦFE343T{(E%o-ۛKyXx{|̓ȓt@Ý骖RUumY#Wֵo6xR}yEiF`sZf瞩j̜g .0nMGGճ=ݏj9d h}ysǴe>~1U7^p(B7f-H=n}ݼw #C*5>v+V,?{U3~1n! erJFFյ~{P%8 f˴,>mjw(h>rݺst@7&|G iğ׵IV_hƘ54Ue$7:&@wi2UNڳko}:hQd*=ܽhZ9c( Z/~DԆMϿjBz4ӻ߶NN8;g[#\:WzI92 k.S ȦDX[>ZR>3y?~?~ҥS 5,+R-t|ʐ=eɻRhRTd= 󣳮PA^dOپWf 1IHNk3[c_9-Ȟ_M&H=z|4ɪs~aШh;;C+w8Moި< ӑW4v|&ܟ-d l笐B@t=j0 A;SY?S}a[E׾gw\>{%'7 >*ixcHJ(od8LO6k.s-UD8O^6%{! bU,*faFewxzn=1 [vQߟ0-1~0Eq{\LDҰEuӊo"7ύp^VWJ(}55֝o|9Gb6A> f^1Yv(U2BZ}woy [WQg~3[~%y ׮ct_ҜSgd̠Yaux&%&6UPWjWm]V$-OfQZm]x&ioZD@¦(]BǧSVyg48: nCC_o;.;ֻ4+l!%Uv5|7/ԋ#Cw5wv6梚"ZM(EU'W_u4 h=>YȖU}za_A1rO{3kO82>cJS}0fyjnX{]5D}yY$T+PQ NZ\{c15 U ]'h^~o@JzyJrgҬ3سMTЪuu=Z3oS߇z|Ot3^ݹ#7]Xl `Ō?nW5-ゥ**EvF1UUqNbS7$^͸XGo$Ws]sTqu} 1мb{Xh>\G* p/~3K/ ԇe.R-t|ć[C w JS/oy<*_׀rT*ySy Y`E2ζNcN7rr_\ˈy -̝"l2qRdշ֯xb[=zFibVmdZnE2oKC4Ϳ]}JiV&$O[ޫ=]KB2 jh{  {0# OW`YNNuFP%d_ MF2B h]zAi>\xmկn+ǜ{?F8᳙`3b%iZVRwem n&7 {oO bd*) XcVmRxgo^1Q84}mVM@X!M!8H\5mB:: gW: V>AKHI.:s-a_EQ83SV,yh v7wgQcYgQH+CTxI[zW[M # Z,5%EѢ4 Y7 2#Хw~ax>+ S\ͫ.hF `S?L+۩۔*E%& R^2->Pj0ۇEP'ۦc {gqGd8F5X6=9|b;3nN+*=p`1N c//d䅣Q̴ eVњcw_ޘf񀘒jvWv u/9[WDo;ٹK~a{5|w+.(Z/C/;oТF]\8~_/] &9W3+6TQަ<86f:8|_}Ry5kXʈWSKӢ A&P ͛f696`eS>tBȃF̘0ag Qu[s6pWuT:5#=5IGnKh}5ׇލ[N6_7J\^[*MNJcHNNgvIMZq%Rwa; y%}1Q4'gU>JպIKUod 0?9I|.>,e Eh{H5s YOő>]|2b'PVM+W[8Y>ev坣RP$U uc3]Y^1 S)9c„q_\0tȕ ?Rj^Lgͫ>7[?Kṭ jvW CU#/$6=ì?cI &=Bݕ.q}pŮ g}cF]u@j h9~a!-Z3!j3_GZh]&ǥ >b萋t얞]\9zgfg`}@a3}{/lՈorV xZޓ9wv/G?ŕ"ŧ;e϶g~T˒~ݿf"Q*4i]Etp(hNnm$GW4dxshśW\aEZhMYס]:[(j?bT7H @m]+uߥ+wnWS C=%=8o' PhRkf5vT; +-t|҇,2y}~Gkt}mHmkϽUw:e(b/f ;~tuZaUb[CPU06E;;Chr*mSYFk/c+:4|ߚc XOGIQ&-/ ؛W 38F7IC @WkƮ=q\(h4,Q! |}tt)_b+UZZŭHLG톅3biS~F׭QQ稥OvD `FE9Ğ>FJ<{BzXK(l%͊q| 2x"QDkЇmpXP{W PP?ϝ{vZ\L8y@ ŊBn^C Bɼ>xt>G P8x,J%e65CXt(aB|Ⱦ+["lzl6vF -8RLJ'+D`BPdR)˵I'@pn8S \9-ʼc|25w_WˑяGkxiSƙ6y6nccc:ϙ"k#3sIũ2ݎ%gԝQT$o(`_>W7}lj#]RFۦ5@ :>NC;Z;n,ڦFllgmVHۭn0s~^sFQ±SVO2S}ɾ%}**E@>y5O yuuqPD@pnh!c問;"K*pzէ6?l &hPzTҵ7MuR߯3^Rsͦl

    NCO뼳h1a4 ō)v˄2* ;ËS/W ( ;oq AQR! d@p^{5Ӵ cn<5|Vr$cuK'Gڣp>wi3HK)eRߟM׌p>7Q:"ѳ=TFhðp>kl˧Zq;Ge2OR"7Jp g!ۧuٴEjYY]YiQaȣLoG*RB[qo>l]OzEp|'+nfkCR.u_"%2$T{!\U@sǏE<|% Bҿ'߬6Ov 2] ܺu}ń ^|AA#޹gƤ$?AZV6[WKA_מS>5,C!yxV;seDR%kVzQA>V:}5i [/+gnF\|NAZVג=t7ME]S(t2Ԥ=rąq(_nn]2GPH衆8>mgoEqǪ^6tj9pLl:a@t5T:7'>}Jr< X7$A~ѫċӱF>ތEg#/gSP=6%}?AJ+ ᷭ+6H!%125׿j_sퟸqRǣxBp-20YD(#ơp>Zus- +(M7 _7o>d;MKE,6B%QӡGj)#oe\kwno"~oO%'k@g߾l5F?.`89%I7sDg^NүgnRIqҼI:/;qTּ!` J7T+uH{n6"m0s+ޓ#鍚;ܕy =o_?&}ˤ ޾S"va9iQ꧀-ۇ6wE2-d#<b [cIx^JFR _̬M\J A)BVϸ>g@|dD0sYX SFb=;~ ۼ=`? o |h'xysZf瞩j̜g .k vgtBӢHTzzx49c( Z/~DԆMWndor;n Ι=dpk.b֫{ huC̀5)dS"R/JD|huI+S|'5snFerP5{  4Senp}kuuMAgD^7? iZK.欺]Ǻ鼿9}yiѠ:@y(3΃ MrQ{5AKdE~,o>$sν3tZ;{\IL?4N޵iD3.j< q oHVJ@:8e݂򦧹62e۪ro%O nF_>>ZNPQ"@kF@RP(T>}$\z+C.>N^,fca"T CC;Z wKJSίp$Ɵ{9*,p͋,ee U„zfQ{LZIZQ[{"hM*Rb#S?j,=v&ԟ:bcJkmrAB4˻ť~`u+ 8@p~ixT^bݬ-xy\ [^_)$5[zki]j1x" V${ՠ(JmdVQm9W/'fϝLay_BնN@k[s7,Ã4c鮈jg\>_i ?jx"-ܙoWޖlҬOQ{ l1ߣƬGK]]'c65@:>NC{[=ɻ97 8ǟ*g] ̡.ȓ(}EL\N^[=lyߍF.)uz'wdhM,j5&a"_d[*ZӂKB2 "E1U%Ռw2_d^e=7qM{ٲb | Ҷ-d#c*_̜qD )M`eQRzۚˠ~pAQ"dS1I@^sx[^H IDAT`i]{S47+EmܹOϽ|EUr})NϝWbI)wѰ%}cd1墢8j-t|ۇ֟bʻu y+TcSC+ q6u!lT7DOb{A/~6x\*Z"GI%,HV BYD˜JF0}=y{ꄰlkуo7LoEvy+DUޯ;ON 3 Ap. {]֭u➨ܭշ:ZW" *nu@Q$`B +}{9Ϲg<}**P/Jěmhᝰw) 6̈́[kN;@c_|xo$piJf]2*)E)2Z]üLE@H2CBpzڄ:kSYuZZng&Qul,XW?wyףd9#)rQl_Bۧ0Cs,WniymEuBgMfUBTb >4ÙD6f^Kmz7J]ycSSHANQ! ?nk:DMUd Y{^ww,lG(2Jc=e|=K_@133M=Tsiи,.[Jۇ/^<{g|ă0(]TQ|T&ž>6Q;0Ki,vH+.2ō?񹔢~R&;krxbpp-JUnpoQQwC@8ꔧte@u( 3nO. ʳ['J]NhUxkHʤG~LZr kM顰S$ S"A ~\߇ދNfz=M C12fvWR_yх_2C]9|HV Dil'݊̓'oQ@JYU޹ ĈYݖu[& RPB׾z[T *ن)kÚFkȑppɗYaj<<mZr?48دaaW`+>^\=Ϭ%ݎ(V$G-u (^63C͜d%^@wɈD*^q۟N٥R ʧ<8v N|j/}&yu s7!\:3BbA{ @󿆗,PF??ǐ'F-%o#/\K{|%} ;d,˻9Z\B 2nO~h3F~eX#GqIW EwzO7m\r5~Q v#Ju-&fJn@L5 +R*#&>ì?w^ʳ~wwQHoحmV>{pC*.2ɗkw$ovNӌ]ґ;%ab|kc}ѧ6 ˞I;ˡ}$:?yxy;K`^ݽŪ}RTM/L/qz taMFM2.sM3놰i.$Z|'b17Y]K <]^3׷.wyaS'g#^۠fK6]sW ]a Z`M_x׎PqJw_jLz1-{/o1mͧ?fl*T?̷c_?۱d|.;ݙ?g`/S!V:l4M;kXfĂB7ߧe; #t*J'm#Y'D lcp( ylZ8rہY猁fkd-I*vS6752e j=,tc+XZsn~fzad+-52bI0Tc/^i v'/@ JF .bz_fyr\|\.EQEjZMER ]9{\lR8K)sgq9*倳XlkM :z{;Z4i'۱;eѪGJU+;;0{`I|q k<@`(}]B.`#/Sa{d<-rGecvlj9.^A88!_Q}:rg.\gY<8q@ rl\h(YBTm('~ՍUca.`ě93(e0 ~ kNs|kVߛw@[w=ςNRލvXr}8c!`0esb32X.R)mw8ЛO4_e)SZU-?X5xA{zk &h#XTs pT3r](?[7oKg345cwzM ͒=L#kZ²g_4Q,[\6&ˌH~ >YN$uՏuBJh$hנ z:x)=qM`mP:JtLr_fC@BSi#}tq2 VG[&SW|xkXE1!WϥQIR=[vޮa]M~w;ʭZŞ\:w8^a*UܝKjE2[ O*6hTP(^ܾL߰{xΕGdRJ-U(_NZ>κR^;=h}OrتNpEQ.ih~+l#?{ਢ޹ g U qsW }Bu[bJ$5.?{m+%'p*4S9S"-x%WTu6;)yxȥ_ѭ{}/{]NZqS'G~YwXZI]Y=)q7_>rtK(ԲbhAeD YP^i?)5\}g|;u*;D}QJ } ֭Po߽Wr|mn?O8 Ȱp{@ !uƻ վM_|5zL-QbWNڝ<#YOM|t;*0M{i9 >}a8&2@q;M8-ml_ӛr  Ѽ+.+ T߭@)݋[8J雧O###c#>IN-.]O=BƇWʛ݂>27jJꥠЇR9m`1eh|@(x=Z6;wc}>^[#I͝rl/Ro_Jܯ1ޮp#7`3`qw/ /6q#fg'w^#Kq Do뙊"б]p G/@MN?|N9rL=bBnxפFWn )aݏ=݋főظY>/ 87Z:Ί=(ɢ3Z5_B,=~ffBٍbҋΝ~ak)kJH}KHO ˈVn~^{cL嶻544&^ umC^2ARzc!<Y{[/4FBuzO bFl-\Y:v(%%.r1PrꃃWhaˤW%¡voڽE ǩ%= !kZg_nE eaZp8opvu2=( vO`ެ5_%> e=éq @gHGm>Z?Ӻi\uVͧ`e{t6CΕ_l:>Z19"ՆVA 5;M\0oL3#~~z ϕ R5HtS*̽_ߵjBLޟiS+~šdN\0v:#ś PXqm"ދ J4k+tl8zƝkOTV} e~ׯZժMF)NL$y*:6( j׭[7-C5?T ѣx4"ZM5z~f&F[(tܽ!EA0LYv78Ҵ6uk3&ֻA;cwN+In\ 7wCmoFөv\O^cʊ@#Ѫ-4nGy!R> 5O m{y#ҡc{<=;$ąG9y廯?.V~q7b+)ۖrJ 7c[|&z"Mմ#CnM!TV'dUô|>pĎ5s* Տ[A @Y\"/Ę7O84KJ%kv;kU.T;B^z74 ^S7Y}u~t#rl N.xbJQ4cڔ8rX\N.9,w+_r7CSfQu#TJj@ DU7yMO3}Ei;wpS/͊=*qF 9yHM˴Zq5eR? Ӥ9L: _ z{toh^"C(sd7nZOeG\ %o*_d-nlnXvWz?*R wEeUh Ss[m/{ϰߵ1.^ ^VDHÏS⣔:xVNѼc0}qi.qlOLkټ=4BFǗ4uOAY?^q(JeTfsc$lm//ݢ!@ߝ8#[}l7#\jLM01Kq#xfV4R-hfhSz*hi#6O1W-abMJ~<]O uxoUK%&ZS&e$o>vox7أ;׻oRKՠ}j-ݮ?2C_p)(k-mT3pxvKWpr"".s;K-+RRfVmK[~-]O.v=@;|r_S凐}Eò#*ƺ''Dcf,~sJװ5SꄲVq(y{ {;N3IXXu&nǪ8xTк/WMYS:9c>K'^}\VNjR;|V>iiލ, IDATU_קӉ:1]?H$TiSy[7Ѐvn]D*V3Y8+4HBFSa7;r2w(kO{ ݬQI57ybd&eZhn~*1הjtˮI{Q5OFAPSC`}zMMiљ+S}*px]vKD6n,Cjɓ+! 2N4]B$ìHXvDE"Q,udd&I8ȧV'iח*`BB$e j{`+k+_)>YUԩlC!}ˆ֬jQͼYe&@\*UʪgU ("jwUpRJ9ѧdvMa֐>^͞veҺ~aݵgVn[? >,kBn9>1g.um`W|P{yX-rʩٱnS(ygKH# (n8\ҧfx&P`Wlȸb4^x{M xEZE_HfW*NJY} ?b S4 n敇w^FP/1n8m+ޚuc{zxbFYr)Rfϥc:Qz 秝R6J氒E]JY~&~V"UſIx% wGjuZұWɒ %iJ"ʦ V88H,4TJ%XLeƩs80,]7eG΋pX%KÉ*߿~s=× |EVY#,T{x&P+Gϊ%- fzš,Z`2 "`kL WwʡϧfS /IӴ͜X5&ׯ"0UHӉFe2x-L;|W?Ƈҽ;QU'̬ji7o@Y)G9{c 7),ѻdNh oE 4ԺӨ|VQ[}dn.m_*HܷPВg!dִædX*lq6L 3a!*ql`Op G)?1OiKt#i[[OQʘgjO:|>1,@BGØm71,`|fZfsUN(UqO wTŵh+eڽƄ1{s9qff{{)}>r]4GVݪڥz@/HR*SC.3LDUkQQCv7't_rۜTRSš)7pN%OiG?8ߵXxhJu\Z@k=|࿗l;.~ؐiJvnqq1 111INIۤUW?0o.- pu?3Kىgnq5\@dȓ< lreB N5%}_Q3(vEtXў()YjQݒfƄD=<3SB4ãLA;(Q*(U J9e@º!XDԴenQ1X+3lqie\'zlͬ3&LU}eBQFl=纾󳋋7_)/ k;w)ӯ7bcJyew~@-{zaKYN%7 )#-&Da>H)рgn )!iNKtX4tޞX1gavB]ژ-|3/thjqv.Jg ȈDQMJnܛ< 8.Ĉj_oь#_Z>z:EF$2jnYǀFb~?-0&ntezoע{5QfR~+ƎnKZ)+Kuw#v.mXp(nl#FƲf#r{r*Na(f~pGrT 8+l:iOnzf<*aY=s>@Sr}zp(7SKt_s?y[TMa`3,kaه?l{a-. ӂCf)~Am6fՏ[AP76҂v,bp(TۦҼJ}& szyeCY-}(]#og6E$bhHAfp$=gW{:$WM[?KdWkPjg3]:nqawWnkLw@laGM.{NlM󽾟ث HH +vćcocfY3fqςLP֝ذ ;u &`KVb(=;͘VI@gX0aZjժ۲YfȈZW>}74{?up r?, 7UoWL˚xx|Xf5tkPyiF*?O q3[[j bi=o|SF gn:Ĝߜ>&m>pjp53[ܥKTZ{"hL˅baɗY6 }&`06HA'Vx k& ?lr?kpF* s~sΈ[qlf=I VhUNMYW Fsz32GHӔF^"EE l4_cP-pbS$$șs$EJ||<1O1Dl>eܮgVWde2`CN%c㖌=8-O'=\.-IV5\:ZqO[ؒ˄_ޤȞK A[26.^C  i)2R8JP*`R^J 1lb ,WMiv-La%9.*...IID"՞eI~,:P rW̰ /aBPP;sP`1-RFɸBYPFZ6~R$E'%D%ĹT)Q"磦Z.Id&'MYmҽK;yM([w=־܎kdA -pC i|lJ2f=xЍ5לDGqqOή@le_f9Uov[oڰjEmV?)]GO^N;t˟vHjȟlg6ƻ[Y1zcw˗/H@zοG y+/aB~kJ]R!Ē-Kv`݇"RcZXuG1Icecף&y孝( =ԒP 9e@R m|^< A Zo<%hVb~8>(YFM֍|>޳F8rgA|.%Vfɋ-R3ݾ'3WsmQA|no]`G5 Nj  e{N כ>*A]n=:tʏ}u[u]޹onKXxgiw.$MHMk~ҸvY߬߿/h$PtLs}"Lǂ CCB'HWNyI*z5M.Lk]{Ij^S!aҍW7XL( Ԣ3r5^ybԓRMzm#@vZF|ӻ&74T7nO4aKäIQ\b __jgA'O_yѡZ5㔒yfxbرX=wJ[J*(sÚ5^?ͦ(VF ?vC#/&PP${إG'/ ;YE/^ Bc=<E\Hs$D|+Id{gCȻjuʚֺbvn>n0H*&a ۗ1pm и߲d}x*+ l3.)иlkcj}mM8n|2,aOa >sʪk,eپO.LڱlJU4nm<_Oj*oiS@KCK<{dN, Vv Bqp 6}fȡ^ɇaL9.\헮 Uo^.~ݘ@r6{_SkL ٕ5;uX뚞 xM-y3 Ozu`KN^qvuG Njz,H88@­]K>08 W=3+^hR^"G>*É;`?vnڌ4f#9Љc))y53n,ءEAtnsqz !kM Q(RcEG e5 5bY7l<9J21gofW* w,.N8=nۛ0!.?Y>T;`#~vc% {\$B'mG)P/w>> .⚹5ga ۗ -v왙c['c(l*l4׻ ;~Mz-J]ߝ:aW{#$;{TN2yl$Ck td-ݵNUSӱH<37mkf516 d ˗Ml7meV6[XFR]?r>fRaw8rty_Ϙ }$nV,=Zxg/LV,_<8q?bLK$,OK)7|Ͽڲ|vwO%t 8?fe;vl\7qO*YQQ궯h!Wi2"LlnCtIb) rl۶qĎh^4o늘L>}ڴi&L,~F&,ߤY]]:}^_bRiۺ)7}N)k.Q M3[-v aǞR4kF4uXө5 xBHlVy֧']׽snɖ*V\֯tZN ѩajW\O( Zʧ(*Sί6mڠA'N\<#lێ`l7moЫ8%^p)UaaM3[l_‚~p}ݸ/-Q EVqXU{f;>}_3`_xNJȋm4˶)rY IDAT#6seGJ-智䝛ci߼rvӎ SÜZVZ  ?>=͡@?S3ɧDYgѠY%%e!t?N|CC/.c P}pμZwFf.}ƩsMo 6d .EI{Neff:2zXz"W.^^ۦ)՛O2)x԰SQNqzqQk$en;{҆bI+ֻ&#1~LZBC>zծє9v M.deBs6| 2Ckvޖv].vS]<3~|(sTIIJJ(a$% >I K}"sAA, 5J"mնFʚ]OEΆƥq:V-!ywD)+giN?M|\R~ۨ*ެVV*9s ۗ0Q~gT_ڂ˛TST #'D?8;O(r7Ϯ*οR'eM²gW%F\Y|xf=*- U/_DGǾ,(XToW>wͼb3QYaeOoKľz[c/Wbhq@(t TTalƿtɀL !淏~ĿWfA ^6BKj%Dz9h\wlܺq[z (bS&C覡lƥwq!3EQN32akJ8*U\GvꏉDSアþ/|) esƃJm_2S[dQ/ׄNlpbňՁr)OњUj7XFҺgU ("hOu_j4J%}NZ*`\E~ݯ_Pߩ.@-Wڸt֡0= <$ԶVBRԯ${I @Ao(`WP+.Prn2j9v#(7Uqt02ɔq\n/9?3'Ăor9UtI@f򺐻vYN\ٱ2\,Su}+Pmۑ?v'Nޣr.=3\jVCMa#c3gKhV/뾑o]0݇R1JZM$dEmV{w3&~V"_T gƏ{fSگ~ n("oB?}M-2l bh)@(H*+Bxoߣx ޹N]LJe/gWeqܻot**T='sg^q(b9xoR7f?3 {`kLNם,t/x>˩ oD4S^sUr33{LR8"յw*4 "|o AiZMemjiy>b')킇7F7"G`ڎ{1";6;>rg3gKhR]7?rq^\+Ǭۊ-&,8~fZ[Vg8$_YuñbQou93OVJvwuX'61fؿ&@ sLԤ[*^H NŽu|DzvfA%f_f.R-[(oTCriBgrZu?z?S3=7FK=Tsy-и,WVΠ_x.Qx0/Ql*(-|T&̸h^(g5;M_\JQZ)kLOLn4Eʍsm1*J3*7Q<-[.QAeꃫe}@dY*Xc/w" Q)yX/Cy>~C[ @˂O]ݩD6}x+2=,wBt 3.*~\qIUoɋ")U@G?d]p-9XO&2?}I 3Sr޳bhY$^с88{La4fZf̎PJ7/+P~~^]f0_z] 3)*6m]ddjx3N.^mճ[M>pU4v>5Pv|"9mO0>]m}Ri.tĪKg kf4QET*0>T~{h#G}Õ@&%x=y‘Z1dQal?~+Jw[ q_C®ץW}8BI{Y-..:*!::K%QlHN?ZtY\P<3mf{JY ƮԨ QsY/ [e_%y1M]<Dž94\5րDw=/ow ̻~gO]Ce'A.2-ﻮ;lB+Ի[fe2Mu?aÍVJ2] ^:?o9.u f2- _ jd5>q՞U)  0ՁwxHtFosڲ LQQ]?{/&7>GO_F'z`,ڎ]5KϺ8ǜӬOfX6|%,X2msTW&`1Y Χ?fl*T?_Kv"mfY}4qq^ !2ފ{njvm:=VVj+*nVߺQ@$` }='ݓ9Q#Bml$X酕SZ=T,~dMW {Ƽf- 21a~\;o;h9HZ{BJ7} n 1 -ӤH2&[.suРk4dkqe·, INӖ6r|zƩ9q> )0΃[/ -~eoV`HSBom5ש~a]N?/(GppitʰS>.Ew[wy5oРA ۿ=nڛGRla_{vH_< -VEs_ un|{T"9qYs/i0OsƋuk,  g[CV!ۯq/_ qub`E;rXK}x)a` 1: ʕ+EynXx9:1>ظ*anUgLgk -ddwfSN|'=OqxZ\?BY4n|]tɏoD.*y9/ G9&~Oꅓ%'_QF Ka5 9`;kx 0?0Kvj  2Lm ? dYX }ѫFQ@>9iQbᾯ/}ڪhooGLz_cY M ĨcLp3Ta@[|<+:a΁"mt{=/Dh;3[K+zъs8o+cbr '#lYnwt[R"->4J,QYXrEk#N(nw֘ryo jhAH\+Pߍ?X׳HTڮ,M.Դ~B M}dǎcWߣ[FYr]ƴwbi >,/ǛI7z*XfH~v(:*^|Qh,{Sk<Tde[V [lI\tbZPHŪb{J #"(_o3NA*:rfV_\ ؑ}$npPkԕ0/9I<>*$t,οʿӨ|⌔ }27I$rI/. aR.n `rS;{EBa,)H^}bcc>VQR5 ɑ2#9.-d`18ŹVFU6ȱ;'EF_}J/Uݛؼ[VAZupR;/\8s;T:kS;` 1kf-c [pƙ'+;{OZcQ;&Uo,S 7{9ɡ2oh\pzǖ 9Pz'rҔGGFM[WM<>|-'kw TSepruMLV3&_|3l3g|wGڕM)cqgFFt 6eadj$3o{Ulz{mY؊vUXl…; ˯~r6Uj1dÛsyԖw U+]0ۇxiͱ|x~r~l96:w5 qf1V3q{=qjse]ٹW*ѩ`s?eΦ6C߽r5 ҁ e!y䴩S]L&Œ U[O8g`ʾg-I?y䴩c50FN:w}$ʶMg0Mn@rBP!uiwޫɐ W[x33n |ㇾQU4.I2í˛F};wuF IDAT@|D7Ll6HIk2 .\tԷʹBu1bC eY1_ՌC.,Qa3.zyIKm\^xϸ4ܾfegNϲܝ /a w}7Ř: T垵[VuH iYQml;67ϥɐ#"*wnli8*kP7TH~le\\1e!C&N8ugYtǎq_$koQ `"#` elT :̋GNo~:;S>dό@AgV퐉` ]3Nae +ZwkWru:8mT|5$6n^4kJ֥^݈פ{CϗSo&݊SjYפ}w=,ڈ ]Pf楽tY}Ӫօu] M9,=Rw~)Q|p0V}ھjgǝ /1o+P5jW*=nvbU79t},>REgh[v>D>Jt")uz&nyh˒x05?siS3_{zȎe=1\@azus/+vN#t}ks}*h;ow*%NQ^]qqڪ <䳖&¡Wrc:Okd{Ϟn;-ڴ(׻Oɿnu_ 6]oA})w՗Y3c/Mnݬ썩o85ihAHPv|Yx +eG.'cȲ^p&9N3,Oq&ٓK+u酝j|ܿ^!:`m$;_3GlIw A}o, Ziݫ+JY^Y&c|{ba ,1zzF-h׮1,TDx 0_`[[^OVʿf1{) BI-ARV~0YK~.i9r!S2pyu1W67$Wk= eg"&"p T3dͭǚIjMR 7RQ/k#Z=$X_?{K(IټUiݲta_4;<jEa2\?'08xTXPӨM-7STrr҃&_5aOlE@ׄZ  ڟNsp1f;ܩuk>b0)mԪqfmZHX@cm*5:9Y>JKfY?<͸6.=(`)ē+O, 7׎iP(ςSN. 7mDpYC+H!nW)`=`ܬzf*'Z}|c(3Lw,5^6AH*50nmybEYJrm*H ϾƋ?xa ׾B_`, o?tN}c;/HYbdτ.깟gۘ5`FtсuY!ch[3yɰM j}YgPQ3x_>JsEDp ˁMF]iѾg"OǛB7~d* U{d|ݭ fKh"?3i8mbq1 w͢dD/~;{XɌ.a?\>YJ+ϊ^XfV-tGY[cgrb|} U лݡ{&@s#8g {aZy\1 D11!$M"^Ow2zA3OU]wEHv^ &Ixꭴ7OJ݄'Pko@:vN4K+:0h'c-Y?%#t} ,XE.9HOiH6!y}xTѻ;Wn0Wޔ-'L0?:'.Z/z !_9Nr:4ȝؿrw5ZөH'>]yy sosoϱw.Yt\ `ӠFo4j=DZeӈVI%~&ߝՑz`'àƯ;/G0)[d/{grW9֌ 1޽Ig|DEl[3j4k3~2+9J\Y!}ye[ϊ7dx R,scs^hs&.cb\^s+  Mc-8 ß+OY]{po>+d#<UÚ& ~=eVmڹ9|N[w5x^8 8Xpݙ/`Rjgƨ+Kanqz_b 7,#piv_:g$󉨡Px/ S>>{5!|}riհܰXO@lvZ@oH~o~z9Spu96O<ҘW1xsn--45{Fuf?mҎYY q7]p[ YFXߏT>]uv seM^&17} 42# 6}/|14+@~ز!<Ɛۻ}knN[ia{ $9"+_쮻ܣUSan\w*Xb&Xw;-]Xڥ.ױcU5O~Ŀ7-IPA郢0/A{2ƵD<;Tz=A@Le 9iȡdyr;g^lC0UشUt?<~Ĉl5vk$_^/pzH`Ea<_w0j;_xR@x/UEH'DN7O.fH뺖BwJEW~sa{O8cˮ7d7tJi1c[v+nqsY-ʺYCARI02YQ7^GkDZbwU܅j%|ryԁ+g`RHcwA?xjMl.O/2.oa~H(Ga vp,C&:>e7)9 '5˱g}'<>^trSbׂM cu`qB;kё2mg7C+&#\2ޫ{ukWn70OfuZ@b4~Y6נ/kldFcEU4I={}o3YgaM-׻9.εݎar\y? t3Qa_l~Y4WSm _3dߋ|JS1d?*ӻ, qݶd=\ Dl.w0lfp7+>&D0a Mv==ã? EL4;7M_sP94_ȳǎʕ~4><د{}B:[EEqb{M֭[nLbOqn$lV6/;c'ã]kFE0\=rGyuYb'Ԓ s8c(ܔV>Of''z ;6T?/Ijnk$?҆vNhh1פ0i:J_Mӳog=`Վr՗=v,˘$jh°>Ij!_'KW%TZ@ WHxhw|ow]NQ)J+fOlsk::%>))9Y <+V )@.ڕ瓪m06LV.Z 4籑AJJJryrKIմB9dkO) H +o|1 E.U?ecmמ\`]")*nOR)wǵ'tʰ,>yK)3FfW<+fv? Gزeۆ/̫kke]tB"z 3~t8#CV㢦Nximz7͚GTAףbOkύnA-R0üD~njZ!}]iII,zo&jy ( Hf?` # KNV?^eμ_^O5h!FwuFeL<"'9*st&/Ҭy.8NOkѼ+J)2?f ySH] MP+kJ85HpײS۸]MRĤ0x--)7|IX؟ōHe!۟K ܷDog%_a^r\?BRb5=?v챘1&/P.|2&~PWXLxv"«{:CYXr. [8s|{>Ac $Z! rig"0&fN%o8?צ_vi'%O%ikw:~63JfjUQ$,qCNz5i''=z ^jT3-I-TVm02 -'1c7jqpփ1*F{nމw>u7*gݾTTVoYǟI7z*XfH~v(:*^|Qh,{Sk<Tde[V [lI\tbZPHŪb{J #"(_"}Jȵg{+kƊg>` u߭Tjl<92oT0BT[{׏Ѝ`M+tF-˱|7Iݨǽ[yjhU P!_tmnٳQVh Q>[ xd{'ͼžY#.mɗ۽x1GGmȜÚpksfpU/mgʪ[̻%gFmi?Sll1S9!ۉ Ǵ--:vgE8ɐ='y+/2U{Jŧ {%.3[ ,;.m8ry"cq+iFb3֚0; |V%]>.Ö nRr^31pkm {_8^nOz@ml>|P҅A J}Alйܥ8z܁6g&UsSWuMł]R#eY|-_fB 9'*Tzx^3"v*Rüx~dkZ}_<#\&<֒djόSLv{ʂ_^3b ͒;f;bZ|{nk.߬Ky6Z}k[yng#~޷GױuS#5nh!/ IΛ;gd߯g2iEr-`21C*z7Anز_µխ%a%I6鼹j^rWBO͝9 Io աuFu2/;KH=4r-!/bܕڰz!zdM|`tFYvŢ)#߬U46..@oX` U޾ky1+&W0=/sFv@qƎ;bWL`cjsWZVmovxԤQoo.zEo 櫏'4O+5+.:_er;^2ե&4nP1u@J\VuH iYQml;67n^ 6C" w3z.V(PIcPDƚ$efzroxMynʼnü~kZ1uwɔ/98oa uxʂU1{[*)I]ud+V|1ܠ^Fs'̽&-YxN֥ FF*u3ұ 1`4\͍ ߬M[ XihZ>wX{ζda1{69\?P2gU"uQ䪓~~Z~:e=1ҔօFJԒqGtPճoMݻcs>^s] ˆ Nf,'E1o+P5PJϷhsSWx>`K{~oKlهzO7"gOje.=Hh$ޞவU]eeIr\<sw",zټ5tMej3N9~H;Y+w]mJX+#ڶ`n-º?)ƲljfY-W<m;]b]ˑo7i2omy'Z/lp'ٯ񟸼~] efo뫬ptP3*<"M_;^n"${J.nUݼ3W67$Wk= eg"&"p T3dͭǚIjMR 7RQ/k#Z3nN-ӗ-e7 (SmHp9ÛϨDĎ̆T6|f_}ڢ{.^匧@e bM?Kלven ˦<"aj\?’58[(`#_A|!_MjT= Ngd7N=%2#̜cOhCesU"T*5hn߾x+&BBlEpbaMlʉV_۩~{jW5cmz+5^rAHguW f's ɚgݩkI/S/~ J\oN4{Jdu4 r<Aw|~1=|C`bt@D "l)9l^aM矏wf&NaFҐqJ|Z\zq@h8 bLeSY7eiDHJ.U EhQ$SrnpC<=N,?:%N7r0ϯ$Z׆0+gmՙ̋G\"?3U6?wJ"X[߰(S,^!ɢPeq-zxؠǒnM^ n;"R=US 05\ 5^*AH}NCy>VT,C j-78uY9=RJuhH WyUx,.~+]膸ȋc@@7OpC(` 8c0g7ZZ?-'=T>Mq-.D0ХY6*wCh{znxvwXE>+90չK<9.5ww~]t՚ݜag4;V8)5l};< )X9b /91.{S!d,Tklyǘ[6݊6sjqQ-*/E$k׹=[yivmwqQbX{׏ŹHM+ |N[w5x^8 8Xp`}oupY8ZΕ;SCˑ,YFj^tۥH*Px/ }9>;XnXf׼"T@lj/ٿG[V˿ U/O֦֞ByW- uj'v6ܩ-'Npwm^N9>u:]Z?O0"#-yjW5ee,8oH% A-3>bֿ,h[4j|O dwwT8&Gq&Үuxg$@S:}҉#'wi.<_&w搼0os"v~c/y/fh[>0uZj.ǿW矻)`ǒIkgXkX-O mra^b\?BT5=??|cOum snn2?>Ofӟǯј)Diqzoe#eT({7~޺1{KLLd_%׻9.N=ZR^*4|@k`>G}LCo4)kV:@4h9- 0Z\>, wCҠӴ},"qj.wAJKC_U3X1T:vx6Yv7fw+:Oj~Xϔۭzssdt&p}.f3k\/G-n9zgwe#4:eX)W7hРAߞ?V 7IM^)6ů=kvr$/I[o乯_ : z7t=Bb*̜4T맹wEú5{v-!8@/Ⱥ\b1 "WPllKΟns:,PXDs,׏HM+\<+ /,#'#[܃N1tشS#jɨP@dyE0v1ohE-̫O[h8of_Xnh)Vb۞Z{0,[ЧBBz\s`%Y*cu/ʔ \j;YcwhjFrꝕ-j:zQ65ܠ~Lc5<O7hjFI(ћ4Z#2\WXW~ ZjCP.]s6uFuCk?j0B~Aδg =UKFCzC,\ɎNHWbwww/o)vlװLQj%x߆T~tJeXj&L1vP,'\Ѩ?ˈ|3$(bSS_%Խb%_O)m"P\@IV*C 7e;w^&>67LfÔK KlclNle>?R%ujgax;&&Fwˌicn|je-Euul*Xz">tG<{บی|Wkm6n[Nb{58eR+iʼn-mmGx/u©G0ɜ8,#XaXOxlȌ[{>i¶(;Ac}`ga_=W\d#7>.`EҜZ%*Ba. S.)[w.][z,s>+3qoR z=)󟾺 @U?7.ӕUiڢjE|8ܡ2Ⱘ§o083(ݥq iM+ ǹ7\6՝bjq| VҊm8JM}6hڅSa]-/bW<"DiNqVwV2YE HJS,̯ 1 38Cu}P=p)͝oxW C=zލ5w`1B> TV_We,-ԍSR IDATe. Ƨ&,kMh(ho97lN '}}HMg¬GO=AG9@ᰣf_òEibgοyc) 3~+_;V$]\8-,Wx Ҁ|yV+mAg=춭6NBEׁ.JJK^gC@B'T%P< =~ƒLJ#5>G#Փ{"jjS%g~xjl&U%u-) @M}ڹ; J'֬ѠY&u| ?q Rު_1-!MVK*zHEQʷMkİگ7<}%n@qİFA*w~Cvⵈg(Hkulզ~MYxl2ꕗj5Ԅ{#G?{e>:u错%t٦YU2GUj;uUϹĞ}P6ѩ}μxl@Άֹܻ5[qLR^yJ\+FȤ I-tYNcNeQHpʋ*+K$ͳWI 5TݡV(؋UкKwD-j [NU,FM7@쟝HD^s΍6>0v#j [5,CbI+~ˏS[.BEex <Έ҂];ht_%a$܏-tgĊ-? 0;AT%cbbffUYN@wԨaNW`49>Iιv&<>&iY\k~>UCˑaYeYa?X5 pg{=~_vI[FIdkN8u߱_L4 d:0xxo6 2n*ߟ ZIJ r4]o?#xoɪsf<{ũ) 6ZRa_pa/@ Ã=vNwJ=/xޮu5^@~j]$zB@#AQ+z'F(:2o0$Tm][]sՉf^]g[Wj%m(k. m}sRQ|qI> rұq9:5}в7$ĥ}niǭvz3OS-{?E^ |+OD:G*fSJ@5Sjy>;GFrQNѱ aYtK`ο4 N1!W7Eex [+ٯ(E},zKHPl &fIQgO62dœ){(Ƭ4Zڵ߇=/LnWn>tf(<\; 4`5Eвrpܿ 1!{po }ׁ?/lu=- 6Oπ'Jam+'w<°t^{I i[V|X@)+wZ0f HFS,?Xn%3@J:{cqm=vm\3j N<4n3ǽSa K:>!@.3~mZd8(pM =GS]Kȿ?̽69[( `ET#HD=yIƎ9ӫ_eXME1ĵhoqTzt5s4@. MZu:]݋es۫ Ni7v?(0^>k>l5aZUnpލ uHP9<ӞEzEZKvT5}Wh8q ([Z]j롓=|OgHɸ(+斷H̻VuPK װlP%5Q (M OA('}N"m\\"?aMMEӎc{֘ WMczJ`ʕKTg ʼn'sKVl o 3x3GL UN azaB`r!387M?-ںgJ~׹2ѫպ tڀ1@*uF7.udGҋ1S1ՇϨlއ#̺Gp[7. 'hሦNP~:؅p _ 6n~n54cLzװ0˒havԶKf:~=p?@@=!y67~kn;|{sاu'ւ.n wipŵK?*fzM󯜹9c ~@hտ<:W8rӣDޚ;ÃIݐawϼvuDAҧCKțޥnž_^NKw5ZUxy4o#nЬzA7ũqME7MFYo/3+#D Bn7LD)ጨܝ;ռq| G(iB.?;׍} tw7 铹\3laSXLVִP71oC࿾Y E >|4xoJT0!;?o@ڬ>˫nwZ}i75t^{W5ޙ~8F[CC1bp>(|E+A =f:o[tf⸚vDHVMV)Sc5pT7BXQ~'kۃp`{|5ÿcOlD1gזϒCw;nq+/ ,(ce)gd;jV^޴Sk=?tTZp"7fHhc5%4|~c^+9.:M ЖkR]t0ֆ!?s>)]TD9Q*i7a¿dW^*'Vs]9%ˁ3!T"$޹nn(R %KLI&2 ]7ʛiЋfRV+/'kzP7.dC R_Y ~7}?o:{I73 @,F1՝&Jck5,uDI+xqᘪt@Q|fjbo!d.. (UAѼv fA%ŷV-ldzw,vX}FKtń<Ck[BQo^t:qpfX{u  VbtnJ;7KzpFŞXr δgùk8~`vlվmbp]kYbG";m"Ejb1bbyƲR,HеV% [ aY0rĜ`P_qv(Wg ϬQTd|fy@ BfBڬ^2*a647$0"/.XQ2 o'DNroƙofA4=4h\iң碧թ)0d͒z, s zUhn؜)0b;%M@:aS&JZQ 7Pc^(z.VFQXvi͋}b݂ M8~iɏ(>RFAP]@wa7RΚZg׮nr}%@ŤaW (M1:5nVac%V9eY^Ϲ6L,rna@xsH`>ߩR$שTTSA|ouI&P\YtRM R66U1:#[b0@QVt o&!̌ih5u5sfaQYfҍ);!K rȲ<5^Ka>QԌ?ܞL) +Ѻϧ4į g_&Ps7Ac`-.^TɘY[Z;Ƭ^:Y((*2Nʟ{+:E ye.o}2NWMQEEW{1"Fc_=hARLRjzfDH{UbWOp^GՃK7V_]8͸, Sz>4Pݱռq| G(i%6DʏՎC߼aO7ٞSOi4z<H(KY<q!i'Uν)u7NטqPC# U*ZB&ᕯopG ={ U6RE )Brbie˟/ ncN}01pzVX@jČXШCFo}Ŵ?^W3̴z Tz쑶cl G~g`JdgxOv|LVd|`_tJN*-ȰG~-C鞇,2SN53&Mf4{?oG4+⯞eqKT?-JMW \a!nnyua-(m6KW@2[[&8~>7-G-炁Vy4ڔ{a0~7P̢ߜ{ ;>i;O/9; ok3= jSGA@ wW0s>hҢ.d!mЩòN:|Mam|-|T Z}c3'Ohm^ST:4w+dUAMɁ뽤) ;֭a{םb $OPʷw;%%>.#>>>#opڹ*Id\ \:e~YO})y6U;Ӗ !_6&jHs 7Ad g̝܊~"|(U*;>&o}"ޢBN Rjp| &;rwIjwKD aEuFOCobf"[}>;hk2bWǴsj4͞y"US叶޹墙eAUt.ogql&wFnrL{ ay7wmM$BE~+};hPPm>sIlɊں>  sښlY]Qw|Zrŕxd |{ū'@kxσO]w+/=%@~;^׳KW߹]K,Tf#Wn|n^.5) M?)| ["TׄkX6y%ن/?ɧf.߼ul9a>3 J{V@'lVb7kpAeS*jmSYQ!3"n=a? Vw^'QԮcwkgYGMQ&=/1WaYbŊ4M?|PP8;;6h4חdPɩr@N *_v,Q [.tyGe>~a/`nMZj8\ !WPςp%7TVw۸&SaӢc(+ww`޹嚽_ r; Cc暅o |߾?7'4gn9AMK2e*KM@L:Qg\1) q3szm1[xFqbZ7iҤI杇boq册XCy ͊^į @lah+Jz|qmFrλ.3_ҼDN9+YcmKW>s-_[ s3*^6U5-"o^4 "6wYGPш@_ֳ6o6Gl2L՝žj:q| RҊm8ÈeF#0 8191,^#XŒ>ghAkYѦ3b%-h#9gjrP j6`䳣L㑰,ň{^bhWm,Zz͛k4/^ºuo;g=y5@p !_Q+v0Sǧs䖍uVjj"ԵO1~q>'DŽm;V)J%SZ2UdbW)f_qegii@x&;>byRT@*Je6ӭoM\8hAf)SZ@͈= Փr\¼Av(FUH$(0JJDz'gg2SUHbfPX^Q(4ύȩ0_Ejh4jK~5Us 8<,W ˥++5.YAŖDu||LZя[&UC q%OOʕ+HKKΦiZ"4MӴX,iZ lD@xScLJgHg/{)oW7g@$XT*#f2oyT*[ ?kzJi6ѥ3fj\Hc=SŴXl:#l`ټ`.:ΈenS=S͋kHpd򖟠b;V_!VWpDYYY@ 8${˝?w:,&'>er=s&/IDATM KW@ n8zŎ@ pqq(pp؜@ N=c[_.-b.z^w%G `d\x@ H$\=8rEyZLF4jر5Sr)y)r| r2QS 2aHDڢE :nJEfpB9bcvpx@ ,WDEEm۶,rg֪@ @ dɎ;ٳGs@ @ @ 9."""`l/JM~juRɈeE}Hqpo '(+A^;QQQ=>#ԫk.8pS"g%}ګ ^Tb5u)(҃/G]ީ }]9^Z4u8>K~]7g C7<4EԿޯ?'sFr9p/[Uo3le|i.-8/M!XqFd ]J8yrpCM{,#X!XO8~P`G۟iE X[/լh.b@o $sΖRdin]/a˩wܵ˛-ro* )ǗlMp_qe+/_\y:h3tP-g Jq|%X rJ/: ߛlP_5`פ}AzjͲv|饭ZYDuejm JRVzx'Js-X0^w<,ί*c,?ؐEʹ"R\y/E>|38rرcǏ?mڴI&ٳ5"387ua$52R>Íb 㜞 LEgƒ_Eo|P\/iKu8z+ $ 75ωkzmiؚg^y5?;.-hN ~+v\uD(-C[xj~ ߗЫWA@8x]roݾp7ARm{9/_~AkWb__UBc#}=8#>W".>I sVkێ kR#^ͲI@dhZźڜP֥F?bȮu]2SBy-P/n,AasH_youv<rZޤG`%2è$E^/q ^'+%ѭpS 5N_| +԰T)7-+KNINO0^*dˋȋѴNhe5jSI7ħE5|V(݋g_huU*tnwE\+G/o]D"YWvB0v˝3ĉGwçEW`? )R nvvq_Y3byBܔa$Z040[g?Q~ܦOZzEj>3߄l""ŏ#멙"=Z."fZ'EE=OxXZvzڶ-Y' ڊ_v#C|5ĉуyDr#KLyvD')J~u}XChXo''C~ϡCVPeYC 9i?ycwO6qio]~⭦EDZ?#8$bТ0G6d<9w/ F;s-;ՑYJZOK4^{ jf[PǗ8ǪH_ g<`LNfu<rRWK׬?hpѳ%w!.ǝ0H헤\KVZ-t7~(@g}1h v= -G-Pd>Ex|+DX-~id;kO܃E̴fo]47V'R:3O>yrP lќAW} [ׯ6du`e6Og 9 ODB=:ggo; WU .]w꼠mkH2~.иءJ~tಜךҞxtN'Nj\؊y H }N:桐VO;8T^[t}B&РoBūACVzIKY8WPd>65VQrиY_a94MpX>wzaOۊ||ٜ-k_/PH)t[ e_Lcǣ瓅9 ӷSmbZUlA^mȑ & [dFp(U Z̆=K7QElx*{m_vy9tÃR۹:#WF? :p'8T6):3 d█f ߚqZ}cL5ؿJɼ&D !ֲV\[1S>]\҄K{Ox޳J^2bfk"M<)79}? !=|ӞzK>yŬWjx> .N5Z!ky\\v[O;f#,ӯmPyQq<_LO>{#ib[5;v<üK}l`_,Z])bEPߕ]?\w >J){.H5޵ "2o-H օg.TYBNucg­6VvW8*k+SĹX-l=;HbxSNlSHMr;'sahɝPӳc =?x50 ?VEQ ð 3ڧbgͭ>}@u)))))/SQvpi^s6c F{ ۋ5Nm^V_OYF""1|C!E)Nna}n%}v]O]\9nFJnVt0>y͕^/O8^vQez}/ow?iBi ɦr :{,y' )h\nFAlUƂ8/)$h!jhhƽM;1rV9=Sf6汦+B /,_†c,>mz(b M8ŐOfiP_^+q[[m#¶89_޵H$2gO۱~~'syp5=(t b,M.x!-qn؝8x}&lÆ Y5x7,˒B9FYԒ֢ؽW k$ߕW_ݍR.MLqUꎗ!nE,[#mNěak5ܾZE٨EQ> :W^y۪^B>6_ieBKÖ&,1Y_O?6p,\ v3 +=haG-cG(4oRAQgP͆؊ӳ?\vwߛ}\[6rJojѧS9_MHNPb1<>gcJwYDs쬶1PDYO x#՜bk`ΣOb*~~׶yyhOw )LeOrfŠuL˪0#%a؋錽!<#(Ur&6T˿75VV  bҏkR.BޮJNmK:7tX\UߧU E,6#]6~2Qrh<~=(Rpq _z{^C@WPpŊ%XQxvA<}NѴS\`sjW~-c&\Sf0A[Vc+nT='m5[o 9:q҇Bjvw\-AtIBV$_6tG`:Ӱ'_j]O %ʲPy˖sFޤ$>-#65Vspp7z=ك@(lFU}"ZUXtFZgwv:|p Fs3}/˿_:6m]K??&e=ax]tK@*—Ү 5zb Ӝyr`.{h8k)6I+¼Ez"m/})(`7OVU" (XYظܰ4UV} J`^[ @֔p-: V׷Mk?@<0Z(-ٯtt(Aޙ_Gebj>]݉2A ?}:"#C$cmt)N.@?< y/H++sߦaImboO b#ach0Ѩ_:v6z0fGVS}`;`} ;X'RXG",,bX? /67=_ 9#᡻wֆ3^f [ ց8m`pݖﺐUdGZ&i~19eft0n1<PWLa>$9Y#G M`ƠX`af-0āh$9x%pY a׈DGO_$E n/?/Klw1{2i?USIfVfr0JEi4QbAqIJBep/?>k/HTs3GK;ߎOϷC7vǁ""c):HIPdd3YѢͯbyl.>GG&z)\b-9N> ae<@,:X; <}0$}`?8A58 y ``L9:AX"C/$Cr @ yBP8@P!T@%!4Bߡ5AD0!i BaC#Q$D&"QE4#݈q H$RCZ"]hd*2YE !!!Ǒȟ( $a?5F٣|PQTTՀjC&P -4- -VCt}}}=^`0,*Fcqc1y*Lf3bٱX % –aOSUNj\p \ )nNCQI9JSGs :OkR| ~ H  kB!PJHO $2ňzD7b1XO"". jӺ6Ҿ]#IљҥUе=BOC/DCAD_B <$L#yRINA!!C? #Qрї14c/$I }u;)& Ʉ))e]adfcamn^!a͑q<':gg+k. W2iG\?y#˸{yXxyyyny5yxyo~0St(RJeϘ/1:?ߞ?@@ >&B4B*BB' S P٨&$juLVDK$JV(FTE4DJtP !((V!D!$$^%>$P$JHK6INHHKeHK}v.'%(*S'3*(k*!{M\syZyC4o ~ '^*-)(n*)+E+(* *{*W*0XWEꪦP֪U]R=D..]u&554j45)4ǵjk hj՞ i+{UwEOM/EKo؀ޠ!aa႑QQ1̸xĄǤdT4ŴόhfkVn\<ԢblIJ*5ʺ>{d=ltڍڋ8;94:8;:;I;8=tprp8ujzuM-m؝׽߃#=^{.{==ynxYzz6^93[;W7_?P0X8T-8:x%2>d;1B.331<$/'boPxdVxZh1P{LG,Lʼn׌_MpHaoGb $}{ۿo"E'&JNIHLJ7Jo؏ C&0ckܙ陓6eeEgR?T~|Xpߜ2%y>y)=ѓ0 h42&NYSs9_PR}"xyiG`ٱ**WTV==}:zTЩ5F5mµ%1OswFLYg7lq;ڄhkmvk<E˅܋bOPyPa#GW>VzDIǠൡ]Cj=~s_~14l?rmd˙W>>yCzSm;wƕoNOs/Wj}0-E%V?.-䬲6Tyoqmz=aQ)ymlkl;l{;+@z;ñ x1şbG` `^ .)ҁ|3G n! -jm~IIJb{q44Kb?@fēQLhf, =9G g.eQ^E/F`ZH^8@MOĆt+9>yӊʜ*vyj6ZGb4 _cLM=Z~clMg#`jgaxЩ̹ۄ=O[G?, 90/[\ؼ$p9JՁNk}o |;+;'wo_ܻew~v1 ӡg]/.|Uј177oގ;4<>?>xRvrCTiٌO>ΝLyK ߥ{:mnoOv 4#O Y6ZFFl,kezD/aoF`"G#ћaI^d̼ºNpsqGQ3 X f ݥ"EESŗ$eO7,v޷%T+Wc; :3ݽq+v[|];N'?px(q'>M{9 7M|hs/ TB>ACEO;D'MO@oKA%e5S4'f?8dy:ώ`/i+[{'~$)BŒƒBQwb QZR?ʾ{#?0أtMJjZ]424Ӵ+tt^/dLLk泴Jnyjceo0*v$x{k=wsEhGG''zD59b߭Tb{zgÁլl\ռ|kE%'yOWFTT8橛?Ps.9q퓝+oqwYݓѡ"5FO54<"e镔?m7h76g=F8x C1!шsH Y|DPhA8nB#L/VaqT}wFe8#&jbZ3stpϐHiFkd)&nRfv*VA֋lZlC~[圪\i܂܏yxEx_Qrt/ 5 R婛"wEyoK I^,}Il\)rB#ʇT&ūG  ҊN=wU`cL5176+3X䱲>`b{.A‘8Kk[{Gj6gޫ~y!FaiEbTMJVr5u9]sLY &8}W>}lPxDOdJrՑjS-u3 :MZ/^molwVB{BODDQ_PG_QG߈sh Ҭǖ")+W֤wm(ob7Zs Y[""1T@#Psh":9Ec#} C4x_0G"ޢդ{MBREap2\d4\Ȣ򙵆͕}3K#Oo _00JʼnI\R2 F1JuUj꡻4iqk>g2p5\=z<㼕}I~kA+!%a#e.Ǩv'&J''r>M:f΃Pš'G]+9_9a\zlB2jZZg54^lknop UW&MvvzsDWN^;7=5l>{<eҫQ7oߌLorX3=9v.u/_/-cQzҙN?~.(\5\}Sgᵉu ō+sΛ7"osnmپ1?BD]oY^y6 ů@Է¿|#2p^., pHYs   IDATxi%Wv?73^իWU] hp`8;9X2)˲,)m* EY!Lr!9$g f`4zѨFwU/oor[zS `0@7V/ޚydVG9 AAAAQ/@AAA"    ="p   p#    ="p   p#    ="p   p#    ="p   p#    ="p   p#    ="p   p#    ="p   p#    ="p   p#    =AD.>   𾹿C=݅    ^    G-pX^    %G;z0ۺ?Me"%EAAA>x[޽z彨"g   })E>;tcD "qľ!   gWÖfr@|t Pk#1;qUqD"pSd*TJnk 7޾EAAAUaftrm}U٭TVm? #T:M2d3P, FJB2Lp   ǓF`f[9BDQQjiy.߾]^VV7˻zwZ( ##DE O9J$zl:MzB>3671P|9υQ 1ZH)%AAAA8@~:DQTԚ~kK[B ( }@@`Bd`4tW* gN?$ӎIR h!   ]>8VGhο.8ݗ/U/\G84(P3`?`4"3BB6LJOД8q %   |Ε+7ҙW._rn 9L@!v; ϪR7 pdSpN/WK?zʧOG>251_tT4=@=ʷy_9;Dw0iorwd`  ]\.Wٗ^|kނK0 rK6*?4)4)dH&JDZ#>Z>Zm@Axw8#sŁ >~逈cIw0?UAAACk qە9wKg/-\]\3F)8 B=/> Y{b~rl2v)Jyi)^B"þ6uֻAn~nʵNTmzGpIY:!`Ņ_Og=zl3k0َ{(5kʁ۾4`rxy9yx^sc⢘{}ƻw%e z@=wwޖS)AAx Vh4ׯ|G.v4\B!GA)14 `.rxt8֛̥ T&<;TDd8P @floW/VwUz+[5IG b=xfD@,no^y{y뗿M]ŇO6ɿeF*(piXO}wSt?!"q>66LndGahwХ'XŸ3ιw*bÑ`AAAqv~oWg^ρRpx<6bv0%p`S rl D`@`+cSJs'Zk6f7o`t@kh@=E''XLJ7v?{0wߣ; Zkm6 c?ks]&qrn6q+='s]8p㣈-B$~;VOkx+.kɝ Ħz]JAAA8 Z81x?~G/߂s |H#‘_8uS3dB+*f=Dq q`Xڄ F;D`b"&b)"CRM5+m_z~^"* 0#sE4" ?DCON w}{@~pN: H;)^aºY~Pн0 m(bC!Z{B,ܼ[ш;bZk* Iw]|+N]#>v屴ADJb!怅PAA qJ]֟=\j$]dR ? EA3ePN%\i;ATm3`@"E DLb0Հ7*֮92ft (H.O<`w?I6v4HA<:jy?V<]謺aT65 ΙcNcu8\7qL(k0 ZVlw J"d2y^"at2Qٝڟ۵a?Ӿw>xqnUwǼ>~(0t: ƇܭUT*H$8t_Uꖱ e#~iv}*8V%!AAtpE)l ~•[{戤׃u\D Y̍ʱCOLeӎZ1D.b6RPL ClqD`VL hf؀ c\RϤn3Q<Q@# O_f}w7zM lM!6,E+G,t5bqzC cn4;;;A8d2L** LeFh4v^ Booy}<~-4`#ld+Jq5h[&Lk_vǮ?~aX׫jExfl6N Xzj1&NJT*8a#i'{Gbi#k >Kcf{RbzI0Vq<{w_bAALfnS??}ʕH%pLUp. 7<}ht0N&SdSPV#%@3U1rUNAijjj 'yay!כM=aTC}{ki[KזQi  a_O~3λrۻAl|u=ZY7`Vy^z6tRBLST6773L>/t:Hiw;J6rf}kZZlvpp< qɤhrauJ%"J[~'CZfY(Ţ1&ywU1bin*Zf JU+&jQY# VU.+JlyJGwa{m̻Kgwww5(N%ɔSiu"6tN=\Xf^$9 iCAAS>[,`um~[. B6 (8 R09.`vĉX)F uR^@0& (&0+ar; 4AA(0k8 e"Hozϗz3%lWp<ʅnTE~ԩ#wMm t0N&ݝ7qKݶnѰigVӓJ/0Vpv6lk% JB`XkZѨjf3Nqt:S(lOOOTz޶_d2jĦjT\/8Nq]T* nuj8AX#i\D2nGGG$(ͦ>vww[V=r===Lfxx~[T===3~pubu{aآ$kAb'Ll6J%ә`"kmN';f[oZ~wkF0zzrTq#oW(  Ǔ@ ߾?_h@Eƒ!$\ ӏ}p!A-?4z"FdT: C32O{zst|V`lB& faۛ3VjinEϟH _Gp42.kڣ8zq$mt(V|wuTH&b{ (텅Zfu8ӶO-R$EjEGIӎDQt֭ťrlq38RT6- CCCӓhFGGSTV^ kX .tX,RJR `d2988Ȫq)GpYv.鬭%əq[Roܸq[nmnnV*znUJ)k?tPzsssqqq}}b$㉭2_s0 EQT.j'?=}htd4J* r[A`@ %޾ eU7o\^[+w`{r{wAAAw|Giuˋͧ~X6tJB9{` ONH>ciD:w\2"9b&iCF"0Ɓ&'Iuy.HQoںiA e k20LZs̤"1xd]'NivRSqu] I@>/1o~O?Qm[ܶ\V=쳗.]"l6k=w+a#d2@TZ__T*[[[[[[6"H\U񶋉&㬯3Cz{{<+˗/_^XXqFѰ.N؏ݸ-=p]9yÇjNcfg`p[8ٟ]rW)e;ޕP-IĆk||oa+>, l L&3;;;44400+++;;;YI<wt2CCC:~ɓ'GFFz{{766VWWJR.ZӨ2%vmCS b@뛕kWFnvhװ˜ut'=ӓb‰|q`MvK+k[[ccV)V;_   |uo>oLIDp].\3c_>5q[JfEL9d 4`ÄXTGcP!&'s uPGA1{X3Bb&F"V̬#PrHElʝ bO'ɑ٤̈́+P<D"x SigsTs{xŐ`s{݋/>쳎 Hnu]sssG]\\\^^tL ЪVZѓ'OX__p›ol6mD*d2&rWwո_I,7nܸtRT:wC='ׯommY$qۥ+/|̙S&'N/bOOq cLVkZB.,//?ŋׯ_ذsXl l6=9}" ͕Ϗ=çO;tPOOBV}ߖtmHwe2SEjN=p2.oʭ7kh0@sT'?ggJydWwd2/wY   s><#NVV6oOp@ L&ql?ıå^ߘ9&""BP (bh!( ɾ\6h#LFM+4B0ဉ2(hBh@(Ab?5 9 D!R p_ 7Ҹɂ yӄR^?ƘFQo߾}ܹl6;77O~zؘ}t+SwÖEDQQ(?>88ٳgϜ9sʕjjp[}OAs'y?~~~^z'>NLLu@4ZXH$z{{>d#BυnjjsssǏwgkk_~_R; ";$Hojmٳg}/| 333>իW;u;/cu^ D''Fq~-?pkbbZ%*  p! DoȦP\8.<EKW. H)EJ!`#L#p@c3X8Πɞ@!* x[H _D eX1y"DL`a@`1LD c1`Fzr_?:1o`RH($S-_V7ѯ*q{t*X!J##ޯRf|/ǎ;|pEz @yBat:V566f+S~;wneen[YN~{\y,Ax뭷*͛7/><@2Z 0 c׉;b))v;  bJnإZi666(zgffR/^(3T*Nwwwϟ?NsTTZ^^m208 }7PXcLSJ w^H$@n]7bB́/Yy}}}Ƙ_>7;;̫d2.ɹS ܍w 5WOǎ}5*am5mnnjSNMMMZ~{V"ɺE3s̅BAkSOj}k###=؅ 5؍5 qn=ϫT*Q;ȅGU\Z|zOڰ)߳ofF"a|XWw'FƠիW[΂   Sa!ի'߼t uH"\L ?xlP_ TKJE 2 G PĤ )`@ 6It(畲iAi6bCfB63 7ڍ8Ҋ) "f-SH2  y3*&D аdzxfoEplƨ6~G|?g`< !$vHC B76N$D8`ph&)@f2=miHfVPL("M2T:CÆbr`BfE1P 8 1afD L|n߼y*M^IureumnZg/ JyH5m0 E&p(! "vgoB*BOPi_eg&v\NRDdgE}JAA{ͽ8l sF_ "?42lYa4a2dB|@D`cL&dɤ 4,Nu󩍠uB0 `1 T%4 ]ѕF5,׃h3W>v~lF9o2oF%VGHR<\luw{ka6JeSN$,DJjkvI!7M;&VV2)QMNNK.=ׯ_Irؗanb8==}Bb&kkk7nܸvڭ[jR~})o/Z|;V[$v|VJY>)[oa\^[[{7_{۷ok3LwGl'fA,..b%$!i}ߏĉKKKoOww[Nb+ccc<'9gϸ4c]6v^?tʕ˗/7t:mFVrgΜ:|'_n b llh4jکi'+g(gp\0OhgV6_Z\7o&Fi% XʅkgoJ߷KKl&(bfl v^ (`Ɲ J5 1DC3kf0 ( \"fb[b eHW 幟.ZȺHҕn|9ó/658,\.EQdGlؽ_*;6::sٖ 6oZfW՝bX)$6}1fmmmxxxzzn R*LQ]BmJthjj{'FFFzJl6mKlTJđ#GΜ9s…mmJxT*s=z3̱cΝ;WVx;  FTرcԧ~V=tvvv vә.JKKKvhk&IRrTť.A3L& OѰ%d2ӓKR&񜛛{.\PuXIZ.,,+'+++kkkF 똨RbF"w( E{&Qu13': vjAG5y3L١pxX\E 5h+7pbfVӱb\UD4AAAsϛ{o}ݚgυ rIoiPdH A=448BC%KT糳 Cj:??a;bZ٬Eݾq[o###'N׿>113ܾ}ؚx_V}׏9ߟL&r`ȁ)*qFRu o6lc_aۣ6l4؟K0R~K_̌KKK׮]̜L&=ϳFfs5":qLT* /BR:HĮ3HAoΞ>}ZXQ ߼{oFl=TɓSgO1[׮/.;O)C 1&LzË/]\@t!T?^X##nכ  ppOE_ٗRix\\#PRa(EQT`G.&Ff(ä v G""9`ÎuQ':䢔N2Q; 6l"hHGQkU )ͺB"VA)`-a0c1"6 X2B fp5"qP̖>Oކ#6whFZ'I1EQөFիJĉ'NXYYVƘd2i8)tZ(b[b{:;vl```}}+++\nZ8q_C=n/_mk4z{{٬>SEf\T*.\~'={gnݺe+`߷b`^v7>7>>n.'ޘw-Qcò}ҥt:m+v#݊=X&بZ-ijjq˗/_x1SA8alԩS_򗧧K d2F%Si={vzzСC?hqNrۏvuu7xFGGzV:U:lF$s~qkeDP (ǎinӨ_f{14Zkok'=9=ۻXނSJ!yesT)Jva JQAAAsOh dH U{R@.T&*E 1)pPeA SDy/M("Á]"0fΫ%bcQTJ'fh5HtD!>HE|o0 eM9 U6:#^b尩E 0|/Љ:yo[b7y5l;+iۼswwڵkN>}ԩ7o={m-ͫ#z7lƨvǶ2\t۶׃R*"xk%SSS_Wzfo,--+q @D\Jvۖ~V}Z.y䑑r|ڵjE?j [C5w?~+״ZVEdx嗗L8;>FaLLLJJlQO:hccҥKLLL=`2 c vîLF5kmi9~QNr=o kl]u:d@/BB0@[I Sͦ>;Ep]88 AgfFe=w  d t'!;h#˕v=<<<55kY[#Z'F2&&&z{{WWW/^h4Blhccc_qܹݡ!`um1RxKoooƘ'O>}z{{?n6VU'bcc'O,J/_Uj%*/"ɠP(.l'=.p*5Rq;2&X-'wٳFc||<( f l CCC[[[J*g?=??dluQ*.}ixy A b^ޞH6Ꮽ v/> N|`Gڬخ~??~ԩSD=44dUxy ;$޼yT*MNN~\^^~7o֮_\\\]]rv&XAxw;8llUQ-..Ǐ߾}hQl(ZTy\zvwTy8P[} y AlyCCCLfqqƍarqSNimߓD"asբn#\׵hGFF677o޼?׮]{뭷(teaf[R.wlG؞\ܩA:J!ЌL)VkȰ=LFؗRvtz}{kk}-?34-(B3@"_DT2L+SAAA>e#N\0함xBC>t2ҦLLL mFw+E01eubψܷ̪^g!gHCaєWARmdCd%dچ" $4$=2 ͛߷ zT/ҥKqONNŖyqJra2l6޽{O}S/ʚ1^`0>>>^XXlcXC=j;Id30"z;wݻwOKĈd~~W^iZz@Z-J"S]y#i}X[[[[[Z.[ydaaj1%Idezʭt8:AQ`xbd5I !+;FeGQ$I?~}p(P@ ($8l1s7}h $tÃqeTJ\w` \@ bg"5d]PX%Ji]6wB{ hKD@J ä)`pː"r5@1 ` 2 9c 1 m` )̚YfJX$c4Q-Zq 0<UJz~<SǩzC#%|Vbx@qVjSSS|||,J$I8\eGGGRW[v@!˗/KD_^^(f#Ks]J4M}ߟ,??h4Q^t`)^O=bbU0uGOllO=꒰wJD7޽{Fj9… /_&(&''mby]Dt(̜bkۭV7ߜ1F+?kQI3s JQyj$i Y={vrrGQT,L>r|ܹn{-Pi*Ob7ƖE`uF$~drr+W KIZ-+JpXFI|./>eg-M`GV(M loouwET:|vqZ-9\.z槝gNQD!: +TS]2KR*h"JF$iJ0j:mp/X#lrp>aPN8dֲh>"!U@ (P@?.N>:!teB[G@|-to`XܹsZloo/x;&"뇇Nj,4M(j6moo !* gffVWW+nכ6ƈvJ-$TT*J(vww]m4O=Qb\\U*%81 /Qzr=o$IR.r=>>"#F*̜={۷1l٢1x[j~I%,^&IIAdzA(0}0su}0T*YQOXnZ庾H% |i$KF (P@x@&6TE5%`Ӌb$ 04bG3HHW})Jnw(RP@TFnFqDy@ (P@x>ǰsED| G-ժ_i?%5tRA8+r@JdR@ A3ke,ȁ41H$E< cG!flŏ/\b2f&&_y0-1*3OgZkf\My+2 Mj$)FAxsv3.'4HvzvC^Kv-yc ;&u8oFRy;n%;ÖQ1x㍙ %cC&''IVN>3iZT\MӴK󄂔J|Sp$DJȘIG~DX] ~?33S;NM෻S.ggg v^OObd#"? !dC%PVd_u IdUQR4W/aLFɏ5ʹ4NR (O F$ oQpF# b | Q)=(Y (P@ qJ)Ea>ozYPbVi6Rq9(4L b4!VIA)1RiţJK.`ؤ3"&ɓzOjQ[C`bXb v D23 MÔPGÒWj װa&6 1hf6 .)) 75Y乵R/ͶzQ%QTE<~<ٵn+ i|m:J)Oݘ?K/}F05 ÓD B4T*μQZxn]몐.☐K0g,It!~Q)wwwk'g"te jI$zy^4j#\3WYi<۲u %rb֎)8qcHCV 9dMmdX( lrEEv??E?D4HA5hzq7[ (P@EwajׇB)x.[6ḑRVQ"E훣POy1.nu"za c>&(s+XA!?H1Fu.zeߩ)ވG)d4O!hSa3 1†kT a4qp`@Wn? R5*>\Q+hs: q8 ExHv^ȳ\>њ縤FILChH0!?ka(L-7+ފD$C/CΆk@2@=c52sh 8 6S)q !<|TsavID *W ѽ7oJh}a#NxrrrwDT*ʫzʕ~Z-)weV6~R!aJFhk8$jtA P2KF<վxUV[p9, FqܹgݻwrvJ^O:&8dZh$Ię r,T S!dxش, #p*y>#2pʲ,(daj/^0՜xw t G< '`8 gg.aN42|?(P@ (P3dݹs^E7 σ> 0mxK GAwЀ܀Ck= 8-R2#tʃ,)aOIC_@Zd辁Q ;_yO-NMVO"8xd`9]v۶2|Rf(MbaETŋWWWUV=ϋ\. HF*ShADrYbPmZ$,!=G9V`:lTj~G2J^__ R,pyy… YaaRHWac/D^jvJut:GGGm'wڇb?eȉ$@RiZ|5Ml8/r8JX9qŊGF q+$YZZpxޯwGREDhU_?fBaww?1<55E(P@ (P@pG[[wPQ C‘ nh>Г2]P@ re4 7A L+ !3сyW C)eSb)A .Aid<4@  NCIS8EQ0 Jv{<xL/'U\Qf<{@'xx/Z-85f[H R0s"ߐ(!r @9A^g?i]9rڄ҄e^:77' $6%?yr<z}% Djͦd٭yGc$&cggG"AP.]ו H"" ,S~XbR"]Zz%~[~ J:p \F'Dťzu!7nҴT*َ9C+T@ (P@?<Öm{p RK^(I $ao$_0 T)rc`A4RpXw F#_"8`4 @L)1d|fQ@%:CB$t6#IHq09E)8 PPq(( QG.ek R e\.70~uJ".n+qFŋ[[[vhu?ibtf=H wYsگ[Ir8g9Mp#*7[-J<9H(9јkƎ&9joL9WH$R cxgwwW928E!~D#%coHq~vz3oy{CT8 1 s޽̓Y (P@>AxU 4Xr {A DžA?S?2u⿵^2FHѩTF02 #HWТOUqZic?KF $2ԫ?\bnw~({8~H<sX <1H!Qwk CKF|$>̲Aґ;3$Sc5x$z*3GQTT宰evxH4M777˕5y| CFUd_B јn%{[k @B3T (P@x.уu % 嘎Q΅(/jT~lԕ ݣaf!@ fcZiﺊ(β0NO_ƿ1A CC  s&W*9G & &&n'H""Y+yg}xx\ƣ%i(Kplg-;mGO8;/_T.ܹs||\*$TZclj'<QuYyk8&W֬9jIr,Ǜa^4aQXͼx<[:{K[U|CDbooߎV7WOʊ^"s%;;Bm0 [n?2U~}yTHr@&gB a%}?5*fOBCJiJhA4^W2NV/whiG4j K*4O"872y|-c'c 1Z~7@~S{vGc;W"+6o"wc <"$BեiEQߏdey˯@9o_1\ i8F5K^4Y߻SUYJ`7 (P@ d9Rtv)x 3!÷HE|SW./Wu(2Ilb1Ȫ8F;lJ^aX yb69\ڝ;'_AdXŢ2a6Y~05=j_R0H5 <9a)ôx|2 ),ggg'&&ly|6M~8vfgҹSk}ڵ~/_^[[ VvAx5W'L˗DbSeg!;<1Vo򓑏XO:ND2Ce-#9]=AI6"I`@kvg^|$կ-TL(TA!//÷$FlS@ (P@=N輐a )C3Ȁܡ-?;ɹש3 IDATfʕjlHiM3m HR4™zpXk8J;d5f#q:L)t[ޥT'3r*fNicdFGv\$fi2ka6l1&J2UqA7 䜬Cfp(IY־9== ^DT.3g(sεk׶@nv[[[okǑPIyA 'O$I5"[0z~ #Bx k_9g8̜$Iq]' " @jfѳO8O7}q$Iy3r!adVkcUy>bk@N*"PǢ_Z~O*vyDV$: zrS_?I~_֍7ZA6OF0 Øo7._~at޽~93*؍ (P@>qx>9q!G)>-]@p]*r|8'ژ,&0lL #~:^s_Y*A/uAx>Ge@ke` =lIPC~\KS+Sz3+RL`f)"fm`#}R41$6tOW88K!v*z J\g T ΌZgtiBUjKKZ4_r>f Fp4Z1r-oqhyӄQfr@٬jYɓ}జ6_MDKKKf> zA4 \.#ʕ_8zjTEyD>\hRiv^'8TXKp.~$I$B?r( $!"^'}k8}|RBLNNE(_JyEVN!b,666$40WL84<㓓f_ ?ֻװ "LT:4 14J>Z?_NټݻJuiiIf^.F!(P@ (PCp8 5}j/6?3W܌_d8R/3aH)P1pFZU@Wø@J ˾Jt(P0Gcδpp,K6J/_(ʹ˛;B_Va8ainx)K8Ilˉgǘ _Mk) 6ckN%!˲;wA0;;rxx(G"M\.7` evEFCjrJp <55E,YX!~Q%T*y+xS ɯc `}}j1au+O0rݲF!Nfjbi4ju?sKvxƪ!!b~fTit"!҃eZŌZ;@&8 >\uov9[kN> r5'Cw!Lv'нO"8ƴRfcS 8_!{u,sё1ƆeA֖^/MSq~??[$Irrr;wZ(8d>Z$J@eZmrr2Ik׮ Y zԛCD󫫫2Cqlܩus1==:99#2c}V+#D\*i*֞Rwttjժ$Z%80̙3.\݊lP2enb8B|lddq< RIh#9%oW9MBH΋+v̙U*?WAӅQ bw^\ yw\.Y^ڐ?Q@ (P'Ǣ9"ky<G_,u:9_i4u+e҂Yd$Y +@"P`$Yn93̙ "mXsF^QgW .*#5<$80 ucW WǓy!z )\.f911a{MLuFqܹ=ѐ2Ւc_[*ex.Zp%GGG+,VDtxx4| 6AYg!8N ːkp iF3??l6wvv0 89s|(,5!#lȶ)GgvvT* <}Y:ն3I;~F3W.]w̿x7!JQ P m=SiWʧ^sW|ۻIRF6Z"EO(P@ (PcQQ>><<A$kZcgr9MSrY$"zċJ%;%>-~Ắ$uYf9==GDɋ8D7777;;{M,)B`U!0i# )h2821MrD\DZֺ\ 3Wƭmx&niHC v7|}Uzݺu[Dw#Ԓy֏c7ȮR~ (P@ (8S.`zi|e '9voǚsma60DQ&լe@^FhV_$8frR]tv irNڅ+2 \!j:4͘hƐȘI)u` !:}$ɰh'^B+_uqG`렵vv3W}cYdYV.}ߗ8?T vd󎏏X/%-*'r޽Fh4>^ZZ:1fssVyA7zte-c 1]] 0ƀgUR65‘)M3 3'bH؂#9JLY[W9vbUjKvk+Hd0ԢA >)JKpIm:TҢu0dYl6ѬĈg{{{W^=|$(> CQU#9%uaaI۽rJ^FfD[VgffRҰV5ȓ;cN;;j$4!fpgggϞ]]]1 Iߔ##npҥjz}=~L%g$6tG''Z5?(o^{w( B8Cuf_xzi:llFTZ Z%4Mblm-N[l (P@ (Cp(ղ`Y0\DU!}Urt1-vj{[FZf0Hi`WT Lq,J6!C3`R(۽;vahhj@G| fΌɌδfcX<)HM?֏{8RGDF f@ v|.`ćARK2g/S,*RIA[sk8Кk0tR0NA[lr><'iѵ*!n#Fҗ/8v7&'[Q  ޛXWb7⽗/}nv7{asHQ졚ɱFȒF<h``/? 00 6@C4H&{ʪά%+o~{_ˬxnܸI;qc{(Zg:}+_uѐ71 2E@^kv $DqAp'0LCQ{e }ؚ_.6ۓMl Z@^'wJGY_ xkZ9ԱΑuJߦ5!%$0=(0(αu;{˻ݯav+ n0x|p)p[3o 1()$qvvԩSV+rmPJ5, ^y˗/DImbQ)bֺZ T^2{ffŋJ~Y]]e4(LYkkkEQ<ǎ{'Μ9o^~}wwWKޥ>̌䒮+UJ ?~FQلVtep8j2vj?CbZJ*#^_}G~gΜ9}խ- .P^pϋ<'FWD+Hpvcyxxh|w{m(l `cb2v{vvg7ZHRwGd|<"̒@F¸I} ow&2_؊!E; r`up"p3/|S{sljac< V\ V9vA!&tY]b^P8(R0\'VNM:_k@ĎB-.`Dq f YR!P!8u=bDv#B$R߿tښ@u@XQ,..tuW!@VY__ؘ>}^B*d֒jY։'&''ƶRj v_|4((-vV-///..vݗ_~UUu %>gSիW_z}˭VW_vFC)%zx"z^w񥥥I;_CCCBi]v7߼zjRQ9:T1H R펍///.~ J? @/䉓ɑqaW!ȶ.`dDSvivvv]&I?QBBBBBBBBBB] 8ɥApJ}cqPekYKpVUyaH^!Ąe ["ϑ”@HP¢9P bnZ|[C t;0P>B98 Y6!C-[?¢R 5Z-ޢۻvDcH ܸGn;;;ۭVK,$s Il6vww]666&yaa믽ZV$VgBDfZj_>===;;;333??Uc666666^z,FQtRjN>̗/_^__wh,Z˥<.]ZYYqSòJ Ƙ1c̹s笵gϞ}ꩧ#4UZpmllZ-I*8 zvtݮܗH2ܜdJHX=aΎvrtϯf_w1@hֱϾ=Ϝ4U9i~O=N`c1&MHHHHHHHHHHxq7N84k-L`P((kRABh+`r&Y 9+MXE!s `,,4< 샜)ו8`'q#p({(K/*:ߐ2Ƣt(0u`Xsfb*͈TD 9~*!=3wvzdНTvfffΜ9+WQ(|hfsjjjee%˲SN9s/]$%t< -H^KDY IadD!D|[ɲ9'AcccgΜkۗ.]rIhI[Q̑!Uks)g $HQccc?8]rd#Axf$E1:v5p$JrȂGC;zj\'Y5!pߕ4CS͡sװA-  OUL[u$;y/=t =-ƔntIHHHHHHHHHH;!GƏ:̲vO uGsb#q0zA pp4@ 4`C#zF"̅DfpELʾ }ISUIJJpLpX 6DÎ {l%iF55ZultnFIg#9%!!!!!!!!!![Tp T8A)ҙK?S/Q Y ?JCgF@RgDePCJ)+rj t ^S@)  '5& X CKM48Ơpst'#=D y= N6t^#/n~@$q#˲'N4ͭsε홙"FQ'R3ښ48qę3gFGG/^Y\$ˈH D~FJј|衇z4K+rX#edJK %y n*f7v^mnn;wsĉzjtttuuuggGSnGr$YU28˲4jŇ~X\՚ͦv7J0sY3P՝=34vBwwꮞ/!!!!!!!!!!^-U?*;6=oF14d@sn*Z\.6:҆hX]M 늆Iu'ZJwH(AWP+ԳQ!H[t+ WvoU<$D#l{2䘜aYȹ¹!k-s]:m5vak gkZ9BLzs?91$zh|n'Y^Mǃ֏ɡl4077gz‚y@A!Atn֋1رcsssyʕ"%SPUp y7'O6Mћ\xq|||xxX.-ҀYy|05Q%sCCCn/_/XXX8yƆDDQL-/G.1::*ڍ,WWWGGGc 8rUǡlltpvESIٸwGϠts [}xّ{_Xr͡_7xA:j54jkr29t_fjW Z#={J!m"b(. ]nx(sP(4> |@1;!VzjCv;)ږ45awEa]ǔVڐLtrǔ( %E>gY@lHjD5&.*oQ4%ahhhbbbfffddD.ﯭj9Il4"߈Xʆ$;`rrr… /_>u#<233#}^www1կ76 4?錏7MitK.C45jGL@ػqyxej^z+W<䓓nWETh0|dIu%Ŏ$y>==t1˗/llliَmwFY{tbg`SS 춟_^h6s>>BJ";|CPegScO7gQi8z‚{Ʀ>UB:ؖo\1`ft|schZMnCg>tbCanLjJBBBBBBBBBB»w::9@z"4[@1P#uh  p`Z2j|;p|)/| >):`e8yyآv/'^;Kf>r-c h4:΅ U9ͻqcʲzn(^7>>.ErThţy+WZ鬭moo3֖BHV9z[[[v( kޞ@FGGFvꦉJ^w:_~Y&` IDATٙr䋽^ohhBZ{ZǗK[644$uzl6Mѹh,;nz" bjn^ڢ8 L:#dJ9\/I'|Yi*b& (~BFpM#l}[(KhFa 458G*ju +M\/ûYĊ XvFEeR 9ZɡLE.x`0k}* HXo/M|?89/=44Tʊb )c'$6͉jTc1o"DQe'N`n{…NeȈ,FGGI2XWޞZݮ&''gff$~IqzBcebإV77'ծq"c2^羕ʑHA8SZ->;p$&&&bȡB9T|#)țJkr- OpOq  2>~[~{j(,@ N:(`,HC '? {س gm?hĨQ t0sYSԆsG؃mV$HfXL͎~GO>$_@VNJoۮ"WB4dG5hJmieX ɥKHneTRc"E%q5 /:8 H.4AdՕ߽r*S#b2s|YHD,8d n"9" 9bŧ;HHHHHHHHHHHx(8yh={PP} (iP@a+8ĺB>'V!*&:[{'ks̝^7SPC:JXww~:58)&V^!< :BFmڈb ok+V#%=}v;#Hv\U*Јd[0Vˑ2ȲL (舲y^%/ߍ}I\: 7fd(dʀƘ1nuP.7vc`RҫД g()IPTTW[ T8 m߻voP9, > !%A9KWs(PdA PPK!(Drx7>ş"Ey ]on=x|bdF"FMv֑,/__ÞCٓ^-$dtp&G :`<)a{Ui \7/|{LuGU15֥w7Urc:@gnՄTX1\bM~BD.JjT'DEtLJue{`HDC","7 CtxAly[D$J?8-xjb=Pn.҉6/-C7P9zBA-*Wܟmup$?Cn_}}]Dm$$$$$$$$$$q6O|/}^y 0 c0$8/VE&?Ņ&,^i?]8]WO7tiV&7`,olQa,g v> $Wy&7 g j.WLqI)Dv$ȅ@&OD)|͕a{f1ut7o7oW >ȃ@ D RN(TPITE4T %⁸ݑzV>8!!!!!!!!!!]{Kpv?7^zu,2FN0"8fπ8 f[.*6m=t\SW?pxd,J+>فҪ\(EvְJ$Flo9q Y G`5[JY)Ƣc&W~gfPw׮XE^GÍO`d3燛702N׶ ]>9 5Cnf} ԛ>`&o{|aGGoEp7wZ.1Bmߵ%CGd)[yܢE=t4AӁ!")+j)jSq4,Z5H_C]@< &"P >FR $!]-HA{O)@H&"&RPPb fhRrEB=P EDdZK) ̅"R":,|Jrh`7 R &VJ đ)޺HP :#&$$$$$$$$${qo WzxkPD-LAO#C)d]h6wηA@ )qP-WBir,<ϕ_eg 'sMr">wÆaJݰƠ׫5>OxYy~}a-71oAgG'8 M,}MZH_VjО8x[Bov:'[bߡ9>CwP qݲeVj1搷N 3ar`+R=?3)#az$?;T8 ebJm] RcDD<:C5(׈k5RФD5R a=if-PZ)N ӡBZA Х""ȈX"bf ń Z1 MNAiZ| {.1DФRbȄ3~XkELxYN5~p?Ƙ_/\:=8:JAŌDh*"zRi W^JnYխr՜&EY* !}\C(ky)Vr臌Zp$q!k4/??qb֗7l9L;%}5jZ=Zz#]X!3#P?ZV5`s4jz"ɴF]utmyDp2'Y|b'-K zH_)hU4pՁaEDuhH)5 P#9FP#%5RVhT}3ڂ44 ! b(.†CV9E2RrZ)ELJT3?³TU OH1a(IsC e?G R& h/>%C[@8l 6F^ÿ'e?w&Wzdeemv23>)0a--0%L%#6LKa`JoQlgsW~S2o3z#@rEp|&ȑbFͿt[O3Q1PSuG\T +m-^Rp/ nġ F{jp@Pg BmC N3,2, Z$cc:X7V*.!Uߊ0l|UaXn$$$$$$$$$$$$B屳~>v~nY&_!'?+7daH/Xsӌ !P+H~*Zi]r՝aߢcI3WXJ`Ko=Yni-] (2 ༈-] ؁-9cQq2 }@A:q;>](PPN[CpJ w`a&8 3)Gj11P q!@> k@U iTZCWHEq灯28D§2hM~9 '?2AGCepPyo˞7}&d(· $T'A >??/^A[*5V`%tx+J0X(ARk܃B%ij.>Rpec\|}l~9 Oh@PH3U\98^a\ݰl@Y>/~XP( >7wb(4#rX%ESUe , a[|ȠZTgTqA+{L}  Rq|0" P s4a<]azh"ᩪ TX Έ0,A3@paA|G4&#DTHAQ84UG;]oЄЁnbJHHHHHHHHHHxSfo9S fCK@"=|a`XT$ܑਟ̟eLS^kmŵvvo93uP 90U\(KX!8JcryiG?ٟa!zn9S 9'>cW_Kr&|/F}#I5-Ɠ-'tφS[-g ` Tp 802l` g|GMZ Gϼ,ˌ1YvW<oEh *H  !#˲}~{G`߷p6CiKg0s l cJp>py(ܗ=zp.7-a %‹5!܊S\1 Xndg_^LA ?xg,:<ťWV1=FZ:MȔ`%eƳ H% A3*k3` g+ 042P(K:*<))GO|? zՈD]!D^لs.Ks;M>`V}mML7?,Nۛ|<鿦4#t wVT?R9chh`J^,$!p,CkD2(!:MwJ.L.Ep+Q8H:P7Q@p׌c 1#zUO~,~w쵝.z PӾTrSP:XFQuX%  RSF <}6A+kW>!C4)aLGE ^fRր(J8i6zC?sp)Lo9<#4 RM: y yRBlzeI#5XRoR>xEPW%3Q`?W%Th;FS4b4!#8"VP& d+@PD@gY~#9)DE,4, n(%0bR BZ rЕ5PFB1`/BܤZ ;*!!!!!!!!!!^#8Bs_<#s5XF` \$3 ʡ5(J̗kDP(-92_  SL(Jt 8 -QZؐi,zN?O=$CY)ZZ``VpN4fQFPV 5Ꮄ*ZA7X&&g2 \A a i*et/C%KHB_V:ЀRH'd,200I0N9 +"Lz ) #79H胚R!dh"a4AA2p !h(%LN1 -.EMQ ~@X"bfVA!,G#@s&$$$$$$$$$$0!0jg~'>Y{߀a4Ҿ}( 1(q!ae`*r/p( a i8JH 18@(YsM5\9+nx( 4cۜ##KCgvT$x@J",OpABk4A0t(6%8Pc&xH4~+" R#'R@p&(h:ȄBW.fʔ"B*F#MQ$ a'p*J3>_9Bfu]JS! VPzQ}j_Y_їאQ' א3P ӣ8}ىWneJ :CFބ"6$8iBpޅ6Bs81:plgζ_u4Ey|y/|'NO#S^jysՉ;:@Vq2gdѸRw:^Y9rFW~@W摉 np}WKk{"/UP~zvb薓g&2|i}](vɳ' K+ 'ue^"bbIJ!u CȭgRi@5Hk@b5C>W&^QP#W $w% c *ERC=uB#xBPvD<գiWC;<mI %܍W&& " Ɏ%CPYDs$$$$$$$$$$K9Zq_7wϽ|JEM9sxSY5t hZ>CU^sH7L aq|'f]y+0J eO?4ԣ'>~>w_FB+3Iၖ1A!-҅8a}M:=0 ga,ih=ڨ?O<`IRBBBBBBBBBBBBBJp \o~җwzk-^ylg=j0@ x!`6B0‚sE;+X|{33㓓qmpBBBBBBBBBBBBB;pITg4ɯ}ۯ{sR9676hf@JXc&D3%13,[m%޻}X^ok<<O.9gZ-bj-Mw5s+w08~6nƍnvδ-x  =C-}3{ղWڵz7^̎` S:VHVE! ک8绱dzȔc COyŏ>{psvr,s9AԆ*F;? _>}`?%= n_oBBBBBBBBBB}b`*pmsoƛWϭ][tͫ;ۻ@'qPu32ͩىN;CO?r|yaf9dGL)!!!!!!!!!!!!!!»iYҊ`KG-VW\vus}c{ck~[t;nQƖZ%T1O~uӤZвeKEC -' ߊƒ<$Fk\3">}~8Yвe[նd-߲fEp'RY2Gy֦j<##иe #^9L!BHY(AH{~ :fy 0RoƎ@CgmUZۗGþɥc"1vԊΖ ;z%-ngoЄn FwӴ|-2Ke!,U5 S/Z'nOȹwo3 &B!,Z-cT\t#͐w3K+OήSV2 :B|oOv8l"iKn䓱t?c[xuT[뺠VjYNj^?ʕ8l#\C1B!^o7a)Sou܄O=~ґ8fVia11qnO(&u،ɸ͌Aa'_Xo;ҥ]C#L,(L}dKzʷ:[r};׽z+Ə^uQvS6B!* pd sꕐ' i޽z9vVkwI} nV{#A,a)Ö?f#ﶬgv;_ٺ{[]YeZ~4j6zS51u;u6{j-2Vql' ~A@髙<%废<d[+׸kB!|*$rgnj D0q Iy gT pCLy u@W:M;-И[mOũ/S|H,17L3cSsEb O̾UOKҐU83CRRƙ,*X5#5~\SA5|f%GLh<`~@\8@Jr݀uK80# ~!BGʝD 5>V/;ܔ>cxXnUK)~vB faF͛5ȶ7{-"+)"ݺSf϶x*G2lG+Όvp̅)Rg?/aեD7̺xrD H[k7]+2n>sla-h--Ky0§hWLW 2ܽ,Q;⮱cgӑ*;oMOFflgOjdNW#`]B!SvYLGb=_p+3YNÈeA`% dG MC>0n{Wg/vLF' snIxtZo~ AǤNZ*:\ T+jql5T9tBka )Yt!" | )XM~Qjwtֺ|ȻM6mZliJ 9JLP2ٸ'lWkNG6:r/`D,Ö;0xѱ;o#sC9R)^=؞װi \˱ *@WM"c}T=L[n{[C{SYh%%c6|ݑ?";=fuCR/I Ͱ"Ë* /@fEe9DYѶ_62ÒXiZó@B! Tn߿,_6AgN]^}^_{:†OhnSs_y{CY~|r\PcN.7 Cy@#Iժ(ZQnv~+Hϟ7#hK웗߭UҜ?ٕD]iO&j(^i]rn‘Y;٫c -r2~Ӥ- 22r!j~AO>;h EAݴntC<=!N|xcO6+s՘ʠn_bdsq }hc H{8^#rڬ|˲,>}qcGhw~,Vq+ 0|skkG5K3 x ~)3}||.ൔʫ(}zZUŧB!TJ43wT;YF˩bV]L-XT0aSRXþOlAض+Fx_rPrzr)KPIFme8R9噣}+7}|Dw\!EdD?`zUc_{ ~Y:.G@ʍ Xm.-taBTǞtxyV3O?2,?hO6ر$$˦c^Ebd'DWKz&[TKM*udBݶ-29Y+5 seX$ю[`t|hFR:?>R|}G)fL>5`~x3- v-^8GC0Uԫڰ$+Lb9.@~ 36Ҫ'NRbW4_!4&QxBfn}KuA<@%JNb~n,iߨBsy! ,ġ}afR :{+/`?p@"yQt'ZB.ϒĴkx1͑ݦ>#G *ӇC sРv5`Tg/`ϕ7A@ !'&Y*7BE1Bj{W{ZX[7Pf$!ٌkH$qO="%%Lg9bHᎻy{,9Μ9ɹiGesY~)UjZ:JQ#KRȎ+˸njX|z?' KfAjľh6#[WE>=1tͿz?G;;~-c|V+,wn\hW1aꆛ~Mj^])2ё|ԠOq;LF p{M˝f7#OO|B>KF٨DyLx'#`ajUTU0U|/V9{+'wB{1Z\xIr?}PIR9QхIsRaPR"cq[ ;Z⟔vA |rx|GmngiV@۵?F}֢-.}47m3nzY|;r^aZ+I|"tSvkCkщ:~/rw;5]m76ݽ2Ŀ(gFpDݬ^݆R;H^oZ%j_X>oS = !!9k*I]'qs_P3b6F|Qoz pJȞ!_^ιE } ҌDЬv5ZGtuF͕K(a/rQYի{eсߍ^rJ߄C5QEVUQTBjW,9e }CG=xOۯߕ2\ŕ(cYbM|~ۙ\(wW ˿JbcE+k  0 3"7,co~TTUsu.MgVu`:w[Yή:σli \d~~U EPXiz8ڤs0вmmĪ웊4#_q&h3[^kݳLV8&_w?F:yK+i'˛h  j5gN i  e`aۚ6sŻ|1aKz <"lܱ9>Z@*`YO IDATߎN䦩$/zTǬE(Z-Me4)U,ִ[!akVm< i[mmު^]ޏÂ1:׬8ny:m~q¸12^n|%M)Χ:A5l[I<&}M dbvb֨{% +6&IKs@hҚ.沘 !YN00W .@zTTd)p)X~ƃ:d?2q1θsy#<bVdXnK&7Rn܎ u\UEy=,tܺ v~Nڡ]#^!(r65Te]wWܻce REU|ʼn-U%EO9]fKS.w olpǘ,DRe2\L @Ɲҫ.1cMr1zx+O֖ʄ|z ۭ{??Pōּe*I\m z|Wl5N9eؐ!lk-%+<ԫx0vژf̗/wveRFM,ge?.ŖnРGjc<=\z򙏐o}ݕVS9WE%ʙo U**r {,ts*>Gv٫ kwi[W^mq#s$7l<۰ʮ( 6l>;=]zW45SW?/ 7QjLR[5&LiZRˈ?j%$נ~"kow=Sʉ? P9_lYEEӰ|@P64VC`J|ː_x`DtlE΃m~_t誷 t=xtw\w 7[=$`2K9%T|ÎM #~n?ɓϣ2utw0̹i~3u p8G3&-\t<8/~)зi8ڥJn-e N`ʹgs}JиIEe%<,atJj\J{ʴE]FbfGݽ|J`o#g#,vhZ9noOzhivA)s‚lv WIWw׷%ӹ,y/ںߒL>EeSF,fg8/'/aծK3wJ,n | 5Vu7 4Qw-!eG_pxeb@^m{;ί1扲o[PT.襋RN /n/5X8tZEd+ϼ>Q\VĠo6pV *R'uM"95#/bIN]_ #VVEEiRu}~c1l~LgzI|SF?ʼB @=a#ZW`{F{^+VC.ˁgѮ} 1C6m( c-UƢ՗\vQV%%墫fq)7VL?WLn1䡢a:+4*RW^v]U'BHP|ЅR{?8> =?/+ܔ@r7 -Pv\SRRCJФj!uӦ5yڼZ%hԤbIyM[$ ޾{|=cH6l0 ~GSZ3b mIuR()]`.y†yؠЉF@GFD˦~kfgԋteuI"  X˵]=.4!Ry~IKYޫ*e>4zo)oܿxuys2>D JPOe)))oB {aRQJr6'4&*ޒ)~0{M˕r,?>9KNv2m$# _ S3ZVEEiP5cթ$[!o!׺4nrQvky*]R L[UCʍnOI >M>O@A3 ZA46j/*b:, g)XXiZK*b*wd{HE&ݕϲs=P|ueg*8N]t|ܺk Nd1f)+mL6+0BȧEy*c_)7Ç}l@m&?HVp Ns-؃;c '%Ĵk@sPF7<f 4$|3k;N; `cH'Fa~N[ű9Gz"[QvԮ kԢm ڞ E6HbHM/VquHeBtup?zi)+dz~-ni ^EOA! V'֭Zn 6kzo*7n)&5|ƠD"a䭃%b='t(w隬fNo8M*gbE"0#3ݻ晚ӣʨ(MUĘ\ cu8m:O|L$ X7؋R&2r?‹eG;y*FR$W dY6|%IAI5d(kUb3*Lh_[*زgW)'n_tOTĤ/PЏ 8_>vSϽfu5)ȟ{/%N,]n*G rׂQ.-4j!UTc{͔f?%?Ώρ}ٷMf{<xOoy8q9#GN 'YR=/w.wY+Wx%)M4}WBBBB.K\oRާn?~˰ +BVda>N߅;OZ]'X:eA8\ J(Mֱ1w??EN1K@!hFAbf{_~W10@0319YR=bp-EϿ\xA@7!t|Q"qût.&Mb Io,=;BDw(b&qX_}?6,~E'i_ W )>*eFwܧ*%U5n@0UÚi{v0Ƒp-{rN+Ꮊ qkan*Hx?1B,c}ҶmgWɇ_6I^yr[eE~Sѹ>l]S^|sMzeMuxF;-bܦSK$kd oƑ*9Z06Uw)FH9qS{Nq'ud+dxs/>>.#耞v/|{S$01G賬̬MVfi?}(qo>x2R'3u:[>=cgfjmfTM _g:q:\=3KCs%SS0a3? r͚4Ahrk}x^cy \)u] {,MJ'v+37=kho,d{$Pgo -M[UYM*$B:\pFe324{=1K c /daha#92h䉚4ZW{>R2KG PFԲoĘ0ݾt4W*;;&Sm>zL~Ӹe*UcT***30״iKZphҰFqLmZ:ka۪mF<ˉgڶU&ͬZ7A_%;kѤYMnYֳqhٵSڴwhвFkܩC{ʪ枟#|8>;m(:VNB OL=h\4ս"+^δk!F]`^v٫,wj~Yw+T}cCUR|qLV ʟI `j:% `Qv-?dF5Z'ɗhk q G8p誨O\Kk˸~E^w?ze۵>&&)~L烂GVPB!|ϝ @sc^4e|V\xF(DŒWάv>~ Bi=+2#܌JVwT,wj~J—OBc>Dq~2YaV%ޡ0޽RIS>Dx#B zӌힶ,Ȏ9 {aw𑝙^(̈K~/ "mj/Z5jxd4;UHt-w[ +'ᥐBH}7q &c‹a׮]v!$Fu{]UCiJ`%o>PhH$6w<( n)|oʭ^9H~o6`D!B!(j٥Ϻz(rizɕG, p~fYQyf3Ŕj5KPI6fe 0p"r^ڑ- O>k<3k1>vK%v a/p}1ܼkgÜ[>rKW%)9;/=-t=V@j/5jtd48^n_*ϳϓ5kP' = "WΔQIWEi6淙_|Vri詍i4;ƳrdE B!\*`%(-pȶӟhzmm-2|UHm<;I[(Cy֠MT,A륺E\ے}I,LǸeEjuJШIJ7--D(LtW~}g1df6jNdUPqX٢,]}3Ud΃g׉wܦf)Cׇ9tƣLP{=ݞ&u^=]"5oPj*FST!BW ^=+*Z4jR1f635aԱS.Uy6ԨKGUdWEU#:5΋Sd X%c;sz*X(A!B!_6a׻/`hcDz?|mƋȢ&.j*))U%exvx򮮉eZc6Ǩ!B!,IRx  qMGEߏR<~sZhV :81%1Q +9 IDATj俎rpB!RB!1DBq @›UD)) l!3ߦ֩= )OP;sY}L{zͷCRŵ,FFVw!BIV=LֶE :Rj)ý?_sfAQD鏯1>[WKB!B!T:%Fq>S jqyэALR=A~qHA޸6 V.#}9);Vt&dMݧ0U:_ݿ0:%5.%=cdeڢQ.{v1AZZo~ AǤNZ8uaB.bF {O.&k'FqkB!B!jqҥf͚_ewܹvZϞ=/e}ݫWn]~rUHH.s{G1h(%fq&,#Pӥ wl>e`_d axnI/MɃ݇exLZjB!B!P #84iRhcӦMccc5/LQ oq4$E\1"WŚ׳JtKخSO8}\etCdjbվKJdE76 -H<-k#!B!RTKKB_|ٻwoZ޴-\ ܈5/OtdH=VM_0y_kX{O7Kn%B!Bjj*}W^"NH´uL&l?G!!,xZͰdzG]_WB!B!P=۷oɷon֬˗/߾}gM Ⱦ1sٙTHpQogv|RD;#B11#E9b _7!B!|ʪgwvvfY$].uc7E&gDgn `Ίn}=\_/=( ߄_Z?a}Q×1Ph(39^/]]&5C5XnV%B!ɫrI)$[MfpFo[\WEiw3Ϛtn8"A j"{(/B]rQumvA^aDn徽&AYvFF[gZB!B'Fps{j߰>^{%>'-qh溾 عe6xG M+0q 𸵁NE<|ж }u-ٷ)cWf 4JDԻbB!B'd݆baFd<=S:Ud&ǽ!73+˫G%g jH \8bi0_eSt!B!*P[?4f\B!BHVK<8pD `WnB!B/ǧ'߱G0a31B!BHU)*c$ ۸y;[n !B!R(A!B!ZB!B!֣!B!Bj= pB!B!֣!B!Bj=Nu7@k&0L#0nnc[}ͪlnlȭZTRx>[C!B!VUl]9 ^Is]'qs_QH/&8RS>}4k\ou()O}}} JB!B!Hl6v;114EjnRb ö{NIp<ψ:en=mj[6R3?0EB!Bj޹-cG7P!JsOëCuL\E\G펺e&Iu{u6q4HшU B!B!5WpD?篛0Go * fH|'Ge7>as"}21LZ իmokd:CGtdƕdGݽ|J`o#g#,vhZƐ VdƖn[߄GD'E0hܳ9+aSRR3FV-u2g"/m_j#%<A+vıw.*y;EmFzmtqtpt-*{D)b+0315Dsߤ?xgWBdQ7qt8'x͇/^1a56B!,F&UwlΡ3UZl^YG =g t޶s6FO>ȐQǝ79+偮v;68iNvo]F' ]iy-WYQj_}"26'v^lzӹ{Ƕ䳽n^%62vs%OEZPR"4N1β ^*DŽ;l߷)Q]NnƎ۾U~ѝT1nQꋌ+w-`3> N GɎ0:VvVUb$$$_ۻB4͛7oFFFeJ!TqҥDKKKgg +T"͜)y7zYz;~VFmw|:H^oM;:g2d]W{gcLWv˹>`ٶa+/!ga_w7ؗ5|$Tf1Ȏo30Gk  +!xsN;26r!%5RY{ɃF/4|ܡu5o>n"~r=S@ki*J}GW?sKڝtmE t[>kLݐo7uߢK2CAQU$e"q @NA%a/-%'UF7o>:K~ }SS ]F͍ 5ujjce 0^Tyr aæM͛ghhbX,0ʞ J/i׻+Zf)~uU2UtWˤ_W]rmY,y֬YaM@!_58.]ԬY3etܹsڵ={j\'7RuբG> ]|;`5P&̈X2ݰmc/F كUVrT33d*wqV5Y@p縏泏 lU͎!)lk$ 4raf Nixo 51svֶVPv MH[\6gu(>Q r;,;ʁS {LcIIC wmy3UXtaa 6hG,'*H(36e@nsqݾE<W$la҄;v->/^HIIis,ŋ9vtJ{R>,ȾTA)uN6/9nJ>6/ިw3fH~9 t9x /:XIxL&Jgâ0vEbXPM[Hlijc,1hS4&1hL4&QWTQt,v)"z~Oܹs睻sf9\u0nW!WC&wVZ}  zppdeehBe˖hQDzI :\RŎ Rʋs%@kƏdv; i"_WV:r8^Z]-FZqP(^N.{E]ܷ⾭솮6~3ܯhNfnanHٻpIܷK[GƮ/j_EYg|wҟmo\?L+ssppn|};1]٤eŶmky]vi‰{?SeӜ[i.'?=0Tڸ$.hKQϽy-PxtbG{ݍ[0nI{هZnsba8^բAQsh5g`M!ע _vW^Y1JN  zpp4n866VhMI#x][>zq9N^Gj9-M5M-cז.ڸizJdu|?bHYEoWKv@ LrY>:ѿƽ:wӧOJլ;ɴm5)K 3gtt-Xtn FxiBP;yi'z^jốe5NI:;\w=L;$U-{LɳÛ99lf |L;TQwk@rfqG]0ﮜס)63nkoUч:(G/<@T}RH?'ORDDDLLÇaakA䩇y~lrPdϞ=۷o5ZH1ϛ)M\;)b b@l2ƂoF͍V0jR_ǭ9oo 3lD]HcȔ|ig9\cΚk<}9X!Euf拥[:o? i.j>+vo$P+rJ.?ΰ l6}foHЧf4{x{6cK#7L=ZxnG=tTqZm{IQ|nQ˵qYIFl}(>e%choU.qMD~{ٔ8`1>, PssJMa߯jΎ)Xprb.7'')o\\`ƍɔT"  ڰa#""˗޽{ݺu5oqKz'Qkf;t-Y%$scpѻ@c@WF܅LNuPʪd@>}U5)z:ޮD!/ںDNSg~[ ?F .jP(<Mb$\'}nRRI g֠MPh*Viu׵Gn PwNUD˜?y7|Ɨl?cA.cbeVa5H~#:m:Qs7ևeo;vϦRXGyrEowd_SuT; ?1o6yʕ׮]AQԛ}ƍc_~`iՆ \jѩw:MQי߲[ׅ\vҽBT*-,ʸyb'zMyDQ׹zĨ?.ܕJ WM9mW2yX; E*KY=NWTTXP5*AȆ7K9c4vQ2JD['KYc٩be~3}I~z{FrF7BBv#Riƙ-+<^R Q >QW'̸d-mf"?7w2'd?$ݬ5g)pBnrɢZ@-<o^moӚZڻT҇ӹ r7FZ:`3Em 7o7dժU~GF! z>sFToNJ9[se鏮o&0رНcF.ppukl57C"89kߚbfTَ&ȉHPѻٲ5՚$nbę ձVM^>**x)}h@UmYSm7ZG5S4bө M#?f5E OcTLH$_VU} 0Sgm!C_@PVcwNZL$ܪM)  jma6mXɞϗ*6FhkymcNjݻ+W.^~Y$7AQ4ܤF*5wxnwn0|Aˋw!n!kyn 3ˤ^ѡ6S?_F|^b%N1[讛 Q-Ԍ[;~tGGTliL#qkr ncGD{.[5y'v]E*{۔Hg=;nZX!/Ҭ]o97l9y7OCyoDE vMxc{ZxnoR^p#-1i^J(\tQ}WU5l0᭶6Z in `]s !-/ԫSey%ע~OkKv_mM{  7Y#e[8?OLO+,.*lm B=zXyY5['ТvfC⸅Ņ <7 z IDATaa |ʢn+ǭ0[sdy\XvjNE6͵Um"6h&eIR`YZ ŠRiaIn;vr7Z|,3\kݽ 0ک86!FX IwaFޜk'laɬQ?býJb3~ǽ3S{(R٠R#{N'ߧr v4XVxi߮dnrJbvQ" LyIŒ[>#8 z:8 _,v혯^5:Gإjfy{[PRBн־冋ˠپM269,9mG,A}3h^96\-z%X?g2'i?^گU?kT8Zmr$7QD~klZf|'伄JcFCf, co7)4Oaڅz߿?!!Aow{{_][?+p\eADB {ꆙPҗ m%Yii'N[l*_-Qȼ^Kc\eo{CZڪo[4poŠ]LKO]3nfoPA7s?|0kD$E伹hj٥(2vΪ2.6}i"8A7zZV^vBinf٣|<#jT$sLSΰ\.3: 6漅6-}sNI?7X́avڵk7ydV\a]<ORQ !A9&K|tDvK̔#w^Fg5O ќi ao@(Tsr,#r0kL4grz:BQWNw4;صn,[^g9RܢLa[hr,OImgo \.WTYx |McT*.PQ ! sc:%$Fsy+˴HhhZF~;DkF7Iou;ҫ}`M] Yw'hTH'eL~@q]ΠF/]N\ʲ՝CM%Լv?)z0ZkcafM]~_6,z,X[+}f! z) x>*ZdbZ% wwM{ Q Gc57s]kx2=j҂bbO,6AHg>ΗJB!''JR \rE$xrR |ֳ~YA:]W\:@Sՠ$S7n_X"((]+5 }I6p~_TO?0;%K6l ,:%{/|~vw@ae˖}7- g~{ٲe  a!Oэݗ!ˮYur>ABoEmڶys4ꝗGu@}%zwgҊk9a5vjn0̛F_,Ծ93XlݺvBM k֬YfM} !  :وWMFpcƯ?n[k#(|}6̂c\b俣fTAx: FRyɹnjql   0>;vl֭Ǐӣ%,'~^2६^"υʹw3W/c}\ u4»io%|a%H)#+0Vjz Ō Zp%    ZYYY-Z+lٲezzcS8ATZ   0B=887nWxm'/^ф   Uѯ_ٳ}}b(|'GnP @AAAY**6l6mZ|||Vn߾}uՋ'k^6(Z\Zs% AAAA4tmPq茌~ӧd#{GP9YͲ-"|#ƶm(fՕC~cQ(>w_''y?U,AAAAKtUqO A-KU}M}Ś  ;LxY׉}(AAAAuPeEsj}|?=6pyr ) Ν7S:O0~-+%GqаǴSov}g޴~6l-IgN\q|~W? )\wvfCN$i9zZpz!SAAAA5n~xiލ25)-fї?ӷ8>oE ZssOfG|D?NrF]5޷gg>t\3g[    NObqP #wʽdI}/t~ JYܺL߿I6)3qK>]@Ĥa{@a!3z93|>U M)/9K Oڴ5!T3   3!P apH!*mi"_Ih%Ӹ~Үy`Ԩ>k^-67#MP蜠VvNsrtUTݰpzENA9bZ    "8uض<#-EJXQp}}W`u{M`dY2>^ƻ=ZxfLfLwOr Tܜ4v]^GkAAAQ-2'~n4(Os|t?ސb;uaQ Q ]( ʎ!1k1S墰KpE§/cKCuoǽ$Bm$oQ%clX/E}\ O(Y;AXnkRPlWTLkS=+N.x*[ X+/N8my~]Z`JŜ  %ncV;6⎀MCm1EzLTsV[+ЉFZ?~GU *}4ߩS1ͯ@G<=M5l̞﵃א^9i'cƘxеGMۋG_{!$sSgW,QpN[ ž^]s86MLB@غVJ9:~cL 6|FP,!׎ kP2 #; Tz;]۫KͬF%$_uQ)<܏B .44>{SxۗqJ=;6ӹY(N{7( pɧ5!(Vs]\<ϫ=j#XxҘo,r6wnzJ d, ubOc# GN)_"Fz!<Jz#:[.|`jďӻ7eaٞ֫caC>?5V0)ط(yՇo\u:t f\{E<^R=7 A4,D|" I0hӴ墾Zc{F^Ō싏K2%EUGG~>@FԓZ`@fF2͆\3rIK8ZSV~Gk$(lG}[ Ԛ V4MuE gL v2vsُ2?GjOz|\p};hf^w=y=1nAe>çf81@oʊ7#guN8矓wO;r2@eۍ6}fR 8>*Q q %ͪf`}ztߧ 7%^ 7G>}F?GWE ii `JعGoTv,5n$ YPv~aˉ+?N`WA >=q&.r8o~, uh֫"ŝhڣD׵u}'?M9.Hλx2/`ለMڿ˲섶G/\uzXk5@ښs)mJYXs8\],d,sh,Te.@4HcID|a 7 Λ[j (и* K|r^騟̭ˤ߬VTk:^ v0:ulA& IDATYɜ>^zGq?oݱ߂OF ~ _һupwn.w|zieWiw`2AaNcQI[ wԆAWwWS K^?vr -zE4~'4Ou}|_Y;ൕ+*;TTQ CxH;9=߈1ul4FtSsRWd,d,σ \*H :X< 8_eIqNqL@vk9naqqaEO+,.*lm2͆ 5"h+"X_,4A’"ݾ%mO lk/`/`[\Q`{V,w!pv6Zڨ[&6$ @ΕE\3\ѮMۙw5U[ 7ْjGVB{kN?Ȁ98/}Ô ͝UzG7V! CNc+1g^tWBΒA^/ӦVg\V~N+k..lNM\gc"B5@<>5.܃{W=Lk0;IWv&c4 3̗Z4 yH R&j2EW*22}Qcg@.&Nm(d, Xxwb)OgbX,ލ Bn{j!fTNfȩZ F⢂2 pY|LZHv-V=P;'@~\j`yώɞ¹bV[f>&[hg+4Ut9=(Qh9͸bOgin5ߌh\NUΜ)v3i7wAʑ\&^ݹ<}hVצZe]fE5n[").tǷB)ה0gP0#3/!-%1_t]jf2Ʌ֔> aO֭%yc:%NݣF_6+P.x*U_9hAțyqU%9@.3T W$f=jr=61տ)_@fj2bեEgXxu[]^4e2j,4A a\QMekD[2 ~ԹuC͵s*!~-%Osv%p?ưz7*;;Xp^>qTѱ bJoov~Ce˨WNF98w@FqlyU74C~imɴIPqk\@F릯xB@pnZcxLP;~{]PȒ WQJ\bjJI]j8C]HuĐ˚冡<5H^gaCi~̬JGU3Ú 8v ݴ_.}-YK><Kց\9˷ګTNŗRYejS PT\KÄEҫʻ Pͬ%d, ) )I0#j(*.mYLzu~_;_qv#*f'Q<>0Î˰borKt3Vu?XKzLP ϛu1XX%~wC->|ܪY6X#;M( wQ<[c(#Qt`vQ?5CE6qv!V:8Vٰ&9rtJ;c]?o֚WXg,{q'/Go M\^>eSJ=M[l7JUGS IcU d,d,ϦPdqj@ADӹE(MbTro$u"hP@⩘bݪ^=h,ٝUjv=CG:7朌fBΕ$}4|k/`P-Hi$[Prd%m={f31 Uw%=Zܡ{ߡ u.2Fv?3*YtTdn=sMbnZ\铐Ejzĺމc~s<&}”켡ȓ$[TC$!_"CXffMf6kWY% @ @v͂k fZ#`Iz5aKT–T#C~ dO[,IlZ84 V@*[79JTk~]CpyvZÆu68.׶kyP[7m^BOd,fKJT M#l%R5Mx^߁D(-rbL~!=Pi랉CB.}}ww{7/ɾiD/qXMd(er$y폣]_E؋lc'JΟ<d-gy4-mhuT{/[q' S'Qjys!B5ܻqz11.v=_Hۉf}y컟|#;46yw%:j[fe[NPI#iȵz ,<|w-WEr>mt֍jH8[pm&6JM} Lٝkh'd)i7VO3>ʨuÂ{ {e `2RO+d3 +hiN \EAMRpg7+k/Xy-$/QW|oIݻD|ʾ̹4o%f4=@_ZP XchwqӾEܮ1}L1]vn&3潰e`6;-8޽])tܪYe%/[%}RA3GؽGykҲä; kh)hąTwj+o:fYsT0|O d,Kͯ X,T@b/ L~s}ùݨ[vzuNM D-+M+4g*ADgE/T!?;jCXL^?W=#|0>W9@/w_S.Wp[ ׏|*#J<@5dqA?U҂ LHH(T&)TBսڋr+V{bHP]3& D 6 %QPݜxQKs:f{,ύ=3௞ѡWed\nƿK[1e"rQ)قW^ *֞a(R|ΰHQ ^,6`y hR/eJl Ǚ_St22Q2jF'@QϊovZb6|d,U ^aN!cx+.kUn]&Mq9z򿛏tprqIUvQ7_!Û֐_ _rAadW zݲ%UN}΋$iXjShª鲅~Ƅ6ra%1װ kXjuhkF FsIdPJ#^ _X7Ҝ*xm2>?xD,Wv"ABYg rrh2L˚# Km+tlnU_҇w?i6OS]RCb kXj~X,a]~ K-pG{Z )eL=HҢճ%G6ɽגȤ קyV ?omKBa3{W\~S==cӃW? dPp_wZ @y„/kiU&/jO|>Mwv.?ģ'I)s7hx8GEQz71pa媍B&/,)/rP[l,:)WkQE 9YyT5=xf<.)?=f$&Ы%$OZ 4XiiPKBBb&W#ДwـQ/Y2 777mͅ@13s\{SfAScD@q}bgh=ҡmD8eї_?0l"MC4I-rE=A<𬼚̻';z9V]ia}&P!Ь|P@pwpӞ-hhjd,а.Ab kXjeb;uǏ[kwȭ&ɼ,}ZM(~_|Voú!v||0wJH.er>ޗEr_0/igMKrF7>m^@߅ KqC'̛ڰBѯ{aР&|ݿ}"JJPG1+#</hժkv=T2v'f5=_~,HܙAX07   g>uԐL6mƍ/8B8<Л_G0V6`|ڕ|2LjH;y+!"OhCc %q-LZp}} >nkȲdh#g1/jqY/= y4Կ炲ATN,P * ̄ zpp;vUV gϞ=yd>}j/n(G /L\߰2T9+u8VIE8v۾pqצk#PpÀ77D/=wG / SՌ`_k~ν?s|45:s}a 6_,7Xy3υgXksAAADâdeehвet;p+ٱCn,[ E*G.{acv=xJ <8q [_Tb?>N}S |hb<!Q% N(r&!waQ\_vzDK=j IDAThILLiAMh^hbXPl H*1 .ewKy9sgsg{.@ @hBkkHϞ=4hp8Rߞ{9vVݻM}>7!OMN'f~r"QYYֽNsmb (x|):(~i=L:kvzi2ʄ<;ow5DضЋTٸAnaG}*F>#k1 @ @ :FLK͘z`{νSoؒ-8%Q?-Ił3lB& ^ WXǩ8lM L>X!ָɚ(QGL*ŵ(Sٞ0-܉D?bc T@ Іi-[NntDobSWn=w{1 G5  fw>duOa]~tOeҒowkPh?۫}Ykޤ%:^0Nizz i$@ @ 2L&kmZQYYAy@ r,C KQ'@366h A)chhZnkmpInqH{ACX !b!4F,/ji!*- ȨŋշqlB_:4aUEeU2.X]rZ- 5*/,H-XRC$X^?D,: 뇈EG!byHEU rn Khnވi)kـAmm$= }ԏjkK9O :QIULjjljjRqE>UUB;tۢپ)XTD,Oǖˏ Jz66EG!bQA'*ɋϼRXYO,|kdOwAĢV,OV۬jƈXmV  L¯}0}2Ͽ?WFγ?k۵| hɚd9ꀀ^v=ϧڜ~g/0 y4{^@-K Euo&wx^l"Z_UZRjD,: K Euf<H@ }wyr*Knu 8miМnez7t&ur5|0)8z/:6Ў-c_Դ}; e=ݻ9lq Znײ>E1*RSTPjNBhjY_/1cPs£v|}J P,2 vz&Ua!@2o °Ύl}QLV'nS! `[@ v%0͝:N6۱f'-R=Ia8LS"5|iVk_HD,׍Vb1JnHF\߂Mw:v\,o3mϞaR,XZ  b1>3>)`{yN}5≲B*V4XWo_?*w5G[nʄyeٕS{]mM }& cGcb37)^3L vq^Iy''w2҈O8بP)/BpVh\|Id|Ѥ,LeNOhJU5UBq󙙩B=#c^ bK!6OuT_K g%XX(=w4E)oIw 1 Y_Iv7"my/CBRrm|i즿uwk(]cgEGŢ py1fߡCz/yN%AN!\z8% {y-=nXȽ:'"ZqbjYVֱ%<|oGvik)Jy R9PxA! -+ }.E5_C#BI g*o ~SH?]kTqh Wprȍ/FLX+˜Z Ac%'ɡOn|:g! GI޾^X-2yX",ĵi,:9 ]ԋaE#Xh:DI(uL˛a)h0[@z^6+|2mUwH &?1sn[ JYsK1 $&ʯa"xZHx -+\|x?\  )UuBnYy:q3PnY0cLsݦsd̈jCoVk~xjY> POԅH xg[! '-6נ`~`na;ʜJӒw>:~(=(偍}ݶTwNE7?},6Z:){N,E *'X6Ss[H^/:S8jp_k VΜ:ä֎x'.ȻznhMoad;̋!_1 ?OuƬatv_Jw8L J9LKK~E4%nVg^s6otuu/R U@Ϟ*bg9eİĺAsرౚm@h$K g .WD,D,*)pRjpy~s:w@U'׳y}ԡM!XtT,耋#J!Yq̘{bՂ6M m3/YM{Q)zrQoa-}p퀒<7ncyuh+.n;fA(Jb-FܫiO'rR`/Hg"*(0nd|YxE;"+7 tl*>kn qEeέ_WA1[GKn6Dunt}$L ,Ia |Ն rbM}.RUh'j/M\y1W{Z!-Ef3'!z3L {ke޶D,j_|C`6px7wH__y&e4ւE -" >a@`m47}5vJLyrb^{hŤC=9cjZy%6~dߝ4WMME4hM%nXJ 8;洬"p20(y P6~ wk[1̝-Q犴JA`Ö<1U[Lroh"䟦AGэ띈֤z2Du έeh; FҴ +ǁm@kOnVL줲:MEXhxP$6^CRzDqhbk"EX\K{7CVڵ1|h;UWD,:2J-| 0!<fMĢgwMb+_&uWRlK> ~­[H$8Z"&~)C5sj(6/v]GHRDŽ%c~;1|X~ ۹1FGa>&PR ӟ_1^B[,0}5]Ͻl C\4pW*u.or\:\ul.~9ؼqjOn' v #F0r1)X5Y!bQXz†)+p+)+l"4uBkY.}]mvv"CӷvOGr ?]a4!N%UM>g\@'z?εY ;0 r? 6OGҋ^eeP6=zOѼvJ`9|1gjfE>Vadh7yw r0?Zع+a^D;{޾};%BEp@u-+ŗV,c;)0c5pᰐ{0{jP`#1a) v7k纖 c@faF!͢4,*J[cG;H3\`PGݤVVEЗG1^rD0qm'PTOF.6Ǯ_,dCgA%լCOUJjoD,4*biEMD,D,mN,Ly ;qĽ8j^F&!fMpND,'ʨŬח|@fxϻ%w{ku/('ҶUs\zxuoȣ(knذaCЏn7EAА+Oϸ &Mf{8v/ Io4:g伌ߟ3El~OYv(g3V}wr{9Gdžfٙ\m#婼"of6c&T'Jy?)#OC~e|V#ታު?>3u<ٵsZKvOOOILheix# Flno5æ?W>zW.\m b=?0Vjj^9S1|P%0/PF Vyp|ma^۟@̗nc-ݬ;N]HZp(XZNׯ8rLιvZKoX4 sIMJ|mm)Nn͟L\,W7Ry!74K"كի-^\Y̤y>zÀ%\j0t2zƞAƄ PqLJ. \Z 1n"7+@RyvCykT:Zu(-0)p>zuͷ0t=5_5HNݹvb3R>9x Hbfp`o_bC)Fq. %[{;ScYQRJmԭIƥEw u(.UZܾ1[䃐Ǡ*}N\mG6sMPKsoX4~u0dK+N]ܲDv &b!bokUȍ1lD`&Lp]SeD,D,4Mа~r0(y.mXڤXiǼy\]]#""A[3 E2'O=+opJo쌓C8n:gI4ME+f(up84Tq4  U,<_?3\@挚]{%WԶP'öލ<M(:CT͛7gΜիWϜ9s͚5SLY`AKcQ JUhx?JKGD_Xw0LWf禥=wAr Uwn ? h }p꺪  "x^{ ø*#b!b] rCwƾ;@R?A˝PԮO[$гb.D,mK,@d;SիW###׬Yr дѓ}慳>,,}>7:z'f0vt*ʟ2>pe~MH_ƞسgkhBx"{qm]39?cEo}bu=c4AgQ\lM[K75i68qQ*"U^U*eiVGT^U* ZD% F&FF rCȇ mU"[@U駦dgg%d&V }m4sQyUz&}u}+Oe*(,_ U+XZ^,|:D/AXڎXPk0n %bi"6%C0%\;uꔕUg fu09v)Li5FEЍEMs70wwl'k:#1ZeG.C0C=B}?h ȶ)iņ5ߎmgFu|&% %O-d @lkC:gYhSrxlbNnշ~xӿPضl IDAT6MVy\,l#[M/K{i#bAK腈4"K5శTYٳA^d/Lw f0n\ڋQ8,kⵀ>^kvo{{ynN\MpNCg$j4qK°ӝ߸qۗRb]]!o|/QbX9XboXMR; [u閷6n]~-[nyD+8 : ը3s2&~s=,yOs/nxԏQgcI5Dy/V.ңAv j0:Q-՗v@x ahрI-bض6f6x`AĢ u閷:&[D,[nyHkFΚ9&~|wyoCW QA9:>bW.@9mk9񅑡VK&Msŵ'뻅=%w4upzf[ +KCيSmU6%)vMq% @ B+PĺJ=̱ǰ1K|= þz>Q=gySws/o|3Zؐj/>U MoսJPe\`3Nk4vr=ưt=kR[UkxjTw_?߆j@ @ %h DeeI}4Dee఍``=4q2;'O(gfk]@PVYTN'7Ѯo*Z#M @ V/.rFF6FFjm ըQ쌾c R3\#MSk@ @ ׈Q!@ @A EF0^ b!@АVu@x= @ @ t @ @ @ 8@ @ <$A @ A!@ @ :N[z܍ MzkîcOt9YD .#F¯**qy-fIhIVyaAjǒ2--غ{!by(D,"݄ KB&k['K^1뛃K:(AgJA,}#.Kŋ=v ] 8ȱ:؍R*, ߦ Ź<-i@?vl-\K$_T֮X>%hN{- TŤƦ&WSU%ۿCi=-[E U\ N/, viz7w7SMvAĢԋQW^=Ͱ!e Rݗ`;3S-cߡ:Oxi@5]1oszR7JxQ͊=̴(APExVfT~Yv9Jw{Yݺ`\`tvJL|6d;fR@~!vpZAAɼ%-u~EÔ@~2*`w rؖ筻|gQK$UyvM, 7^`(o49i. j bAs$^WZ~#$BR̝7ưa@CSY5n"8}UzF;/ Pr)U G/٘NJ]-4|M?"ʵH?ruGGēOa?S|G-V2ۻ"&n w-@6O@a]dr*Knu 8miМ GFF3)oc~E{ & 4">hǖ/jݾMݜi8-kY mRݘ}&9OoMmW| Z0AB @ \CYX:Mvԑ~\00 ܥ &ooѣ],u?cԭx G TDg`n6F7 8OiKw{ ɹ5޳(y+D~>z^*(2p{ގ KJ`;fYzԁcG"=++.._hu4¬'b* z_*,܇<ʦ4!N%UM>g\@'= ¿ ~I{auXgI>gǨsu ~3L ܏fl8uΡTex(oB1xZDd;umcE-R=IaOm4p?_1x;3]ELlto $b! zi?.JxeZ b!,ܭW\%~z)|v3~7w-%]EQM3HѬ)y,?q1q_>t>dߧ)N%/TĬ?\&5rzWsQk:yMzĄ=bs3k_Fǯ6/:j;k]4;o,**Jϸw8W pT0`E]7rjJ=M#b$?|g|RLE1٥>,DYIi\ @v«ML;ˣB[~eBRԲʂ|ީF &>„1#QܱI1ŀ{u}R/w'/V$2qnԪ Pv Jp`ѤpErٲT$' f)H12 UOigݳr`WI2]=|79n،cn5'KW fahr4+"@§4")ǃ!iy3})_nq_6[r4hիWsss\H/*./=7v&3)kfp@j1pƌ]󀪗poLfq۴9k=] bI9iS D_Yf`v70ŃM TJMto/* ݹ@tyLJd>IJxMoЇɡLul/'  h)k WD(H-3 êA:V$Xw]џ {n*񅘺SڴAj!btR,Ptoӳ󧌵2 t k]" n3PdPX\U؍ "6טq'<"Go.\'250,Yf^_uVS&$[7-7D!-wlL\#튲w\̣xQnHMgANl>0/{L+Kht[v_Y!~'鐚20R.χOi{V[@iҤ]wUT|U@× E'퍫ޗ @ēԽOR dzzG~7 KN3m6̼y󢢢d2YDDĻrBpHF??>4=K&is/Ç7gݭ V2U3yƩϚ8ma{B |OP8 kߑ?9h1}SW=eAjh߹n`D!׉36?]% KA_ 22}]1࣐iG;@ 1 \c-O+*_h?oO=g-,(wB -z:r2.I`Gګ뜅߳gP׉{לrR߀c??O6}K ;,dF\? pcMjM6e$VH]XH knS~4N'=>m@ĢN,<LW1@v nE~C m !bQp~FKvUzFAMtcqsхJ:|aDզ0g~O췴*y_)at:-ttcW/&:c>H `/N.ۭ^-2=&H ͘u4pf2ԐΝ`O膃թ}xf/Է]st=l:#$# g5 *#ef]O?ߴC8gyW՘5 iTmӃc޼ys6l… ,طo_ .K>ߖlwtzq8Ir Dzyf6tƯ(,*/ʋMc" P #}_H]&m2>O?z0~?Y|W`=ۃ7I)8 0Ww2- 0o:?;6FmǪ?+8_mnc50/^8O‰O7̝ 21lG_3XbwBڃr QaLHG\ŸbuuN~vWh5EMyZҵqgNaRkzR<佝7NH?twDU`^ _y3f;]mҞq';߳'޻x6/b'=9޶D,Ťs1b8}c\OP*4%NBRhE7ҭKCp(rKr޳-G 㯭1w5ߺޞ>=xWc{('hp:y|r=z HOI? fRw +`Džh(SVDߔ1'CsyL'*{ _~vN.o |{Wejoȑ{Yzu 5zp67ybcAU(|Zsj,@^ю[d)1Vl+S9[hqUW?Js4|.w]ݩL(Q 0^ZTG#᪏t%\YDvZ3=Yg9eİlbj XxQtjWU"De9HhABR7_,ZVmPX!H(p3Wq=C 㯖{@J/Ggr$悚 .Ew} 5YnX@9Eo[^=^.솎4M 9ɴ5DoVJ[ld-%X5X!@ nUmUET >B1%{x-rP ǵzp_.+n{֤-~&8` mSi=溸ԩSVVV 5vs}PMbT逹jZ;ڻz^iڞ5+Pc'P^Q5Q&(%>b؈{U9}sq8qG %=YUi%@Qt#K+JM7X鰁 |@g-$G=b@d7vWiz؝yװGunt}Le,Ia֛@-9&>F=VTQܫr4r2*to[ "5TE8{?^DaƉnau4ւŨZXmkK.Jfv٥X4+5r8@bD|.̸x%%wJN{Wu 8x%vG>ܭt'4{ !"3# z[>r`,K~.P[$dRWs$elp0+@# -》xϟR㤇!3L fR|oQoA+8###UV>{lРA){+~GQFk5D,:J,( IDATEGaJ |xgϞݽ{w֌Y4ȹ\wXz-5D]}p[^7ʗW)oi٫ '͵OUs% ee,1KD*`D0ql'c. <=A\}tRSɲP 5s]K0 3 nEE)c-4W"Z Jc3ԹL Vĕy*N ׎AeCt՛~S:E Mb6؆,Cc=6W?d?"^ڄX4I" ,]Ԅ(A#/HL#;&\^얉nq rV֔c!tU˗w5gԈS-tBh{zob8]Ry@=:Y 8RY2űl G֨;PPTF} bh* G7dZk<_u-3SA*:ݬY޶ܷo_@@EQ- .-nc}E@PQ}O}/nbz!M tgpxwo߶m[ВC'._^E ( goߑib #y"of6I_T'JI?)#O{wWg5M'Ζ?2:sY]5֒S@hY=kXyp_n2|v (MM{0<`4J8eh1wHdJFB{8RnBIя Ju+ d<FߠhlIڰ?Y-+3@v?D ;GSgFywy-OFmK $@-c1a3Y[>3 [0LB7ۀW^yh`RyvCykT:,7ptQ [NaR}qo`]> gQt*,8xܜ-g]ksf }iRlϺ#1 MX"~ebʋuƹ4^o?HN}eEI)aQ&Z{@Iu u(.U]ܾU1[䃐Ǡ*}N\mG6p`B@[M^soX4 _&,Y2^vN֦s;o2@d7yy.D,D,rڒX"qydLOϷZV%2b $"[~PZɊM%8.>d=b>u桡 >Ml'JR⿯zg`wCe_ĭ/2?2HyG5h%Ʉ2]7| )Ψ;ΫIH}2-4xkFu4 0?ު\7 N8g㚺?@HD!(8P\ UgU:Zj_]EmmhkZV7.pF2)0BB ny|$7s>9Q3Tf<.6 ~O^Ɯ?`n@E+d3An`I ٕ9Pnіdw f-ھclV>s<";9H|`ރ|dr}vtI^A8UiRLѡ SUl dPo'#B&ZxU+pR{Z~Wڹ[/Vo4iXSʴB\ՕCvlo<ϿWS:;3ޞ6'rZ=sYkwu\_(VmT%)%E2βcTJt5RPeBcsۚ}H^I )Y~PB?{U/bO3g(tM:с+~8|U3)NC-OZI 4ۤV3Zm-i䙮URK `bfVgt~he }uF}[V b'y;$hzڕNBĢ L2<=@c$+"R,޸[ZHwqGiD"|CU[k@ tG.8 J։©+L rU-PuRZroGOK@ǁgm(h1UTѳq#uDY~I@6u'wcS+Useӝ7'$Kqĥ蝓Y p={* >~O}1Q9 *;Zt[Ƿ\O!n@{#c` ,|XB+WF>"&ʕPUԹսշ~~0˩F%\(f4ҋM*c?^U2K'nȸL|NPfއS؂?+pcNq 74ۧ\]u30󶿶\b뜁 `Oi%ޡQ&WU.tlzIَ{p5rx [ Swtc/{F*Ww@Ꝣ"pc'1R\lf'K3LM ^It ]NX&W]۳coPY;6  b¸gYDBR%KuS蠫F}Y\bQjqt(1pk_Y,YKQ:6Iխd艥hu 5<]?խ3% *JWQ **LsÈV*n0-{-{D[e4k{߭iQ!Y7Էkv;.lP;Sg Ԓ#F\#v&\xpΔ rfCq6]K.4Rp-{wN;fY:)捣f ;2܃WЬAbMh1xvHO߮NU~ f`_X@h:V{=N]%rRЀib1x,+AumK)9,#iA!bOkﭿL ,cSSC([*5RpLM\\.Hx &G K&̟1LeH2(&|C).WeA(IT%̼ ieEfP EM>\"p9Nɕ?wdS1g%H) `?ꃮ3+W~v'm_&SB~*e%j$n*l!k U6c󍄨A6r$2g &* ήvJ~FKu}.ݪSBe';.U'C޻mC`-|X_%O>-Ƚ k_ֶ}X } emۇ k_ֶoC?>%;]aǖd7j/y>8ux-vŬ%98W spX(ɍX |`A\QPW,͑Pb@`ɐ -I[ְ":G`-   M @JҲ唂oԡK)NV MNH2JD| nvmu5XKrpWJ}hC/Q ζ-Ж}Yێ bC~v k_־$(|{_7(gKörjm;6:@ @  qp@ @h)*/9yYr vVyoOHr3JK-/GТf(@"&D,] &7^һ@qg. Z@ol-P:e Wn_zvz^f,Yv|{uwjs/e?_}{VlƓ1s")ѓ:1@ @h;4C]؆D>E>hxδ~!o能$kψ6={:\8jF6G͹->XPZ(zэkmK@ Цhn'<%U\d' jѮ!M>ʏ2=B3Gc_PyG|0ҵ {.z?pE**nvhj̳[ۂi1?9x)W$"&D,M6籅rpX.cߌMs,ɜg#:8ʕMc]>g @dEnU31fij;BWl_OQ55 Д@ @h]LђSw 3Cb?Sl%:(KSnv!%0*`5ddo! cs'fI* 9;"F v4>C )Nz֬qGϞKG~~=zXqB%g-H;hIM$|ľgl}G͟7ICZ)H "K$UNIA~9rߧcRJ&m\,YGy^l[4Lh7UFxr N@ @ hB hO|Zc{+9_(g?2AQQDJMq[Ȯ>y֘[j[[wcn_ z/9arIH[3x׮03{nCҟ?*Zލ*,z VH=>ggJ]zvNѐVo^sz(5v;ȳOO}~_ e~8eY{{͝VȊp6ƪw@ @ h!ǩ3Z۹U}*86}fLکJDO=9a `6cLǮu59V)dDèɠTWjZV?fw~gy?V)~}<+f!(Nz*|_ K()NFoN RG&\'}F)7D&…oJa3G~& L1?m:Yf&sUWjcjwCI@aGTJMJS e߱Y`9*APonn4U@ @xhgol{;ö1"ֳk)e&zJHӏ>3.,5  0UJ{#>l۹ ]o}>-sߐi99v4Q!-~sgRy`#snD&,MJ 8lؾ K͞y1 ћмE .OhZ +%_ZMvVI^nRǒVVF6M@X^)X+Kc byi2D8`3Vo/ZT ` eإ##ݎܧ炙롿K9o1J2zUMҢ4)A٥SKkB 0 *PGTܼxR{!muD ճ!tiK!oe56ƀHc'%%PE߾^>F& bi KFK C)J=<(//xwظK=4&JrB"vqǜ}B# bi KS."ɦ IDAT14F,iW}fj鸔~fYc&ՑT XCS ?֬-3V642XZvk39Ӣ2FĴ8dcC.Ɔ Qſ?ZEE]8H.nIgl 5,; q43JsS&ąov[д6^EE]D,VXdcgsA ' =[*|gߌ|mʳҙ+XL uUsDuϬW_ڝXV[ӛPwׁ*BbL1J&g-:$jؐ!Gaz1}.hqmztpiIdVp[/e]3Tc oЮf pXFȋ" ,]J>NO j;OӳTחƹnrgv.I+Y vgeZtz#"4^Kv8sK0L,mjtZb|hw3}I`=;Op7A`^f VI'HΡ`E"BOG\v#(%t&- l|ŋR7o7A>^lR75G!eܼ&ug*i'[ xż^]TTdkC9(^gbN߈k']X=tGǎͻ r+ٮ]l!ūbwH^{4ʝ/pYą iCIJ +Ekucq)r LRQnҹkM{<--Ib$0ٻQD)*P'J7-UY+nX(+gvnnWs$|Ƹ($?O.QbDFBbi""],::D̬V`@ʑ?K b!byR\ Fǂ;<}' K EfFpIxs\^L5{(M[wÂD(чG_wNj|q(N+/oؙB7뮢COƯA6M (PpxNmʞY'jBJ :9rhue;<&}nwy]5\^ZkvV̈Szd# ZgcvR1w^'/0TΫ|wo9^T;e]VO-ޥKyʰwPyK}Xƕ^ZW={۱YHKͪgo/qf?'ܵr ʿX+橒>JEp76D5X£"TYl{ǙPNY:}ǫL|NPtc-,ܽjJXĝ?"^.j4Eҭ {*?{$їR@ȇX7.l]3H &QŨ҃ŢCF"E+%ifD ̺D,J^f5Rm3 ТbQC򺮘/Ѥ0Uo@ iAm'?ٔϩ c3|㹽Վ3cͷ_lUr4*`>tَsDU 8&⻥!޵6=C TY<,=z(lBgjTÌa=Ɗ>aBW $oSW:V?i ^k7NmquڗsUO8LPLۮ@VmuEǦ5b'[cjcBKM dF=y_6ꭂy]R) df 0s&H\G߱wDu[wsĢ}g V, 9;ǩM u 6g[rw RROǨNĸ7{fݿkU3jI=T5 "X1`tiAB*1ҌU  b!b!bA,#/wRyD,-,*vo]FAi+kog؄\c?rCOD\^]{OrKJ9>_hVɒuě_xBT_|053$%c)LY-.+-Hp|cKAm׫͈ 0Yiλ1؝7ވUsxuo-,r*AЙ +Wޤczuu6;c?A/$P:u4 v`3`T/rOJ|L۹'OͤZ w+ 1,o.ޝ=EWW_i{_$ CTgQ@)ۯ|' Mu3˿F P0! F_EYFsQ_Wjk'W:XG Ώ?/^Z!Ff &i #3)u%0eCLsu`sY1KA""WE,bۿP2}?}@C&b2~}h@aXh$v -X ʻC k=f\A[y7#uy782,HyvԢ1&~r|7,_{٥c#-*jh{}UfbiP2&Q}"^k.P6[TL@ms@fݳ @r WY ՉmEp򇝴F~Fk;x?^#|LV;[M3-MSYr|a}}'.ꌦh[x 6d-"L?J`C{g?]RI;W\g)3O/91)`5p%sfy:*c5Z biu8j b!bB]tJdJ酈)?VE`Ӭ΍M":zXB NھrM/(qW#3^)DU6*OIJql B$&/0))ʋ R?1*J9(W4?\)zWB@ubu -:T=e ,Ԯ)_43E]en{C&חV=5ޠhV!HU8sBf਷)W PXZW,.uXiXxF֔ˋK7nmZR`_?Qh8Wz!biBҒbʼ|m)|1),iS夰2]+K#m 8V{7&w֓c%܎U z^<-<9,9:i<{ @v)2eeshlv35'% 3cLtj;t~)k\Tn!ysMf<վt%xJ@k"@4`t K C(JY|Sc6osoD,- K/g1OP[O5FU#J$ {Pf~z~ViH |{^Ͷ2-#S0̲lTd)snf]'v,Kn Tʾ1_IJv#u>} k\xFpvʱw:р] Eܕ) q@2a>lna S̼yTaMy~~SôٯZ}'qG J0΀Haf)?#}ަ8׊/@a<4rPoץ0V5S7UƩ9u&jMK XXR Veev C,;Ees,R^a re \uH KKLQ!zh &#g'гJ.Uvavq\G &6+T:,.{zCuU (%SӋ W3t$ Dvh%tDs-E |aUT} Fsc=nJ|QqG]I)m}G!9n$i.Vs!r97fu^^V],myuVk+RsTW48P;t>mha4jtCbhQ:&WnVEYmca;@c/߾s@\D,mJ,wfw(( f$>|kZW{p, ; hkf [y37AnPkU~>ZykVV,Ko^>t Xn 6-7D*4FtKޏ_Q#m˹),l>t~bp18wn 0宱 &W"Vh&ʥvL56Rn{rK3wl=$| 3T\u|*/D{.M OO_o~[at sN^X[liX+MtXc@E,!m$D,-,ƏjX X871&r!4c+{Nm zQuH""WY,II7rcXڔX!=ڣ=;rjYM_ 6\j}¶(wq:eZf 5gQb0kss] PM(DVE,c̤yu  x5ϔ9^ꙥM2<=0|52_u:RwNC-OZI ΅;fR_:SCےgOZ.?~G}_äuucCm,D,-+ƏX .%F\1~QJ@BU =XږX ST͊83+9)3SGVKZUَ4ٖV{K}apW Fɛ}q46agHOESeGd&јK [,,>5AzN%mv2aw_WQ{dmC!b biER "Q,;š6qqz[4Ӌ.ΨvED,mC,:Me&\}yYtdY I;Zв(L`RJD$JOzi(kb!4kX%ycXVzqKKʋ%ߴ9B˸~4w&VB̓PPXBSS%(&ekKgštZ RYC4;iIyLFlVz9-9p[q3hX,((VQAE ""-XځXEe:V1ED,u$P?K;"I?~3bM3Ğ Z Zʇ44VgQ|u;J;:6)7r;w&e'.lt-f-D, b-#C8 "hbiX!M>*=dzHsѵgƾ(0`kֵ@ 6bw, k_3%*/zRI!biRk_ֶ>D,MJ~ևIi_ïmFYپy0|[Uшc*N<#t4 ʈ2&ˀyO} ))($֑eO>-ȽmV5h_ֶD,E~Vh_ï}YJ4k5<;rFoEgGcMwQ}w0SsQYIRԩE.|i01a݅Hr]<qR(LL]"4ȶv7싹DTL],LQ}Q{#"/|ZHN-vI9S${fBZųSXKE\VHnjU]rf4rp;}CRJp{O:.5iM󧏞=}??+ L;zt'Dbp$!*rk7IrS [W(VqyFAI^4GR@c'C$ְ/D,M  KCBļb1(G{up<2vց<ӷ}W H=>ggVܵx =޿eo^[Y Yýo$0_loci9pQӱmΩevp~5vQZ4.MoD'ujZ^^I@^B!b!hBB Фa\&ܹs;]J 5zs2 w>V(:y{ [&Sj9R FZa?`%o^!̈́\8xK&+wg&ٽaFCU l\_cԶ-S~>{#vIپi^æj_?M.]n/GSZO@ @ ʴZP77Ν;GGG;w4doJLH W[n~-|'Νk.K3R%8m$rxSXpsMS| to<%XzE ڽ!F|8*7+wխ-&.ZxoŰ]63_f(b5Z.tY?ߊa3>ʇnG񃟯CoFbwq|+(I5$O gV&Ĵ4KAu@"&D,MX;wk׮ ....]{I!-3<!=w6 h̔_"ҐݛHkG]G`U ڥg%=M4pnUuӮό|iO2}LiӦ}w3F|FY]U(ׄe|Z`w{Ե;)YEM @ ֦ٮZt钞NB7:`sdrJ2zUhW #=y߽l |+ .QZ.mISXv]ZU(NI-NI/H/HIMMϫk^a Kޙ>!8G98@ @ 0Ecǎ111Zygx$PQq q V@ 22J }?9:7jQ4(PpxNmʞY'jvJF^իT-ʵCb, ؽFL۬I.\zm[:;hӿ>{/cd(чG_w4')I/J8EUn .pQ @ @ $ 8&⻥!޵6=C F*b,=z(l2B5QX׳aYaVs>L` R|{&ٽqF*8Q^ό##&wTXQ,`3jfs{bgƚoTҘfgڸxvĊe;n q=@ @ |@mKJ%6R_JqvJ򕾉J`/i^#L `K3+Ebf\nGZHWp9v9"QnI 9 f~**De%ʌM~6Ū[TBݐUTUTzUTTE>ZsJ3Lhqvnv[K Y A=k]k@_Jmtr\f<˃LT\T i9(D -~mzK69B;dH* M<_T򄦭Rkg&Hy,9ajee~i~Z"f@[9z*lǤXvgZ,U@^q i Z[QYTzQF UPwǎ3ztjoX w=Sܧg\R9|;[ֵCj$Q'ߠD<,֬BKkB 0g߽7/^"q1T ^:h n^=[oH7!oj-{#l&%]OJJ(,͡ʋ(})}hz4nt x$웒ƯBBxPM)lYd¯޼TWDl&X6ƻ臩;¼[z3M`%ա.K `w~0p|RG PJ .GG_Ř.]TYd,J0?gߺťa;J99g<;:p^z~? 7}+U\9 cR/>rױljs Eh4M+]D,88^=Cwcߵ5/Em@J0V8GJ ]lMLNPQ2R.&dp! ڰTs:- ȸo#.ti* L<hcK-C\<0CbV+"$|eqTcۑS`cꍕV¬uW#r8~KY%yҼ̌ES{Y7W`q~~[vifxLH ̡im&e\,*"by!W-:FL⮦ی#L6Ӳ:]rW󏾻 (Nvy]Lh Q\ۭF29N?TΉ68:4g-*_Jm_wҮ|ϼӃOwu%/ߝ5GY_/x {  -k'vL8Դftvӑr*ôY`Ê7gLL{^w_`Ü).IϺY(WƶkO+]k P$yeEȷ_7sf*)SkA|k=7d?+-Gfy >W`. ~ZX-簀3'RwЇP%OD38o{7J--Tc"`POPg?S="^BiIkobc2]MpD,;[wk݋?̤`{;酩 `'*N{rN J\>Q;d6|'.!`췬amGX?!!abv rr8=]NđI*M:{ 3Vxa޿b%IV;{4ʕT]*TNZ]o^[ ޹PW̝ϊ)>zݮBQ|Ƹ($?O.QbDW(] b!bi4A tQdjHJEG<ݨx曋yj/"ci(~8s8s]}Ҥ~J:c;:r5&B a·IeeIv]sjglIkXGycPŠ;.V]? |0rcTqP|j ߬:Tt惕ʿ UݶUh闡EwmPπ!vVFZ AdoV @/pa)bQ 9/);fL|Vm Q#e-6nm(ggl uY$Ia#zˮ*s'f) -Dx)Tɸ˺XwAms-ޥKʰw|b!ܬkۗұV z1N+_Gi Ɔ;X[` PgO?8K5y6f*ϑs';=T >[^՝7 @c!X0rU] |ƅSBM_'L8v/"ꌖ:5Wv*2D,BۃQeUw.>FtM[x;wY~wg #婊4n=:q^ߢ2E;n+_1'v8.Y}vE<5Ju!$.ow2qp=p:х* ekR=G97'GUIc~ .9ܯӶ h |ujZW*9Cs4j\()T}u0Zc}ORo3UVS,]#FWw.gVFQU-Fڿi1Oat B ] 3S&HjMTU)hX1*gٵ `z]YpeazzM:uVuVM_߯y`@5W/j ]Z4AInvFcikנFL V<o^Ԙw~= 0̟&H?pn3*ΕPnڞZ4/?0Tݎkj'Ev?,߬mJ|޴|0!c,sBg}#[';gR@Z ~vK[{Ó^G!v%B5^b`cr2S&WOncEkLn;~#2"uo͈?7QY>_L8|0r^=ρ8!_DnJrt3^|Uۢ\̓KI_KY9X}kAG0),y1X4ݑeA Oz 5Ҿp蹲hջ Buѣ9}:ʹʶ=SN3gJ@3}Gz[(p ?Jxj 8/rV:|68g {ˊiWt_;(ABm`Yd$U #M׿7ҁB,7*GiuVmx ߧ 5nz̚!͔VIe05M0INJa kTO1̄o;0>&h"%-R7yϙV:"uSCVIݬ4DҤ2S5*t+nlw^QJBdVd[t{;]iw@+@U%Uf dH-b]DSڵ]P ]ר!bHs6^w>nI s l'3".9Vhw $L3o oyHBmC=;ʎM5+7'#7 lv<ٱ`CSEɌk:^2ulл( A;փO~<=I,𷢸,+G Cz;sX&BXIhGRZ^֍[NdXX/Y#xNO%8pe.d-d8)K;G꺽F-@Fv4R`Yp/ KnGaMo?zg1\|3,޻]V|0ԣ60sDz.Q٨}&P}ܳQ]$a]7&h@'+nYCIv}ga%TM"V9 3ɢ!Z6Pi Zʢkj,DYE B՞&M+.%cg/߼|l{~ѠI~O3܈ Ax<*Ŝ0*|-h%,Sy籔9Ew.>jۄԬWīJ_~4R@XsR&^ݛx5^͂.$|S&@CMk IDAT 9>Jyҕ>IVޞ?F8_V{QX+niMf`ѶZ)틌 Ay2_ZZk˓)<}AXڭpM$ZŚdB\4+ ,1Jhb͗nz6/cq7^Sa!535@Rqܑ  K!qgE͟'8 )8+s_n% r3d.w?YBEXj_"6lˏz#> IюiCLY"Aurj֚Saܛ&e?nȜG$&tW8rg 'JɁLu">/eaBiŦ뢹01$ܲts,JKP rss۵kWddn88DJ0× bn7go@'ĕuv4;kyg7-z,s褙zΛ)E7}Adl5mP-\xBܦ6R'1z淥^eIe(0,}O:u9IAjjl#@4,`ۯPI<_fO<.= n,&kߓ2S^t" \_޲7WGFk/86v'EݦO Gs8| jS (ƈE/DJ}D+_t/ifRR3> Tu켽=#P (-#vQܳ(7uXԺ> (+Ѩvƫrk>L[^H0m4`OeSYQ 5Hy`# 5j:#K^C FnWz9NYb+ͥ%4FUh)-Ay4Λ7oΜ9+W9sUN:6,;`FzVܺ!*ȸR3)dɼw@κ!mf }OZ69[A V>cnjG;m?}\zu-%ߵ00p= idP#ٝuzndFԠNQ]Ƃ-nUNx@Ԗ_FI'yjC5+ k=/^n(AaoRu4l4,Uj(-AÆh$FGEw@5qQt~5RW+Lz[|qKB[%e>V޶J0&/$3cնe!Cr{Yr-Yu/Z/Ww$%m'҉ EQѯ6/i @S6t╺}v$6Slt5%-9C~IۅGHHȮ]N8&K9zӧO V`+će. ?~ףc&){i{dXqjkT_qi J!6G΄T (t"%m'҉ EQѯ6/i F~jM~jݗ~6zw!~IKxЯ_v:Jdd<==?~-[Vs~MWUh'o9pHkNKXS]AƱ Xɜioz{T̻ux5ayYnuUY)máK_v"PjCq |SRYT.*PoҰKNꗴ ~%mӶݶmۅ rrr-WB! W])L>l' ʌ?klYE ߎ\z?\^{sW?Gǒ(o{4X72pro;24M%övn9gW@D:5j=Ն%-v7w>l_3间]N3ph嚔Z@{SIԯ/`WL]7/}us[pjNՑe3%=b @ @vs΃k#md*¥ș6K@ Љh5~%* ʅor6ޥe!@ І@ @ 9/@Pf-NvBS} B 4QACt BgL"@ @ b @ @ @ 8@ @ =A @ A!h0/Rʱw0n1('%a:X9( @ @ 1k(~3bge9u&ED ߗ0jD͞LtsYRq⦝{u6.{g,PX6@o554gib\UYTV)Ɽ kkr ] ,ޭ(²J&ƠΖF{Cʲ*X\\^Co#HaR}n{@w:3vg_>.yûMN:e= _\o(<;>[XJMlca2j#X9,r/O%.Jw}TN[/:|J,TI=\TTH:XXyyi O3g tӞnH`j[- $%=,*j(vnll=͕E 0cQmg`p˽( ]i O~lIa=4% B8۞f̳ڶ삌ug%\OvRT[4++&i|AEN%UR o'Yzf S%=,p+C譁Ri ۃϢ2Bg)?B ȕЃkC>y7o8K궈*mg,/>P[^.@-g C };R`blVGP(M' ξsؐ_OJLcj6.fYq60z$]G؇wQ+jMhf!B 壢D?v 5z8QCЕUu؇ZYz`Ι( ]i RذAqfض=8 Pre*: ~8W i;b7s\$dF>ȄY 6cH;R 1*:8~uHGəXچQUM&^p fstÌ*>_ #=ʋAƵs GbL5P(aS;n?taFSΙ⡲F#a~`́aoi0),8=`we,|{^ut3qnz7kE6O%,6 qdiȴp>uӨ9C S :~I+?pE^VY1M|wa: 64Ef!BR !t8Lpn)w>8s)zNXJ 4G-=p+gX]lʑKKU轁#dnN5|T%S&5拵֑E~,ëTISn;rV-{,);vxPUJ< t3Y(-M/q4/}nw*XwVQS]X@q9ۡB^꿏0n`p|ɯ)}WORM vQSP7 A-RcrΤ UzX?&KII>fYb+PoP'GIwdbp(G"1r`(xiѿW?}&4XqmP(=Bey}*~a|*P*MjP[2)=vh0]kc+Q#L~^"&/@Ci_dQY??6п{xjֱ|Um,DY4GAY}aBi)`ûushf7))=/EiLᇜJ. tm! BDGER2khOnKz|Ie_+c]L#x,i&Ye8'ЌK2(n͟fQ|4W؁LPsj0v\(MusorIMoG)%,LI~;b9ÁOs,Ǻ畤z$; gvDHszXH*޽wHl18ΎM @9iH*o~;玐@sk=}̘gK+b{h*-i:gggs!>':Q9s }sۏ$ѭ|fpٻ/d\~)L*70?k5 _d뤾Fnu&I<#2kXF%=~R#k_ZN@ju/?f(*:W+){k% &pr!9\hF(UcCĄYȋwUQJo^XJtafY"z4~/%?EX6*<ä'Ќ(7*`Abׁ&m[ݔcwOsQxg ;gQ^я x?6nO^B/44NA)n 0e-m)d`{sspAʱXJ+pK&E߉*2WJBSWY :_i( @ESPܿ1 {̳Sx="H2RlO$(@D' ?$*&U[ڑצS")|q-&/BY R!,@Ґmafc{Ab"wTḯJ~S[*SaEu?=Ȅgaju7AlT z5?#﯐}2heӮ:'Gު; AZ䃴a՝Z(|;Zgp8kN+%]͛Ot\\… u.G ɨBE0^~H#mYtZe~#6냰~^g2V iɾt@ٍ{ylvKkR73L<(dƸN<Xxr^7͡KՐ٬rE6 HBKsf+mfͿZ> j/z,EKb6|Dwb뉲eueue_L;rwBӡ^S?]m0Je4Ty!{pd˕19t6%%ߜV%ܬaԸLڀ Rcsr`Cbnf/U}x -xxnr_+֝KRu5vڠq55t Î A7$d>n.W&:G}fqѽ:Ҝwܔ2憦s$D8&]"6Qu@ji :˃c޼ys5j'ϟm6 K?ٔoZ6娽 ' etMCQ?$+&.&Tk^PMpLpW 65g IDAT^WFwb^MyBxs@h |gk AmLgp:2`I>s*l@ sFX(x1~@fYWc>*' fRΫ9PdMN}M]m̐o{E~=V%L$ŀO~{ݾF}5WǠ#_μ?Ru9#pwdsYl/,"FbيDY@x5M5vQ f?תz+nɄk6o)٠U-Ӥ5% BU9?)߀AD`=GQG g8WBiYf~z9`7 sxkj'Ev?,߬EwN87h|Ȟr|BO~G ֳy52';[owEnϰx/|e|::e:h:yҕwƏmoT}ZO_(ۄi9qcU Y6gNBss_k%illgu@XXŋu.aR/ Vxoh>QL*.fA$4V뉆,? o6A}49cg 9cƌom@sͩ ap,D>` ]7` `9Yi6%ĕ9Ief5_P >⪿ݾ_)Q$K9l03>R&U20{wfWzf׹tV+ ޭ{Ó!Q ٕˋ@8e]KEo߅}_m|:GtĎ#:Z' ) _~4V]M,DYty@n_m*Ts~703D9[(SǧI+! B(#-V~+|QvHUYZ(QP֤TƜ&@d[>-װAQeR]̓KW <߂l4q_HJ \GbC%|x =~Qfk Uu(ի5YaI?S,V)F }NI(үN1 PJ PYY\Q\Y\@h 7:(QGyiY;W10ƥIWۣ#*VMD-X}qѓ?@YSp~VGbA) yӘw#;45 SV0M\67Y94.KZ\$Di.5Ҿ苲^^YRd]j,Ťnsٜ/j^f44i-DAZ)-Aii5Q`Wj ^欣FyMqIY9 μ1\ G*I 6Y!P+iqɱ C\;W^HGWPE Vj7rhHKPLvvv.]RI|[S6>ʧ(eM- 0Bňejvan{^'֗̕g4Pgxm_amPQY nO&^onðsp-<}<0cHVNIu]hQPnO8nB(TҀS$bzatL'8BBBvuĉ0YѣG>}:lذVk4䧈7m'\b3聯f`lGl 7feZ/U0>2t0B!(qu7gD^u,z1\|M,޻h'k$0Br%t4pHu}x7ʮwNgcFwh6j,OY6jь$[ ¬R>X))9@ BU=-5( A$Lm;z}TDACRZ^mdhL1ddH(ӈKgq+K'>ymx0%xYȤuERK͊FZL<͟½tzw&BEX(|t^f&(Q".90(.@QVl @i]@.PՙYf2tvgh10,1hbKݫ2 ^Ra,t(%V?Uӌ۶m (*$$MޗJ_1pM|n /0Vp1^9^{sWW2E7N~iC -Qi˼kNzR!yxȗW@h_x6$ Ǟ($ܣॾEٹ鍼#J3nfM+rF#A/]s`"gh *6Q f6åpܞ'4ѕ#%Vk"yH)ۚ[sl-%%,M`?/>`/!NK割eisT u|'o+SI41' B]᱔FjEw.vjGFe:y Ҁty3Ŝ9ngPvI >`} 7up3M7j|jSu$曲51?SqU*:Oҟ,S搒gݡ䓊Pcn#lo ý 8UG8r߽R+?͕?$VɨXN؎2ڎoWqSiٶ%3^N53"F~^^:v֞g{_{n}xOdu1E]ኋ%cy%Ndž;}.wô5D FcUT68 R3FdgMWyĺy+vH~(+rƭdKKh.,A(Ksc7↶jBAr> _;O @;(Qvԃ>(1.ۯ5[l\&;w-3GN-<3nCNWEO/R?ח~l 6k7_xdq?'a3'Āth\ #tZ?e; Wf@jc$&X b݁j.-ivF'-drvGx&3tJ8x64'7_X g`gv}\ ˗UdZI /JRT%fmܧיb)ϲ19Vʓc^/}FQ*w7=C}(9"ͳv5?YV_ГRu z͈^-KQj]hZ+i_lOYNSDY~4zu|`*M---4XSM(Qf>_c(f%[SIרLǹņyZC{sF*(װY3>`ߐۀˑYCx wPZkd~c@"U&~r<E6?80؍fvZQX6ϫj4:,hcUŴEu6zvAKSfWX53?(^FM\5m%4EUʸKZl>sk |urrAP|3Ų+kkdal;QeMLlw6qmA l} bomc(v->هSY,MCEX[s[ jkDZذ5uu+(i4 =ᅳ^6@13YTǠ hE;_%AMyX&&ͪF(p9mr `K/f[롄b }̞)8~i ]۔ЖfFئ|Vmw,\ $&wsQfJ 9ytmhw^ǥl H)K@EQJ6L:L B9rɫeZZw Lxlf_IO͝{[Af=;(!.b/9l } @0wg$q7p뽗%6SlZ\ܱ藴 ~%-9@~IKxw^0ȒoDoɪ[ #Xer\D@ t5u_݅KZs~u?#a vQޡ_]N405]_T_T#g{mM:KAbp/(,jrJ*EJ70/iۃAh~%m{ТPYTjeJ{/W/iۛcpkrK-m4Rk2PT^[]UV!,f@pfoe?m~Iސv7w r~IKxЯ_v bhS间Q,Q-,;[/i$dg]@ @ z1p@ @{ťa(@h,e!4.@hCQ~gįÀ#<4L@ @ ~Qڏ_02p@hh6уŰ8Eo@ @ sK)L&gggrg N,/ !B@,QGVw[86(@ @ B>K7>íq}lQB~޺)L(_ d2-2}ƏܽLX c=7߻{vfqINqN!efog\,:@ @ p轁Cmႁ?]OQw{.N!J>(. {^.0AQ1WsMlƳɳq7.c~-]M3P Op;_[̀6/@ @ u:gggs!ĬUIra7_] _oDq dVUqĬqvG'>f`32ג peeJ|S$ 6cƐ<٥uP㒿G\f@ @ v rss۵kWddn 8fFȄNM1XAsvv֦I!!gV ;ۿ 2`NC3ce߭Z.*CԖlx ۵0z+WSۥu@ @ Ǽy̙3j('N̟?۶mt:W"f$Wl._E=,L"쿠]T|\f)eUWVEfR3YT@ @ N'8bcc===/^8tPmK˚=BJ1mf2r󁘁b+W@QTń^2\FgnxRBTFPS\QCsy&-Nʢ´J%5fX[> ʢ Q K hQNh\8 3 \m:ĠQ~nj@Uw<`L^IfL <!m+*b7Qi((8cCWo_g镀CURq1̊ &9-g޵b<౫5^' `((%QKa +,*0e8gQ(}ߎTQc6%a鯹(,m(F'8BBBvuĉ0YѣG>}:lذ;iޙ+:j!>]0GHI ]iVSWZsKUs_EVQ"=hC]}{ }rA/;ίd*\ȦYv2%ɸAcKm+39@ceQw?{yu\ ,.c$1 .,αB6:FFUеEN]DY"(22r ?~-[t+JmAgi ln07#Nu Us M2=>--!`R0 Xpz3Y.@f6,5m^F~sV~LbY( C41x&ڕp>uӨ9: S :~I+?pE,4WEr] ( kPe?0;V kwaQ\_J&= IDATRAAAW4֨%$Fci AMXDI,16(U"e6`,<3wsSΜ{ vAjAKhbiSMRogϞ4hP rsx~:\C僊EwVN4#{~É>%G&(uc}XU% ;߉KЎ b ax)i eeewu”-cIzZRYm9UuAF-()Y1dKr! u>k(trus@\/6?JEn#1- {{}?Dב $1Lldbk[!CM$7 ialͫBY?x)4wiT,]ˈsU1=Y5(q>GPV=9wE!:΅)c|Ow g= Pj9E,  g Kпc7@xҙ=2n}fBwo\)2UTy>A4 TST,Vhh)GK<;b0te w2vJP:]3d]o`ۣ`X˓] bib|o?evj1?8?1<v؅]}~q2w^yhƗeW\4頼[OzhA?.2͋mnm v~үiqFuyVQa)}w ); d(J?p.@ԜH?C>ը4TnFIS0(ïP]QAt6ĶsRwlrzloS\Tn✤Q(Q~LP~#.V-+ؾQ}h@-RpKrriQ20g-\2 0) } NzDe6cGը־`/Pp2-#opm"%߱ivqFBT/w9>s P%Dd)rSۤKEdր 8G#OGfVqxC3\ly\wJ5"|AНf(kIY ,KGAz"E'=plM8 +*rs||j/@О=[Avo۶}*PfB $lOpW ^rtJ2x3(ʥ@bf܇/  DYV6aFyʺ9PZBaBA--clS^S4mViE:`A3DǜCo(lE7 Aa!} }+z|GߪxENsuj/J,% wmg1 3qf;dbag4'b!b1;v.ÆeaL$z0)$$@oERX}XQ/w@2-XuGVʷ}2uZf)3%HU+~V6Xxh 7~/T,л4 bi_bIIA}tG͕2ŋZU؝Gqe4yŋkWËK<|SXC@h̏v028bӟQ5`>jzxZY_*&SDrm;ץ)_[ޣq`RΫ8PMQY9JB<>YGc?fGA&Le=.ceΒ&eUW0),2Ts8HsX5־дuO_#%b!bi<&0UɲqU?FF[a=˫Q_~83hXԾER%i MQ JRV^>KXy *U ~KFIK:by '\qAj{^o,-I=~:Dv9Zg N }௕49ťUg/;FS6X"˜ 1NcaW[i;JL mg)X(a6[vwk%DZD,ҸX ] kn %ϞJD*!]@_gk7^V(7Kk\蹫ҫ]ozNިU7L3zTL0< IRpV(,;qB_3z -ЖbSpqgv(kca~Z[ /Oᣛ0)j+j ҳy@6jlN2~] 0/&5JzCpjlcHUU!nM# 2 ^Fzfuwk-DB(ޥ K+m;?LN4WurXm[Lr־a259؛a#yTakVUah`eY89'5ws$Ԑye;JZFBk hSbQ?hſOw+Fu]߷2zS *"B iT,pG2jkkR2j4m)|j7+ey_Kd(N ynpMPlptRL9»ۓ]CĢDXZлB"8HE )K9wfE@sIr0)VϤҍX%`*޽NfaM:{uNáuvռҨӜI !0ce^UozL>Ph!-:KÉ?rUDH{"KF+l˝f`Mr>=76d U۬ K,.d,Cee X"+< Z 'i|l9޻\~KYZc7m1`sW/drXKh6%;뇼Yyz+FKmCBJ.f75M5 ˧`od'F~ZZ?3ݹPp\Ϗǰt [!mׂ%biT,-]M@~BQxQN(G{Ԟ\ e•77}8ll&`Ǚ"QD;5h5ڲX/~<;:#b!byX,LBǁb<'r29xȘ&B ξ8 Tͅu|')r1@ Hܻ7t$ni EwBbm2D@ 4J3{9t66BUWEUǒЫ*ngaΓF 7N{y*gdiR<&q2fiiŎc אzH}B\4sl_~<_믘AOħaDM 3g5gvfFto,`D_3{]Cdi/LƱ?VE)="]U|VX[998IUaV,Unڽ6 o-A6.]HDE_sXX* m( 1 &0f`{sU&%o|騛o*/"{>͜V@<D.ɉ]!ؽJ`j!cUyRZ8Ft<l,Kbi}jhm"m@hQ71ć*rurr6T/9xg/ͽK>?UmJئ@5 ~١7v`}|feݧTLKgZjȍ{vHа};ty7~8΃N$J CWu[fo-Av! b!byB,#7f˛1}^GT|?ĤZеb5 "^"~=`Ƅ_ ֤ ~Yx v[Zl,Ksbi}j@k,d @h `WWn($jK1Wܔ2\XΎ=Z3qzBų|`<.f\ytߩf\̂).j.`ƌӰ&|èL8AS1s}~hXgF+@-Cbi5w8V6q*g k ڴeɶf#b!byڴL, M3bN*0{?;- ͯzbZWԿ~C(|c`M3l,R-ˆkʢZ"O En-#T&Xqu<΁ Epi{N`mG݈@"E<1ЦF|h<`[ʋ%X"K,<.Ri:93PV+ @ lݍmdW. k$G6;Ҿ%t0Wk_O%e-Ѿ_V$(@Є)Tdl.mr^gQF >7l7e)Ѿ_yA:v}I[KҁiOem=m!;{_J몒*3حr ߍڳµe0>Yz'=j̺N|{T$?i8NAӦ7^%튎p-Y{+^5,Eq۷rJS|O^}'u> 9]3qw0_ a9lz|WsOGuXO4l'p~BT$|L Sqt/k?D,OڗϟcwTڗړD,ڛ:xIFEwV̎f=:ALVTsxΔӵڵh˾vINŮ}L"ƽZ:E ǃn^~99W9 Itsv7m_WJ O %EO4B@I2J 4 @ !b!􄈅@Г%}Fz [̂q>pg)}#|tg}dXY>]sfB0u7kz\EF/~ߵkώͳMtI>|{jc{ӏni~%S/2222仅{GOG==bݺ5+?1LZpT'B@ @ xfCTtZ6#/o˾#*yV=Yi/74.@X;%L*)!^aApϞIk]tgȥnMS%iݐe |t"^qs]7͵W<2|V<3ʦt/R]/cA+CLcM`@ @ ȴ]LW|*n1_Nɬ~sKٟ/+~ _%S io~@sk"eEwi99o&PB!TO~-8O3=&^CP]RZU),)-UYH񈉜J D%_uS/ _->@ 4  'D,Og}ys;=†W_Bw" ^=5hZNĥ3)PjOBh&W #Qޡ\c{fW)<3YeUq& hWs/^.{L@?[nMRۥj Q\cа+Mb@ @ <#GWKs0!َ)hj*L>bI:S_g9JC%)I36B]doЬukV.N>T6w'a!@ @ /WN$dqW3t<{4b} _.LˈX.N Wv줂&Zhl+T,$]m3U$=4dbAOX=i06L%ifrcU9WL} ڋ|%3g$B)8d@wIۿӽZ{Ľ]TcL@ @ g4MtWi3UkO}Tpwk.[ f|W/O+Y5Ue]b& l~i+Wl@ @ ȴ5gOB".TS;i eM^/}'C#߼_)O;sifePm~NH{XYYYY~)_%J_[(~H.]-.ɿvo xLǮE__ݦM":qFXH @ ‹SkMcꎙL.62yce/=wXTF?;M?evh!@ @ /,LݏЖf?bltH*R6ԔoS't&w" )|TgTy֝IXݫVU6=0Rkm2@2 4dA?zR/hcsqyy<{2B6] zK=PT_E=S=w6km'? 潖vC뮝6n]6f!QymBf̴";w+؏.L έ O腑.JH7_|d9ӮZpπ+*2 NfeS8VpQ: "_h!ݼ'w jPGSPBERS";Fco'IAh@;6D %phgT@2~|!8t킡jb~4!S\lW>DEEMeUzL7VN,?~=Ȃ!}VZm Tã#*ZޓePTtr񾓣-ҧAA0d1.~Y (bt)IVj#*2L,OpOv+`<;ɀh4 0کXj0gu?m88q¯?؝=)/'wЮ JJVT ̴o/o'0-n((ɿ_*L,c{wfU=|sMwۙR/(=x+9~ƈe¢{ sb!]M~n+yBu3Аʣm_Ϡ6/`){q)çyYD쬹p'ӞFyw/Ĩ .;>3exxmL .~HXɧ ڟX$B k5G 1Gge3ˋEb|V,Wp&sgͽ+7;u[gV"_CtbGy)0햽a^4Y(+r2F*S5>%ֻmM"FzrĴ2UEr>1qOZZF&ui;ĩk IrKƖjM_m4*ViCePV=9wE! f;>ֶqX4Bc γpEGD,D,],  gKN@ hQqgWNq/7!ކUߜ]UAV?=55x -r#MIעU!?D#\O1t=m4L‰+[WgK^ۉ6diaH۱ICQ{wٌٛߋ*8(3wrF+ ξθYzw[݃s^Wi ?\&R^N:u%'(/ HE1+^KDa?Ce&q4eҶ}v'sިa[}4Ϛ:k_# Ębe_jg$G{w9>sPTfQ;8a8nQE+VS3/z2nÎd|.&EQ<(wZRVPTm4Bj9E c~[;69=67).*Oo✤Q(Q6{&1w!zoYٚo WniҿC64fQ20g-\~;CǤߣ/(%;w1>rYّZimۀE? $+. @BaJ,DtZcl X%3RZJ[R ލ1bf?6r(lK CK s&|ƾ؀a.>Nz1O7S. 2 ibx UAk7Ɏ"LPO*m4w{[c;LyX |(Nma=.ǰeΒ&eUW0),2T3Z#Ik(D,j+ M8olD,D,\,"Fhg9EvT\;t(FCǏ|3'ݷ]ʦM6mt@M!~>iC9,6SndP/NL^VBןT2Z˼ sd$(-ν^\i%{5W(dLh5SS@SSxա~ Vnɭ:W*! ] U}C()ĊhXO .Vw^jhsmr/Xq{57;%||;Zs>j b-;RmD͉HseAPFS6X" (UusARhϜҪIaŢS>Qwk5Vb{Gjos틋Ίq4ss[&j(gz'8;x_l 5_C< bW941èX(թ)\7b)i=Qg?fJArPNOѨU!)AЌ+iJ68Ű &R{sxs`8L'LJ V;}5ֶU|X(8|tsY&#sYmE @Qz6ORv" L >ҸE?G6[)Ű +>@lqT;l5vI+Kg86ڠFf o[]O&jтL0< pWLJ=Z#6 XFsWUIk+W,<>ew K+m6}V k'G6(66K+m^w&' p=p[hiŋZjG,̔/j՟ .~BiWiJxdM09>3hU7;zR;5S6״K ;/_@l,~|8 \Y @R&`*M JY ?G@^I;hXX }A7~䪈."v9KF48&({=AjFu1SE4a&6L.E%o˿jtYoACV)[:֐ɞ[v8 ݠՒFa[:5uۇE~$RBK ~$ȡ M m1]s"bx<#[ƝFgW{#Ex NG}">`伹sj_9Y1 2n|VNbžԫ䰾.Vz ʌ;x<HْN0Sj`Qg.O)Lߝk`f! $W0gmf z!~+MTn3~ftFMx=N#;ᏼ.3iκJ SIqyΑ>#"v~/i1d\/> hNmP0 #?이P+)&~a8Hx6/HןqQ28>8?QȨO}> EfxOSCKҽ}|dnScgv/Yp]Q5`2X҄|%!!.W x/~F3px;|27ˑj~]Dm,lJӻ]*sK^Z+Ob8qbA*pk"эnbpֶK^XpEyY]5w!}Z1wgJ""bvC 9=$>%/e_fwL7ߴpqkx @z*YsehdI>DE[mmR+PsFL-_btN`2@ z?q{Q?oS6$NwB- CL?37ޜfDյ i/g:m!G2c Uq34AĜ>.τ[U]xs{b|/FjXl*ngaΓF 7N{y*'ccU"f#2%~YZgrrPRWOchΞfug `=PZEZ}=Hb5%Ϝ%ԤfFٙuӽLF}@&v 1e#qr~bR@gyv1 evA5v *v&!c,'9-cV8>J~F(D,D,^XB")ҌU b!by3xXy]^0mK ߗ}mGTa9ą{/1k>%?_|O^Y ϯ~嘎zp/ &Ncrغ|c=^b;C80~lك3xtx]4)={&)Fj`ȑK -ݚ:}(+ IDATz3P4qbئ)IC!\0 8̥rkv_4^6~ŠGuVٔN6Ԡ _Qx|hüg橭Sy5uOoFMy: (-`};wԗ1-=G%eѳB| wؼj%gYr=K JU#nbp˓S5R5F>得(i} WX{Û]L7܈\"0@Fng; T+ pDu$=m(ʙ!fĜ`Ua  ~ܬaj~V&TKGl*g0{`{rn2K |gǍh-'X:ra\J שhN}Nm,f̘z0 m7 YIS3h1#fN <~Vh8{+r }f}dc3N.rQ冕tkrNۤnj| Q'*?iAap5řCc-0sq*dUY%jۋF()G?[nM>FnOh۬̾%P]KP+ &c sDF29)\m+bI:Sa(hl  Ω_U*1OlFZoc-=q<5Fl7.jantEU<3^3X 6_6 \#3~FlC,NI|`Bly'C dlڪM}ЦFF&r|+}ZR@ qJ&k * @B45V҈oHqWR;uWVdzͮO穎K;ط鞃ϏP,9ArMWBE&'+*M\i@$dqV)L%'g'U|i ]SxO+N/\%r{) F|آILnp֡I' ~URZl>/Ƴ}YKhD_ؙFvrcQ͎^~/k ڗF~ZxQMEm#Y5 \Bum&;8 Q#c 5B(Z_J|ٙ3g\Dy+81[R6z68+6E %uJ?kO53s}Ya W[G2?xO<ij ?յ/k>D,Ѿ_Cb/k987횇WyS>k{ߌT uWNןYs`CՀ5Į^#V}O}aN;S,B(`eŤ =2OJ8`s4P2ݳCsHT u,UY4V z683 `9Y3q*_!c~5aEyAMuy)̨CN<g8?f;& RT {"jbI˥\ܝ%#ͥ]LL &gb;X("/, DW^˳3ߙ<;<!P֨k9 \ۡ঒1K X3*2ʒ}ՃnNO_5A@67xgQa^oͶ'zyi,!k~h/ۊg~b!و`GK#[+=??bna-T7s;)}9%<+W:MQqh{ fWk[?!B!Nŧ(ʻsv7xGU]]RS@nb`<EmiaqͥtH\رLU SEVۻ%wf<҄B!YtҝGy)'ovv`GҎ6ޭCK0'B!B !B!B}ם{pB7BbcQ% !<5%E!B!B5pB!B!Q!B!B5pB!B!Q!B!B5pB!B!4#BEyJ%sq}бt1E}Yu=+:t֖jJKrkT2V.ppwGdd!ϮVup(:rFms, 5pBF"j}SoDL@q>q;auofZ\ыZkh:ٺt,\xk3J]{JJNX8?@7Mf߶.2x+kld!dQM%u ۆ:T<ҪRI2Xx_1+}'s7Y3ɇ:KKXҷsc&g^B!ؗpuW߮P6֖,_[񕌝{ d]-YD?:D7uX/g(j;w[g&)(Y?j tIPp_, "܄ 7DK1jD׺I7Z pY?떩%]`js޳SӗV{H8YZ74=U̽`Wdiӓ.!+bN! d4Ylw \ygo;8ˮj)Y;j 2w_WB.5?Yo(*P=P>}􇜂\%E׈F6I+BE/){ r6u o5 4,K,t {|pIHY!iW.x53p<8nWI;Jڤ_՗k|Sax<{?1Xۑ= %*A[2Sk+Б+GGմ[4'~̼-sgn'{L70;d:v7'3I4ʆzo)Q֨Lʨ7xG_zKZ0j(BEO'. >(Qzcy 񉶛d1Ik3.--5JJ,I7ddR`9mZjXpnc;}KÒ,j  7$'4]`#SF&{t/)_q|STvuCOW Wd*s+.-fV¾!^dTe%vz-Ac{ 2*a3 :֦~sW{ݗYW!cw~SڸsT^HʆUw սxzdxOExإ 95W|d"?][,kȦӠx5g\4`ZH8yj0b|Bq ulh[JTbv0b2@-R8pnQ#%z#-zFƨmؼfKķj#0;94_eWϮ<,-v)d>hu\7/>AicMY}I am)җI1;ksN3ၤݶ#vS, 悥HwmddbUIRXCrKZ7(Y(Yzt(DQ:+`mN6wH^(BG,'K#n!_ KԐhOmV9b5*!xYS HMUŌZUk TȨ TRoTQj ĒW&jY5)VIEm%& ^ϣ̈́@awM}eD5B/*+ʨᢪ\ a//4x>S:Ь!`pT+5o_-8ElxinO?ե򝷸OEZ>Y)?"џy4cR(Y0f|bݘEx\cǍ5?0qs(Y(YzvPĥeK>拴'Ks^+FqnrV`s' $ N !8J#[_U0>%l~l,7MUc sW+5'.5<#GqnmJbYT=TtLwS)dôM-Fun.q{n|H9B, ;gV,^G6 gF!15J@j%K` E1% '=4c}n_ mZݎ|Ih-JE{S?k(Y:mE%LkHcë 7$*}8޼˳xvd`VzpBZt*H˩QRwjN3 5-ԋ:eOs!61 %Z8XRgYIL'hs{`8܆+|ʑaX;J,p ̱k@IOʽy͇Aq\/r՚oh3J[J#Y ~5#TPUW;bQQX9JK;^ǼYۏdE; XۇK4=>/ƨ@|7yBT,= %F]n@YU4&EDXt늊Su3jl_II5'R>ey7Ng#F\+ܭ&ZZC'6Eb`ֆ3)os6o]a0A,Z[E'$T~6ކ9eios %K[ŀ(S-M(Yzޚ,*@ (C!tKÿZ$;u+}5)%K3r?2R[@ƦeĆuEYZ`y%٧D0ԹʹJ ڱee9\ ʲ C"fR,T1j3"n͑Vs*k;)9*l5k|6?ʵ"lP wu4t,,=.Y{@d]g&KQ7} )J,A዁(5|<uiN|^_i*"PW 7~y@7uES%XݷKVJ=wDWn nyaGyӾ=SyCsFeԧb=wݞ5+4)' Z8q仴yee٧b:B+3LjK0pw-G~E_km J6Y$Ç423f1ٻ7IBɂ^,M$;٤fOddA/N=$7 6,^I_1% % =BO8$HQ~愱^bHU[yyCʮ؋ό}i (,2 IDAT7ݙ+<+QYo?ظ;YMmщSY?9<5YP /fG_a>sc'{BÇ_{7?9H!r![SUck@;oc#g@60(p`> #+Wߏg]_䫀&uv E8TqG^6cx@70ʁ-{R7kt]Noߪ2Z۾q^`E|l[*?~7!V&KW'֥˾?"YYv½{~Mw(Y(Ydሐ8<[,,zk2ns~|hLUtgOp_L5 %Kj Xkx<ʤlV>l*- Qufi}G[su-K?'v_0|K8"yQ.)srrcW\\p[3^fn LuZcϷ4z/|qΫ&ŭʍ{_h I=CCzP}572ܖPgpHۖ7oҺ%ky?!"sGR`ˡO2آE|$QPpzkn'#[΀;a#F|~ey[~nؚ) MZqQ!;?N\>1r4gӌ`0pr=x8T^G01[;`An~b 0? Fdž>q|#-LfIKCBtw9Jx@ǒ[fxB4A]ga}Xs ښO-{sSTX;l3qI/9u8YX\}{^y>~}>=Oki@mGQyؼZ_ޛ<b%K>t&'p 3B7%x:S & :#_(Y8,z`t% PK˅WɖQpznP! G'oΞ9E1/֣2uEKz:+ZXg]ђƺ?2ABn|Vgdv/^N?F>n\QYW%Eh?JXg]v,Ϻ w185%iHzGG%{5Ube9"m*'% Vq5h)Yz*8XE,=U~M"Zc!w$=2al֊Pa]uEkE(YϺ",|Xg]Z%B!BգB!B!X=zDޫ6BH(Yቒ(Y!B!B5pB!B!#*^V4?!5NGh t{O?,}qQB!B!X=j B!BգB!B!X=j B!BգB!B!X=j B!Bգib !]GBJjjcbzV*su-Ք֨d"\n#yBBBTWU!up>PڋQ*+k:8=a57k!IeO[Hj#jί#کt !]Gq##Opؙu+Me`u% X|H gvwW໿'5r~"O O=YQ[W2 Dv869gpΝ>X^2cAN+c,Re緻@ʾ4tTW|K\Yw0J@+uZ5!zne/(3,M؂a,C NW0 !'8t4^;,wxԝl]:#ho$4pŖ!X.m$eg%e'G,IyгtdiR]Rp oG!ߴj% +T>@:<1RѥRm`Дÿ$Wai5h _Zc/ZkpTt@ QVݸflюzͺۉj8!]ރ/uա5ҊqKsE>\1[7 ]:La=G>+X=B+fEPdyi!`(?ؾW  h:@߬iSV&(qmZ pY?WPCfa]K@Loe~vΑ{v |}~CzkѭMޙ/ӪKP 6žh6B Hսmx/%'+h-+%q3 gG;J XVyan6o dCocI w4ݭf;<.A5B c2UQJG2Z#" G)UPzxjDXoN"T WicPVFu%V֕3R~v7 Q0+8$fVT΃Ǎ*iGI; |6ʯMNLV?$B(1Ga `v$݋g%*A[2Sk+Б+GGմ[4'~̼-sgn'{L70;d:v7'3I4ʆzo)Q֨Lm[PQӉ?߬b~aTwOҶ$>vs$Y?JR#QTe'j%KK(Je.fpPQsj^KQ\[J\_2}˗/n*:WvG >Tͬu rko՗2ЀףeNߜVxL HG]??,] Vd4Sz1as*c^^u f_8S `Qq+U@\z*4'2݂4P4~zpjP\Z΢zg]&s E c/2FĒF62uNjԞmB*/)_q|STvuC5Gq޾D(:(9E~ɭp6ǚY wT7zuf YZRQZAbUAc{ 2*!)AF|K*M9r,׫H;)m*/$eCʪ;_oh&  +KyY\GE'5cϪ\quZk?[3{ u0$ اS^M5l3~% УU&`ReKo51%hco{oGAuOD7_W^Ӕjwjܺ.8nCOSv?z<4$MِRZrFE2gbdh o_)oY T#pǥO,pe*gg)M']^KlH-eEw@8Sh2c`sqslc_lgrF]K?⪷ o;~@v,ʚ 6=oCx+Z-;4`JO|1~ O C"d'~-jJ:]TM %ʮ<'׭,wxem 5Cv4/JY wU^LZ&qڳ" jy/L{H$lϮS_*ĻCƨ^KK*Dqp4ct?e C ȑj(PifĻ1KctMdNcT"FU1As6=JVEMlލ1_<% yQ]3.hǓNmKfv)ǞЭkg>L5qM֍ʁh^,-?d1pjv$<4nabgbW 73J)Tlമa{Ï>4` swE7 8iQؕD^; @[A.1;#]&h AڏNq?:ee@+w6zGh?Y(XZgC̈GV m4%k;k 8`tJٴu~4EnWWSr5/m証œfrn4׭úf1s2e!K,@ݹʗgTyE+J q64/n3xi#bsK},N<;э ?ѵi.jYCQBHVNZD_T2ᾯ @lϡ{6D;vi!`eGW>zG,kȦӠx5g\4`:H8yj0b|Bq ulh[JTbv0b2@-R8pn-R#U/7k#[U u>x3E7Fn5[ VaEǤ*zd~vaoyJuO. ՝ߘ?;=ubPݸBHKX(W,LEg0}jc;.FQPT|W ˚UFjڨ*ftl*uMUuᝊn%F^N@,yer,.’ZKMj 2q (1zUT5wn:){{a^T`W4FQEU^^h\%tYC{ W+5o_-8EXpscTitk- ,WrFҌ*OHj%KOMO](HqCjغ`QC5Fy\)(6x_|hD@Sqvh;к}j4u*0 A/*$qćܔt:#ME V?l,ndpՏuJhmjp,kAIkNE\T5 y')ތj])z 2T-H}bp۾PqkeJ '|pR7:Ɓ2'LE۹f{cbhVK@1uB79F˹3sGu궜Kl,Ub1nlH1'=JS!F,:Gs*Q( }#vY8F\Fs*jMxZ7AOԄ?$׊tKƎ RH>i䙰9!!c}ݼELNt /':TCu0zpBZt*H4Vr(QRw5FqPރRKOuʞ 6AяQ(q:Jf:G˟╫ 6*jh(Q%qKci'X]!HOʽy͇AqY/r5s팶;Ÿie9JިA$ Tv҆ ɵ ɵ C,o|dm]DdPy{ƻp /60qz_O+t `V2d=8XdP%C$bKp¤c4{W۞X`% mDn|@; j6ٵUvX;GNrO=/iHnZlcWm[ۦ[#?Yҡ@jnzpB.smִq}U*I+;=ݺT݌efAwRRM:^C[k}fӒj8vnJtӦuneԂTP5*K| V IDAT2$dQ)E*ْ%:hʌV4m1,m&HK,H+ QPje.XDXv'ԦBMӃh9遅CzR$vχؗTF~Sz7we~_juIiokE{A y ތA]RXKZJS@ ܕ{;Fvq]H~,SV</>5K't _3ĺT1/_ 1|lj%t% \N.X'WG5T7ҽ!tg*8iA*FlgPIXea]+Bn|;X*BI) unJ̴J ~r3R \$wKm*yx̋ny,`KiFTp ⾚n[}s\uNJzgu@@nҁXSIҞhMKڕVPd>R,@(Tf, mydisZv`%'.7 6 h;{ٷffQϤ|&I;@qGrM_2N&=֡uZz$:fmvww\?1ʫ[?Luy&89y*XYk E۵fTU V2<rɤ( ^t{6-?Zzk(zDŸqHJ>gp cB>5] @$@P#Wo3u9WylWpB3$~qvڢ:B?9<5YP /Ghd]#䉰~vlïÂ]$Q W޹wJ񷘳G6lQp`> #+Wߏnu]_䫀&uv E8TqG^Ҡ6cx@70ʁ-{R7kt}oߪ2Z۾q^`E|l[*?~]fO(nVG% @+j\zA4v((Y(YF+MNx]*B]Wpg.]n@GEb _Kngߙ+ΡuLћu_3sC 7Tq$eC:B*ZY\p~M yh"㝇;^_a%PpP|sЎ_7}⒛[Z)y?  ~4oHQrsMdƽKJ/oK/bC* o* ;5-39QÐsb0&T.ܑ]c@DLɎvF:f {KPv_Sg164F,AQ{ `M炐9|pD_ҕ˯\+hEϺ TCk(j Xkx<ʤlnt/6a˅(:3ϴG#-߹L% /%րj@wIz/(̏Y<.%b/r%˷_nZ@ʾ4{Vx̉?MݰM9㼴7.׹5KtFpƸR@2{3[I//%`çٺT1eIK0;2.JNt$kOzxi4ch`gk藖#]k,}WS _'/͒ %q3;st6:ni B' 2^K4'KznA[eon jUcm/7v&.x;Es"ǻ3kqo `uObp-lT<mct1v8DdiR3V,,Yt..BKG}c܂0= 䠬s| StH)`4u醫΢"tgo bi5 `Јb؄n><Rٺ>d1g?m; r/aБƽ&5\m+x %mU̡]3 s>lѩ5s(?:.тr3Leӣ>gFxna ;}Gj>9|PL f: yD{t]Һtg?IP(qc ܨ<$&m{',n# ٸl:M5zFO[Q e5ös(BUz-[Q7CRW)%"`Ҳ[Xog$SU";ۛ?iw0e qS'5jI}`h(*i+D"w[\~T5TR;VɩjtÉ{6,_4i Uay@d߷}s{Ӹ,#BҲn,-HQT dD*@**X +qptNݵ<VQFU%Ѐ:ǣ׍!]p^ @ӥoɖ( uph5JD*sۜ;6p e޹l[(Ue =m!mNq>5KI|e"m~W*5X|<n^Vlg w~Z _cjYgET_Q!Xn}V:Sc17{"J  FU5m)f\.|BlXn؍z%m\.Й OSh %L+6DdGZӫEf#46ݵp zN!cB8OqjyUB %B!Yc,&+WwG-8i"3Hi~CݝtV僣xfn`%bvm;t^A,T~çк@\ema];j(NBz꣓[RxsgϜn]1/uEKz:+$JƙmW8Vmх(YïUDicptnan~Ν=fM͊:V~VJUU坺jePfO hu_h;k B̼]T^Inu=Uhi BH)׃K57LnnfEm1S_Ɉc|.BwOk~cE,cCZA"BMUzΝ7r3JKKRoaAtlB}QP)]jhpQJ{P /T,YhmӾ %}A ȁZYqӦ/m?8}wl?,z^cѤ쬤$%sx$횄u;{iI(Ң^,#v]9q5#kDҝd1%ohwaQ\ꮆE6eqC`TLh~.1&Lɝ$7{Ꝍz,WodL"f81AMp4Q NCo?zaQ ϓITW:oSNX`X'|FurKmup0," u:Lͪkp*X}G8'aY07D5FM /=qsXVfnra+u B]ھFlN{âk0~g3xzQ>Ņ_/߆_y̰HC ceX }.w Ka= ؗ, ^qpZ(aa$n*/'/}l];ho=j|u9G#!^w96Xa˹5tizyX4p 3nR|ɣ^c. 5 >,evya-6G+7뀄>8" MuշQc׀ߤ^(K/}w8魊⚸6'u\)opa+eTEjvڼ_5 Y{/D%zIYψqIV8 ˯ڶ|ҹOuq%4˸"+ڱ9e^Ɔ LͶ v b*] VR B8!bXŨ'EĝKcMe/oNȋMqz=0,F=9,`e",bYӹ4`vn77L{!gCE}[ɐ 07d1,9yrU;"&F^=/WB&P{T( +R9VOo^l@D%E- hH||틍Nq(ո]#|X>.cnz輼-K #p}tU:@r v6v2ԶT@^X`<"Nدwʍ |R?>*;yL[p7ڂ`" Rf`Ÿ~|ku0xӲ)/ US`u~Ju vr"Bwd9;aaXztXJ6QAm]sZ.aa O8Srp^ =KAD6! Wj5v@nvf]» I"eAy;S?, 5 N[?7[#tHȇ[=zđB1Tȭxw>UP^ؗ9upwiƺ4l P^tY)sY7Ը\Us2M_/NXhe9 $P`J ˧(]&w '?LĤsǯ0>X!97nhZ('W'D{4~Z 1S| .tE8Ϯ=r!tÑAw7 ܸ8!t;NGw#~qx^_abES й٩~Y@8YCb4pxp݌)z3n#^ }㍿4uJo[jX?OKf~ Us9]kJ@xY6jEo'UaUq?JT5ncg}GN:` T5^i Dpvs% UbW(m'cX,,bŹ;;_ֵXM ŞO_MRK:t4_=3,Cܦ6#3 O{& K  6EyY qD2`߁wy*kң74cʳOs|]+HV+/UE_i'Q"HS` 'pir~wE#FDMU#w Jk?Zgl|v5B'?9t2_=-y e')WWmk->p?ӛ,>-oޡP8bYO# mNbXZxXV\#$°tE$o`܈S 7f$ L9rjxؐ+ &Q~+= yQEa"rӐ cMﭐ [RYSxz0R +GWr.ѯs=ƽܹysZ"JOMj@#c"5?ؕXpA=6SgGk_2:|1O]_ x$>;8|7{\DMd~.tr :;MMer}6/0pX>wZ̺R@pV`+3~~1i3`pE?P5k6) +M^y~:+>;zQW'򙟍k:u&W70녯Z{ܚ#mEeV/~\G2tWr7lR??lVGN Ur–!޼62tW&꡸嬏4&xcXZhXLe%kf~2BkaqX ca=7,{^(hfei&"jIe_Kkk3lNt/:wt--[5ReS^\rK:Y.\P+*@\дQ6ipE׎vWIgYY˞Z֨J5$`0rZKHtQ1M6`˾[-ű́6GZa0{ Hi tAX^\׺b}O~v ްqZ°7,m,̖R K+ϗCDwӫy?65к4u u.ʫ`ʒzw( A}:fv*0&EAwj4 ZDϫh 0(<՞m tš][޽% w`tf4uzruSZ:^Z BǮ5ّ]$fY) @ *0ai 8WY^!nIǷva${ KVVi=SIIҩUciADRWu\ʠ {с]L "°Pi^cX:J{1,LZOZN& K䱃$^Mm "1,D]#8HADDDDDDDKTzr2nGU"1,Dje\ADDDDDDD""""""" 憨F{޺'nKXܹe&NU.JHPjJUݺfb(M'n/AbXDt(\{`6@.'zi~=媈8QW{7t/Sdք]pmwsl@ sk[D5όڼ0Ȕ}{::drWBkc%ͩ`V~6gҭj۱)5w2l!kȺqj^AnuS?)mSGYQ^;5m_AAnW/ ^޲QQYgVSnP _1y\DywAyuYKKGT7ϞQV (/}0q|A;/':\+}fMiUU n^>ODJbиm w˸G.{C3x</FEU^ .񱱊*,…i5bI'k9D;#3#2fv cK?>9EP_1 ZQ݊w#v zjՐɯql[D* vrD[v+#J#ij3{A~1B(:}Mx]x%tJ؝*TH),¢yťU.-zȺyR Kg.0, Kg`YsLZ(9#ժAnJmN};fnNM=1joj}J/y`Q98e{mZ$h[-Q5/2NY͇QC3B$ y: !j27)q mb)8mT5 7鹗DK_Nmzry񱷸&4W @?\~a+eT Z ʺ<Hk>|{/d%af 6?#~%[ lr6/rkWI>Maĕ Ҍ/Zܟ~ή0t >5?OjsG˷#PT@>b}1j/*e?831a{eԗ|sZ*ǪͫQ (PN~6Q d}q j*R.vbcaR;J + ckprCm9DH?29\L/;%~>VUVY/wQa"rŽyNU&ږ*x D6r!_2t@eEŮԏ *xbń<> (?vŴZ]T-wHK"O5v`-?[u1U6^Xwج.m٫Z6 o=-ݾ?k\Oʿ4إx%t|IJ(R %ukO:aaX%4qvYaX`ٴz'+\EPl)L~7MHG/{l #ߙ<~xP 󨭿{78MT`ء| hiܓG/(K*w3XU }S7}yf3g<60S`N7I-0iJaz]awz}y$_ǛǰqqB|?8h+0g-^ jaOc? ΜKGͮo\#B=$w7 I5+HT&ߍA~x NgO6}jfY!;vHղQxa6G.{siۆD=7 f9^ vJ( K'bX8U1, Kږtґ WZ(E߫h0RxAPGh\T ]?ԻO{.ptcseƛfLdtg@oݘBYmT-'Cv|iE7w5 {bԥs߲i7~A' C; W*qT;rҡ`P@g|AgOk'xgw<3/^fF]Y LqbW(mg[̴(cLW {>a|yx%tJ=*TH.,aaXdjư0,"jS?E9+}yw?>w_YʳO>YCMv-BJǏG:X;| coG¡Cnir~3]\i-ˏ6@OGƏ6пVّumƜҢIZ#yHDyx>]qeW| ƛ 9w7$Q1n8-*dK)V-s-1j= j$+aK̰۫8a`i{gkR1, K;diOoX/̩+̩Z\{RNբOj9|+<-,;aa˛nϮPmXIwy%ugG(k#a,r1b9hݸ5PY[b]tm9z'ףkh׮p?4q+,:XW|h񭫝_1wChNC(PۣخZYcdJԒb%$[$ð- KjM 9{w\E(-su4poXA%]"/)^n{M8l#}g93Yǿhn9C\skٜH=mV-}Zx%t|IgJ(R KaXH0,]60, K;%d-|狼Wko|gh 1'y+ V5QSjXaqck-*.iZx]V*V|eђbBd.muRJ3mg\&oJmkޅOrd^Z𴼽*dA0?Lnq"'pgz䔥٧>Z|̶n 5#??7IV '˷m]4t43)@n=DMc-Ɔݸ^^iqRoHr /A=4fwuQ^]` ;g*/mWpj`9>G%J TBbXmu rQY6^&[UT4nfd2, KgbX:^0,ΖZb9-y^sYyCٸپLҼ\"TNY]H IDAT} D9v0`v{&/qfE=*P2X/\TouPo?a\M_\*Cgnx%w&5;fEjzV5''l)0kI?_kkY/|r qkLro{s*Cgn\~em_Kjoqloiͪeu#9Q ^P:P*$xJyeۍXT-0, KTX:^1,NZ%*DԒi"P>g,ٜ<?yO _u;Zj˦2]󰼋_7Wъʾ2njע["gieJXb>zTn}zhhI=k5ZXg$a`yCdM մM^0?%a}ֲV5R 9~4 ܸGyƒ7g/]~S XlVi/`qLȆ~ :.Mu]eCju<4JCPG_JI~~QP]Z% eQj<: w@g5fDGn(޼y` 򴵨j\=~^+ 4uaбkvM{(57Jg@YCU˶8FFWB'k[X ťvzvX:6U R x B'-|mN9 (b1DB'iw%ץPoY[ rt{ޞANz * nP9C N\D7nPUZrJK=JK=Jpޥ3.4 7jJN!VVyӹkDnUTj52uN+ӱJrHI]aWUU.98wR s|`ZUOt`W;B'?puJKaViVViVVi&$DDDDDDD$yDjlcXİ9a! ADDDDDDD"""""""<^B{dDf~DbXİ9}qqI;8HADDDDDDD"""""""Ņ_/߆_yl_UC. ,zVDKagho[7a ;8 >?}i}[]ݞaVU78Ñ5\<B\xeUnXʎmC{1Vb jS)'l\OB {$qU9q~c`k`PL_YK&zi럎@,PܵNtmwsl@ sk[D5ڼ0Ȕ}{::d b闞 j@ z2Ek?9dTNeen0?cI9,ݪv1,=;8Uj}99%:X3+$h OJ̽͝?7@%[0zn\ZW.:2z[wdfސyC"B̧P._3*҈ĎrkZ-骓&6>)g#x&_׍V/8Ʊ?5ul E#˺pa{zfZM90rC\Z?-7pb~|"sc cϋݩi r<bGv+n* o\FZ_jU{@8%;_?#Z@1aN0v#%iR 1\[(C(°0,k3,'sdwfr5 54ƌTumFf‚ܪڛEA}=/"Ӹjkq'g P掎n3NŪ.,W\]Eײ;b̰tV°DdMDAVT646*pa͌6dղEIōK g\g]|)+67EY{eI|:!46Rs0 _&Jm JD'k>|{/d%afL+mq5=t$@F\ ,2hjޢrv&du_h|/콜26,HX`jwhXDܹ4TQM愼ޔ_ `X )mrq&_4q !)gvAcZ{~Lnk;1Ji~K3-+^9Sai;8&*,/naX@CMEn_l5 hLd>W;ic)@L*+*v~|T{Z}ָ8iK;OLVFPk5w~?YiΎ 7l4_eҩm8S ;y>OLJAQ2T)iO?>еXia?;0x)/ US`u~Ju KUs&*`p!U (aG|lQ濒`c=*u;P>byQ~}Í\Ӆw8_l6A㻊-aiD°Dd"L̺'=Ǐ|/NlAz'+\EP {4~6(8sŸK/y4G.{si0ۆD=7 ۃr@),Peir|WX0rܝ6Nbi{+0g-^ jra>Xq߸ٵG.{82H`Tsi ̉󨭿{n7wN*~q8@_a"t K%=0<57ć-KТj]۫ۉ`X7jcnj0_u5R йhkJ@xY?{K> :@pr-cGJۛH/,=MKuh2, 3쇥`L?4ʪ^0MW\gZحuk:bz[X]-wˉU|&Gwǜ\4bDlOPPQ^q|2#QUՏ\i-`d*ݒVUi}^Eʥ'U-ohU;fvjQ|&G9|S/qeW|mAV'GKo 8p+k`ℸ RKŰ0,68V)KsIٚTeq*mj (ƪzUX]cYQ6Ήy8dbIM1f9n烴#-K-T */̟oWt%@Iwjߌj7ˁkWh|<.qk?29-k7.488;SB U]gRz5WUvweПW?u6ƪ-aiGa!vp5)\}g?;~Ne-!rNg#Ƽ\Dœ" G.J5SW 0߸@;lY.q࿖l|<s`U nop\i{'iE4 k'Z[KVMN ↴ ҁfjq2}}L/_mPg`z.2amaS'FɤUY 16vT[]S%yy\/F5Re\j:Գm@U! ~l̳pmT/֬*̽Ot̆uVr6DgsکWLX^4@g1UQ:mLw `XG Wql 2% 6;6^.^k!"+Ue)?hP|pqL[ܸut1<»I׼ Jɼ/t[w2 ώlz8~7UfϖUh [x6˖# `pDc޿t0:YLbaQ+ƍ?qc&Nđ# iaaX:atcZ}uvHt j].+ʞ.3, K8,(=7 ǎsSPxbW `~1)]{$.-jԵWrmsCovնj'b<j>=..7/LL _,2d /-D5GpM#'Yisˆs1@-;pdPKӈPձn+c۶.:PsKٷK4L Ovzy$!A661HИ}>0]Dy[oLv3f(;^LaiNc}<°tbRjzpWC;Fݧ8E{(k?o[]CBk맿sh`jܪ:^quwS4f+zvX:^0,Ζ`hI͟{/Й^ݰI /p_Z6v)>;zQW:g'Yzۭg';駦#O5''l)0v_KZkonZY/|&y[sd{ۛS:s+kP\rG_Vx`ѩҒ$ad6âEhIJm>.aQYSֲ"Yk{ ,@t"VR^{_"lvvc׮f =x YТ["gi?w̓5v$Ǯmz񐀹GkJ*վQ=jU~{|`CPԋF(KA]a9[W޽$V?`~K,mΙKsߔi,컕 |qP9м|p}p]oc8:XLai_aaX:cal݊gQ]C< s~x WM5\M*W6{өXv 8X1VtFbXZai`0 HzW~z=lk8`cI-2A (y5-J=x)\-mx t;0ȳ4UEƱi>* ꪢ:W_?7 Լ]krqPYV =sv;NaiCaTAh àT"<==]l5Zfdڋji~QD(**7)6(dnM7S<>gsxkU|Z fh4?JEPv]KR%ɰt ňaqCaQGZ6'c^U1,1,F>,VADtw5o,/ @Sj_t^fywIHIkIJHIw;8œœL;qݸViVViגVEViVVi ]:uͲJMQC<=CھjޑViWJSVViIJNaX?iU?i)[B'?0JK=JK*JK=J{6DDDDDDD$y """""""%*DWӅmDdB A QW"""""""fLF$QMP5x\ں_CCpJںVSiq8ΩDk%U4" O eDd.$f"Sy}ef^Y߬v8 Qu0f_˼`o;]D2 Ӎ't2n||Fy'[z̐!Iw$mܠ'Z?a=C`Jn.IDATaYVpL4nT 'f}#3`|sӿU 5iAUcm5kb.,$}v^'UAW{WL_.aghὫbWEŮ}d}t6ba-Wp8i,"]P/7921Vc-Nmu&zދ앂.O菆\qӽa#,:͒.awIg#{CƕuGO6" ݟ)cxJ|Up A-Y5O׿K^x;ũY_>lc˳5UupཱུE ;o,z>ŹLSq#ԨomݜDDfOtщ XIDo9|A5U*sfzw;]Dx;dgǵ"""Үpu5z:.ޔĄn <fc{jٍZx$+sBՙg7DD^4OݚWy6|^ pv/K}(btP-*Tٍ RK9fI7D$ަ~+ϚD%fݾ!m-!֭yEӈAD|4."Պӣv E醈5U~1՗?  M࠺"v]iǴi$Rrr99lcޯb/Z])dUoiӍnL 18^yղ٣cc[w=ZLoaTi0hR}*X7^c5ޓ܄3f1'0>iblZS㵲?~WC'mASvֿ#)JٔC'*v~ :( gquI?/G7*Q\ybYOګDvY,Uz4E7d<}蟞5r6zx8l}Z%|}KUSV H+UGg  $۱렦=:~IWfOګ}R'lCT!#::/(xIĘ}b6snzոup2HA4urv#ʢ%ߗ*w<Р I}vSy*y_T_]SjL@DS0TR"{H8ԝGg9Z$֨c?$n֑DTG߮ؽ<Ҿ}Iy?]Ǻ9j}46G/M^n'8?e^hȕsw|Ie,8ȓ;&Mg^݂sxK̺a3O; .Ϊ@іYnݢtO,Vbeĵ>g]1}#1*fVzIl'q~]7o}{KQzrTYWΒ!n\qNDb½sĆ%+Ut@@5vl7a;:m3'=B2 wqpu(mTXo>^r6;4x,j\+ED/sg[ǘj;.{?gnj,h^qo3D&Ɗث>u;,OոTp +IDD̺ ٻNqEO8oN}8=Ga nQX^39%?].|"]}(V\ؔIo}IDDnْ})Ј4x5nw[7:?QN>#EԒ/g,*Pqt /1rL٦|Gťs32/jyt,ѱ9fѤ*ձh7*QR9=7r^}G%'\NsVfI%":^#=&&v\s]VVpոhm`>û$rhLy "2GB ,WEXչֹAa݁V" \wFg289hOT )L b~~!UR` are now accessible. * Instead of static pages, pages are generated on the fly, allowing users to drill down to find out about a :ref:`specific host `, rather than only having one huge page with too much information. Installation ============ Quickstart ---------- :ref:`appendix-guides-web-reports-install` Prerequisites ------------- * sqlite3 * pysqlite2 (if using python 2.4) * `Django `_ >= 1.3 * mod-wsgi .. warning:: There is a known issue when using an sqlite database on an ext4 filesystem. You will want to remount the filesystem without barriers (-o barrier=0) in order to speed up the operations of the database. For more information, please see http://phoronix-test-suite.com/pipermail/trondheim-pts_phoronix-test-suite.com/2009-March/000095.html. Install ------- 1. Be sure to include the specified fields included in the example ``bcfg2.conf`` file. These can be specified in either ``/etc/bcfg2.conf``, if it is readable by the webserver user, or ``/etc/bcfg2-web.conf``. Any database supported by `Django `_ can be used. As of version 1.3, `South `_ is used to control schema changes. If your database is not supported by South, any updates will need to be applied manually. Sqlite is configured by default. Please see the :ref:`reporting-databases` section to configure alternative databases. .. warning:: If you are using an sqlite database, the directory containing the database file will need to be writable by the web server. The reason for this is that sqlite will create another file for its journal when it tries to update the database file. .. note:: Distributed environments can share a single remote database for reporting. 2. After configuring your database be sure to run ``bcfg2-admin reports init`` to create the schema. 3. To enable statistics collection in the bcfg2-server, add :ref:`server-plugins-statistics-reporting` to the **plugins** line in your ``bcfg2.conf`` and restart the bcfg2-server. A report collecting daemon should be run to import the collected statistics into the backend. Please see the section :ref:`Report Collector ` for more information. Detailed installation instructions can be found :ref:`here `. .. _dynamic-http-install: Apache configuration for web-based reports ------------------------------------------ .. note:: Reports no longer needs to be installed at the root URL for a given host. Therefore, reports no longer require their own virtual host. In order to make this work, you will need to specify your web prefix by adding a **web_prefix** setting in the [statistics] section of your ``bcfg2.conf``. .. warning:: When running with SELINUX enabled, you can have potential problems with the WSGISocketPrefix. One solution that works without too much trouble is modifying your prefix so that it is located in a standard location:: WSGISocketPrefix /var/run/httpd/wsgi An example site config is included below:: # # Read an alternate configuration file # # SetEnv BCFG2_CONFIG_FILE /etc/bcfg2_testing.conf # # If the root is changed update the static content alias as well # WSGIScriptAlias /bcfg2 "/usr/share/bcfg2/reports.wsgi" WSGISocketPrefix run WSGIDaemonProcess Bcfg2.Server.Reports processes=1 threads=10 WSGIProcessGroup Bcfg2.Server.Reports # # Manually set this to override the static content # #SetEnv bcfg2.media_url /bcfg2/site_media/ # # This should have the same prefix as WSGIScriptAlias # Alias "/bcfg2/site_media/" "/usr/share/bcfg2/site_media/" Options None AllowOverride None order deny,allow deny from all allow from 127.0.0.1 This configuration is suitable for use with the default installation from an RPM or deb package. At this point you should be able to point your web browser to http://localhost/bcfg2 and see the new reports. Upgrading ============ 1. Convert database config Run `tools/upgrade/1.3/migrate_configs.py` Beginning with 1.3 the database configuration moved from [statistics] to [database] in `bcfg2.conf` and `bcfg2-web.conf`. The old settings will be accepted but a deprecation warning will be displayed. 2. Replace the DBStats plugin with the Reporting plugin. 3. Migrate historic data. Run ``tools/upgrade/1.3/migrate_dbstats.py`` The reporting schema is now managed using `South `_ instead of a set of custom scripts. This creates the new schema and imports all of the historic data to the new format. .. note:: After the database is upgraded all of the old tables are left intact. To remove them any table starting with **reports\_** can be dropped. 4. `(Optional)` Run the :ref:`Report Collector ` Add "transport = LocalFilesystem" under "[reporting]" in ``bcfg2.conf``. Restart the bcfg2-server and start the bcfg2-report-collector. Configuring =========== Most of the configuration is handled through the ``/etc/bcfg2.conf`` or alternatively ``/etc/bcfg2-web.conf``. An example using the defaults is listed below:: [database] engine = sqlite3 name = /var/lib/bcfg2/etc/bcfg2.sqlite user = password = host = port = [reporting] transport = DirectStore web_prefix = file_limit = 1m Configuration Sections ---------------------- .. _reporting-databases: database ^^^^^^^^ If you choose to use a different database, you'll need to edit ``/etc/bcfg2.conf``. These fields should be updated in the [database] section: * engine * ex: engine = mysql * ex: engine = postgresql_psycopg2 * name * user * password * host * port (optional) .. warning:: If mysql is used as a backend, it is recommended to use InnoDB for the `storage engine `_. statistics ^^^^^^^^^^ .. deprecated: 1.3.0 * config: The config file to be read for additional reporting data. This is used to restrict what can be read by the web server. * time_zone: The django TIME_ZONE settings parameter. * web_debug: Set Django's DEBUG and TEMPLATE_DEBUG settings. This is known to cause memory leaks. Use with caution! reporting ^^^^^^^^^ * transport: See :ref:`Transports `. * web_prefix: Prefix to be added to Django's MEDIA_URL * file_limit: The maximum size of a diff or binary data to store in the database. .. _dynamic_transports: Statistics Transports --------------------- A transport is required to pass the data collected from the bcfg2-server to the bcfg2-report-collector. At the time of this writing two transports are available: * LocalFilesystem: Statistics are written to the local file system and collected on the local machine. * RedisTransport: Statistics are sent through a list in redis. * DirectStore: DBStats style threaded imports in the main server process. Future transports will allow multiple servers to pass data to a single or multiple bcfg2-report-collector processes. New installations default to and should use the LocalFilesystem transport. Upgrades will use DirectStore by default in the 1.3 release. .. Note:: If DirectStore is used, the bcfg2-report-collector process will refuse to run since this method is not compatible with an external process. RedisTransport ^^^^^^^^^^^^^^ This transport uses a single redis instance for communication between bcfg2-server and bcfg2-report-collector. Multiple servers can write to a single redis instance and multiple report collectors may be run as well. An example configuration with the default values:: [reporting] transport = RedisTransport redis_host = 127.0.0.1 redis_port = 6379 redis_db = 0 bcfg2-admin commands operate slightly differently in this mode. Instead of querying the database directly, rpc commands are issued to the report collectors. This only affects the minestruct and pull commands. .. warning:: At the time of this writing the version of python-redis in EPEL is too old to use with this transport. Current versions of the python-redis package require python >= 2.5. Usage ===== .. _report_collector: Report Collector daemon ----------------------- .. Note:: This section does not apply when the DirectStore transport is used. The bcfg2-report-collector gathers statistics from the bcfg2-server process and records them in the backend database. Options are similar to the bcfg2-server daemon:: -D Daemonize process, storing pid -o Set path of file log -h Print this usage message -E Encoding of cfg files -W Web interface configuration file -Q Server repository path -C Specify configuration file --version Print the version and exit -d Enable debugging output -v Enable verbose output .. Note:: The bcfg2-report-collector is not set to start by default bcfg2-admin reports (command line script) ----------------------------------------- The bcfg2-admin tool provides management and maintenance capabilities for the reporting database. A few useful `Django `_ commands are provided as well. * init: Initialize a new database * update: Apply any updates to the reporting database. Unlike the syncdb command, this will modify existing tables. * purge: Removes unwanted clients and data. * -c --client [client name] - Remove interactions from a single client. * --expired - Remove all data for expired clients. --days is used to exclude clients expired within n days. * --days [n] - Remove interactions older then n days. If not used with any other modifiers, all data older then n days is removed. * scrub: Scrub the database for any orphaned objects. Django commands ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * dbshell: Connects to the backend database. * shell: Starts an interactive python shell with the Django environment setup. * sqlall: Print the sql statements used to create the database. * validate: Validate the database against the current models. bcfg2-reports (command line script) ----------------------------------- bcfg2-reports allows you to retrieve data from the database about clients, and the states of their current interactions. It also allows you to change the expired/unexpired states. The utility runs as a standalone application. It does, however, use the models from ``/src/lib/Server/Reports/reports/models.py``. A number of different options can be used to change what bcfg2-reports displays:: Usage: python bcfg2-reports [option] ... Options and arguments (and corresponding environment variables): -a : shows all hosts, including expired hosts -b NAME : single-host mode - shows bad entries from the current interaction of NAME -c : shows only clean hosts -d : shows only dirty hosts -e NAME : single-host mode - shows extra entries from the current interaction of NAME -h : shows help and usage info about bcfg2-reports -m NAME : single-host mode - shows modified entries from the current interaction of NAME -s NAME : single-host mode - shows bad, modified, and extra entries from the current interaction of NAME -t NAME : single-host mode - shows total number of managed and good entries from the current interaction of NAME -x NAME : toggles expired/unexpired state of NAME --badentry=KIND,NAME : shows only hosts whose current interaction has bad entries in of KIND kind and NAME name; if a single argument ARG1 is given, then KIND,NAME pairs will be read from a file of name ARG1 --modifiedentry=KIND,NAME : shows only hosts whose current interaction has modified entries in of KIND kind and NAME name; if a single argument ARG1 is given, then KIND,NAME pairs will be read from a file of name ARG1 --extraentry=KIND,NAME : shows only hosts whose current interaction has extra entries in of KIND kind and NAME name; if a single argument ARG1 is given, then KIND,NAME pairs will be read from a file of name ARG1 --fields=ARG1,ARG2,... : only displays the fields ARG1,ARG2,... (name,time,state,total,good,bad) --sort=ARG1,ARG2,... : sorts output on ARG1,ARG2,... (name,time,state,total,good,bad) --stale : shows hosts which haven't run in the last 24 hours Screenshots =========== Grid Overview ------------- .. image:: GridView.png :alt: Grid overview :width: 850px :height: 530px Detailed Overview ----------------- .. image:: DetailedView.png :alt: Detailed overview :width: 850px :height: 530px .. _reports-calendar-summary: Calendar Summary ---------------- .. image:: CalView.png :alt: Calendar summary :width: 850px :height: 530px .. _reports-item-detail: Client Detail ------------- .. _reports-client-detail: .. image:: ClientDetail.png :alt: Client detail :width: 850px :height: 530px Common Problems --------------- .. image:: CommonProblems.png :alt: Common configuration problems :width: 850px :height: 530px Item Listing ------------ .. image:: BadListing.png :alt: Item listing :width: 850px :height: 530px Item Detail ----------- .. image:: ConfigItem.png :alt: Item detail :width: 850px :height: 530px bcfg2-1.3.3/doc/reports/index.txt000066400000000000000000000014151223671746500166320ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-reports-index: The Bcfg2 Reporting System ========================== Bcfg2's reporting system is its killer feature. It allows administrators to gain a broad understanding of the configuration state of their entire environment. It summarizes * Configuration changes and when they were made * Discrepancies between the specification and current client states * Clients can be grouped by misconfiguration type * Configuration entries that are not specified * Overall client summaries according to these types There are two systems, the old system, which builds static reports based on a series of XSLT stylesheets and a new dynamic reporting system that uses django and a database backend. .. toctree:: :maxdepth: 2 static dynamic bcfg2-1.3.3/doc/reports/static.txt000066400000000000000000000070311223671746500170120ustar00rootroot00000000000000.. -*- mode: rst -*- .. _reports-static: ============================= Bcfg2 Static Reporting System ============================= The Bcfg2 reporting system collects and displays information about the operation of the Bcfg2 client, and the configuration states of target machines. Goals ===== The reporting system provides an interface to administrators describing a few important tasks * Client configuration state, particularly aspects that do not match the configuration specification. Information about bad and extra configuration elements is included. * Client execution results (a list of configuration elements that were modified) * Client execution performance data (including operation retry counts, and timings for several critical execution regions) This data can be used to understand the current configuration state of the entire network, the operations performed by the client, how the configuration changes propagate, and any reconfiguration operations that have failed. Retention Model =============== The current reporting system stores statistics in an XML data store, by default to ``/etc/statistics.xml``. It retains either one or two statistic sets per host. If the client has a clean configuration state, the most recent (clean) record is retained. If the client has a dirty configuration state, two records are retained. One record is the last clean record. The other record is the most recent record collected, detailing the incorrect state. This retention model, while non-optimal, does manage to persistently record most of the data that users would like. Setup ===== In order to configure your Bcfg2 server for receiving reports, you will need to list the Statistics plugin in the plugins line of your ``bcfg2.conf``. You will also need a [statistics] section in your ``bcfg2.conf``. You can find out more about what goes there in the ``bcfg2.conf`` manpage. Output ====== Several output reports can be generated from the statistics store with the command line tool ``bcfg2-build-reports``. * Nodes Digest * Nodes Individual * Overview Statistics * Performance The data generated by these reports can be delivered by several mechanisms: * HTML * Email * RSS Shortcomings and Planned Enhancements ===================================== When designing the current reporting system, we were overly concerned with the potential explosion in data size over time. In order to address this, we opted to use the retention scheme described above. This approach has several shortcomings: * A comprehensive list of reconfiguration operations (with associated timestamps) isn't retained * Client results for any given day (except the last one) aren't uniformly retained. This means that inter-client analysis is difficult, if not impossible We plan to move to a database backend to address the dataset size problem and start retaining all information. The move to a SQL backend will allow many more types of queries to be efficiently processed. It will also make on-demand reports simpler. Other sorts of information would also be useful to track. We plan to add the ability to tag a particular configuration element as security related, and include this in reports. This will aid in the effective prioritization of manual and failed reconfiguration tasks. Capability Goals (posed as questions) ------------------------------------- * What machines have not yet applied critical updates? * How long did critical updates take to be applied? * What configuration did machine X have on a particular date? * When did machine X perform configuration update Y? bcfg2-1.3.3/doc/server/000077500000000000000000000000001223671746500145715ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/admin/000077500000000000000000000000001223671746500156615ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/admin/backup.txt000066400000000000000000000006451223671746500176740ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-admin-backup-index: backup ====== .. _Samples repository: https://github.com/solj/bcfg2-repo Create an archive of the whole Bcfg2 :term:`repository`. The archive is stored directly in your Bcfg2 repository (e.g. ``/var/lib/bcfg2/``) and named with the current date and time:: bcfg2-admin backup A backup is recommended before you start using the `Samples repository`_ of Bcfg2. bcfg2-1.3.3/doc/server/admin/bundle.txt000066400000000000000000000014171223671746500176760ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-admin-bundle: bundle ====== For a list of all available xml bundles use ``list-xml``. ``list-genshi`` will list all available genshi bundles.:: .. code-block:: sh # bcfg2-admin bundles list-xml # bcfg2-admin bundles list-genshi ``show`` provides an interactive dialog to get details about the available bundles.:: .. code-block:: sh # bcfg2-admin bundles show Available bundles (Number of bundles: 4) ---------------------------------------- [0] motd.xml [1] snmpd.xml [2] bcfg2.xml [3] ntp.xml Enter the line number of a bundle for details: 3 Details for the "ntp" bundle: Package: xntp Path: /etc/sysconfig/xntp Path: /etc/sysconfig/clock Path: /etc/ntp.conf Service: xntpd bcfg2-1.3.3/doc/server/admin/client.txt000066400000000000000000000015331223671746500177020ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-admin-client: client ====== Create, delete, list, or modify client entries. :: bcfg2-admin client add attr1=val1 attr2=val2 Allowed attributes are *profile*, *uuid*, *password*, *location*, *secure*, and *address*. A full example is shown below:: bcfg2-admin client add laptop02.example.com profile="basic" For more details please refer to the :ref:`Metadata section `. With ``list`` the file ``clients.xml`` is parsed and all entries are shown:: bcfg2-admin client list server01.example.com laptop02.example.com This is useful for a quick check after adding an entry. If you want more in-depth information about a client, ``bcfg2-info clients`` can provide that. Please refer to the :ref:`bcfg2-info ` section for further details. bcfg2-1.3.3/doc/server/admin/compare.txt000066400000000000000000000005021223671746500200450ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-admin-compare: compare ======= Determine differences between files or directories of client specification instances.:: bcfg2-admin compare If you want to compare two directories recursively then use ``-r`` as an option. :: bcfg2-admin compare -r bcfg2-1.3.3/doc/server/admin/index.txt000066400000000000000000000010441223671746500175300ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-admin-index: ===== Admin ===== The ``bcfg2-admin`` command provides you an interface which allows you to interact with your Bcfg2 :term:`repository` in an administrative fashion. To get started, run ``bcfg2-admin help``. You will be presented with a list of different *modes* which each provide various administrative functionality. Available modes are listed below. .. toctree:: :maxdepth: 1 backup bundle client compare init minestruct perf pull snapshots tidy viz xcmd bcfg2-1.3.3/doc/server/admin/init.txt000066400000000000000000000022261223671746500173670ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-admin-init: init ==== Interactively initialize a new repository. Most values are automatically detected or a default value is provided. :: bcfg2-admin init Store bcfg2 configuration in [/etc/bcfg2.conf]: Location of bcfg2 repository [/var/lib/bcfg2]: Input password used for communication verification (without echoing; leave blank for a random): What is the server's hostname [conf01.example.com]: Input the server location [https://conf01.example.com:6789]: Input base Operating System for clients: 1: Red Hat/Fedora/RHEL/RHAS/Centos 2: SUSE/SLES 3: Mandrake 4: Debian 5: Ubuntu 6: Gentoo 7: FreeBSD : 1 Generating a 2048 bit RSA private key .....................+++ .....................+++ writing new private key to '/etc/bcfg2.key' ----- Signature ok subject=/C=US/ST=Illinois/L=Argonne/CN=conf01.example.com Getting Private key A toplevel repository structure was created under the provided path. :: /var/lib/bcfg2 |-- Base |-- Bundler |-- Cfg |-- etc |-- Metadata |-- Pkgmgr |-- Rules `-- SSHbase bcfg2-1.3.3/doc/server/admin/minestruct.txt000066400000000000000000000005741223671746500206250ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-admin-minestruct: minestruct ========== Extract extra entry lists from statistics.:: bcfg2-admin minestruct [-f xml-file] [-g groups] Hierarchy of groups in which to place the extra entries in can be determined with ``-g ``. The ``-f `` option specifies the xml file in which to write the extra entries. bcfg2-1.3.3/doc/server/admin/perf.txt000066400000000000000000000011011223671746500173470ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-admin-perf: perf ==== Query server for performance data.:: bcfg2-admin perf ================ ========== ========== ========== ======= Name Min Max Mean Count ================ ========== ========== ========== ======= RecvStats 0.000378 0.001716 0.001367 5 GetConfig 0.018624 0.039495 0.023589 5 component_lock 0.000002 0.000057 0.000016 20 GetProbes 0.000523 0.000666 0.000591 5 RecvProbeData 0.002260 0.004550 0.002979 bcfg2-1.3.3/doc/server/admin/pull.txt000066400000000000000000000006221223671746500173760ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-admin-pull: pull ==== Integrate configuration information from clients into the server repository. :: bcfg2-admin pull [-v] [-f][-I] [-s] The following options are available: ``-v`` verbose ``-f`` force ``-I`` interactive ``-s`` stdin .. FIXME: No example yet .. A full example is shown below. :: .. bcfg2-admin pull bcfg2-1.3.3/doc/server/admin/snapshots.txt000066400000000000000000000001541223671746500204440ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-admin-snapshots: snapshots ========= Interact with the Snapshots system. bcfg2-1.3.3/doc/server/admin/tidy.txt000066400000000000000000000001351223671746500173720ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-admin-tidy: tidy ==== Clean up useless files in the repo. bcfg2-1.3.3/doc/server/admin/viz.txt000066400000000000000000000007171223671746500172370ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-admin-viz: viz === Produce graphviz diagrams of metadata structures. Make sure that the graphviz package is installed. The following command will produce a graphviz image which includes hosts, bundles, and a key:: bcfg2-admin viz -H -b -k -o ~/bcfg2.png .. note:: The graphviz package available via DAG/RPMforge has been known to have dependency issues. We recommend installing the package from EPEL. bcfg2-1.3.3/doc/server/admin/xcmd.txt000066400000000000000000000010411223671746500173510ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-admin-xcmd: xcmd ==== XML-RPC Command Interface.:: xcmd For debbuging the following command can help:: bcfg2-admin xcmd Metadata.toggle_debug Those two examples can alos be found in the :ref:`Package section `. To rebuild the packages plugin cache:: bcfg2-admin xcmd Packages.Refresh To perform a soft reload to reread the configuration file and download only missing sources.:: bcfg2-admin xcmd Packages.Reload bcfg2-1.3.3/doc/server/bcfg2-info.txt000066400000000000000000000121731223671746500172520ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-bcfg2-info: ================ Using bcfg2-info ================ ``bcfg2-info`` is a tool for introspecting server functions. It is useful for understanding how the server is interpreting your repository. It consists of the same logic executed by the server to process the repository and produce configuration specifications, just without all of the network communication code. Think of ``bcfg2-info`` as ``bcfg2-server`` on a stick. It is a useful location to do testing and staging of new configuration rules, prior to deployment. This is particularly useful when developing templates, or developing Bcfg2 plugins. Getting Started =============== First, fire up the ``bcfg2-info`` interpreter. .. code-block:: none [0:464] bcfg2-info Loading experimental plugin(s): Packages NOTE: Interfaces subject to change Handled 8 events in 0.006s Handled 4 events in 0.035s Welcome to bcfg2-info Type "help" for more information > At this point, the server core has been loaded up, all plugins have been loaded, and the ``bcfg2-info`` has both read the initial state of the Bcfg2 repository, as well as begun monitoring it for changes. Like *bcfg2-server*, ``bcfg2-info`` monitors the repository for changes, however, unlike *bcfg2-server*, it does not process change events automatically. File modification events can be processed by explicitly calling the **update** command. This will process the events, displaying the number of events processed and the amount of time taken by this processing. If no events are available, no message will be displayed. For example, after a change to a file in the repository: .. code-block:: none > update Handled 1 events in 0.001s > update > This explicit update process allows you to control the update process, as well as see the precise changes caused by repository modifications. ``bcfg2-info`` has several builtin commands that display the state of various internal server core state. These are most useful for examining the state of client metadata, either for a single client, or for clients overall. **clients** Displays a list of clients, along with their profile groups **groups** Displays a list of groups, the inheritance hierarchy, profile status, and category name, if there is one. **showclient** Displays full metadata information for a client, including profile group, group memberships, bundle list, and any connector data, like Probe values or Property info. **config** Displays the configuration of the Bcfg2 server. To leave the interactive shell, just type ``quit`` or ``exit``. Debugging Configuration Rules ============================= In addition to the commands listed above for viewing client metadata, there are also commands which can shed light on the configuration generation process. Recall that configuration generation occurs in three major steps: 1) Resolve client metadata 2) Build list of entries for the configuration 3) Bind host-specific version of each entry Step *1* can be viewed with the commands presented in the previous section. The latter two steps can be examined using the following commands. **showentries** displays a list of entries (optionally filtered by type) that appear in a client's configuration specification **buildbundle** Render a single bundle template. This only performs the template rendering step; it does not fully bind all entries in the bundle. This command is very useful when developing bundle templates. **buildfile** Perform the entry binding process on a single entry, displaying its results. This command is very useful when developing configuration file templates. **build** Build the full configuration specification and write it to a file. **mappings** displays the entries handled by the plugins loaded by the server core. This command is useful when the server reports a bind failure for an entry. Debugging and Developing Bcfg2 ============================== ``bcfg2-info`` loads a full Bcfg2 server core, so it provides the ideal environment for developing and debugging Bcfg2. Because it is hard to automate this sort of process, we have only implemented two commands in ``bcfg2-info`` to aid in the process. **profile** The profile command produces python profiling information for other ``bcfg2-info`` commands. This can be used to track performance problems in configuration generation. **debug** The debug command exits the ``bcfg2-info`` interpreter loop and drops to a python interpreter prompt. The Bcfg2 server core is available in this namespace as "self". Full documentation for the server core is out of scope for this document. This capability is most useful to call into plugin methods, often with setup calls or the enabling of diagnostics. It is possible to return to the ``bcfg2-info`` command loop by exiting the python interpreter with ^D. There is built-in support for IPython in ``bcfg2-info``. If IPython is installed, dropping into debug mode in ``bcfg2-info`` will use the IPython interpreter by default. bcfg2-1.3.3/doc/server/caching.txt000066400000000000000000000050341223671746500167300ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-caching: =================== Server-side Caching =================== Metadata Caching ================ .. versionadded:: 1.3.0 Caching (or, rather, cache expiration) is always a difficult problem, but it's particularly vexing in Bcfg2 due to the number of different data sources incorporated. In 1.3.0, we introduce some limited caching of client metadata objects. Since a client metadata object can be generated anywhere from 7 to dozens of times per client run (depending on your templates), and since client metadata generation was made more complex and powerful in 1.3.0, caching these objects provides the easiest performance gain. To enable caching, add a ``[caching]`` section to bcfg2.conf with a client_metadata option containing one of the following modes: * ``off``: No caching of client metadata objects is performed. This is the default. * ``initial``: Only initial metadata objects are cached. Initial metadata objects are created only from the data in the :ref:`server-plugins-grouping-metadata` plugin, before additional groups from other plugins are merged in. * ``cautious``: Final metadata objects are cached, but each client's cache is cleared at the start of each client run, immediately after probe data is received. Cache is also cleared as in ``aggressive`` mode. ``on`` is a synonym for ``cautious``. * ``aggressive``: Final metadata objects are cached. Each plugin is responsible for clearing cache when appropriate. These are presented roughly in ascending order of speed, and descending order of reliability. That is, odds are higher that ``aggressive`` mode will result in stale data, but it gives the biggest speed boost. ``off`` will never result in stale data, but it gives no speed boost. In addition to the :ref:`server-plugins-grouping-metadata` plugin, Bcfg2 includes three plugins that can set additional groups, and thus may affect the caching behavior. They are :ref:`server-plugins-grouping-grouppatterns`, :ref:`server-plugins-probes-index`, and :ref:`server-plugins-connectors-puppetenc`. All of those plugins **except** for PuppetENC fully support all caching levels. PuppetENC is incompatible with ``aggressive``, and may result in some stale data with ``cautious``. If you are not using the PuppetENC plugin, and do not have any custom plugins that provide additional groups, then all four modes should be safe to use. If you are using PuppetENC or have custom Connector plugins that provide additional groups, then you may want to start with ``cautious`` or ``initial``. bcfg2-1.3.3/doc/server/configuration.txt000066400000000000000000000165411223671746500202100ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-configuration: ====================== Server Configuration ====================== This page documents various aspects of server configuration. .. _server-dropping-privs: Running as a non-root user ========================== Although the Bcfg2 server runs as root by default, it is possible (and probably encouraged) to run it as an unprivileged user. This may become the default in the future. This can be done in all versions of Bcfg2, although it has become easier in 1.3.0. The steps to do so are described in three sections below: Common steps for all versions; steps for older versions only; and steps for 1.3.0. Many of the steps below may have already been performed by your OS packages. Common Steps ------------ We will assume for the sake of these steps that we are running the Bcfg2 server as the ``bcfg2`` user, who is a member of the ``bcfg2`` group. To create that user and group, you can run: .. code-block:: bash groupadd bcfg2 useradd -g bcfg2 -M -r -s /sbin/nologin -d /var/lib/bcfg2 \ -c "Bcfg2 server user" bcfg2 ``useradd`` arguments can vary wildly on different OSes, so please read ``useradd`` and run a command appropriate for your platform. The Bcfg2 server has to be able to read and write its data, so we need to set ownership on several things. The config file and specification data, of course: .. code-block:: bash chown bcfg2:bcfg2 /etc/bcfg2.conf chmod 0600 /etc/bcfg2.conf chown -R bcfg2:bcfg2 /var/lib/bcfg2/* chmod -R 0700 /var/lib/bcfg2/* Note that this does not change the permissions of ``/var/lib/bcfg2`` itself, which would prevent the ``bcfg2`` user from enabling a new plugin. If you depend on this capability (e.g., if your specification is stored in a VCS and checked out onto the Bcfg2 server by a script running as the ``bcfg2`` user), then you would want to ``chown`` and ``chmod`` ``/var/lib/bcfg2`` rather than ``/var/lib/bcfg2/*``. Note also that the recursive ``chmod`` will change permissions on any files that are using ``mode="inherit"`` in :ref:`server-info`. The Bcfg2 server also needs to be able to read its SSL certificate, key and the SSL CA certificate: .. code-block:: bash chown bcfg2:bcfg2 /etc/pki/tls/private/bcfg2.key \ /etc/pki/tls/certs/bcfg2.crt chmod 0600 /etc/pki/tls/private/bcfg2.key chmod 0644 /etc/pki/tls/certs/bcfg2.crt The paths to your SSL key and cert may be quite different, particularly on older versions of Bcfg2. .. note:: This step can be skipped if you are using the CherryPy :ref:`backend `. CherryPy reads in the certificate data before dropping privileges, so you can (and should) keep the keypair owned by root to prevent a compromised Bcfg2 server process from modifying that data. Most of these steps can (and should) be done via Bcfg2 itself. Steps on older versions ----------------------- On older versions of Bcfg2, you must change the location of the PID file. This change has been made the default in newer versions. This can be accomplished in one of two ways. * On systems where ``/var/run`` is world-writable with the sticky bit set, no change needs to be made. * On systems where ``/var/run`` is only writable by root, create a subdirectory for the PID file and configure the Bcfg2 server to write its PID file there: .. code-block:: bash mkdir /var/run/bcfg2-server chown bcfg2:bcfg2 /var/run/bcfg2-server chmod 0644 /var/run/bcfg2-server To change the PID file: * On Debian and derivatives, add ``export PIDFILE=/var/run/bcfg2-server/bcfg2-server.pid`` to ``/etc/default/bcfg2-server`` * On Red Hat Enterprise Linux and derivatives, add ``export PIDFILE=/var/run/bcfg2-server/bcfg2-server.pid`` to ``/etc/sysconfig/bcfg2-server``. This includes recent versions that are using systemd. * On other platforms, take the appropriate steps to change the PID file, which is given to the ``bcfg2-server`` process with the ``-D`` option, in your init system. On older versions of Bcfg2, you must also manually change the init script or process to drop privileges to the ``bcfg2`` user before the daemon is even invoked. * On RHEL and derivatives that are not using systemd, modify the ``bcfg2-server`` init script to run ``daemon --user=bcfg2 $DAEMON ...`` in the ``start()`` function. * On Debian and derivatives, modify the ``bcfg2-server`` init script to run ``start_daemon --user=bcfg2 ${DAEMON} ...`` in the ``start()`` function. * On systems that use systemd as their init system, add ``User=bcfg`` to the ``[Service]`` section of ``/etc/systemd/system/bcfg2-server.service`` * On other platforms, take the appropriate steps to change to the ``bcfg2`` user when spawning the ``bcfg2-server`` daemon. Restart ``bcfg2-server`` and you should see it running as non-root in ``ps`` output:: % ps -ef | grep '[b]cfg2-server' 1000 11581 1 0 07:55 ? 00:00:15 python usr/sbin/bcfg2-server -C /etc/bcfg2.conf -D /var/run/bcfg2-server/bcfg2-server.pid Steps on Bcfg2 1.3.0 -------------------- .. versionadded:: 1.3.0 On Bcfg2 1.3, the default PID file location has been changed, but it is still owned by root since no ``bcfg2`` user is created by default. Consequently, you simply have to run: .. code-block:: bash chown bcfg2:bcfg2 /var/run/bcfg2-server chmod 0755 /var/run/bcfg2-server Additionally, the server daemon itself supports dropping privileges natively in 1.3. Simply add the following lines to ``bcfg2.conf``:: [server] ... user = bcfg2 group = bcfg2 Restart ``bcfg2-server`` and you should see it running as non-root in ``ps`` output:: % ps -ef | grep '[b]cfg2-server' 1000 11581 1 0 07:55 ? 00:00:15 python usr/sbin/bcfg2-server -C /etc/bcfg2.conf -D /var/run/bcfg2-server/bcfg2-server.pid .. _server-backends: Server Backends =============== .. versionadded:: 1.3.0 Bcfg2 supports three different server backends: a builtin server based on the Python SimpleXMLRPCServer object; a server that uses CherryPy (http://www.cherrypy.org); and a version of the builtin server that uses the Python :mod:`multiprocessing` module. Each one has advantages and disadvantages. The builtin server: * Is very stable and mature; * Supports certificate authentication; * Works on Python 2.4; * Is slow with larger numbers of clients. The multiprocessing server: * Leverages most of the stability and maturity of the builtin server, but does have some new bits; * Introduces concurrent processing to Bcfg2, which may break in various edge cases; * Supports certificate authentication; * Requires Python 2.6; * Is faster with large numbers of concurrent runs. The CherryPy server: * Is very new and potentially buggy; * Does not support certificate authentication yet, only password authentication; * Requires CherryPy 3.3, which requires Python 2.5; * Is smarter about daemonization, particularly if you are :ref:`server-dropping-privs`; * Is faster with large numbers of clients. Basically, the builtin server should be used unless you have a particular need for performance. The CherryPy server is purely experimental at this point. To select which backend to use, set the ``backend`` option in the ``[server]`` section of ``/etc/bcfg2.conf``. Options are: * ``cherrypy`` * ``builtin`` * ``multiprocessing`` * ``best`` (the default; currently the same as ``builtin``) ``best`` may change in future releases. bcfg2-1.3.3/doc/server/configurationentries.txt000066400000000000000000000024311223671746500215730ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-configurationentries: ===================== Configuration Entries ===================== The full semantics of each configuration entry is documented with the :ref:`server-plugins-generators-rules` plugin. .. _boundentries: Bound Entries ============= This feature is a mechanism to specify a full entry at once from a bundle. Traditionally, entries are defined in two stages. First, an abstract entry is defined in a bundle. This entry includes a type (the XML tag) and a name attribute. Then this entry is bound for a client, providing the appropriate instance of that entry for the client. Specifying a bound entry short-circuits this process; the only second stage processing on Bound entries is to remove the "Bound" prefix from the element tag. The use of a bound entry allows the single stage definition of a complete entry. Bound entries can be used for any type. Example: .. code-block:: xml altsrc ====== The ``altsrc`` attribute lets you remap configuration entry names on the server side so you can reuse a single concrete representation for multiple abstract entries. See :ref:`server-plugins-structures-altsrc` for more details. bcfg2-1.3.3/doc/server/database.txt000066400000000000000000000072021223671746500170770ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-database: ======================== Global Database Settings ======================== .. versionadded:: 1.3.0 Several Bcfg2 plugins, including :ref:`server-plugins-grouping-metadata`, :ref:`server-plugins-probes-index`, and :ref:`server-plugins-statistics-reporting`, can connect use a relational database to store data. They use the global database settings in ``bcfg2.conf``, described in this document, to connect. .. note:: Although SQLite is supported as a database, it may cause significant thread contention (and a performance penalty) if you use SQLite with :ref:`server-plugins-grouping-metadata` or :ref:`server-plugins-probes-index`. If you are using the database-backed features of either of those plugins, it's recommended that you use a higher performance database backend. Configuration Options ===================== All of the following options should go in the ``[database]`` section of ``/etc/bcfg2.conf``. +-------------+------------------------------------------------------------+-------------------------------+ | Option name | Description | Default | +=============+============================================================+===============================+ | engine | The name of the Django database backend to use. See | "sqlite3" | | | https://docs.djangoproject.com/en/dev/ref/settings/#engine | | | | for available options (note that django.db.backends is not | | | | included in the engine name) | | +-------------+------------------------------------------------------------+-------------------------------+ | name | The name of the database | "/var/lib/bcfg2/bcfg2.sqlite" | +-------------+------------------------------------------------------------+-------------------------------+ | user | The user to connect to the database as | None | +-------------+------------------------------------------------------------+-------------------------------+ | password | The password to connect to the database with | None | +-------------+------------------------------------------------------------+-------------------------------+ | host | The host to connect to | "localhost" | +-------------+------------------------------------------------------------+-------------------------------+ | port | The port to connect to | None | +-------------+------------------------------------------------------------+-------------------------------+ | options | Extra parameters to use when connecting to the database. | None | | | Available parameters vary depending on your database | | | | backend. The parameters are supplied as comma separated | | | | key=value pairs. | | +-------------+------------------------------------------------------------+-------------------------------+ Database Schema Sync ==================== After making changes to the configuration options or adding a plugin that uses the global database, you should run ``bcfg2-admin syncdb`` to resync the database schema. bcfg2-1.3.3/doc/server/encryption.txt000066400000000000000000000201571223671746500175310ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-encryption: ===================== Bcfg2 Data Encryption ===================== .. versionadded:: 1.3.0 Bcfg2 supports encrypting some data on the disk, which can help protect sensitive data from other people who need access to the Bcfg2 repository but are perhaps not authorized to see all data. It supports multiple passphrases, which can be used to enforce separations between teams, environments, etc. Use of the encryption feature requires M2Crypto 0.18 or newer. .. note:: This feature is *not* intended to secure the files against a malicious attacker who has gained access to your Bcfg2 server, as the encryption passphrases are held in plaintext in ``bcfg2.conf``. This is only intended to make it easier to use a single Bcfg2 repository with multiple admins who should not necessarily have access to each other's sensitive data. Two types of data can be encrypted: * :ref:`server-plugins-generators-cfg` files can be encrypted as whole files. See :ref:`server-plugins-generators-cfg-encryption` for more details. * :ref:`server-plugins-connectors-properties` data can be encrypted on a per-element basis. See :ref:`server-plugins-connectors-properties-encryption` for more details. In general, Properties encryption is preferred for a few reasons: * It plays nicely with your VCS. If you change an encrypted Cfg file, then all you can see in your VCS log is that the file changed, no details about how it changed. With an encrypted Properties file, you can see which element changed (although obviously not the changed content). * It is faster when you have more than one passphrase. When decrypting a Cfg file, Bcfg2 simply brute-forces it with all known passphrases; when decrypting a Properties element, the passphrase is given by name so only one passphrase must be tried. * A Cfg file can only be encrypted with a single passphrase; Properties files can use different passphrases for different elements. If you are using different passphrases to segregate data amongst different teams, this lets teams collaborate more closely on files and other data. .. _bcfg2-crypt: bcfg2-crypt =========== Encrypting and decrypting :ref:`server-plugins-generators-cfg` and :ref:`server-plugins-connectors-properties` files can be done with the ``bcfg2-crypt`` tool, which mostly tries to do the right thing. I.e., it encrypts plaintext files, decrypts encrypted files, and automatically discovers if a file is Cfg or Properties. Its usage is thus generally very simple, e.g.:: bcfg2-crypt foo.conf bcfg2-crypt foo.xml Since the behavior of ``bcfg2-crypt`` varies significantly depending on whether you are dealing with a Cfg or Properties files, these are documented separately below. It's also well worthwhile to familiarize yourself with the man page for ``bcfg2-crypt``. Encrypting Cfg Files -------------------- To encrypt a Cfg file, you can simply run:: bcfg2-crypt foo.conf This will write the encrypted data to ``foo.conf.crypt``. Once you are satisfied that the file has been encrypted as you wish, you can remove the plaintext version, or you can use the ``--remove`` flag of ``bcfg2-crypt``. To decrypt a file, simply run ``bcfg2-crypt`` again:: bcfg2-crypt foo.conf.crypt On Cfg files, ``bcfg2-crypt`` is more-or-less equivalent to the following commands (encryption and decryption, respectively):: openssl enc -aes-256-cbc -k -in foo.conf \ -out foo.conf.crypt -a openssl enc -d -aes-256-cbc -k -in foo.conf.crypt \ -out foo.conf -a Those commands can be used in lieu of ``bcfg2-crypt`` if you hate convenience. Encrypting Properties Files --------------------------- To encrypt or decrypt a properties file, simply run:: bcfg2-crypt foo.xml If the top-level tag of a Properties file is not ````, then you need to use the ``--properties`` flag to ``bcfg2-crypt``:: bcfg2-crypt --properties foo.xml The first time you run ``bcfg2-crypt`` on a Properties file, it will encrypt all character data of all elements. Additionally, it will add ``encrypted=""`` to each element that has encrypted character data. It also adds ``encryption="true"`` to the top-level ```` tag as a flag to the server that it should try to decrypt the data in that file. (If you are using Properties schemas, you will need to make sure to add support for these attributes.) On subsequent runs, only those elements flagged with ``encrypted="*"`` are encrypted or decrypted. To decrypt a Properties file, simply re-run ``bcfg2-crypt``:: bcfg2-crypt foo.xml This decrypts the encrypted elements, but it does *not* remove the ``encrypted`` attribute; this way, you can decrypt a Properties file, modify the contents, and then simply re-run ``bcfg2-crypt`` to encrypt it again. If you added elements that you also want to be encrypted, you can either add the ``encrypted`` attribute to them manually, or run:: bcfg2-crypt --xpath '*' foo.xml You can also use the ``--xpath`` option to specify more restrictive XPath expressions to only encrypt a subset of elements, or to encrypt different elements with different passphrases. Alternatively, you can manally set the ``encrypted`` attribute on various elements and ``bcfg2-crypt`` will automatically do the right thing. You can also run bcfg2-crypt in interactive mode to interactively select which attributes should be encrypted:: bcfg2-crypt -I foo.xml If you want to use different passphrases within a single Properties file, you must manually set the ``encrypted`` attribute. .. _server-encryption-configuration: Configuring Encryption ====================== Passphrases ----------- To configure encryption, add a ``[encryption]`` section to ``bcfg2.conf`` with any number of name-passphrase pairs. For instance:: [encryption] foo_team=P4ssphr4se bar_team=Pa55phra5e .. note:: The name of a passphrase **cannot** be ``algorithm`` or ``decrypt``, which are reserved for other configuration options. This would define two separate encryption passphrases, presumably for use by two separate teams. The passphrase names are completely arbitrary. Note that this does entail a chicken-and-egg problem. In order for the Bcfg2 server to be able to decrypt encrypted files, the passphrases must exist in ``bcfg2.conf`` in plaintext; but, if you're encrypting data, presumably you don't want to include those plaintext passphrases in your Bcfg2 repository, so you'll want to encrypt ``bcfg2.conf``. The best way to solve this is: #. On your Bcfg2 server, manually add the ``[encryption]`` section to ``bcfg2.conf`` and restart the Bcfg2 server. #. Update ``bcfg2.conf`` in your Bcfg2 repository with the passphrases, and encrypt it. The first (manual) step breaks the mutual dependency. Algorithm --------- By default, Bcfg2 uses the AES-256-CBC cipher algorithm. If you wish to change this, you can set the ``algorithm`` option in the ``[encryption]`` section of ``bcfg2.conf``:: [encryption] algorithm = bf_cbc The value of ``algorithm`` must be a valid OpenSSL cipher algorithm according the naming model of the Python :mod:`M2Crypto` module. To get a list of valid algorithms, you can run:: openssl list-cipher-algorithms | grep -v ' => ' | \ tr 'A-Z-' 'a-z_' | sort -u Lax vs. Strict decryption ------------------------- By default, Bcfg2 expects to be able to decrypt every encrypted datum. Depending on how encryption is implemented at your site, though, that may not be possible. (For instance, if you use encryption to protect data for your production environment from your staging Bcfg2 server, then you would not expect the staging server to be able to decrypt everything.) In this case, you want to enable lax decryption in the ``[encryption]`` section of ``bcfg2.conf``:: [encryption] decrypt = lax This causes a failed decrypt to produce a warning only, not an error. This can be overridden by individual XML files by setting ``decrypt="strict"`` on the top-level tag (or, vice-versa; if strict is the default an XML file can specify ``decrypt="lax"``. Encryption API ============== .. automodule:: Bcfg2.Encryption bcfg2-1.3.3/doc/server/genshi-xml.txt000066400000000000000000000010321223671746500174010ustar00rootroot00000000000000.. -*- mode: rst -*- .. _xml-genshi-reference: =============================== Genshi XML Template Reference =============================== Genshi's XML templating language is used in :ref:`server-plugins-structures-bundler-index` for templated bundles. The language is described in depth at `Genshi `_. The XML schema reference follows. Genshi Tags =========== .. xml:group:: genshiElements :namespace: py Genshi Attributes ================= .. xml:attributegroup:: genshiAttrs :namespace: py bcfg2-1.3.3/doc/server/index.txt000066400000000000000000000017401223671746500164430ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-index: ================ The Bcfg2 Server ================ The Bcfg2 server is responsible for taking a comprehensive infrastructure description and turning it into a series of configuration specifications for particular clients. It also manages probed data and tracks statistics for clients. The Bcfg2 server takes information from two sources when generating client configuration specifications. The first is a pool of metadata that describes clients as members of an aspect-based classing system. That is, clients are defined in terms of aspects of their behavior. The other is a file system repository that contains mappings from metadata to literal configuration. These are combined to form the literal configuration specifications for clients. .. toctree:: :maxdepth: 2 plugins/index admin/index configurationentries info snapshots/index bcfg2-info selinux configuration database caching encryption genshi-xml bcfg2-1.3.3/doc/server/info.txt000066400000000000000000000043421223671746500162700ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-info: ======== info.xml ======== Various file properties for entries served by most generator plugins, including :ref:`server-plugins-generators-cfg`, :ref:`server-plugins-generators-sslca`, and :ref:`server-plugins-generators-sshbase`, are controlled through the use of ``info.xml`` files. By default, these plugins are set to write files to the filesystem with owner **root**, group **root**, and mode **644** (read and write for owner, read only for group and other). These options, and a few others, can be overridden through use of ``info.xml`` files. Each config file directory can have a ``info.xml`` file if needed. .. xml:schema:: info.xsd :linktotype: :inlinetypes: InfoType :noautodep: ACLType A sample ``info.xml`` file for CGI script on a web server might look like: .. code-block:: xml A more complex example for a template that generates both ``bcfg2.conf`` and ``bcfg2-web.conf`` might look like this: .. code-block:: xml See :ref:`server-selinux` for more information on the ``secontext`` attribute and managing SELinux in general. :info and info files ==================== .. deprecated:: 1.3.0 Historically, Bcfg2 also accepted the use of ``:info`` and ``info`` files, which function the same as ``info.xml``, but are not XML. They lack the ability to specify different permissions based on client, group, or path, and cannot be used to specify ACLs, either. An example ``:info`` or ``info`` file would look like:: owner: www group: www mode: 0755 All attributes allowed on the ```` tag of an ``info.xml`` file can be used in an ``:info`` or ``info`` file. You should not use more than one ``:info``, ``info``, or ``info.xml`` file for a single entry. bcfg2-1.3.3/doc/server/plugins/000077500000000000000000000000001223671746500162525ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/plugins/connectors/000077500000000000000000000000001223671746500204275ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/plugins/connectors/awstags.txt000066400000000000000000000102201223671746500226340ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-connectors-awstags: ========= AWSTags ========= The AWSTags plugin is a connector that retrieves tags from instances in EC2, and can assign optionally assign group membership pased on patterns in the tags. See `Using Tags `_ for details on using tags in EC2. AWSTags queries EC2 for instances whose ``private-dns-name`` property matches the hostname of the client. Setup ===== #. Add ``AWSTags`` to the ``plugins`` option in ``/etc/bcfg2.conf`` #. Configure AWS credentials in ``/etc/bcfg2.conf`` (See `Configuration`_ below for details.) #. Optionally, create ``AWSTags/config.xml`` (See `Assigning Groups`_ below for details.) #. Restart the Bcfg2 server. Using Tag Data ============== AWSTags exposes the data in templates as a dict available as ``metadata.AWSTags``. E.g., in a :ref:`Genshi template `, you could do: .. code-block:: genshitext Known tags on ${metadata.hostname}: {% for key, val in metadata.AWSTags.items() %}\ ${key} ${val} {% end %}\ This would produce something like:: Known tags on foo.example.com: Name foo.example.com some random tag the value Assigning Groups ================ AWSTags can assign groups based on the tag data. This functionality is configured in ``AWSTags/config.xml``. Example ------- .. code-block:: xml foo bar $1 In this example, any machine with a tag named ``foo`` would be added to the ``foo`` group. Any machine with a tag named ``bar`` whose value was also ``bar`` would be added to the ``bar`` group. Finally, any machine with a tag named ``bcfg2 group`` would be added to the group named in the value of that tag. Note that both the ``name`` and ``value`` attributes are *always* regular expressions. If a ```` element has only a ``name`` attribute, then it only checks for existence of a matching tag. If it has both ``name`` and ``value``, then it checks for a matching tag with a matching value. You can use backreferences (``$1``, ``$2``, etc.) in the group names. If only ``name`` is specified, then the backreferences will refer to groups in the ``name`` regex. If ``name`` and ``value`` are both specified, then backreferences will refer to groups in the ``value`` regex. If you specify both ``name`` and ``value``, it is not possible to refer to groups in the ``name`` regex. Schema Reference ---------------- .. xml:schema:: awstags.xsd Configuration ============= AWSTags recognizes several options in ``/etc/bcfg2.conf``; at a minimum, you must configure an AWS access key ID and secret key. All of the following options are in the ``[awstags]`` section: +-----------------------+-----------------------------------------------------+ | Option | Description | +=======================+=====================================================+ | ``access_key_id`` | The AWS access key ID | +-----------------------+-----------------------------------------------------+ | ``secret_access_key`` | The AWS secret access key | +-----------------------+-----------------------------------------------------+ | ``cache`` | Whether or not to cache tag lookups. See `Caching`_ | | | for details. Default is to cache. | +-----------------------+-----------------------------------------------------+ Caching ======= Since the AWS API isn't always very quick to respond, AWSTags caches its results by default. The cache is fairly short-lived: the cache for each host is expired when it starts a client run, so it will start the run with fresh data. If you frequently update tags on your instances, you may wish to disable caching. That's probably a bad idea, and would tend to suggest that updating tags frequently is perhaps the Wrong Thing. bcfg2-1.3.3/doc/server/plugins/connectors/grouplogic.txt000066400000000000000000000075451223671746500233550ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-connectors-grouplogic: ========== GroupLogic ========== .. versionadded:: 1.3.2 GroupLogic is a connector plugin that lets you use an XML Genshi template to dynamically set additional groups for clients. Usage ===== To use the GroupLogic plugin, first do ``mkdir /var/lib/bcfg2/GroupLogic``. Add ``GroupLogic`` to your ``plugins`` line in ``/etc/bcfg2.conf``. Next, create ``/var/lib/bcfg2/GroupLogic/groups.xml``: .. code-block:: xml ``groups.xml`` is structured very similarly to the :ref:`server-plugins-grouping-metadata` ``groups.xml``. A Group tag that contains no children is a declaration of membership; a Group or Client tag that does contain children is a conditional. Unlike ``Metadata/groups.xml``, GroupLogic supports genshi templating, so you can dynamically create groups. ``GroupLogic/groups.xml`` is rendered for each client, and the groups set in it are added to the client metadata. .. note:: Also unlike ``Metadata/groups.xml``, GroupLogic can not be used to associate bundles with clients directly, or to negate groups. But you can use GroupLogic to assign a group that is associated with a bundle in Metadata. Consider the case where you have four environments -- dev, test, staging, and production -- and four components to a web application -- the frontend, the API, the database server, and the caching proxy. In order to make files specific to the component *and* to the environment, you need groups to describe each combination: webapp-frontend-dev, webapp-frontend-test, and so on. You *could* do this in ``Metadata/groups.xml``: .. code-block:: xml ... ... ... Creating the sixteen groups this way is incredibly tedious, and this is a quite *small* site. GroupLogic can automate this process. Assume that we've declared the groups thusly in ``Metadata/groups.xml``: .. code-block:: xml One way to automate the creation of the groups would be to simply generate the tedious config: .. code-block:: xml But, since ``GroupLogic/groups.xml`` is rendered for each client individually, there's a more elegant way to accomplish the same thing: .. code-block:: xml This gets only the component and environment for the current client, and, if both are set, sets the single appropriate group. bcfg2-1.3.3/doc/server/plugins/connectors/properties.txt000066400000000000000000000267721223671746500234020ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-connectors-properties: ========== Properties ========== The Properties plugin is a connector plugin that adds information from XML, JSON, and YAML files into client metadata instances. Enabling Properties =================== First, ``mkdir /var/lib/bcfg2/Properties``. Each property file goes in this directory. Each will automatically be cached by the server, and reread/reparsed upon changes. Add **Properties** to your ``plugins`` line in ``/etc/bcfg2.conf``. Data Structures =============== Properties adds a new dictionary to client metadata instances that maps property file names to PropertyFile instances. A property file can be one of three types: * If the filename ends with ``.xml``, it will be parsed as XML and handled by :class:`Bcfg2.Server.Plugins.Properties.XMLPropertyFile`. See `XML Property Files`_ below. * If the filename ends with ``.json`` and JSON libraries are installed (either ``json`` or ``simplejson``, although ``json`` is highly recommended), it will be parsed as `JSON `_ and handled by :class:`Bcfg2.Server.Plugins.Properties.JSONPropertyFile`. See `JSON Property Files`_ below. * If the filename ends with ``.yaml`` or ``.yml`` and PyYAML is installed, it will be parsed as `YAML `_ and handled by :class:`Bcfg2.Server.Plugins.Properties.YAMLPropertyFile`. See `YAML Property Files`_ below. The XML interface is undoubtably the most powerful, as it natively supports schemas to check the data validity, client- and group-specific data, and data encryption. Usage ===== Common Interface ---------------- Different data types have different interfaces, but there are some usage patterns common to all properties files. Specific property files can be referred to in templates as ``metadata.Properties[]``. The data in property files is accessible via different attributes: +-----------+----------------+ | Data Type | Data Attribute | +===========+================+ | XML | ``xdata`` | +-----------+----------------+ | JSON | ``json`` | +-----------+----------------+ | YAML | ``yaml`` | +-----------+----------------+ For instance, in a :ref:`Genshi template `, you might do:: {% for item in metadata.Properties['foo.json'].json %}\ ${item} {% end %}\ {% for key, value in metadata.Properties['foo.yml'].yaml %}\ ${key} = ${value} {% end %}\ {% for el in metadata.Properties['foo.xml'].xdata.findall("Tag") %}\ ${el.get("name")} = ${el.text} {% end %}\ The raw contents of a properties file as a string are available via the ``data`` attribute, e.g., ``metadata.Properties['prop-file'].data``. .. _server-plugins-connectors-properties-write-back: Writing to Properties files ~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 1.2.0 If you need to make persistent changes to properties data, you can use the ``write`` method of the :class:`Bcfg2.Server.Plugins.Properties.PropertyFile` class:: {% python import lxml.etree from genshi.template import TemplateError lxml.etree.SubElement(metadata.Properties['foo.xml'], "Client", name=metadata.hostname) if not metadata.Properties['foo.xml'].write(): raise TemplateError("Failed to write changes back to foo.xml") The interface is the same for YAML or JSON data. If writing XML data, the ``write`` method checks the data in the object against its schema before writing it; see `Data Structures`_ for details. Note that use of the ``write`` method can cause race conditions if you run more than one Bcfg2 server. If you run more than one Bcfg2 server, you can disable Properties write-back by setting the following in ``bcfg2.conf``:: [properties] writes_enabled = false XML Property Files ------------------ The data in an XML property file can be accessed with the ``xdata`` attribute, an :class:`lxml.etree._Element` object documented `here `_. In addition to the ``xdata`` attribute that can be used to access the raw data, the following access methods are defined: * ``Match()`` parses the Group and Client tags in the file and returns a list of elements that apply to the client described by a set of metadata. For instance:: {% python ntp_servers = [el.text for el in metadata.Properties['ntp.xml'].Match(metadata) if el.tag == "Server"] %} * ``XMLMatch()`` parses the Group and Client tags in the file and returns an XML document containing only the data that applies to the client described by a set of metadata. (The Group and Client tags themselves are also removed, leaving only the tags and data contained in them.) For instance:: {% python ntp_servers = [el.text for el in metadata.Properties['ntp.xml'].XMLMatch(metadata).findall("//Server")] %} ``XMLMatch()`` can be run automatically on properties files by using the :ref:`server-plugins-connectors-properties-automatch` feature. You can also access the XML data that comprises a property file directly in one of several ways: * ``metadata.Properties['prop-file'].xdata`` is an lxml.etree._Element object representing the top-level element in the file. * ``metadata.Properties['prop-file'].data`` is the raw contents of the property file as a string. * ``metadata.Properties['prop-file'].entries`` is a list of lxml.etree._Element objects representing the direct children of the top-level element. (I.e., everything directly under the ```` tag.) The XML data in a property file is arbitrary, but a matching ``.xsd`` file can be created to assign a schema to a property file, which will be checked when running ``bcfg2-lint``. For instance, given:: Properties/dns-config.xml Properties/dns-config.xsd ``dns-config.xml`` will be validated against ``dns-config.xsd``. Although Properties files are technically freeform XML, the top-level XML tag should be ````. JSON Property Files ------------------- .. versionadded:: 1.3.0 The data in a JSON property file can be accessed with the ``json`` attribute, which is the loaded JSON data. The JSON properties interface does not provide any additional functionality beyond the `Common Interface`_. YAML Property Files ------------------- .. versionadded:: 1.3.0 The data in a YAML property file can be accessed with the ``yaml`` attribute, which is the loaded YAML data. Only a single YAML document may be included in a file. The YAML properties interface does not provide any additional functionality beyond the `Common Interface`_. .. _server-plugins-connectors-properties-automatch: Automatch ========= .. versionadded:: 1.3.0 You can enable :func:`Bcfg2.Server.Plugin.helpers.StructFile.XMLMatch()` for all XML Property files by setting ``automatch`` to ``true`` in the ``[properties]`` section of ``bcfg2.conf``. This makes ``metadata.Properties`` values :class:`lxml.etree._Element` objects that contain only matching data. (This makes it impossible to do :ref:`server-plugins-connectors-properties-write-back` as a side-effect.) In Python terms, setting ``automatch=true`` is the same as doing the following at the top of each template:: {% python for prop in metadata.Properties.values(): prop = prop.XMLMatch(metadata) %} The example above that describes ``XMLMatch()`` would then become simply:: {% python ntp_servers = [el.text for el in metadata.Properties['ntp.xml'].findall("//Server")] %} You can also enable automatch for individual Property files by setting the attribute ``automatch="true"`` on the top-level ```` tag. Conversely, if automatch is enabled by default in ``bcfg2.conf``, you can disable it for an individual Property file by setting ``automatch="false"`` on the top-level ```` tag. If you want to see what ``XMLMatch()``/automatch would produce for a given client on a given Properties file, you can use :ref:`bcfg2-info `:: bcfg2-info automatch props.xml foo.example.com If automatch is not enabled, you can force ``bcfg2-info`` to perform it anyway with ``-f``:: bcfg2-info automatch -f props.xml foo.example.com .. note:: Be sure to notice that enabling automatch changes the type of the data in ``metadata.Properties``; with automatch disabled, the values of the ``metadata.Properties`` dict are :class:`Bcfg2.Server.Plugins.Properties.PropertyFile` objects. With automatch enabled, they are :class:`lxml.etree._Element` objects. .. _server-plugins-connectors-properties-encryption: Encrypted Properties data ========================= .. versionadded:: 1.3.0 You can encrypt selected data in XML Properties files to protect that data from other people who need access to the repository. See :ref:`server-encryption-configuration` for details on configuring encryption passphrases. The data is decrypted transparently on-the-fly by the server; you never need to decrypt the data in your templates. Encryption is only supported on XML properties files. .. note:: This feature is *not* intended to secure the files against a malicious attacker who has gained access to your Bcfg2 server, as the encryption passphrases are held in plaintext in ``bcfg2.conf``. This is only intended to make it easier to use a single Bcfg2 repository with multiple admins who should not necessarily have access to each other's sensitive data. Properties files are encrypted on a per-element basis; that is, rather than encrypting the whole file, only the character content of individual elements is encrypted. This makes it easier to track changes to the file in a VCS, and also lets unprivileged users work with the other data in the file. Only character content of an element can be encrypted; attribute content and XML elements themselves cannot be encrypted. By default, decryption is *strict*; that is, if any element cannot be decrypted, parsing of the file is aborted. If you wish for parsing to continue, with unencryptable elements simply skipped, then you can set decryption to *lax* in one of two ways: * Set ``decrypt=lax`` in the ``[encryption]`` section of ``bcfg2.conf`` to set lax decryption on all files by default; or * Set the ``decrypt="lax"`` attribute on the top-level ``Properties`` tag of a Properties file to set lax decryption for a single file. Note that you could, for instance, set lax decryption by default, and then set strict decryption on individual files. To encrypt or decrypt a file, use :ref:`bcfg2-crypt`. See :ref:`server-encryption` for more details on encryption in Bcfg2 in general. Accessing Properties contents from Genshi Templates =================================================== Access contents of ``Properties/auth.xml``:: ${metadata.Properties['auth.xml'].xdata.find('file').find('bcfg2.key').text} Configuration ============= ``bcfg2.conf`` contains several miscellaneous configuration options for the Properties plugin, which can be set in the ``[properties]`` section. Any booleans in the config file accept the values "1", "yes", "true", and "on" for True, and "0", "no", "false", and "off" for False. It understands the following directives: * ``automatch``: Enable :ref:`server-plugins-connectors-properties-automatch`. Default is false. * ``writes_enabled``: Enable :ref:`server-plugins-connectors-properties-write-back`. Default is true. Module Documentation ==================== .. automodule:: Bcfg2.Server.Plugins.Properties bcfg2-1.3.3/doc/server/plugins/connectors/puppetenc.txt000066400000000000000000000077301223671746500232020ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-connectors-puppetenc: ========= PuppetENC ========= PuppetENC is a connector plugin that adds support for Puppet External Node Classifiers (``_), or ENCs. Output Format ============= The PuppetENC plugin implements the Puppet 2.6.5+ ENC output format with some modifications. The basic output format is described `here `_. The following modifications apply: * ``classes`` are considered to be Bcfg2 groups. (This is basically just a difference in terminology between Puppet and Bcfg2; Bcfg2 calls "groups" what Puppet calls "classes.") * As an alternative to the Puppet-specific ``classes`` value, you may use ``groups`` if you are writing an ENC from scratch specifically for Bcfg2. * Since Bcfg2 does not have the notion of parameterized classes, any class parameters provided will be merged in with the ``parameters`` dict. * ``parameters`` are presented as connector data. (See Usage below.) * The ``environment`` value is not supported. If present, PuppetENC will issue a warning and skip it. The ``parameters`` from separate ENCs are all merged together, including parameters from any parameterized classes. This is a shallow merge; in other words, only the top-level keys are considered. For instance, assuming you had one ENC that produced:: parameters: ntp_servers: - 0.pool.ntp.org - ntp1.example.com And another that produced:: parameters: ntp_servers: - ntp2.example.com This would result in connector data that included *either* the first value of ``ntp_servers`` *or* the second, but not both; this would depend on the order in which the ENCs were run, which is non-deterministic and should not be relied upon. However, if you add one ENC that produced:: parameters: ntp_servers: - 0.pool.ntp.org - ntp1.example.com And another that produced:: parameters: mail_servers: - mail.example.com Then the connector data would consist of:: {"ntp_servers": ["0.pool.ntp.org", "ntp1.example.com"], "mail_servers": ["mail.example.com"]} Usage ===== To use the PuppetENC plugin, first do ``mkdir /var/lib/bcfg2/PuppetENC``. Add ``PuppetENC`` to your ``plugins`` line in ``/etc/bcfg2.conf``. Now you can place any ENCs you wish to run in ``/var/lib/bcfg2/PuppetENC``. Note that ENCs are run each time client metadata is generated, so if you have a large number of ENCs or ENCs that are very time-consuming, they could have a significant impact on server performance. In that case, it could be worthwhile to write a dedicated Connector plugin. PuppetENC parameters can be accessed in templates as ``metadata.PuppetENC``, which is a dict of all parameter data merged together. For instance, given the following ENC output:: --- classes: common: puppet: ntp: ntpserver: 0.pool.ntp.org aptsetup: additional_apt_repos: - deb localrepo.example.com/ubuntu lucid production - deb localrepo.example.com/ubuntu lucid vendor parameters: ntp_servers: - 0.pool.ntp.org - ntp.example.com mail_server: mail.example.com iburst: true environment: production ``metadata.PuppetENC`` would contain:: 'additional_apt_repos': ['deb localrepo.example.com/ubuntu lucid production', 'deb localrepo.example.com/ubuntu lucid vendor'], 'iburst': True, 'mail_server': 'mail.example.com', 'ntp_servers': ['0.pool.ntp.org', 'ntp.example.com'], 'ntpserver': '0.pool.ntp.org'} (Note that the duplication of NTP server data doesn't make this an especially *good* example; it's just the official Puppet example.) So, in a template you could do something like:: {% for repo in metadata.PuppetENC['additional_apt_repos'] %}\ ${repo} {% end %}\ bcfg2-1.3.3/doc/server/plugins/connectors/templatehelper.txt000066400000000000000000000044171223671746500242110ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-connectors-templatehelper: ============== TemplateHelper ============== The TemplateHelper plugin is a connector plugin that adds Python classes and methods to client metadata instances for use in templates. This allows you to easily reuse code that is common amongst multiple templates and add convenience methods. Using TemplateHelper ==================== First, ``mkdir /var/lib/bcfg2/TemplateHelper`` and add **TemplateHelper** to your ``plugins`` line in ``/etc/bcfg2.conf``. Restart ``bcfg2-server``. Now, any ``.py`` file placed in ``/var/lib/bcfg2/TemplateHelper/`` will be read and added to matching client metadata objects. See :ref:`writing-templatehelpers` below for more information on how to write TemplateHelper scripts. TemplateHelper does not support group- or host-specific helpers. All helpers will be available to all clients. .. _writing-templatehelpers: Writing Helpers =============== A helper module is just a Python module with three special conditions: * The filename must end with ``.py`` * The module must have an attribute, ``__export__``, that lists all of the classes, functions, variables, or other symbols you wish to export from the module. * ``data``, ``name``, ``fam``, ``Index``, and ``HandleEvent`` are reserved names. You should not include symbols with a reserved name in ``__export__``. Additionally, including symbols that start with an underscore or double underscore is bad form, and may also produce errors. See ``examples/TemplateHelper`` for examples of helper modules. Usage ===== Specific helpers can be referred to in templates as ``metadata.TemplateHelper[]``. That accesses a HelperModule object will has, as attributes, all symbols listed in ``__export__``. For example, consider this helper module:: __export__ = ["hello"] def hello(metadata): return "Hello, %s!" % metadata.hostname To use this in a Genshi template, we could do:: ${metadata.TemplateHelper['hello'].hello(metadata)} The template would produce:: Hello, foo.example.com! Note that the client metadata object is not passed to a helper module in any magical way; if you want to access the client metadata object in a helper function or class, you must pass the object to the function manually. bcfg2-1.3.3/doc/server/plugins/generators/000077500000000000000000000000001223671746500204235ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/plugins/generators/account.txt000066400000000000000000000064671223671746500226350ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-generators-account: ======= Account ======= The account plugin manages authentication data, including * ``/etc/passwd`` * ``/etc/group`` * ``/etc/security/limits.conf`` * ``/etc/sudoers`` * ``/root/.ssh/authorized_keys`` User access data is stored in three files in the Account directory: * superusers (a list of users who always have root privs) * rootlist (a list of user:host pairs for scoped root privs) * useraccess (a list of user:host pairs for login access) SSH keys are stored in files named $username.key; these are installed into root's authorized keys for users in the superusers list as well as for the pertitent users in the rootlike file (for the current system). Authentication data is read in from (static|dyn).(passwd|group) The static ones are for system local ones, while the dyn. versions are for external synchronization (from ldap/nis/etc). There is also a static.limits.conf that provides the limits.conf header and any static entries. Files in the Account directory: ``.key`` **Format**: The SSH public key for user . If the user is in the "rootlike" or "superusers" group, these keys will be appended to ``/root/.ssh/auth`` ``useraccess`` **Format**: "user:hostname" on each line. Describes who may login where (via PAMs ``/etc/security/limits.conf``). Everybody else will be denied access.(?) **Example**: If Alice should be able to access host "foo", Bob should access "foo" and "bar":: alice:foo.example.com bob:foo.example.com bob:bar.example.com ``rootlike`` **Format**: "user:hostname" on each line. Describes who will be allowed root access where. The user may login via public key and use sudo. **Example**: If Chris should be root only on host "foo":: chris:foo.example.com ``superusers`` **Format**: usernames, separated by spaces or newlines. (Any whitespace that makes pythons split() happy.) Describes who will be allowed root access on all hosts. The user may login via public key and use sudo. **Example**: Daniel, Eve and Faith are global admins:: daniel eve faith ``static.passwd``, ``static.group`` **Format**: Lines from ``/etc/passwd`` or ``/etc/group`` These entries are appended to the passwd and group files (in addition to the auto-generated entries from "useraccess", "rootlike" and "superusers" above) without doing anything else. ``dyn.passwd``, ``dyn.group`` **Format**: Lines from ``/etc/passwd`` or ``/etc/group`` Similar to "static.*" above, but for entries that are managed "on the network" (yp, LDAP, ...), so it is most likely periodically (re)filled. ``static.limits.conf`` **Format**: Lines from ``/etc/security/limit.conf`` These limits will be appended to limits.conf (in addition to the auto-generated entries from "useraccess", "rootlike" and "superusers" above). ``static.sudoers`` **Format**: Lines from ``/etc/sudoers`` These lines will be appended to to sudoers file (in addition to the auto-generated entries from "useraccess", "rootlike" and "superusers" above). bcfg2-1.3.3/doc/server/plugins/generators/cfg.txt000066400000000000000000000655011223671746500217320ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-generators-cfg: === Cfg === The Cfg plugin provides a repository to describe configuration file contents for clients. In its simplest form, the Cfg repository is just a directory tree modeled off of the directory tree on your client machines. The Cfg Repository ================== The Cfg plugin is enabled by including **Cfg** on the **plugins** line of the **[server]** section of your Bcfg2 server config file. The repository itself lives in ``/var/lib/bcfg2/Cfg``, assuming you are using the default repository location of ``/var/lib/bcfg2``. The contents of this directory are a series of directories corresponding to the real-life locations of the files on your clients, starting at the root level. For example:: % ls Cfg bin/ boot/ etc/ opt/ root/ usr/ var/ Specific config files go in like-named directories in this heirarchy. For example the password file, ``/etc/passwd``, goes in ``Cfg/etc/passwd/passwd``, while the ssh pam module config file, ``/etc/pam.d/sshd``, goes in ``Cfg/etc/pam.d/sshd/sshd``. The reason for the like-name directory is to allow multiple versions of each file to exist, as described below. Note that these files are exact copies of what will appear on the client machine (except when using Genshi or Cheetah templating -- see below). Group-Specific Files ==================== It is often the case that you want one version of a config file for all of your machines except those in a particular group. For example, ``/etc/fstab`` should look alike on all of your desktop machines, but should be different on your file servers. Bcfg2 can handle this case through use of group-specific files. As mentioned above, all Cfg entries live in like-named directories at the end of their directory tree. In the case of fstab, the file at ``Cfg/etc/fstab/fstab`` will be handed out by default to any client that asks for a copy of ``/etc/fstab``. Group-specific files are located in the same directory and are named with the following syntax:: /path/to/filename/filename.GNN_groupname **NN** is a priority number where **00** is lowest and **99** is highest, and **groupname** is the name of a group defined in ``Metadata/groups.xml``. Back to our fstab example, we might have a ``Cfg/etc/fstab/`` directory that looks like this:: fstab fstab.G50_server fstab.G99_fileserver By default, clients will receive the plain fstab file when they request ``/etc/fstab``. Any machine that is in the **server** group, however, will instead receive the ``fstab.G50_server`` file. Finally, any machine that is in the **fileserver** group will receive the ``fstab.G99_fileserver`` file, even if they are also in the **server** group. Host-Specific Files =================== Similar to the case with group-specific files, there are cases where a specific machine should have a different version of a file than all others. This can be accomplished with host-specific files. The format of a host-specific file name is:: /path/to/filename/filename.H_host.example.com Host-specific files have a higher priority than group specific files. Again, the fstab example:: fstab fstab.G50_server fstab.G99_fileserver fstab.H_host.example.com In this case, *host.example.com* will always get the host-specific version, even if it is part of the **server** or **fileserver** (or both) classes. .. note:: If you have the ability to choose between using a group-specific and a host-specific file, it is almost always best to use a group-specific one. That way if a hostname changes or an extra copy of a particular client is built, it will get the same changes as the original. Templates ========= .. _server-plugins-generators-cfg-genshi: Genshi Templates ---------------- Genshi templates allow you to use the `Genshi `_ templating system. This is similar to the deprecated :ref:`server-plugins-generators-tgenshi-index` plugin. Genshi templates should be named with a ``.genshi`` extension, e.g.:: % ls Cfg/etc/motd info.xml motd.genshi See the genshi `documentation `_ for examples of Genshi syntax. Troubleshooting ~~~~~~~~~~~~~~~ When developing a template, you can see what the template would generate on a client with :ref:`bcfg2-info `:: bcfg2-info buildfile E.g.:: bcfg2-info buildfile /etc/foo.conf foo.example.com To generate a file with an :ref:`altsrc ` attribute, you can run:: bcfg2-info buildfile /etc/foo/foo.conf --altsrc=/etc/foo.conf \ foo.example.com Sometimes, it's useful to be able to do more in-depth troubleshooting by running the template manually. To do this, run ``bcfg2-info debug``, and, once in the Python interpreter, run:: metadata = self.build_metadata("") source_path = "" name = source_path[len(self.setup['repo']):] Then, run:: import os from genshi.template import TemplateLoader, NewTextTemplate template = TemplateLoader().load(source_path, cls=NewTextTemplate) data = dict(metadata=metadata, source_path=source_path, path=source_path, name=name, repo=self.setup['repo']) print(template.generate(**data).render()) This gives you more fine-grained control over how your template is rendered. E.g., you can tweak the values of the variables passed to the template, or evaluate the template manually, line-by-line, and so on. You can also use this approach to render templates that depend on :ref:`altsrc ` tags by setting ``source_path`` to the path to the template, and setting ``name`` to the path to the file to be generated, e.g.:: metadata = self.build_metadata("foo.example.com") source_path = "/Cfg/etc/sysconfig/network-scripts/ifcfg-template/ifcfg-template.genshi" name = "/etc/sysconfig/network-scripts/ifcfg-bond0" Error handling ~~~~~~~~~~~~~~ Situations may arise where a templated file cannot be generated due to missing or incomplete information. A TemplateError can be raised to force a bind failure and prevent sending an incomplete file to the client. For example, this template:: {% python from genshi.template import TemplateError grp = None for g in metadata.groups: if g.startswith('ganglia-gmond-'): grp = g break else: raise TemplateError, "Missing group" %}\ will fail to bind if the client is not a member of a group starting with "ganglia-gmond-". The syslogs on the server will contain this message:: bcfg2-server[5957]: Genshi template error: Missing group bcfg2-server[5957]: Failed to bind entry: Path /etc/ganglia/gmond.conf ...indicating the bind failure and message raised with the TemplateError. Handling Dollar Signs ~~~~~~~~~~~~~~~~~~~~~ In a Genshi template, ``$`` is a special character and must be escaped by doubling, i.e., ``$$``. For instance, to embed the Subversion ``$Id$`` keyword in a Genshi template, you would have to do ``$$Id$$``. Examples ~~~~~~~~ .. toctree:: :glob: :maxdepth: 1 examples/genshi/* .. _server-plugins-generators-cfg-cheetah: Cheetah Templates ----------------- Cheetah templates allow you to use the `cheetah templating system `_. This is similar to the deprecated :ref:`server-plugins-generators-tcheetah` plugin. Cheetah templates should be named with a ``.cheetah`` extension, e.g.:: % ls Cfg/etc/motd info.xml motd.cheetah Examples ~~~~~~~~ .. toctree:: :glob: :maxdepth: 1 examples/cheetah/* Comments and Cheetah ~~~~~~~~~~~~~~~~~~~~ As Cheetah processes your templates it will consider hash "#" style comments to be actual comments in the template and will strip them from the final config file. If you would like to preserve the comment in the final config file you need to escape the hash character '\#' which will tell Cheetah (and Python) that you do in fact want the comment to appear in the final config file.:: # This is a comment in my template which will be stripped when it's processed through Cheetah \# This comment will appear in the generated config file. Inside Templates ---------------- Several variables are pre-defined inside templates: +-------------+--------------------------------------------------------+ | Name | Description | +=============+========================================================+ | metadata | :ref:`Client metadata | | | ` | +-------------+--------------------------------------------------------+ | name | The value of the ``name`` attribute as specified in | | | the Path entry in Bcfg2. If an :ref:`altsrc | | | ` attribute is used, | | | then ``name`` will be the value of that attribute. | +-------------+--------------------------------------------------------+ | source_path | The path to the template file on the filesystem | +-------------+--------------------------------------------------------+ | repo | The path to the Bcfg2 repository on the filesystem | +-------------+--------------------------------------------------------+ | path | In Genshi templates, ``path`` is a synonym for | | | ``source_path``. In Cheetah templates, it's a synonym | | | for ``name``. For this reason, use of ``path`` is | | | discouraged, and it may be deprecated in a future | | | release. | +-------------+--------------------------------------------------------+ To access these variables in a Genshi template, you can simply use the name, e.g.:: Path to this file: ${name} In a Cheetah template, the variables are properties of ``self``, e.g.:: Path to this file: $self.name Notes on Using Templates ------------------------ Templates can be host and group specific as well. Deltas will not be processed for any Genshi or Cheetah base file. .. note:: If you are using templating in combination with host-specific or group-specific files, you will need to ensure that the ``.genshi`` or ``.cheetah`` extension is at the **end** of the filename. Using the examples from above for *host.example.com* and group *server* you would have the following:: Cfg/etc/fstab/fstab.H_host.example.com.genshi Cfg/etc/fstab/fstab.G50_server.cheetah Genshi templates take precence over cheetah templates. For example, if two files exist named:: Cfg/etc/fstab/fstab.genshi Cfg/etc/fstab/fstab.cheetah The Cheetah template is ignored. Exploiting this fact is probably a pretty bad idea in practice. You can mix Genshi and Cheetah when using different host-specific or group-specific files. For example:: Cfg/etc/fstab/fstab.H_host.example.com.genshi Cfg/etc/fstab/fstab.G50_server.cheetah .. _server-plugins-generators-cfg-encryption: Encrypted Files =============== .. versionadded:: 1.3.0 Bcfg2 allows you to encrypt files stored in ``Cfg/`` to protect the data in them from other people who need access to the repository. See also :ref:`server-plugins-connectors-properties-encryption` for information on encrypting elements in Properties files, which is often more friendly for tracking changes in a VCS. .. note:: This feature is *not* intended to secure the files against a malicious attacker who has gained access to your Bcfg2 server, as the encryption passphrases are held in plaintext in ``bcfg2.conf``. This is only intended to make it easier to use a single Bcfg2 repository with multiple admins who should not necessarily have access to each other's sensitive data. See :ref:`server-encryption` for more details on encryption in Bcfg2 in general. Encrypting Files ---------------- An encrypted file should end with ``.crypt``, e.g.:: Cfg/etc/foo.conf Cfg/etc/foo.conf/foo.conf.crypt Cfg/etc/foo.conf/foo.conf.G10_foo.crypt Encrypted Genshi or Cheetah templates can have the extensions in either order, e.g.:: Cfg/etc/foo.conf/foo.conf.crypt.genshi Cfg/etc/foo.conf/foo.conf.G10_foo.genshi.crypt Cfg/etc/foo.conf/foo.conf.H_bar.example.com.crypt.cheetah To encrypt or decrypt a file, use :ref:`bcfg2-crypt`. .. _server-plugins-generators-cfg-sshkeys: SSH Keys ======== .. versionadded:: 1.3.0 Cfg can also be used to automatically create and distribute SSH key pairs and the ``authorized_keys`` file. Keys can be created one of two ways: * Host-specific keys, where each client has its own key pair. This is the default. * Group-specific keys. To do this, you must set ``category`` in either ``bcfg2.conf`` (see "Configuration" below) or in ``privkey.xml``. Keys created for a given client will be specific to that client's group in the specified category. Group-specific keys are useful if, for instance, you have multiple distinct environments (development, testing, production, for example) and want to maintain separate keys for each environment. This feature actually creates static keys, much like the :ref:`server-plugins-generators-sshbase` plugin creates SSH certificates. It doesn't generate them on the fly for each request; it generates the key once, then saves it to the filesystem. Creating key pairs ------------------ To create an SSH key pair, you need to define how the private key will be created in ``privkey.xml``. For instance, to create ``/home/foo/.ssh/id_rsa``, you would create ``/var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa/privkey.xml``. This will create *both* the private key and the public key; the latter is created by appending ``.pub`` to the private key filename. It is not possible to change the public key filename. You may *optionally* also create a corresponding ``pubkey.xml``, which will allow the key pair to be created when the public key is requested. (For the example above, you'd create ``/var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa.pub/pubkey.xml``. This can speed up the propagation of SSH keys throughout your managed systems, particularly if you use the ``authorized_keys`` generation feature. ``privkey.xml`` ~~~~~~~~~~~~~~~ ``privkey.xml`` contains a top-level ``PrivateKey`` element, and is structured as follows: .. xml:element:: PrivateKey :linktotype: See :ref:`server-encryption` for more details on encryption in Bcfg2 in general. ``pubkey.xml`` ~~~~~~~~~~~~~~~ ``pubkey.xml`` only ever contains a single line: .. code-block:: xml .. xml:element:: PublicKey It acts only as a flag to Bcfg2 that a key pair should be generated, if none exists, using the associated ``privkey.xml`` file. The path to ``privkey.xml`` is determined by removing ``.pub`` from the directory containing ``pubkey.xml``. I.e., if you create ``/var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa.pub/pubkey.xml``, then Bcfg2 will use ``/var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa/privkey.xml`` to create the key pair. Use of ``pubkey.xml`` is optional, but is recommended. If you do not use ``pubkey.xml`` files, you may encounter two problems: * On the first Bcfg2 client run on a given client, the private keys may be present but the public keys may not be. This will be fixed by running ``bcfg2`` again. * If you are including an automatically created public key in ``authorized_keys``, it will not be created until the client the key is for requests the key pair. As an example of this latter scenario, suppose that your ``authorized_keys.xml`` allows access to foo.example.com from ``/root/.ssh/id_rsa.pub`` for bar.example.com. If bar.example.com has not run the Bcfg2 client, then no key pair will have been generated, and generating the foo.example.com ``authorized_keys`` file will create a warning. But if you create ``Cfg/root/.ssh/id_rsa.pub/pubkey.xml``, then building ``authorized_keys`` for foo.example.com will create root's keypair for bar.example.com. .. note:: In order to use ``pubkey.xml``, there *must* be a corresponding ``privkey.xml``. You cannot, for instance, populate a directory with manually-generated private SSH keys, drop ``pubkey.xml`` in the related public key directory, and expect Bcfg2 to generate the public keys. It will not. Examples ~~~~~~~~ ``privkey.xml`` can, at its simplest, be very simple indeed: .. code-block:: xml This will create a private key with all defaults. Or it can be more complex: .. code-block:: xml U2FsdGVkX19xACol83uyPELP94s4CmngD12oU6PLLuE= This creates a 1024-bit DSA key for each group in the ``environment`` category, and keys for clients in the ``secure`` group will be protected with the given (encrypted) passphrase. To complete the example, assume that this file was saved at ``/var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa/privkey.xml``. If a client in the ``development`` group, which is a group in the ``environment`` category, requests the private key, then the following files would be created:: /var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa/id_rsa.G50_development /var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa.pub/id_rsa.pub.G50_development ``/var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa.pub`` would be created if it did not exist. Subsequent clients that were also members of the ``development`` environment would get the keys that have already been generated. ``pubkey.xml`` always contains a single empty tag: .. code-block:: xml Generating ``authorized_keys`` ------------------------------ ``authorized_keys`` can be automatically generated from public SSH keys that exist in the Cfg tree. The keys in question can be generated from ``privkey.xml``, or they can be manually created. If a key doesn't exist when ``authorized_keys`` is generated, the key will only be created if ``pubkey.xml`` exists. If that is not the case, a warning will be produced. To generate ``authorized_keys``, create ``authorized_keys.xml``, e.g.: ``/var/lib/bcfg2/Cfg/root/.ssh/authorized_keys/authorized_keys.xml``. ``authorized_keys.xml`` ~~~~~~~~~~~~~~~~~~~~~~~ ``authorized_keys.xml`` is structured as follows: .. xml:element:: AuthorizedKeys :linktotype: Example ~~~~~~~ .. code-block:: xml ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDw/rgKQeARRAHK5bQQhAAe1b+gzdtqBXWrZIQ6cIaLgxqj76TwZ3DY4A6aW9RgC4zzd0p4a9MfsScUIB4+UeZsx9GopUj4U6H8Vz7S3pXxrr4E9logVLuSfOLFbI/wMWNRuOANqquLYQ+JYWKeP4kagkVp0aAWp7mH5IOI0rp0A6qE2you4ep9N/nKvHDrtypwhYBWprsgTUXXMHnAWGmyuHGYWxNYBV9AARPdAvZfb8ggtuwibcOULlyK4DdVNbDTAN1/BDBE1ve6WZDcrc386KhqUGj/yoRyPjNZ46uZiOjRr3cdY6yUZoCwzzxvm5vle6mEbLjHgjGEMQMArzM9 vendor@example.com .. note:: ``authorized_keys.xml`` allows you to specify the group whose public key should be allowed. This retrieves the public key specific to that group (if it exists), *not* the public key for all hosts in that group. This is due to the performance penalties that would be imposed by that approach. Similarly, it is not possible to allow access from all keys for a given user (i.e., at a given path). Hopefully, the performance concerns can be resolved in a future release and these features can be added. Configuration ------------- In addition to ``privkey.xml`` and ``authorized_keys.xml``, described above, the behavior of the SSH key generation feature can be influenced by several options in the ``[sshkeys]`` section of ``bcfg2.conf``: +----------------+---------------------------------------------------------+-----------------------+------------+ | Option | Description | Values | Default | +================+=========================================================+=======================+============+ | ``passphrase`` | Use the named passphrase to encrypt private keys on the | String | None | | | filesystem. The passphrase must be defined in the | | | | | ``[encryption]`` section. See :ref:`server-encryption` | | | | | for more details on encryption in Bcfg2 in general. | | | +----------------+---------------------------------------------------------+-----------------------+------------+ | ``category`` | Generate keys specific to groups in the given category. | String | None | | | It is best to pick a category that all clients have a | | | | | group from. | | | +----------------+---------------------------------------------------------+-----------------------+------------+ Deltas ====== .. note:: In Bcfg2 1.3 and newer, deltas are deprecated. It is recommended that you use templates instead. The :ref:`TemplateHelper plugin ` comes with an example helper that can be used to include other files easily, a subset of cat file functionality. ``bcfg2-lint`` checks for deltas and warns about them. .. warning:: In Bcfg2 1.3, deltas **do not** work with `SSH key or authorized_keys generation `_. Bcfg2 has finer grained control over how to deliver configuration files to a host. Let's say we have a Group named file-server. Members of this group need the exact same ``/etc/motd`` as all other hosts except they need one line added. We could copy motd to ``motd.G01_file-server``, add the one line to the Group specific version and be done with it, but we're duplicating data in both files. What happens if we need to update the motd? We'll need to remember to update both files then. Here's where deltas come in. A delta is a small change to the base file. There are two types of deltas: cats and diffs. The cat delta simply adds or removes lines from the base file. The diff delta is more powerful since it can take a unified diff and apply it to the base configuration file to create the specialized file. Diff deltas should be used very sparingly. Cat Files --------- Continuing our example for cat files, we would first create a file named ``motd.G01_file-server.cat``. The .cat suffix designates that the file is a diff. We would then edit that file and add the following line:: +This is a file server The **+** at the begining of the file tells Bcfg2 that the line should be appended to end of the file. You can also start a line with **-** to tell Bcfg2 to remove that exact line wherever it might be in the file. How do we know what base file Bcfg2 will choose to use to apply a delta? The same rules apply as before: Bcfg2 will choose the highest priority, most specific file as the base and then apply deltas in the order of most specific and then increasing in priority. What does this mean in real life. Let's say our machine is a web server, mail server, and file server and we have the following configuration files:: motd motd.G01_web-server motd.G01_mail-server.cat motd.G02_file-server.cat motd.H_bar.example.com motd.H_foo.example.com.cat If our machine isn't *foo.example.com* or *bar.example.com*, but is a web server, then Bcfg2 would choose ``motd.G01_web-server`` as the base file. It is the most specific base file for this host. Bcfg2 would apply the ``motd.G01_mail-server.cat`` delta to the ``motd.G01_web-server`` base file. It is the least specific delta. Bcfg2 would then apply the ``motd.G02_file-server.cat`` delta to the result of the delta before it. If our machine is *foo.example.com* and a web server, then Bcfg2 would choose ``motd.G01_web-server`` as the base file. It is the most specific base file for this host. Bcfg2 would apply the ``motd.H_foo.example.com.cat`` delta to the ``motd.G01_web-server`` base file. The reason the other deltas aren't applied to *foo.example.com* is because a **.H_** delta is more specific than a **.G##_** delta. Bcfg2 applies all the deltas at the most specific level. If our machine is *bar.example.com*, then Bcfg2 would chose ``motd.H_foo.example.com`` as the base file because it is the most specific base file for this host. Regardless of the groups *bar.example.com* is a member of, **no cat files** would be applied, because only cat files as specific or more specific than the base file are applied. (In other words, if a group-specific base file is selected, only group- or host-specific cat files can be applied; if a host-specific base file is selected, only host-specific cat files can be applied.) .. _server-plugins-generators-cfg-validation: Content Validation ================== To ensure that files with invalid content are not pushed out, you can provide a content validation script that will be run against each file. Create a file called ``:test`` inside the directory for the file you want to test. For example:: Cfg/etc/sudoers/:test You can also create host- and group-specific validators:: Cfg/etc/sudoers/:test.G80_foogroup Cfg/etc/sudoers/:test.H_bar.example.com A validator script has the following attributes: * It must be executable, or specify a valid bangpath; * The entire content of the file is passed to the validator on stdin; * The validator is not called with any flags or arguments; * The validator must return 0 on success and non-zero on failure; and * The validator must output a sensible error message on failure. For ``sudoers``, a very simple validator is:: #!/bin/sh visudo -cf - This uses the ``visudo`` command's built-in validation. If you wish to disable validation, this can be done with the following setting in ``bcfg2.conf``:: [cfg] validation=no If you have a very large number of validators, you may wish to disable validation by default to avoid slowing down the generation of configurations on the server, and use ``bcfg2-test`` (for instance, as a post-commit hook or as part of a code review process) to run validation. You can do this by setting ``validation=no`` in ``bcfg2.conf`` as described above, and then calling ``bcfg2-test`` with the ``--cfg-validation`` flag. File permissions ================ File permissions for entries handled by Cfg are controlled via the use of :ref:`server-info` files. Note that you **cannot** use both a Permissions entry and a Path entry to handle the same file. bcfg2-1.3.3/doc/server/plugins/generators/decisions.txt000066400000000000000000000063241223671746500231510ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-generators-decisions: ========= Decisions ========= This page describes the Decisions plugin. The client has support for a centralized set of per-entry installation decisions. This approach is needed when particular changes are deemed "high risk"; this gives the ability to centrally specify these changes, but only install them on clients when administrator supervision is available. Because collaborative configuration is one of the remaining hard issues in configuration management, these issues typically crop up in environments with several administrators and much configuration variety. In these cases, the client can be configured to run in either a whitelist or blacklist mode, wherein a list of entries is downloaded from the server. The client uses this list to determine which incorrect entries should be corrected during the current run of the installation tool. The Decisions plugin is the only stock plugin that generates entries for client's whitelists or blacklists. .. note:: If the client is not explicitly configured to run in whitelist or blacklist mode, the list of entries is not downloaded and decisions is not used. See `Decision Mode`_ below. The Decisions plugin uses a directory in the Bcfg2 repository called Decisions. Files in the Decisions subdirectory are named similarly to files managed by Cfg and Probes, so you can use host- and group-specific files and the like after their basename. File basenames are either ``whitelist`` or ``blacklist``. These files have a simple format; the following is an example. .. code-block:: xml $ cat Decisions/whitelist This example, included as a whitelist due to its name, enables all services, and the path entry named ``/etc/apt/apt.conf``. All these entries must already be present in your repository, the Decisions plugin just references them. In whitelist mode, only the given items are applied to the client; all other entry installation will be surpressed. In blacklist mode, every entry that is not blacklisted will be installed. When a client asks for its whitelist or blacklist, all of the files pertaining to that client of the correct type are aggregated into a single list. This list is sent to the client. .. note:: Using this plugin does not present additional prompts or safety nets to the administrator running the client, you have to control these via their respective options (``-I`` or ``-n``, for example). To add syntax highlighting to Decisions files in vim and emacs, you can add comments such as this:: Decision Mode ============= The whitelist or blacklist is only generated when a client is run in whitelist or blacklist mode. This can either be set at the command line with the appropriate option (``-l (whitelist|blacklist)``), or in ``bcfg2.conf`` by setting ``decision`` in the ``client`` section to ``whitelist`` or ``blacklist``). Client behavior is not controlled unless the decision mode is set. If you do not use Decisions, all your entries will be installed normally. bcfg2-1.3.3/doc/server/plugins/generators/examples/000077500000000000000000000000001223671746500222415ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/plugins/generators/examples/cheetah/000077500000000000000000000000001223671746500236425ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/plugins/generators/examples/cheetah/crontab.txt000066400000000000000000000021131223671746500260300ustar00rootroot00000000000000.. -*- mode: rst -*- ============================ Writing crontab with Cheetah ============================ This example randomizes the time of cron.daily execution with a stable result. Cron.daily is run at a consistent, randomized time between midnight and 7am.:: #import random #silent random.seed($self.metadata.hostname) # /etc/crontab: system-wide crontab # Unlike any other crontab you don't have to run the `crontab` # command to install the new version when you edit this file. # This file also has a username field, that none of the other crontabs do. SHELL=/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin://bin # m h dom mon dow user command 17 * * * * root run-parts --report /etc/cron.hourly $random.randrange(0,59) $random.randrange(0,6) * * * root test -x /usr/sbin/anacron || run-parts --report /etc/cron.daily 47 6 * * 7 root test -x /usr/sbin/anacron || run-parts --report /etc/cron.weekly 52 6 1 * * root test -x /usr/sbin/anacron || run-parts --report /etc/cron.monthly. bcfg2-1.3.3/doc/server/plugins/generators/examples/cheetah/simple.txt000066400000000000000000000021031223671746500256700ustar00rootroot00000000000000.. -*- mode: rst -*- ========================= Basic Cheetah Templates ========================= This simple example demonstrates basic usage of Cheetah templates. ``/var/lib/bcfg2/Cfg/foo/foo.cheetah`` .. code-block:: none Hostname is $self.metadata.hostname Filename is $self.path Template is $self.source_path Groups: #for $group in $self.metadata.groups: * $group #end for Categories: #for $category in $self.metadata.categories: * $category -- $self.metadata.categories[$category] #end for Probes: #for $probe in $self.metadata.Probes: * $probe -- $self.metadata.Probes[$probe] #end for Output ====== .. code-block:: xml Hostname is topaz.mcs.anl.gov Filename is /foo Template is /var/lib/bcfg2/Cfg/foo/foo.cheetah Groups: * desktop * mcs-base * ypbound * workstation * xserver * debian-sarge * debian * a Categories: * test -- a Probes: * os -- debian bcfg2-1.3.3/doc/server/plugins/generators/examples/genshi/000077500000000000000000000000001223671746500235165ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/plugins/generators/examples/genshi/bcfg2-cron.txt000066400000000000000000000013521223671746500262020ustar00rootroot00000000000000.. -*- mode: rst -*- bcfg2-cron ========== As submitted by Kamil Kisiel The following is my ``/etc/cron.d/bcfg2`` file. It uses the python random module seeded with the client hostname to generate a random time for the client to check in. The hostname seed ensures the generated file is the same each time the client checks in. This cron file helps to distribute the load on the Bcfg2 server since not all machines are checking in at the same time.:: {% python from genshi.builder import tag import random random.seed(metadata.hostname) %}\ ${random.randint(0,60)} * * * * root /usr/sbin/bcfg2 &> /dev/null You can apply the same concept to the other time fields by adding another ``${random.randint()}`` call. bcfg2-1.3.3/doc/server/plugins/generators/examples/genshi/clientsxml.txt000066400000000000000000000072161223671746500264470ustar00rootroot00000000000000.. -*- mode: rst -*- clients.xml =========== As submitted by dclark Here is an example of maintaining the bcfg2 server's ``/var/lib/bcfg2/Metadata/clients.xml`` file using Genshi templates. There are two main advantages: #. Password storage is centralized in the ``Properties/passwords.xml`` file this helps maintain consistency, makes changing passwords easier, and also makes it easier to share your configurations with other sites/people. #. You can template the file using Genshi's `{% def %}` syntax, which makes `clients.xml` much more readable. An important thing to note is how the `name` variable is handled - when just referring to it the standard `${name}` syntax is used, but when it is used as a variable in the expression to get the password, `password="${metadata.Properties['passwords.xml'].xdata.find('password').find('bcfg2-client').find(name).text}"`, it is just referred to as `name`. There is the disadvantage that sometimes 2 passes will be needed to get to a consistent state. Possible improvements: #. Wrapper for bcfg2 client runs on the bcfg2 server, perhaps using a call to `bcfg2-info buildfile`, so clients.xml is always generated before everything else happens (since the state of clients.xml can influence everything else bcfg2-server does). #. We really don't care what the client passwords are, just that they exist, so instead of listing them a master password combined with some kind of one-way hash based on the `name` might make more sense, and make ``Properties/passwords.xml`` easier to maintain. * Cfg/var/lib/bcfg2/Metadata/clients.xml/clients.xml.genshi: .. code-block:: xml {# Doc: http://bcfg2.org/wiki/Authentication #}\ {% def static(profile,name,address) %} \ {% end %}\ {% def dynamic(profile,name) %} \ {% end %}\ \ ${static('group-server-collab','campaigns.example.com','192.168.111.1')} ${static('group-server-collab','info.office.example.com','192.168.111.2')} ${static('group-server-config','config.example.com','192.168.111.3')} ${dynamic('group-project-membercard','membercard')} ${dynamic('group-person-somename','somename.office.example.com')} * Properties/passwords.xml snippit: .. code-block:: xml FAKEpassword1 FAKEpassword2 FAKEpassword3 FAKEpassword4 FAKEpassword5 bcfg2-1.3.3/doc/server/plugins/generators/examples/genshi/ganglia.txt000066400000000000000000000121421223671746500256610ustar00rootroot00000000000000.. -*- mode: rst -*- ganglia ======= Another interesting example of Genshi templating is to automatically generate ``gmond``/``gmetad`` configuration files. The idea is that each cluster is headless: it communicates with the rest of the cluster members on an isolated multicast IP address and port. Any of the cluster members is therefore isolated on that particular ip/port pair. Additionally, each ``gmond`` instance **also** listens on UDP. This allows for any of the cluster members to be polled for information on the entire cluster! The second part of the trick is in ``gmetad.conf``. Here, we dynamically generate a list of clusters (based on profiles names) and a list of members to poll (based on the clients in said profiles). As the number of profiles and client grows, this list will grow automatically as well. When a new host is added, ``gmetad`` will receive an updated configuration and act accordingly. There **is** one caveat though. The ``gmetad.conf`` parser is hard coded to read 16 arguments per ``data_source`` line. If you have more than 15 nodes in a cluster, you will see a warning in the logs. You can either ignore it, or truncate the list to the first 15 members. In our environment, a profile is a one to one match with the role of that particular host. You can also do this based on groups, or any other client property. Bundler/ganglia.xml ------------------- .. code-block:: xml Rules/services-ganglia.xml -------------------------- .. code-block:: xml Cfg/etc/ganglia/gmetad.conf/gmetad.conf.genshi ---------------------------------------------- .. code-block:: none {% python client_metadata = metadata.query.all() profile_array = {} seen = [] for item in client_metadata: if item.profile not in seen: seen.append(item.profile) profile_array[item.profile]=[] profile_array[item.profile].append(item.hostname) seen.sort() %}\ gridname "Our Grid" {% for profile in seen %} data_source "${profile}" \ {% for host in profile_array[profile] %}\ ${host} \ {% end %}\ {% end %} rrd_rootdir "/var/lib/ganglia/rrds" Cfg/etc/ganglia/gmond.conf/gmod.conf.genshi ------------------------------------------- .. code-block:: none {% python import random random.seed(metadata.profile) last_octet=random.randint(2,254) %}\ /* $$Id$$ $$HeadURL$$ */ /* This configuration is as close to 2.5.x default behavior as possible The values closely match ./gmond/metric.h definitions in 2.5.x */ globals { daemonize = yes setuid = yes user = nobody debug_level = 0 max_udp_msg_len = 1472 mute = no deaf = no host_dmax = 1800 /* 30 minutes */ cleanup_threshold = 604800 /*secs=1 week */ gexec = no send_metadata_interval = 0 } /* If a cluster attribute is specified, then all gmond hosts are wrapped inside * of a tag. If you do not specify a cluster tag, then all will * NOT be wrapped inside of a tag. */ cluster { name = "${metadata.profile}" owner = "user@company.net" latlong = "unspecified" url = "unspecified" } /* The host section describes attributes of the host, like the location */ host { location = "unspecified" } /* Feel free to specify as many udp_send_channels as you like. Gmond used to only support having a single channel */ udp_send_channel { host = ${metadata.hostname} port = 8649 } udp_send_channel { mcast_join = 239.2.11.${last_octet} port = 8649 ttl = 1 } /* You can specify as many udp_recv_channels as you like as well. */ udp_recv_channel { port = 8649 bind = ${metadata.hostname} } udp_recv_channel { mcast_join = 239.2.11.${last_octet} bind = 239.2.11.${last_octet} port = 8649 } /* You can specify as many tcp_accept_channels as you like to share an xml description of the state of the cluster */ tcp_accept_channel { port = 8649 } /* Each metrics module that is referenced by gmond must be specified and loaded. If the module has been statically linked with gmond, it does not require a load path. However all dynamically loadable modules must include a load path. */ modules { /* [snip] */ bcfg2-1.3.3/doc/server/plugins/generators/examples/genshi/grubconf.txt000066400000000000000000000025651223671746500260740ustar00rootroot00000000000000.. -*- mode: rst -*- grub.conf ========= Automate the build of grub.conf based on probe data. In this case, we take the results from three probes, serial-console-speed, grub-serial-order, and current-kernel to fill in a few variables. In addition, we want at least two entries set up for the kernel: a multiuser and a single user option. .. code-block:: none # grub.conf generated by Bcfg2 # # Note that you do not have to rerun grub after making changes to this file # NOTICE: You have a /boot partition. This means that # all kernel and initrd paths are relative to /boot/, eg. # root (hd0,0) # kernel /vmlinuz-version ro root=/dev/VolGroup00/LogVol00 # initrd /initrd-version.img #boot=/dev/sda default=0 timeout=5 serial --unit=0 --speed=${metadata.Probes['serial-console-speed']} terminal --timeout=5 ${metadata.Probes['grub-serial-order']} {% for kernbootoption in ["", "single"] %}\ title Red Hat Enterprise Linux Server (${metadata.Probes['current-kernel']})) ${kernbootoption} root (hd0,0) kernel /vmlinuz-${metadata.Probes['current-kernel']} ro root=/dev/VolGroup00/LogVol00 console=ttyS0,${metadata.Probes['serial-console-speed']}n8 console=tty0 rhgb quiet ${kernbootoption} initrd /initrd-${metadata.Probes['current-kernel']}.img {% end %}\ bcfg2-1.3.3/doc/server/plugins/generators/examples/genshi/hosts.txt000066400000000000000000000010231223671746500254130ustar00rootroot00000000000000.. -*- mode: rst -*- hosts ===== This is an example of creating ``/etc/hosts`` based on metadata.hostname:: # Do not remove the following line, or various programs # that require network functionality will fail. 127.0.0.1 localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 {% python import socket import re ip = socket.gethostbyname(metadata.hostname) shortname = re.split("\.", metadata.hostname) %}\ ${ip} ${metadata.hostname} ${shortname[0]} bcfg2-1.3.3/doc/server/plugins/generators/examples/genshi/iptables.txt000066400000000000000000000222231223671746500260630ustar00rootroot00000000000000.. -*- mode: rst -*- ========== iptables ========== * Setup a Genshi base iptables file that contains the basic rules you want every host to have * To be safe you should have a client side IptablesDeadmanScript if you intend on having bcfg2 bounce iptables upon rule updates .. note:: When updating files in the ``includes`` directory, you will need to `touch` the Genshi template to regenerate the template contents. /repository/Cfg/etc/sysconfig/iptables/iptables.genshi ====================================================== .. code-block:: none {% python from genshi.builder import tag import os,sys import Bcfg2.Options opts = { 'repo': Bcfg2.Options.SERVER_REPOSITORY } setup = Bcfg2.Options.OptionParser(opts) setup.parse('--') repo = setup['repo'] basedir = '%s' % (repo) # for instance: bcfg2BaseDir = basedir + name + '/' def checkHostFile(hostName, type): fileName = bcfg2BaseDir + type + '.H_' + hostName if os.path.isfile(fileName)==True : return fileName else: return fileName def checkGroupFile(groupName, type): fileName = bcfg2BaseDir + type + '.G_' + groupName if os.path.isfile(fileName)==True : return fileName else: return fileName %}\ # BCFG2 GENERATED IPTABLES # DO NOT CHANGE THIS # $$Id$$ # Templates live in ${bcfg2BaseDir} # Manual customization of this file will get reverted. # ----------------------------- FILTER --------------------------------- # # Default CHAINS for FILTER: *filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] :NO-SMTP - [0:0] #Default rules #discard malicious packets -A INPUT -p tcp --tcp-flags ALL ACK,RST,SYN,FIN -j DROP -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP #Allow incoming ICMP -A INPUT -p icmp -m icmp -j ACCEPT #Accept localhost traffic -A INPUT -i lo -j ACCEPT # Allow already established sessions to remain -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT # Deny inbound SMTP delivery (still allows outbound connections) -A INPUT -m state --state NEW -m tcp -p tcp --tcp-flags FIN,SYN,RST,ACK SYN --dport 25 -j NO-SMTP -A NO-SMTP -j LOG --log-prefix " Incoming SMTP (denied) " -A NO-SMTP -j DROP # Allow SSH Access :SSH - [0:0] -A INPUT -p tcp -m state --state NEW -m tcp --tcp-flags FIN,SYN,RST,ACK SYN --dport 22 -j SSH -A SSH -s 192.168.0.0/255.255.0.0 -j ACCEPT # Allow Ganglia Access -A INPUT -m state --state NEW -m tcp -p tcp --tcp-flags FIN,SYN,RST,ACK SYN --src 192.168.1.1 --dport 8649 -j ACCEPT # Gmetad access to gmond -A INPUT -m state --state NEW -m tcp -p tcp --tcp-flags FIN,SYN,RST,ACK SYN --src 192.168.1.1 --dport 8649 -j ACCEPT # Gmond UDP multicast -A INPUT -m state --state NEW -m udp -p udp --dport 8649 -j ACCEPT {% if metadata.groups %}\ # group custom FILTER rules: {% for group in metadata.groups %}\ {% include ${checkGroupFile(group,'custom-filter')} %}\ {% end %}\ {% end %}\ # host-specific FILTER rules: {% include ${checkHostFile(metadata.hostname, 'custom-filter')} %}\ COMMIT # ------------------------------- NAT ---------------------------------- # *nat # Default CHAINS for NAT: :PREROUTING ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] {% if metadata.groups %}\ # group NAT for PREROUTING: {% for group in metadata.groups %}\ {% include ${checkGroupFile(group,'nat-prerouting')} %}\ {% end %}\ {% end %}\ {% if metadata.groups %}\ # group NAT for OUTPUT: {% for group in metadata.groups %}\ {% include ${checkGroupFile(group,'nat-output')} %}\ {% end %}\ {% end %}\ {% if metadata.groups %}\ # group NAT for POSTROUTING: {% for group in metadata.groups %}\ {% include ${checkGroupFile(group,'nat-postrouting')} %}\ {% end %}\ {% end %}\ {% if metadata.groups %}\ # group custom NAT rules: {% for group in metadata.groups %}\ {% include ${checkGroupFile(group,'custom-nat')} %}\ {% end %}\ {% end %}\ # host-specific NAT ruls: {% include ${checkHostFile(metadata.hostname, 'custom-nat')} %}\ COMMIT # ----------------------------- MANGLE -------------------------------- # *mangle # Default CHAINS for MANGLE: :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] {% if metadata.groups %}\ # group MANGLE for PREROUTING: {% for group in metadata.groups %}\ {% include ${checkGroupFile(group,'mangle-prerouting')} %}\ {% end %}\ {% end %}\ {% if metadata.groups %}\ # group MANGLE for INPUT: {% for group in metadata.groups %}\ {% include ${checkGroupFile(group,'mangle-input')} %}\ {% end %}\ {% end %}\ {% if metadata.groups %}\ # group MANGLE for FORWARD: {% for group in metadata.groups %}\ {% include ${checkGroupFile(group,'mangle-forward')} %}\ {% end %}\ {% end %}\ {% if metadata.groups %}\ # group MANGLE for OUTPUT: {% for group in metadata.groups %}\ {% include ${checkGroupFile(group,'mangle-output')} %}\ {% end %}\ {% end %}\ {% if metadata.groups %}\ # group MANGLE for POSTROUTING rules: {% for group in metadata.groups %}\ {% include ${checkGroupFile(group,'mangle-postrouting')} %}\ {% end %}\ {% end %}\ {% if metadata.groups %}\ # group custom MANGLE rules: {% for group in metadata.groups %}\ {% include ${checkGroupFile(group,'custom-mangle')} %}\ {% end %}\ {% end %}\ # host-specific MANGLE rules: {% include ${checkHostFile(metadata.hostname, 'custom-mangle')} %}\ COMMIT Cfg/etc/sysconfig/iptables/custom-filter.G_mysql-server ------------------------------------------------------- .. code-block:: none :MYSQL - [0:0] -A INPUT -p tcp -m state --state NEW -m tcp --dport 3306 --tcp-flags FIN,SYN,RST,ACK SYN -j MYSQL -A MYSQL -s 192.168.0.0/255.255.0.0 -j ACCEPT For a host that is in the mysql-server group you get an iptables file that looks like the following:: # BCFG2 GENERATED IPTABLES # DO NOT CHANGE THIS # $Id: template.newtxt 5402 2009-08-19 22:50:06Z unixmouse$ # Templates live in /var/lib/bcfg2/Cfg/etc/sysconfig/iptables/ # Manual customization of this file will get reverted. # ----------------------------- FILTER --------------------------------- # # Default CHAINS for FILTER: *filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] :NO-SMTP - [0:0] #Default rules #discard malicious packets -A INPUT -p tcp --tcp-flags ALL ACK,RST,SYN,FIN -j DROP -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP # Allow incoming ICMP -A INPUT -p icmp -m icmp -j ACCEPT # Accept localhost traffic -A INPUT -i lo -j ACCEPT # Allow already established sessions to remain -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT # Deny inbound SMTP delivery (still allows outbound connections) -A INPUT -m state --state NEW -m tcp -p tcp --tcp-flags FIN,SYN,RST,ACK SYN --dport 25 -j NO-SMTP -A NO-SMTP -j LOG --log-prefix " Incoming SMTP (denied) " -A NO-SMTP -j DROP # Allow SSH Access :SSH - [0:0] -A INPUT -p tcp -m state --state NEW -m tcp --tcp-flags FIN,SYN,RST,ACK SYN --dport 22 -j SSH -A SSH -s 192.168.0.0/255.255.0.0 -j ACCEPT # Allow Ganglia Access -A INPUT -m state --state NEW -m tcp -p tcp --tcp-flags FIN,SYN,RST,ACK SYN --src 192.168.1.1 --dport 8649 -j ACCEPT #Gmetad access to gmond -A INPUT -m state --state NEW -m tcp -p tcp --tcp-flags FIN,SYN,RST,ACK SYN --src 192.168.1.1 --dport 8649 -j ACCEPT #Gmond UDP multicast -A INPUT -m state --state NEW -m udp -p udp --dport 8649 -j ACCEPT # group custom FILTER rules: :MYSQL - [0:0] -A INPUT -p tcp -m state --state NEW -m tcp --dport 3306 --tcp-flags FIN,SYN,RST,ACK SYN -j MYSQL -A MYSQL -s 192.168.0.0/255.255.0.0 -j ACCEPT # host-specific FILTER rules: COMMIT # ------------------------------- NAT ---------------------------------- # *nat # Default CHAINS for NAT: :PREROUTING ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] # group NAT for PREROUTING: # group NAT for OUTPUT: # group NAT for POSTROUTING: # group custom NAT rules: # host-specific NAT rules: COMMIT # ----------------------------- MANGLE -------------------------------- # *mangle # Default CHAINS for MANGLE: :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] # group MANGLE for PREROUTING: # group MANGLE for INPUT: # group MANGLE for FORWARD: # group MANGLE for OUTPUT: # group MANGLE for POSTROUTING rules: # group custom MANGLE rules: # host-specific MANGLE rules: COMMIT bcfg2-1.3.3/doc/server/plugins/generators/examples/genshi/motd.txt000066400000000000000000000056301223671746500252260ustar00rootroot00000000000000.. -*- mode: rst -*- ====== motd ====== The following template automatically generates a MOTD (message of the day) file that describes the system in terms of its Bcfg2 metadata and probe responses. It conditionally displays groups, categories, and probe responses, if there exists any data for them. Cfg/etc/motd/motd.genshi ======================== .. code-block:: none ------------------------------------------------------------------------ GOALS FOR SERVER MANGED BY BCFG2 ------------------------------------------------------------------------ Hostname is ${metadata.hostname} Groups: {% for group in metadata.groups %}\ * ${group} {% end %}\ {% if metadata.categories %}\ Categories: {% for category in metadata.categories %}\ * ${category} {% end %}\ {% end %}\ {% if metadata.Probes %}\ Probes: {% for probe, value in metadata.Probes.iteritems() %}\ * ${probe} \ ${value} {% end %}\ {% end %}\ ------------------------------------------------------------------------- ITOPS MOTD ------------------------------------------------------------------------- Please create a Ticket for any system level changes you need from IT. This template gets the hostname, groups membership of the host, categories of the host (if any), and result of probes on the host (if any). The template formats this in with a header and footer that makes it visually more appealing. Output ====== One possible output of this template would be the following:: ------------------------------------------------------------------------ GOALS FOR SERVER MANGED BY BCFG2 ------------------------------------------------------------------------ Hostname is cobra.example.com Groups: * oracle-server * centos5-5.2 * centos5 * redhat * x86_64 * sys-vmware Categories: * os-variant * os * database-server * os-version Probes: * arch x86_64 * network intranet_network * diskspace Filesystem Size Used Avail Use% Mounted on /dev/mapper/VolGroup00-LogVol00 18G 2.1G 15G 13% / /dev/sda1 99M 13M 82M 13% /boot tmpfs 3.8G 0 3.8G 0% /dev/shm /dev/mapper/mhcdbo-clear 1.5T 198M 1.5T 1% /mnt/san-oracle * virtual vmware ------------------------------------------------------------------------- IT MOTD ------------------------------------------------------------------------- Please create a Ticket for any system level changes you need from IT. One way to make this even more useful, is to only include the result of certain probes. It would also be a nice feature to be able to include customer messages on a host or group level. bcfg2-1.3.3/doc/server/plugins/generators/examples/genshi/mycnf.txt000066400000000000000000000012471223671746500253770ustar00rootroot00000000000000.. -*- mode: rst -*- my.cnf ====== The following template generates a ``server-id`` based on the last two numeric parts of the IP address. The "slave" portion of the configuration only applies to machines in the "slave" group:: {% python import socket parts = socket.gethostbyname(metadata.hostname).split('.') server_id = parts[2] + parts[3] %}\ [mysqld] # [snip] server-id = ${server_id} # Replication configuration {% if "slave" in metadata.groups %}\ relay-log = /data01/mysql/log/mysql-relay-bin log-slave-updates = 1 {% end %}\ sync-binlog = 1 #read-only = 1 #report-host = # [snip] bcfg2-1.3.3/doc/server/plugins/generators/examples/genshi/test.txt000066400000000000000000000122501223671746500252360ustar00rootroot00000000000000.. -*- mode: rst -*- test ==== As submitted by dclark This file just shows you what's available. It assumes a ``/var/lib/bcfg2/Properties/test.xml`` file with an entry like this: .. code-block:: xml fakeBCFG2password :: Hostname is ${metadata.hostname} Groups: {% for group in metadata.groups %}\ ${group} \ {% end %}\ {% if metadata.categories %}\ Categories: {% for category in metadata.categories %}\ ${category} \ {% end %}\ {% end %}\ {% if metadata.Probes %}\ Probes: {% for probe, value in metadata.Probes.iteritems() %}\ $probe $value {% end %}\ {% end %}\ Two main ways to get the same property value: ${metadata.Properties['test.xml'].xdata.find('password').find('bcfg2').text} ${metadata.Properties['test.xml'].xdata.xpath('password/bcfg2')[0].text} One way to get information about metadata and properties: dir(metadata): {% for var in dir(metadata) %}\ ${var} \ {% end %} dir(metadata.Properties.xdata): {% for var in dir(metadata.Properties.xdata) %}\ ${var} \ {% end %} dir(metadata.Properties.xdata.entries): {% for var in dir(metadata.Properties.xdata.entries) %}\ ${var} \ {% end %} dir(metadata.Properties.xdata.label): {% for var in dir(metadata.Properties.xdata.label) %}\ ${var} \ {% end %} dir(metadata.Properties.xdata.name): {% for var in dir(metadata.Properties.xdata.name) %}\ ${var} \ {% end %} dir(metadata.Properties.xdata.properties): {% for var in dir(metadata.Properties.xdata.properties) %}\ ${var} \ {% end %} When the above file is saved as ``Cfg/test/test.genshi`` and generated with ``bcfg2-info buildfile /test test.hostname.org``, the results look like this (below reformatted a little bit to fit in 80 columns):: Hostname is test.hostname.org Groups: bcfg2-server Two main ways to get the same property value: fakeBCFG2password fakeBCFG2password One way to get information about metadata and properties: dir(metadata): __class__ __delattr__ __dict__ __doc__ __getattribute__ __hash__ __init__ __module__ __new__ __reduce__ __reduce_ex__ __repr__ __setattr__ __str__ __weakref__ all bundles categories get_clients_by_group get_clients_by_profile groups hostname inGrouppassword probes uuid dir(metadata.Properties.xdata): HandleEvent Index __class__ __delattr__ __dict__ __doc__ __getattribute__ __hash__ __identifier__ __init__ __iter__ __module__ __new__ __reduce__ __reduce_ex__ __repr__ __setattr__ __str__ __weakref__ entries label name properties dir(metadata.Properties.xdata.entries): __add__ __class__ __contains__ __delattr__ __delitem__ __delslice__ __doc__ __eq__ __ge__ __getattribute__ __getitem__ __getslice__ __gt__ __hash__ __iadd__ __imul__ __init__ __iter__ __le__ __len__ __lt__ __mul__ __ne__ __new__ __reduce__ __reduce_ex__ __repr__ __reversed__ __rmul__ __setattr__ __setitem__ __setslice__ __str__ append count extend index insert pop remove reverse sort dir(metadata.Properties.xdata.label): __add__ __class__ __contains__ __delattr__ __doc__ __eq__ __ge__ __getattribute__ __getitem__ __getnewargs__ __getslice__ __gt__ __hash__ __init__ __le__ __len__ __lt__ __mod__ __mul__ __ne__ __new__ __reduce__ __reduce_ex__ __repr__ __rmod__ __rmul__ __setattr__ __str__ capitalize center count decode encode endswith expandtabs find index isalnum isalpha isdigit islower isspace istitle isupper join ljust lower lstrip partition replace rfind rindex rjust rpartition rsplit rstrip split splitlinesstartswith strip swapcase title translate upper zfill dir(metadata.Properties.xdata.name): __add__ __class__ __contains__ __delattr__ __doc__ __eq__ __ge__ __getattribute__ __getitem__ __getnewargs__ __getslice__ __gt__ __hash__ __init__ __le__ __len__ __lt__ __mod__ __mul__ __ne__ __new__ __reduce__ __reduce_ex__ __repr__ __rmod__ __rmul__ __setattr__ __str__ capitalize center count decode encode endswith expandtabs find index isalnum isalpha isdigit islower isspace istitle isupper join ljust lower lstrip partition replace rfind rindex rjust rpartition rsplit rstrip split splitlinesstartswith strip swapcase title translate upper zfill dir(metadata.Properties.xdata.properties): __class__ __contains__ __copy__ __deepcopy__ __delattr__ __delitem__ __delslice__ __doc__ __getattribute__ __getitem__ __getslice__ __hash__ __init__ __iter__ __len__ __new__ __nonzero__ __reduce__ __reduce_ex__ __repr__ __reversed__ __setattr__ __setitem__ __setslice__ __str__ _init addnext addprevious append attrib clear extend find findall findtext get getchildren getiterator getnext getparent getprevious getroottree index insert items iterancestors iterchildren iterdescendants itersiblings keys makeelement nsmap prefix remove replace set sourceline tag tail text values xpath bcfg2-1.3.3/doc/server/plugins/generators/hostbase.txt000066400000000000000000000173461223671746500230070ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-generators-hostbase: ======== Hostbase ======== IP management system built on top of Bcfg2. It has four main parts: a django data model, a web frontend, command-line utilities, and a Bcfg2 plugin that generates dhcp, dns, and yp configuration files. Installation ============ Installation of Hostbase requires installation of a python module, configuration of database (mysql or postgres), and configuration of an Apache webserver with mod_python. Hostbase was developed using MySQL, so this document is aimed at MySQL users. Prerequisites ------------- * `mysql`_ * `python-mysqldb`_ * `Django`_ .. _Django: http://www.djangoproject.com .. _python-mysqldb: http://mysql-python.sourceforge.net/MySQLdb.html .. _mysql: http://www.mysql.com/ Configure the database ---------------------- Create the hostbase database and a user. For MySQL users:: mysql> CREATE DATABASE hostbase mysql> quit systemprompt#: mysql -u root hostbase mysql> GRANT ALL PRIVILEGES ON *.* TO hostbaseuser@mycomputer.private.net IDENTIFIED BY 'password' WITH GRANT OPTION; mysql> quit As of Bcfg2 v0.8.7 configuration options for Hostbase have moved to ``/etc/bcfg2.conf``. There is an example bcfg2.conf with Hostbase options located at ``bcfg2-tarball/examples/bcfg2.confHostbase``. Edit the hostbase options to correspond to the database you've initialized and copy the configuration to ``/etc/bcfg2.conf``. To finish creating the database, from your ``path to python/Bcfg2/Server/Hostbase`` directory, run ``python manage.py syncdb`` to do all table creation. Configure the web interface --------------------------- Now it's possible to explore the Hostbase web interface. For curiosity, you can run Django's built-in development server to take a peek. Do this by running ``python manage.py runserver [servername:port]`` from your Hostbase directory. Django will default to ``localhost:8000`` if no server or port is entered. Now you can explore the web interface. Try adding a host and a zone. You'll see that a ".rev" zone already exists. This is where information for reverse files will go. For production, you'll want to have this configured for Apache with mod_python. Here is an example of how to configure Hostbase as a virtual host. .. code-block:: html ServerAdmin systems@mcs.anl.gov DocumentRoot /var/www/hostbase/ AllowOverride None # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. LogLevel warn ServerSignature Off # Stop TRACE/TRACK vulnerability RewriteEngine on RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK) RewriteRule .* - [F] Redirect / https://hostbase.mcs.anl.gov/ ServerAdmin systems@mcs.anl.gov DocumentRoot /var/www/hostbase/ AllowOverride None # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. LogLevel warn ServerSignature Off # Stop TRACE/TRACK vulnerability RewriteEngine on RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK) RewriteRule .* - [F] SSLEngine On SSLCertificateFile /etc/apache2/ssl/hostbase_server.crt SSLCertificateKeyfile /etc/apache2/ssl/hostbase_server.key SetHandler python-program PythonHandler django.core.handlers.modpython SetEnv DJANGO_SETTINGS_MODULE Bcfg2.Server.Hostbase.settings PythonDebug On SetHandler None You'll need to copy the contents of ``Hostbase/media`` into ``/var/www/hostbase/site_media`` in this configuration to serve the correct css files. Enable the Hostbase plugin -------------------------- Now that the database is accessible and there is some data in it, you can enable the Hostbase plugin on your Bcfg2 server to start generating some configuration files. All that needs to be done is to add ``Hostbase`` to the end of the list of generators in your bcfg2.conf file. To see what's being generated by Hostbase, fire up a Bcfg2 development server: ``bcfg2-info``. For more information on how to use the Bcfg2 development server, type help at the prompt. For our purposes, type ``debug``. This will bring you to an interactive python prompt where you can access bcfg's core data. .. code-block:: python for each in bcore.plugins['Hostbase'].filedata: print each The above loop will print out the name of each file that was generated by Hostbase. You can see the contents of any of these by typing ``print bcore.plugins['Hostbase'].filedata[filename]``. Create a bundle --------------- Bcfg2 needs a way to distribute the files generated by Hostbase. We'll do this with a bundle. In bcfg's ``Bundler`` directory, touch ``hostbase.xml``. .. code-block:: xml The above example is a bundle that will deliver both dhcp and dns files. This can be trivially split into separate bundles. It is planned that Hostbase will eventually be able to generate the list of ``Paths`` in its bundles automatically. Do a Hostbase push ------------------ You'll want to be able to trigger the Hostbase plugin to rebuild it's config files and push them out when data has been modified in the database. This can be done through and XMLRPC function available from the Bcfg2 server. From a client that is configured to receive one or more hostbase bundles, you'll need to first edit your ``python/site-packages/Bcfg2/Client/Proxy.py`` file. Add ``'Hostbase.rebuildState'`` to the list of methods in the Bcfg2 client proxy object. The modified list is shown below: .. code-block:: python class bcfg2(ComponentProxy): '''bcfg2 client code''' name = 'bcfg2' methods = ['AssertProfile', 'GetConfig', 'GetProbes', 'RecvProbeData', 'RecvStats', 'Hostbase.rebuildState'] Now copy the file ``hostbasepush.py`` from ``bcfg2/tools`` in the Bcfg2 source to your machine. When this command is run as root, it triggers the Hostbase to rebuild it's files, then runs the Bcfg2 client on your local machine to grab the new configs. NIS Authentication ================== Django allows for custom authentication backends to its login procedure. Hostbase has an NIS authentication backend that verifies a user to be in the unix group allowed to modify Hostbase. To enable this feature: * first edit your ``Hostbase/settings.py`` file and uncomment the line **Hostbase.backends.NISBackend** in the list of *AUTHENTICATION_BACKENDS* * enter the name of the unix group you want to give access to Hostbase in the *AUTHORIZED_GROUP* variable * in your ``Hostbase/hostbase/views.py`` file at the very bottom, uncomment the block(s) of lines that give you the desired level of access Hostbase will now direct the user to a login page if he or she is not authorized to view a certain page. Users should log in with their regular Unix username and password. bcfg2-1.3.3/doc/server/plugins/generators/nagiosgen.txt000066400000000000000000000152021223671746500231360ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-generators-nagiosgen: ========= NagiosGen ========= This page describes the installation and use of the `NagiosGen`_ plugin. .. _NagiosGen: https://github.com/Bcfg2/bcfg2/blob/maint/src/lib/Bcfg2/Server/Plugins/NagiosGen.py Update ``/etc/bcfg2.conf``, adding NagiosGen to plugins:: plugins = Base,Bundler,Cfg,...,NagiosGen Create the NagiosGen directory:: $ mkdir /var/lib/bcfg2/NagiosGen Create default host, and group specs in: ``/var/lib/bcfg2/NagiosGen/default-host.cfg``:: define host{ name default check_command check-host-alive check_interval 5 check_period 24x7 contact_groups admins event_handler_enabled 1 failure_prediction_enabled 1 flap_detection_enabled 1 initial_state o max_check_attempts 10 notification_interval 0 notification_options d,u,r notification_period workhours notifications_enabled 1 process_perf_data 0 register 0 retain_nonstatus_information 1 retain_status_information 1 retry_interval 1 } ``/var/lib/bcfg2/NagiosGen/default-group.cfg``:: define service{ name default-service active_checks_enabled 1 passive_checks_enabled 1 obsess_over_service 0 check_freshness 0 notifications_enabled 1 event_handler_enabled 1 flap_detection_enabled 1 process_perf_data 0 retain_status_information 1 retain_nonstatus_information 1 is_volatile 0 check_period 24x7 max_check_attempts 4 check_interval 5 retry_interval 1 contact_groups admins notification_options w,u,c,r notification_interval 0 notification_period workhours } Create group configuration files (Named identical to Bcfg2 groups) and add services, and commands specific to the hostgroup (Bcfg2 group) in ``/var/lib/bcfg2/NagiosGen/base-group.cfg``:: define hostgroup{ hostgroup_name base alias base notes Notes } define service{ service_description NTP check_command check_ntp! use default-service hostgroup_name base } define command{ command_name check_ssh command_line $USER1$/check_ssh $ARG1$ $HOSTADDRESS$ } define service{ service_description SSH check_command check_ssh! use default-service hostgroup_name base } ``/var/lib/bcfg2/NagiosGen/web-server-group.cfg``:: define hostgroup{ hostgroup_name web-server alias Port 80 Web Servers notes UC/ANL Teragrid Web Servers Running on Port 80 } define command{ command_name check_http_80 command_line $USER1$/check_http $HOSTADDRESS$ } define service{ service_description HTTP:80 check_command check_http_80! use default-service hostgroup_name web-server } Create a nagios Bcfg2 bundle ``/var/lib/bcfg2/Bundler/nagios.xml`` .. code-block:: xml Assign clients to nagios groups in ``/var/lib/bcfg2/Metadata/groups.xml`` .. code-block:: xml Update nagios configuration file to use ``nagiosgen.cfg``:: cfg_file=/etc/nagios/nagiosgen.cfg Note that some of these files are built on demand, each time a client in group "nagios-server" checks in with the Bcfg2 server. Local nagios instances can be configured to use the NagiosGen directory in the Bcfg2 repository directly. Fine-Grained Configuration ========================== NagiosGen can be configured in excruciating detail by editing ``NagiosGen/config.xml``, which will let you set individual Nagios options for hosts or groups. E.g.: .. code-block:: xml Obviously the sort of fine-grained control you get from this overlaps to some degree with Nagios' own templating, so use it wisely and in moderation. ``NagiosGen/config.xml`` replaces the files ``Properties/NagiosGen.xml`` and ``NagiosGen/parents.xml`` in older versions of Bcfg2; your old configs can be migrated using the ``nagiosgen-convert.py`` tool. The plugin does contain a backwards-compatibility layer for those older config files, but ``NagiosGen/config.xml`` must exist (even if empty) for the plugin to function. bcfg2-1.3.3/doc/server/plugins/generators/packages.txt000066400000000000000000000707451223671746500227570ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-generators-packages: ======== Packages ======== .. versionadded:: 1.0.0 This page documents the Packages plugin. Packages is an alternative to :ref:`Pkgmgr ` for specifying package entries for clients. Where Pkgmgr explicitly specifies package entry information, Packages delegates control of package version information to the underlying package manager, installing the latest version available through those channels. Limiting sources to groups ========================== `sources.xml`_ processes ```` and ```` tags just like Bundles. In addition to any groups or clients specified that way, clients must be a member of the appropriate architecture group as specified in a Source stanza. In total, in order for a source to be associated with a client, the client must be in any explicit groups or clients specified in `sources.xml`_, and any specified architecture groups. If `"Magic Groups"`_ are enabled, then the client must be a member of a matching magic group as well. Memberships in architecture groups is needed so that Packages can map software sources to clients. There is no other way to handle this than to impose membership in the appropriate architecture group. When multiple sources are specified, clients are associated with each source to which they apply (based on group memberships, as described above). Packages and dependencies are resolved from all applicable sources. .. note:: To recap, a client needs to be a member of the **Architecture** group and any other groups defined in your `sources.xml`_ file in order for the client to be associated to the proper sources. If you are using :ref:`server-plugins-generators-packages-magic-groups`, then a client must also be a member of the appropriate OS group. .. _server-plugins-generators-packages-magic-groups: "Magic Groups" ============== .. deprecated:: 1.3.0 Packages has the ability to use a feature known as "magic groups"; it is the only plugin to use that feature. Most plugins operate based on client group memberships, without any concern for the particular names chosen for groups by the user. The Packages plugin is the sole exception to this rule. Packages needs to "know" two different sorts of facts about clients. The first is the basic OS/distro of the client, enabling classes of sources. The second is the architecture of the client, enabling sources for a given architecture. In addition to these magic groups, each source may also specify non-magic groups to limit the source's applicability to group member clients. +--------+----------+--------------+ | Source | OS Group | Architecture | +========+==========+==============+ | Apt | debian | i386 | +--------+----------+--------------+ | Apt | ubuntu | amd64 | +--------+----------+--------------+ | Apt | nexenta | | +--------+----------+--------------+ | Apt | apt | | +--------+----------+--------------+ | Yum | redhat | i386 | +--------+----------+--------------+ | Yum | centos | x86_64 | +--------+----------+--------------+ | Yum | fedora | | +--------+----------+--------------+ | Yum | yum | | +--------+----------+--------------+ Magic OS groups are disabled by default in Bcfg2 1.3 and greater. If you require magic groups, you can enable them by setting ``magic_groups`` to ``1`` in the ``[packages]`` section of ``bcfg2.conf``. Magic groups will be removed in a future release. Magic architecture groups cannot be disabled. Setup ===== Three basic steps are required for Packages to work properly. #. Create Packages/`sources.xml`_. This file should look approximately like the example below, and describes both which software repositories should be used, and which clients are eligible to use each one. #. Ensure that clients are members of the proper groups. Each client should be a member of all of the groups listed in the `sources.xml` (like ubuntu-intrepid or centos-5.2 in the following examples), one of the architecture groups listed in the source configuration (i386, amd64 or x86_64 in the following examples), and one of the magic groups listed above, if magic groups are enabled. '''Failure to do this will result in the source either not applying to the client, or only architecture independent packages being made available to the client.''' #. Add Package entries to bundles. #. Sit back and relax, as dependencies are resolved, and automatically added to client configurations. sources.xml ----------- ``sources.xml`` is where all package sources are configured for the Packages plugin. It processes ```` and ```` tags just like Bundles. The primary element in ``sources.xml`` is the Source tag: .. xml:element:: Source Handling GPG Keys ----------------- .. versionadded:: 1.2.0 If you have yum libraries installed, Packages can automatically handle GPG signing keys for Yum and Pulp repositories. (You do not need to use the native yum resolver; if yum libraries are available, GPG signing keys can be handled automatically.) Simply specify the URL to the GPG key(s) for a repository with :xml:element:`GPGKey` elements: .. code-block:: xml x86_64 http://mirror.example.com/keys/RPM-GPG-KEY-CentOS-6 More than one ```` tag can be specified per Source. With the keys specified thusly, Packages will include the keys in the generated yum config file, and will ensure that the keys are imported on the client. There is no need to specify ```` tags for :ref:`Pulp sources `; that data is pulled directly from the Pulp REST API. Arbitrary Repo Options ---------------------- .. versionadded:: 1.2.3 You can specify arbitrary options to be added to the repository config on the server side, if you are using the native yum libraries, and on the client side if you are using the ability of Packages to automatically generate your Yum config. To do this, add an :xml:element:`Options` tag to a :xml:element:`Source`; all of its attributes will be added verbatim to the repository in the generated config. For instance: .. code-block:: xml x86_64 If you are using native yum libraries and need to set options only on the Bcfg2 server, you can set the :xml:attribute:`RepoOptionsType:serveronly` attribute to "true"; or, if you need to set options only on the client, you can set the :xml:attribute:`RepoOptionsType:clientonly` attribute to "true". For instance, if your Bcfg2 server needed to use a proxy to access a repo, and you wanted to expire metadata caches very quickly on the client, you could do: .. code-block:: xml x86_64 Prerequisite Resolution ======================= Packages provides a prerequisite resolution mechanism which has no analogue in Pkgmgr. During configuration generation, all structures are processed. After this phase, but before entry binding, a list of packages and the client metadata instance is passed into Packages' resolver. This process determines a superset of packages that will fully satisfy dependencies of all package entries included in structures, and reports any prerequisites that cannot be satisfied. This facility should largely remove the need to use the :ref:`Base ` plugin. Disabling dependency resolution ------------------------------- .. versionadded:: 1.1.0 Dependency resolution can be disabled by adding the following setting to ``bcfg2.conf`` in the ``packages`` section:: [packages] resolver=0 All metadata processing can be disabled as well:: [packages] metadata=0 This setting implies disabling the resolver. Blacklisting faulty dependencies -------------------------------- If you encounter an issue with faulty dependency resolution due to Packages, please file a bug report so that we can fix the problem in future releases. In the meantime, you can work around this issue by blacklisting the offending Package in your Sources. The :xml:element:`Blacklist` element should immediately follow the Component section of your source and should look like the following: .. code-block:: xml unwanted-packagename If you use the built-in :ref:`Yum config generator `, blacklisted packages will be added to the ``exclude`` list for the source. .. _packages-exampleusage: Example usage ============= Create a _`sources.xml` file in the Packages directory that looks something like this: .. code-block:: xml main universe i386 amd64 .. note:: .. versionadded:: 1.1.0 The default behavior of the Packages plugin is to not make any assumptions about which packages you want to have added automatically [#f1]_. For that reason, neither **Recommended** nor **Suggested** packages are added as dependencies by default. You will notice that the default behavior for apt is to add Recommended packages as dependencies. You can configure the Packages plugin to add recommended packages by adding the :xml:attribute:`SourceType:recommended` attribute, e.g.: .. code-block:: xml .. warning:: You must regenerate the Packages cache when adding or removing the recommended attribute (``bcfg2-admin xcmd Packages.Refresh``). .. [#f1] Bcfg2 will by default add **Essential** packages to the client specification. You can disable this behavior by setting the :xml:attribute:`SourceType:essential` attribute to *false*: .. code-block:: xml Yum sources can be similarly specified: .. code-block:: xml os updates extras i386 x86_64 http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-5 For sources with a :xml:attribute:`SourceType:url` attribute, the :xml:attribute:`SourceType:version` attribute is also necessary. :ref:`Pulp sources ` are very simple to specify due to the amount of data that can be queried from Pulp itself: .. code-block:: xml .. note:: There is also a rawurl attribute for specifying sources that don't follow the conventional layout. .. code-block:: xml x86_64 x86_64 x86_64 .. code-block:: xml amd64 i386 Configuration Updates ===================== Packages will reload its configuration upon an explicit command via bcfg2-admin:: [0:3711] bcfg2-admin xcmd Packages.Refresh True During this command (which will take some time depending on the quantity and size of the sources listed in the configuration file), the server will report information like:: Packages: Updating http://mirror.anl.gov/ubuntu//dists/jaunty/main/binary-i386/Packages.gz Packages: Updating http://mirror.anl.gov/ubuntu//dists/jaunty/main/binary-amd64/Packages.gz Packages: Updating http://mirror.anl.gov/ubuntu//dists/jaunty/universe/binary-i386/Packages.gz Packages: Updating http://mirror.anl.gov/ubuntu//dists/jaunty/universe/binary-amd64/Packages.gz ... Packages: Updating http://mirror.centos.org/centos/5/extras/x86_64/repodata/filelists.xml.gz Packages: Updating http://mirror.centos.org/centos/5/extras/x86_64/repodata/primary.xml.gz One line per file download needed. ``Packages/sources.xml`` will be reloaded at this time, so any source specification changes (new or modified sources in this file) will be reflected by the server at this point. This process is much, much faster if you use the :ref:`native yum library support `. Soft reload ----------- .. versionadded:: 1.2.0 A soft reload can be performed to reread the configuration file and download only missing sources.:: [0:3711] bcfg2-admin xcmd Packages.Reload True This is done automatically any time `sources.xml`_ is updated. Availability ============ Support for clients using yum and apt is currently available. Support for other package managers (Portage, Zypper, IPS, etc) remain to be added. Package Checking and Verification ================================= In order to do disable per-package verification, you will need to use :ref:`BoundEntries `, e.g.: .. code-block:: xml .. _generating-client-configs: Generating Client APT/Yum Configurations ======================================== The Packages plugin has native support for generating Yum and Apt configs. Simply add entries like these to the appropriate bundles: .. code-block:: xml If you want to change the path to either of those files, you can set ``yum_config`` or ``apt_config`` in ``bcfg2.conf`` to the path to the config files you want to generate:: [packages] yum_config=/etc/yum.repos.d/all.repo apt_config=/etc/apt/sources.d/all If you need to distribute a config to different places on different hosts, you can use the :ref:`server-plugins-structures-altsrc` attribute, e.g.: .. code-block:: xml See :ref:`configuration` for more details on these options. .. note:: Support for generating Yum configs was added in 1.2.0, and Apt configs was added in 1.3.0. Before that, you could use :ref:`server-plugins-generators-cfg-genshi` or :ref:`server-plugins-generators-cfg-cheetah` to generate your configs. .. _native-yum-libraries: Using Native Yum Libraries ========================== .. versionadded:: 1.2.0 By default, Bcfg2 uses an internal implementation of Yum's dependency resolution and other routines so that the Bcfg2 server can be run on a host that does not support Yum itself. If you run the Bcfg2 server on a machine that does have Yum libraries, however, you can enable use of those native libraries in Bcfg2 by setting ``use_yum_libraries`` to ``1`` in the ``[packages:yum]`` section of ``bcfg2.conf``. Benefits to this include: * Much lower memory usage by the ``bcfg2-server`` process. * Much faster ``Packages.Refresh`` behavior. * More accurate dependency resolution. * Better use of multiple processors/cores. Drawbacks include: * Resolution of package dependencies is slower and more resource-intensive. At times it can be much slower, particularly after running ``Packages.Refresh``. * More disk I/O. This can be alleviated by putting ``/var/lib/bcfg2/Packages/cache`` on tmpfs, but that offsets the lower memory usage. In some cases, you may have to raise the open file limit for the user who runs your Bcfg2 server process, particularly if you have a lot of repositories. Configuring the Yum Helper -------------------------- Due to poor memory management by the Yum API, the long-lived bcfg2-server process uses an external short-lived helper, ``bcfg2-yum-helper``, to do the actual Yum API calls for native yum library support. By default, Bcfg2 looks for this helper in ``$PATH``, or, failing that, at ``/usr/sbin/bcfg2-yum-helper``. If you have installed the helper elsewhere, you will need to configure that location with the ``helper`` option in the ``[packages:yum]`` section, e.g.:: [packages:yum] use_yum_libraries = 1 helper = /usr/local/sbin/bcfg2-yum-helper Setting Yum Options ------------------- In ``bcfg2.conf``, any options you set in the ``[packages:yum]`` section other than ``use_yum_libraries`` and ``helper`` will be passed along verbatim to the configuration of the Yum objects used in the Bcfg2 server. The following options are set by default, and should not generally be overridden: * ``cachedir`` is set to a hashed value unique to each distinct Yum configuration. Don't set this unless you know what you're doing. * ``keepcache`` is set to ``0``; there is no benefit to changing this. * ``sslverify`` is set to ``0``; change this if you know what you're doing. * ``reposdir`` is set to ``/dev/null`` to prevent the server's Yum configuration from being read; do not change this. Package Groups -------------- Yum package groups are supported by both the native Yum libraries and Bcfg2's internal dependency resolver. To include a package group, use the :xml:attribute:`PackageStructure:group` attribute of the :xml:element:`Package` tag. You can use either the short group ID or the long group name: .. code-block:: xml By default, only those packages considered the "default" packages in a group will be installed. You can change this behavior using the :xml:attribute:`PackageStructure:type` attribute: .. code-block:: xml Valid values of "type" are: * ``mandatory``: Only install mandatory packages in the group. * ``default``: Install default packages from the group (the default). * ``optional`` or ``all``: Install all packages in the group, including mandatory, default, and optional packages. See :xml:type:`PackageStructure` for details. You can view the packages in a group by category with the ``yum groupinfo`` command. More information about the different levels can be found at http://fedoraproject.org/wiki/How_to_use_and_edit_comps.xml_for_package_groups#Installation Abstract Package Tags --------------------- If you are using the native Yum libraries, the abstract Package tag supports several attributes in addition to the standard :xml:attribute:`PackageStructure:name`: .. xml:type:: PackageStructure .. _pulp-source-support: Pulp Support ============ .. versionadded:: 1.2.0 Bcfg2 contains explicit support for repositories managed by Pulp (http://pulpproject.org/). .. note:: Only the Pulp 1.x API is supported at this time. When the Pulp 2.x API is finalized support will be added for it. Due to the amount of data about a repository that can be retrieved directly from Pulp, the only thing necessary to configure a Pulp repo is the repo ID, in :xml:attribute:`SourceType:pulp_id`: .. code-block:: xml Pulp sources require some additional configuration. First, the Bcfg2 server must have a valid ``/etc/pulp/consumer/consumer.conf`` that is readable by the user your Bcfg2 server runs as; the Pulp server, URLs, and so on, are determined from this. Secondly, in ``bcfg2.conf`` you must set the following options in the ``[packages:pulp]`` section: * ``username`` and ``password``: The username and password of a Pulp user that will be used to register new clients and bind them to repositories. Membership in the default ``consumer-users`` role is sufficient. Bcfg2 clients using Pulp sources will be registered to the Pulp server as consumers, and will be bound to the appropriate repositories. Debugging unexpected behavior ============================= .. versionadded:: 1.2.1 Using bcfg2-info ---------------- The dependency resolver used in Packages can be run in debug mode:: $ bcfg2-info packageresolve foo.example.com bcfg2-server zlib ... 2 initial packages bcfg2-server zlib 54 new packages added sqlite less libxml2 expat ... 1 unknown packages libglib-2.0.so.0()(64bit) This will show why the resolver is acting as it is. Replace ``foo.example.com`` and ``bcfg2-server`` with a client name and list of packages, respectively. Note that resolving a partial package list (as above) may result in more unknown entries than you'd have otherwise; some of the package drivers (Yum in particular) consider the full package list when resolving multiple providers, and will not be able to properly resolve some dependencies without a full package list. You can also view the sources applicable to a client:: $ bcfg2-info packagesources foo.example.com ... Name: centos-6-x86_64-updates Type: yum URL: http://mirror.example.com/centos-6-x86_64-updates GPG Key(s): http://mirror.example.com/centos-6-x86_64-updates/RPM-GPG-KEY-CentOS-6 Name: centos-6-x86_64-os Type: yum URL: http://mirror.example.com/centos-6-x86_64-os GPG Key(s): http://mirror.example.com/centos-6-x86_64-os/RPM-GPG-KEY-CentOS-6 Using bcfg2-server ------------------ Once the server is started, enable debugging via bcfg2-admin:: $ bcfg2-admin xcmd Packages.toggle_debug TODO list ========= * Zypper support * Portage support .. _configuration: Configuration ============= ``bcfg2.conf`` contains miscellaneous configuration options for the Packages plugin. Any booleans in the config file accept the values "1", "yes", "true", and "on" for True, and "0", "no", "false", and "off" for False. For historical reasons, ``resolver`` and ``metadata`` also accept "enabled" and "disabled". It understands the following directives: [packages] section ------------------ +-------------+------------------------------------------------------+----------+-------------------------------------------------------------------+ | Name | Description | Values | Default | +=============+======================================================+==========+===================================================================+ | resolver | Enable dependency resolution | Boolean | True | +-------------+------------------------------------------------------+----------+-------------------------------------------------------------------+ | metadata | Enable metadata processing. Disabling ``metadata`` | Boolean | True | | | implies disabling ``resolver`` as well. | | | +-------------+------------------------------------------------------+----------+-------------------------------------------------------------------+ | yum_config | The path at which to generate Yum configs. | String | /etc/yum.repos.d/bcfg2.repo | +-------------+------------------------------------------------------+----------+-------------------------------------------------------------------+ | apt_config | The path at which to generate APT configs. | String | /etc/apt/sources.list.d/bcfg2-packages-generated-sources.list | +-------------+------------------------------------------------------+----------+-------------------------------------------------------------------+ | gpg_keypath | The path on the client RPM GPG keys will be copied | String | /etc/pki/rpm-gpg | | | to before they are imported on the client. | | | +-------------+------------------------------------------------------+----------+-------------------------------------------------------------------+ | version | Set the version attribute used when binding Packages | any|auto | auto | +-------------+------------------------------------------------------+----------+-------------------------------------------------------------------+ | cache | Path where Packages will store its cache | String | /Packages/cache | +-------------+------------------------------------------------------+----------+-------------------------------------------------------------------+ [packages:yum] section ---------------------- +-------------------+----------------------------------------------------------+---------+-----------+ | Name | Description | Values | Default | +===================+==========================================================+=========+===========+ | use_yum_libraries | Whether or not to use the | Boolean | False | | | :ref:`native yum library support ` | | | +-------------------+----------------------------------------------------------+---------+-----------+ | helper | Path to ``bcfg2-yum-helper`` | String | See below | +-------------------+----------------------------------------------------------+---------+-----------+ To find ``bcfg2-yum-helper`` if none is specified, Bcfg2 looks first in ``$PATH`` and then in ``/usr/sbin/bcfg2-yum-helper`` for the helper. All other options in the ``[packages:yum]`` section will be passed along verbatim to the Yum configuration if you are using the native Yum library support. [packages:pulp] section ----------------------- +----------+-----------------------------------------------------+--------+---------+ | Name | Description | Values | Default | +==========+=====================================================+========+=========+ | username | The username of a Pulp user that will be used to | String | None | | | register new clients and bind them to repositories. | | | +----------+-----------------------------------------------------+--------+---------+ | password | The password of the Pulp user | String | None | +----------+-----------------------------------------------------+--------+---------+ The user should be a member of the default ``consumer-users`` role. bcfg2-1.3.3/doc/server/plugins/generators/pkgmgr.txt000066400000000000000000000366711223671746500224700ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-generators-pkgmgr: ====== Pkgmgr ====== The Pkgmgr plugin resolves the Abstract Configuration Entity "Package" to a package specification that the client can use to detect, verify and install the specified package. For a package specification to be included in the Literal configuration the name attribute from an Abstract Package Tag (from Base or Bundler) must match the name attribute of a Package tag in Pkgmgr, along with the appropriate group associations of course. Each file in the Pkgmgr directory has a priority. This allows the same package to be served by multiple files. The priorities can be used to break ties in the case that multiple files serve data for the same package. Usage of Groups in Pkgmgr ========================= Groups are used by the Pkgmgr plugin, along with host metadata, for selecting the package entries to include in the clients literal configuration. They can be thought of as:: if client is a member of group1 then assign to literal config Nested groups are conjunctive (logical and).:: if client is a member of group1 and group2 then assign to literal config Group membership may be negated. Tag Attributes in Pkgmgr ======================== .. xml:schema:: pkglist.xsd :linktotype: :noautodep: PackageType Pkgmgr Directory ================ The Pkgmgr/ directory keeps the XML files that define what packages are available for a host or image and where to find those packages. All the files in the directory are processed. The names of the XML files have no special meaning to Bcfg2; they are simply named so it's easy for the administrator to know what the contents hold. All Packages could be kept in a single file if so desired. Bcfg2 simply uses the Groups in the files and priorities to determine how to assign Packages to a host's literal configuration. Listed detailed below is one possible structure for the Pkgmgr directory. The files are structured to contain particular portions of distribution repositories. The files in the directory are:: $ ls Pkgmgr/ centos-4-noarch-updates.xml centos-4-x86_64-updates.xml centos-4-x86_64.xml backup.example.com.xml fedora-core-4-noarch-updates.xml fedora-core-4-x86-updates.xml fedora-core-4-x86.xml rhel-as-4-noarch-updates.xml rhel-as-4-x86-updates.xml rhel-as-4-x86.xml rhel-es-4-noarch-updates.xml rhel-es-4-x86-updates.xml rhel-es-4-x86.xml rhel-ws-4-noarch-udpates.xml rhel-ws-4-x86_64-updates.xml rhel-ws-4-x86_64.xml rhel-ws-4-x86-updates.xml rhel-ws-4-x86.xml As can be seen the file names have been selected to indicate what the contents are and have been split by Vendor, product and repository area. A partial listing of the centos-4-x86_64.xml is below .. code-block:: xml $ cat centos-4-x86_64.xml ... .. code-block:: xml $ cat centos-4-x86_64-updates.xml ... Here it can be seen that the data is encapsulated in a !PackageList Tag which describes the URI of the files described, the type of package, and the priority of the files in this list. The priority is used to decide which specific file to use when there are multiple files that could be used for a particular host. The highest priority file is the one that is used. Using this system, it is possible to have a file that contains all the Packages from the original installation, centos-4-x86_64.xml in this case, and then create a new file that contains updates that are made available afterwards, centos-4-x86_64-updates.xml and centos-4-noarch-updates.xml in this case. The priority of the update PackageLists just needs to be higher so that they will be selected instead of the original installation Packages. The backup.example.com.xml contains a packalist for a specific host which is qualified by the Client tag. Its Packages have a higher priority than the update Packages. This is because this particular host requires special Packages that are older than the ones available in the updates. .. code-block:: xml ... Simplifying Multi-Architecture Environments with :ref:`Altsrc ` ================================================================================================= Frequently multi-architecture environments (typically x86_64) will run into problems needing to specify different architectures on different groups for clients. For example, desktop machines may install 32-bit compatibility packages in addition to 64-bit ones, while servers may install only 64-bit packages. Specifying this in the Pkgmgr was onerous, because different package targets (64bit, 32+64, etc) needed to be specified on a package by group basis. Two features have been implemented that should ease this situation considerably. * The :ref:`Altsrc ` feature adds the ability to add a "bind as" directive to entries. For example, the following entry, in a bundle: .. code-block:: xml would bind as if it were named bar, while the entry would still appear named "foo" in the client configuration specification. * Pkgmgr now builds virtual package targets for any package with Instance client elements. This means that if a client attempts to bind: .. code-block:: xml It will only include the instances listed in the package. By using these features together, a bundle can include: .. code-block:: xml This in conjunction with a Pkgmgr entry that looks like: .. code-block:: xml Will result in a bound entry that looks like: .. code-block:: xml Altogether, this should move policy decisions about package architectures to bundles/base. Automated Generation of Pkgmgr Configuration Files ================================================== The two utilities detailed below are provided in the tools directory of the source tarball. Also see the general :ref:`Pkgmgr ` and :ref:`server-plugins-structures-altsrc` pages. pkgmgr_gen.py ^^^^^^^^^^^^^ pkgmgr_gen will generate a Pkgmgr file from a list of directories containing RPMs or from a list of YUM repositories.:: [root@bcfg2 Pkgmgr]# pkgmgr_gen.py --help usage: pkgmgr_gen.py [options] options: -h, --help show this help message and exit -aARCHS, --archs=ARCHS Comma separated list of subarchitectures to include. The highest subarichitecture required in an architecture group should specified. Lower subarchitecture packages will be loaded if that is all that is available. e.g. The higher of i386, i486 and i586 packages will be loaded if -a i586 is specified. (Default: all). -dRPMDIRS, --rpmdirs=RPMDIRS Comma separated list of directories to scan for RPMS. Wilcards are permitted. -eENDDATE, --enddate=ENDDATE End date for RPM file selection. -fFORMAT, --format=FORMAT Format of the Output. Choices are yum or rpm. (Default: yum) -gGROUPS, --groups=GROUPS List of comma separated groups to nest Package entities in. -iINDENT, --indent=INDENT Number of leading spaces to indent nested entries in the output. (Default:4) -oOUTFILE, --outfile=OUTFILE Output file name. -P, --pkgmgrhdr Include PackageList header in output. -pPRIORITY, --priority=PRIORITY Value to set priority attribute in the PackageList Tag. (Default: 0) -rRELEASE, --release=RELEASE Which releases to include in the output. Choices are all or latest. (Default: latest). -sSTARTDATE, --startdate=STARTDATE Start date for RPM file selection. -uURI, --uri=URI URI for PackageList header required for RPM format ouput. -v, --verbose Enable verbose output. -yYUMREPOS, --yumrepos=YUMREPOS Comma separated list of YUM repository URLs to load. NOTE: Each URL must end in a '/' character. .. note:: The startdate and enddate options are not yet implemented. pkgmgr_update.py ^^^^^^^^^^^^^^^^ pkgmgr_update will update the release (meaning the epoch, version and release) information in an existing Pkgrmgr file from a list of directories containing RPMs or from a list of YUM repositories. All Tags and other attributes in the existing file will remain unchanged.:: [root@bcfg2 Pkgmgr]# pkgmgr_update.py --help usage: pkgmgr_update.py [options] options: -h, --help show this help message and exit -cCONFIGFILE, --configfile=CONFIGFILE Existing Pkgmgr configuration file name. -dRPMDIRS, --rpmdirs=RPMDIRS Comma separated list of directories to scan for RPMS. Wilcards are permitted. -oOUTFILE, --outfile=OUTFILE Output file name or new Pkgrmgr file. -v, --verbose Enable verbose output. -yYUMREPOS, --yumrepos=YUMREPOS Comma separated list of YUM repository URLs to load. NOTE: Each URL must end in a '/' character. Pkgmgr Configuration Examples ============================= verify_flags ^^^^^^^^^^^^ This entry was used for the Centos test client used during RPM development. .. code-block:: xml Multiple Instances ^^^^^^^^^^^^^^^^^^ .. code-block:: xml Kernel ^^^^^^ .. note:: Multiple instances with the same architecture must be in the installOnlyPkgs list. .. code-block:: xml Per Instance Ignore ^^^^^^^^^^^^^^^^^^^ .. note:: In this case a per instance ignore is actually a bad idea as the verify failure is because of multiarch issues where the last package installed wins. So this would be better as a Package level ignore. Ignore tag entries only work with the RPM driver. They do not appear to be supported in YUM as of 1.0pre5. .. code-block:: xml pkg_checks ^^^^^^^^^^ If pkg_checks = false the version information is not required. If pkg_checks = true the full information is needed as normal. For YUM a minimal entry is .. code-block:: xml In fact for YUM, with pkg_checks = false, any combination of the nevra attributes that will build a valid yum package name (see the Misc heading on the yum man page) is valid. .. code-block:: xml For RPM a minimal entry is .. code-block:: xml verify_fail_action ^^^^^^^^^^^^^^^^^^ The way I have Bcfg2 configured for my development systems. This way it reports bad, but doesn't do anything about it. .. code-block:: xml bcfg2-1.3.3/doc/server/plugins/generators/rules.txt000066400000000000000000000356001223671746500223220ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst .. _server-plugins-generators-rules: ===== Rules ===== The Rules plugin resolves the following Abstract Configuration Entities: * Service * Package * Path * Action * All SELinux entries * POSIXUser * POSIXGroup to literal configuration entries suitable for the client drivers to consume. For an entity specification to be included in the Literal configuration the name attribute from an Abstract Entity Tag (from Base or Bundler) must match the name attribute of an Entity tag in Rules, along with the appropriate group associations of course. Each file in the Rules directory has a priority. This allows the same Entities to be served by multiple files. The priorities can be used to break ties in the case that multiple files serve data for the same Entity. Usage of Groups in Rules ======================== Groups are used by the Rules plugin, along with host metadata, for selecting the Configuration Entity entries to include in the clients literal configuration. They can be thought of as:: if client is a member of group1 then assign to literal config Nested groups are conjunctive (logical and).:: if client is a member of group1 and group2 then assign to literal config Group membership may be negated. Tag Attributes in Rules ======================= Running ``bcfg2-lint`` will check your configuration specification for the presence of any mandatory attributes that are necessary for the entry specified. Rules Tag --------- .. xml:element:: Rules :linktotype: :noautodep: :inlinetypes: RContainerType Package Tag ----------- .. xml:type:: PackageType Action Tag ---------- .. xml:type:: ActionType See also :ref:`client-tools-actions`. Service Tag ----------- .. xml:type:: ServiceType Service mode specification ^^^^^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 1.3.0 In the 1.3.0 release, the "mode" attribute has been replaced by a pair of attributes, :xml:attribute:`ServiceType:restart` and :xml:attribute:`ServiceType:install`, which control how a service is handled more granularly than the old "mode" attribute. The old "mode" attribute values are equivalent as follows: +-----------------------------+------------------------------------------+ | Mode attribute | Equivalent | +=============================+==========================================+ | ``mode="default"`` | ``restart="true" install="true"`` | +-----------------------------+------------------------------------------+ | ``mode="interactive_only"`` | ``restart="interactive" install="true"`` | +-----------------------------+------------------------------------------+ | ``mode="supervised"`` | ``restart="true" install="true"`` | +-----------------------------+------------------------------------------+ | ``mode="manual"`` | ``restart="false" install="false"`` | +-----------------------------+------------------------------------------+ The default is ``restart="true" install="true"`` Previously, "supervised" could be used to start a service during the verification phase; this is no longer supported. Services that have been stopped on a client will be started during the install phase. Path Tag -------- The Path tag has different values depending on the *type* attribute of the path specified in your configuration. Below is a set of tables which describe the attributes available for various Path types. Note that ``secontext`` below expects a full context, not just the type. For instance, "``system_u:object_r:etc_t:s0``", not just ``etc_t``. You can also specify "``__default__``", which will restore the context of the file to the default set by policy. If a file has no default context rule, and you don't wish to set one, you can specify ``secontext=''`` (i.e., an empty ``secontext``), in which case the client will not try to manage the SELinux context of the file at all. See :ref:`server-selinux` for more information. Attributes common to all Path tags: .. xml:type:: PathType :nochildren: :noattributegroups: :nodoc: :notext: :onlyattrs: name,type device ^^^^^^ Manage devices. .. xml:type:: PathType :nochildren: :noattributegroups: :nodoc: :notext: :onlyattrs: dev_type,owner,group,mode,secontext,major,minor :requiredattrs: dev_type,owner,group,mode directory ^^^^^^^^^ Entry represents a directory. :xml:attribute:`PathType:prune` can be set to remove all contents from the directory that are not explicitly specified in Bcfg2. .. xml:type:: PathType :nochildren: :noattributegroups: :nodoc: :notext: :onlyattrs: owner,group,mode,secontext,prune :requiredattrs: owner,group,mode file ^^^^ Distribute an file with content explicitly specified in-line (i.e., as opposed to using :ref:`server-plugins-generators-cfg` for this file). If the file has no content, :xml:attribute:`PathType:empty` *must* be set to ``true``. .. xml:type:: PathType :nochildren: :noattributegroups: :nodoc: :onlyattrs: owner,group,mode,secontext,empty :requiredattrs: owner,group,mode hardlink ^^^^^^^^ Manage a hard link. .. xml:type:: PathType :nochildren: :noattributegroups: :nodoc: :notext: :onlyattrs: owner,group,mode,secontext,to :requiredattrs: owner,group,mode,to .. _path-ignore: ignore ^^^^^^ ``ignore`` lets you flag files that are distributed by system software packages, but have been modified locally, to be ignored by package verification routines. This is useful for, e.g., a package that installs an initial version of a file and then modifies it automatically. .. xml:type:: PathType :nochildren: :noattributegroups: :nodoc: :notext: :onlyattrs: name :requiredattrs: name nonexistent ^^^^^^^^^^^ Remove the specified file or directory. If :xml:attribute:`PathType:recursive` is set, remove the directory recursively (i.e., ``rm -rf``). .. xml:type:: PathType :nochildren: :noattributegroups: :nodoc: :notext: :onlyattrs: recursive permissions ^^^^^^^^^^^ Merely set permissions on the specified path, which is presumed to already exist. .. xml:type:: PathType :nochildren: :noattributegroups: :nodoc: :notext: :onlyattrs: owner,group,mode,secontext,recursive :requiredattrs: owner,group,mode symlink ^^^^^^^ Manage symlinks. .. xml:type:: PathType :nochildren: :noattributegroups: :nodoc: :notext: :onlyattrs: to :requiredattrs: to vcs ^^^ Check out the specified VCS repository to the given path. .. xml:type:: PathType :nochildren: :noattributegroups: :nodoc: :notext: :onlyattrs: vcstype,revision,sourceurl :requiredattrs: vcstype,revision,sourceurl .. _server-plugins-generators-rules-acls: ACLs ^^^^ .. versionadded:: 1.3.0 ACLs on a Path entry are specified not by attributes on the tag but by child ```` tags. For instance: .. code-block:: xml .. xml:element:: ACL It is not currently possible to manually set an effective rights mask; the mask will be automatically calculated from the given ACLs when they are applied. Note that it is possible to set ACLs that demand different permissions on a file than those specified in the ``perms`` attribute on the ``Path`` tag. For instance: .. code-block:: xml In this case, we've specified permissions of ``0644``, but the effective rights mask will be "rwx," so setting the ACL will change the permissions to ``0674``. When this happens, Bcfg2 will change the permissions and set the ACLs on every run and the entry will be eternally marked as bad. SELinux Entries --------------- .. versionadded:: 1.3.0 .. note:: In order to use these entries, the client also needs to be at least version 1.3.0 since they require a client tool which is unavailable in previous versions. Below is a set of tables which describe the attributes available for various SELinux types. The entry types (except for ``module``) correspond to ``semanage`` subcommands. Note that the ``selinuxtype`` attribute takes only an SELinux type, not a full context; e.g., "``etc_t``", not "``system_u:object_r:etc_t:s0``". As it can be very tedious to create a baseline of all existing SELinux entries, you can use ``selinux_baseline.py`` located in the ``tools/`` directory to do that for you. See :ref:`server-selinux` for more information. SEBoolean Tag ^^^^^^^^^^^^^ .. xml:type:: SEBooleanType SEPort Tag ^^^^^^^^^^ .. xml:type:: SEPortType SEFcontext Tag ^^^^^^^^^^^^^^ .. xml:type:: SEFcontextType SENode Tag ^^^^^^^^^^ .. xml:type:: SENodeType SELogin Tag ^^^^^^^^^^^ .. xml:type:: SELoginType SEUser Tag ^^^^^^^^^^ .. xml:type:: SEUserType SEInterface Tag ^^^^^^^^^^^^^^^ .. xml:type:: SEInterfaceType SEPermissive Tag ^^^^^^^^^^^^^^^^ .. xml:type:: SEPermissiveType SEModule Tag ^^^^^^^^^^^^ .. xml:type:: SEModuleType See also :ref:`server-plugins-generators-semodules`. .. _server-plugins-generators-rules-posixuser-tag: POSIXUser Tag ------------- .. versionadded:: 1.3.0 .. note:: In order to use this, the client also needs to be at least version 1.3.0 since they require a client tool which is unavailable in previous versions. .. xml:type:: POSIXUserType For example: .. code-block:: xml Using Regular Expressions in Rules ================================== If you wish, you can configure the Rules plugin to support regular expressions. This entails a small performance and memory usage penalty. To do so, add the following setting to ``bcfg2.conf``:: [rules] regex = yes With regular expressions enabled, you can use a regex in the ``name`` attribute to match multiple abstract configuration entries. Regular expressions are anchored at both ends, so ```` will *not* match a Service named ``bcfg2-server``; you'd have to explicitly specify ````. Note that only one Rule can apply to any abstract entry, so you cannot specify multiple regexes to match the same rule. bcfg2-1.3.3/doc/server/plugins/generators/semodules.txt000066400000000000000000000024731223671746500231720ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-generators-semodules: ========= SEModules ========= .. versionadded:: 1.3.0 .. automodule:: Bcfg2.Server.Plugins.SEModules :no-members: Usage ===== To use the SEModules plugin, first do ``mkdir /var/lib/bcfg2/SEModules``. Add ``SEModules`` to your ``plugins`` line in ``/etc/bcfg2.conf`` and restart bcfg2-server. The SEModules directory contains modules in a layout similar to the Cfg plugin: at the top level, SEModules should contain directories named after the modules you want to install, and each of those directories can contain a global module, plus any number of group- and host-specific modules. For instance:: $ ls -F SEModules foo.pp/ bar.pp/ $ ls SEModules/foo.pp/ foo.pp foo.pp.G50_server foo.pp.H_baz.example.com For more information on this directory layout, see :ref:`server-plugins-generators-cfg`. Entries ======= SEModules handles ```` entries. For instance: .. code-block:: xml The ``.pp`` extension is optional. .. note:: If you use a ``BoundSEModule`` tag, you must *not* include the ``.pp`` extension. This is not recommend, though. You can also install a disabled module: .. code-block:: xml bcfg2-1.3.3/doc/server/plugins/generators/sshbase.txt000066400000000000000000000135051223671746500226200ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-generators-sshbase: ======= SSHbase ======= SSHbase is a purpose-built Bcfg2 plugin for managing ssh host keys. It is responsible for making ssh keys persist beyond a client rebuild and building a proper ``ssh_known_hosts`` file, including a correct localhost record for the current system. It has two functions: * Generating new ssh keys -- When a client requests a dsa, rsa, or v1 key, and there is no existing key in the repository, one is generated. * Maintaining the ``ssh_known_hosts`` file -- all current known public keys (and extra public key stores) are integrated into a single ``ssh_known_hosts`` file, and a localhost record for the current client is added. The ``ssh_known_hosts`` file data is updated whenever any keys change, are added, or deleted. Interacting with SSHbase ======================== * Pre-seeding with existing keys -- Currently existing keys will be overwritten by new, sshbase-managed ones by default. Pre-existing keys can be added to the repository by putting them in ``/SSHbase/.H_`` * Pre-seeding can also be performed using ``bcfg2-admin pull ConfigFile /name/of/ssh/key`` * Revoking existing keys -- deleting ``/SSHbase/\*.H_`` will remove keys for an existing client. Aliases ======= SSHbase has support for Aliases listed in :ref:`clients.xml `. The address for the entries are specified either through DNS (e.g. a CNAME), or via the address attribute to the Alias. Getting started =============== #. Add SSHbase to the **plugins** line in ``/etc/bcfg2.conf`` and restart the server. This enables the SSHbase plugin on the Bcfg2 server. #. Add Path entries for ``/etc/ssh/ssh_known_hosts``, ``/etc/ssh/ssh_host_dsa_key``, ``/etc/ssh/ssh_host_dsa_key.pub``, etc., to a bundle. #. Enjoy. At this point, SSHbase will generate new keys for any client without a recorded key in the repository, and will generate an ``ssh_known_hosts`` file appropriately. Supported key formats ===================== SSHbase currently supports the following key formats: * RSA1 (``ssh_host_key``, ``ssh_host_key.pub``) * RSA2 (``ssh_host_rsa_key``, ``ssh_host_rsa_key.pub``) * DSA (``ssh_host_dsa_key``, ``ssh_host_dsa_key.pub``) * ECDSA (``ssh_host_ecdsa_key``, ``ssh_host_ecdsa_key.pub``) Group-specific keys =================== .. versionadded:: 1.2.0 In addition to host-specific keys, SSHbase also supports group-specific keys, e.g., for a high-availability cluster or similar application. Group-specific keys must be pre-seeded; SSHbase cannot create group-specific keys itself. To use group-specific keys, simply create ``SSHbase/.Gxx_``. For instance, ``ssh_host_dsa_key.pub.G65_foo-cluster``. Adding public keys for unmanaged hosts ====================================== If you have some hosts which are not managed by Bcfg2, but you would still like to have their public ssh keys available in ``ssh_known_hosts``, you can add their public keys to the ``SSHbase`` directory with a *.static* ending. Example: ``a.static``:: TEST1 ``b.static``:: TEST2 The generated ``ssh_known_hosts`` file:: TEST1 TEST2 Static ssh_known_hosts file =========================== .. versionadded:: 1.2.0 You can also distribute a fully static ``ssh_known_hosts`` file on a per-host or per-group basis by creating ``SSHbase/ssh_known_hosts.H_`` or ``SSHbase/ssh_known_hosts.Gxx_``. Those files will be entirely static; Bcfg2 will not add any host keys to them itself. Permissions and Metadata ======================== .. versionadded:: 1.2.0 SSHbase supports use of an :ref:`info.xml ` file to control the permissions and other metadata for the keys and ``ssh_known_hosts`` file. You can use the ```` directive in ``info.xml`` to change the metadata for different keys, e.g.:: Default permissions are as follows: +----------------------------------+-------+-------+------+-----------+----------+----------+ | File | owner | group | mode | sensitive | paranoid | encoding | +==================================+=======+=======+======+===========+==========+==========+ | ssh_known_hosts | root | root | 0644 | false | false | None | +----------------------------------+-------+-------+------+-----------+----------+----------+ | ssh_host_key | root | root | 0600 | false | false | base64 | +----------------------------------+-------+-------+------+-----------+----------+----------+ | ssh_host_key.pub | root | root | 0644 | false | false | base64 | +----------------------------------+-------+-------+------+-----------+----------+----------+ | ssh_host_[rsa|dsa|ecdsa]_key | root | root | 0600 | false | false | None | +----------------------------------+-------+-------+------+-----------+----------+----------+ | ssh_host_[rsa|dsa|ecdsa]_key.pub | root | root | 0644 | false | false | None | +----------------------------------+-------+-------+------+-----------+----------+----------+ Note that the ``sensitive`` attribute is false, even for private keys, in order to permit :ref:`pulling with bcfg2-admin `. You should almost certainly set ``sensitive`` to "true" in ``info.xml``. Blog post ========= http://www.ducea.com/2008/08/24/using-the-bcfg2-sshbase-plugin/ .. note:: The linked post uses deprecated ConfigFile entries. Path entries have since replaced these. See :ref:`server-configurationentries`. bcfg2-1.3.3/doc/server/plugins/generators/sslca.txt000066400000000000000000000321431223671746500222740ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-generators-sslca: ===== SSLCA ===== SSLCA is a generator plugin designed to handle creation of SSL private keys and certificates on request. Borrowing ideas from :ref:`server-plugins-generators-cfg-genshi` and the :ref:`server-plugins-generators-sshbase` plugin, SSLCA automates the generation of SSL certificates by allowing you to specify key and certificate definitions. Then, when a client requests a Path that contains such a definition within the SSLCA repository, the matching key/cert is generated, and stored in a hostfile in the repo so that subsequent requests do not result in repeated key/cert recreation. In the event that a new key or cert is needed, the offending hostfile can simply be removed from the repository, and the next time that host checks in, a new file will be created. If that file happens to be the key, any dependent certificates will also be regenerated. .. _getting-started: Getting started =============== In order to use SSLCA, you must first have at least one CA configured on your system. For details on setting up your own OpenSSL based CA, please see http://www.openssl.org/docs/apps/ca.html for details of the suggested directory layout and configuration directives. For SSLCA to work, the openssl.cnf (or other configuration file) for that CA must contain full (not relative) paths. #. Add SSLCA to the **plugins** line in ``/etc/bcfg2.conf`` and restart the server -- This enabled the SSLCA plugin on the Bcfg2 server. #. Add a section to your ``/etc/bcfg2.conf`` called ``sslca_foo``, replacing foo with the name you wish to give your CA so you can reference it in certificate definitions. #. Under that section, add an entry for ``config`` that gives the location of the openssl configuration file for your CA. #. If necessary, add an entry for ``passphrase`` containing the passphrase for the CA's private key. We store this in ``/etc/bcfg2.conf`` as the permissions on that file should have it only readable by the bcfg2 user. If no passphrase is entry exists, it is assumed that the private key is stored unencrypted. #. Optionally, Add an entry ``chaincert`` that points to the location of your ssl chaining certificate. This is used when preexisting certifcate hostfiles are found, so that they can be validated and only regenerated if they no longer meet the specification. If you're using a self signing CA this would be the CA cert that you generated. If the chain cert is a root CA cert (e.g., if it is a self-signing CA), also add an entry ``root_ca = true``. If ``chaincert`` is omitted, certificate verification will not be performed. #. Once all this is done, you should have a section in your ``/etc/bcfg2.conf`` that looks similar to the following:: [sslca_default] config = /etc/pki/CA/openssl.cnf passphrase = youReallyThinkIdShareThis? chaincert = /etc/pki/CA/chaincert.crt root_ca = true #. You are now ready to create key and certificate definitions. For this example we'll assume you've added Path entries for the key, ``/etc/pki/tls/private/localhost.key``, and the certificate, ``/etc/pki/tls/certs/localhost.crt`` to a bundle or base. #. Defining a key or certificate is similar to defining a Cfg file. Under your Bcfg2's ``SSLCA/`` directory, create the directory structure to match the path to your key. In this case this would be something like ``/var/lib/bcfg2/SSLCA/etc/pki/tls/private/localhost.key``. #. Within that directory, create a `key.xml`_ file containing the following: .. code-block:: xml #. This will cause the generation of an 2048 bit RSA key when a client requests that Path. Alternatively you can specify ``dsa`` as the keytype, or a different number of bits. #. Similarly, create the matching directory structure for the certificate path, and a `cert.xml`_ containing the following: .. code-block:: xml #. When a client requests the cert path, a certificate will be generated using the key hostfile at the specified key location, using the CA matching the ca attribute. ie. ca="default" will match [sslca_default] in your ``/etc/bcfg2.conf`` .. _sslca-configuration: Configuration ============= bcfg2.conf ---------- ``bcfg2.conf`` contains miscellaneous configuration options for the SSLCA plugin. These are described in some detail above in `getting-started`, but are also enumerated here as a reference. Any booleans in the config file accept the values "1", "yes", "true", and "on" for True, and "0", "no", "false", and "off" for False. Each directive below should appear at most once in each ``[sslca_]`` section. The following directives are understood: +--------------+------------------------------------------+---------+---------+ | Name | Description | Values | Default | +==============+==========================================+=========+=========+ | config | Path to the openssl config for the CA | String | None | +--------------+------------------------------------------+---------+---------+ | passphrase | Passphrase for the CA private key | String | None | +--------------+------------------------------------------+---------+---------+ | chaincert | Path to the SSL chaining certificate for | String | None | | | verification | | | +--------------+------------------------------------------+---------+---------+ | root_ca | Whether or not ```` is a root | Boolean | false | | | CA (as opposed to an intermediate cert) | | | +--------------+------------------------------------------+---------+---------+ Only ``config`` is required. cert.xml -------- .. xml:schema:: sslca-cert.xsd :linktotype: :inlinetypes: CertType Example ^^^^^^^ .. code-block:: xml test.example.com key.xml ------- .. xml:schema:: sslca-key.xsd :linktotype: :inlinetypes: KeyType Example ^^^^^^^ .. code-block:: xml Automated Bcfg2 SSL Authentication ================================== This section describes one possible scenario for automating ssl certificate generation and distribution for bcfg2 client/server communication using SSLCA. The process involves configuring a certificate authority (CA), generating the CA cert and key pair, configuring the bcfg2 SSLCA plugin and a Bundle to use the SSLCA generated certs to authenticate the bcfg2 client and server. OpenSSL CA ---------- If you already have a SSL CA available you can skip this section, otherwise you can easily build one on the server using openssl. The paths should be adjusted to suite your preferences. #. Prepare the directories and files:: mkdir -p /etc/pki/CA/newcerts mkdir /etc/pki/CA/crl echo '01' > /etc/pki/CA/serial touch /etc/pki/CA/index.txt touch /etc/pki/CA/crlnumber #. Edit the ``openssl.cnf`` config file, and in the **[ CA_default ]** section adjust the following parameters:: dir = /etc/pki # Where everything is kept certs = /etc/pki/CA/certs # Where the issued certs are kept database = /etc/pki/CA/index.txt # database index file. new_certs_dir = /etc/pki/CA/newcerts # default place for new certs. certificate = /etc/pki/CA/certs/bcfg2ca.crt # The CA certificate serial = /etc/pki/CA/serial # The current serial number crl_dir = /etc/pki/CA/crl # Where the issued crl are kept crlnumber = /etc/pki/CA/crlnumber # the current crl number crl = /etc/pki/CA/crl.pem # The current CRL private_key = /etc/pki/CA/private/bcfg2ca.key # The private key #. Create the CA root certificate and key pair. You'll be asked to supply a passphrase, and some organizational info. The most important bit is **Common Name** which you should set to be the hostname of your bcfg2 server that your clients will see when doing a reverse DNS query on it's ip address.:: openssl req -new -x509 -extensions v3_ca -keyout bcfg2ca.key \ -out bcfg2ca.crt -days 3650 #. Move the generated cert and key to the locations specified in ``openssl.cnf``:: mv bcfg2ca.key /etc/pki/CA/private/ mv bcfg2ca.crt /etc/pki/CA/certs/ Your self-signing CA is now ready to use. Bcfg2 ----- SSLCA ^^^^^ The SSLCA plugin was not designed specifically to manage bcfg2 client/server communication though it is certainly able to provide certificate generation and management services for that purpose. You'll need to configure the **SSLCA** plugin to serve the key, and certificate paths that we will define later in our client's ``bcfg2.conf`` file. The rest of these instructions will assume that you've configured the **SSLCA** plugin as described above and that the files ``SSLCA/etc/pki/tls/certs/bcfg2client.crt/cert.xml`` and ``SSLCA/etc/pki/tls/private/bcfg2client.key/key.xml`` represent the cert and key paths you want generated for SSL auth. Client Bundle ^^^^^^^^^^^^^ To automate the process of generating and distributing certs to the clients we need define at least the Cert and Key paths served by the SSLCA plugin, as well as the ca certificate path in a Bundle. For example: .. code-block:: xml Here's a more complete example bcfg2-client bundle: .. code-block:: xml In the above example we told Bcfg2 that it also needs to serve ``/etc/bcfg2.conf``. This is optional but convenient. The ``bcfg2.conf`` client config needs at least 5 parameters set for SSL auth. #. ``key`` : This is the host specific key that SSLCA will generate. #. ``certificate`` : This is the host specific cert that SSLCA will generate. #. ``ca`` : This is a copy of your CA certificate. Not generated by SSLCA. #. ``user`` : Usually set to fqdn of client. This *shouldn't* be required but is as of 1.3.0. See: http://trac.mcs.anl.gov/projects/bcfg2/ticket/1019 #. ``password`` : Set to arbitrary string when using certificate auth. This also *shouldn't* be required. See: http://trac.mcs.anl.gov/projects/bcfg2/ticket/1019 Here's what a functional **[communication]** section in a ``bcfg2.conf`` genshi template for clients might look like.:: [communication] protocol = xmlrpc/ssl {% if metadata.uuid != None %}\ user = ${metadata.uuid} {% end %}\ password = DUMMYPASSWORDFORCERTAUTH {% choose %}\ {% when 'rpm' in metadata.groups %}\ certificate = /etc/pki/tls/certs/bcfg2client.crt key = /etc/pki/tls/private/bcfg2client.key ca = /etc/pki/tls/certs/bcfg2ca.crt {% end %}\ {% when 'deb' in metadata.groups %}\ certificate = /etc/ssl/certs/bcfg2client.crt key = /etc/ssl/private/bcfg2client.key ca = /etc/ssl/certs/bcfg2ca.crt {% end %}\ {% end %}\ As a client will not be able to authenticate with certificates it does not yet posses we need to overcome the chicken and egg scenario the first time we try to connect such a client to the server. We can do so using password based auth to boot strap the client manually specifying all the relevant auth parameters like so:: bcfg2 -qv -S https://fqdn.of.bcfg2-server:6789 -u fqdn.of.client \ -x SUPER_SECRET_PASSWORD If all goes well the client should recieve a freshly generated key and cert and you should be able to run ``bcfg2`` again without specifying the connection parameters. If you do run into problems you may want to review :ref:`appendix-guides-authentication`. TODO ==== #. Add generation of pkcs12 format certs bcfg2-1.3.3/doc/server/plugins/generators/tcheetah.txt000066400000000000000000000154671223671746500227660ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-generators-tcheetah: ======== TCheetah ======== .. warning:: TCheetah is deprecated. You should instead use :ref:`server-plugins-generators-cfg-cheetah` in the Cfg plugin. This document reflects the ``TCheetah`` plugin. The ``TCheetah`` plugin allows you to use the `cheetah templating system `_ to create files, instead of the various diff-based methods offered by the ``Cfg`` plugin. It also allows you to include the results of probes executed on the client in the created files. To begin, you will need to download and install the Cheetah templating engine from http://www.cheetahtemplate.org/. Once it is installed, you can enable it by adding ``TCheetah`` to the ``plugins`` line in ``/etc/bcfg2.conf`` on your Bcfg server. For example:: plugins = Base,Bundler,Cfg,...,TCheetah The ``TCheetah`` plugin makes use of a ``Cfg``-like directory structure located in in a ``TCheetah`` subdirectory of your repository, usually ``/var/lib/bcfg2/TCheetah``. Each file has a directory containing two files, ``template`` and ``info``. The template is a standard Cheetah template with two additions: * `self.metadata` is the client's :ref:`metadata ` * `self.metadata.Properties.xdata` is an xml document of unstructured data The ``info`` file is formatted like ``:info`` files from Cfg. Mostly, people will want to use client metadata. File permissions ================ File permissions for entries handled by TCheetah are controlled via the use of :ref:`server-info` files. Note that you **cannot** use both a Permissions entry and a Path entry to handle the same file. self.metadata variables ======================= self.metadata is an instance of the class ClientMetadata and documented :ref:`here `. self.metadata.Properties.xdata ============================== .. note:: If you want to use Properties, you will need to enable the :ref:`server-plugins-connectors-properties` plugin in ``/etc/bcfg2.conf``. Properties.xdata is a python `ElementTree `_ object, loaded from the data in ``/var/lib/bcfg2/Properties/.xml``. That file should have a ``Properties`` node at its root. Example ``Properties/example.xml``: .. code-block:: xml /dev/sda You may use any of the ElementTree methods to access data in your template. Several examples follow, each producing an identical result on the host 'www.example.com':: $self.metadata.Properties['example.xml'].xdata.find('host').find('www.example.com').find('rootdev').text $self.metadata.Properties['example.xml'].xdata.find('host').find($self.metadata.hostname).find('rootdev').text ${self.metadata.Properties['example.xml'].xdata.xpath('host/www.example.com/rootdev')[0].text} ${self.metadata.Properties['example.xml'].xdata.xpath('host/' + self.metadata.hostname + '/rootdev')[0].text} #set $path = 'host/' + $self.metadata.hostname + '/rootdev' ${self.metadata.Properties['example.xml'].xdata.xpath($path)[0].text} ${self.metadata.Properties['example.xml'].xdata.xpath(path)[0].text} Other Variables =============== * **Template.searchList(self)[1]['path']** is the Path name specified in a Bundle * **Template.searchList(self)[1]['source_path']** is the path to the TCheetah template on the Bcfg2 server Simple Example ============== TCheetah works similar to Cfg in that you define all literal information about a particular file in a directory rooted at TCheetah/path_to_file. The actual file contents are placed in a file named `template` in that directory. Below is a simple example a file ``/foo``. ``/var/lib/bcfg2/TCheetah/foo/template`` .. code-block:: none > buildfile /foo Hostname is $self.metadata.hostname Filename is $Template.searchList(self)[1]['path'] Template is $Template.searchList(self)[1]['source_path'] Groups: #for $group in $self.metadata.groups: * $group #end for Categories: #for $category in $self.metadata.categories: * $category -- $self.metadata.categories[$category] #end for Probes: #for $probe in $self.metadata.Probes: * $probe -- $self.metadata.Probes[$probe] #end for ``/var/lib/bcfg2/TCheetah/foo/info`` .. code-block:: none mode: 624 Output ------ The following output can be generated with bcfg2-info. Note that probe information is not persistent, hence, it only works when clients directly query the server. For this reason, bcfg2-info output doesn't reflect current client probe state. .. code-block:: xml Hostname is topaz.mcs.anl.gov Filename is /foo Template is /var/lib/bcfg2/TCheetah/foo/template Groups: * desktop * mcs-base * ypbound * workstation * xserver * debian-sarge * debian * a Categories: * test -- a Probes: Example: Replace the crontab plugin =================================== In many cases you can use the TCheetah plugin to avoid writing custom plugins in Python. This example randomizes the time of cron.daily execution with a stable result. Cron.daily is run at a consistent, randomized time between midnight and 7am.:: #import random #silent random.seed($self.metadata.hostname) # /etc/crontab: system-wide crontab # Unlike any other crontab you don't have to run the `crontab` # command to install the new version when you edit this file. # This file also has a username field, that none of the other crontabs do. SHELL=/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin://bin # m h dom mon dow user command 17 * * * * root run-parts --report /etc/cron.hourly $random.randrange(0,59) $random.randrange(0,6) * * * root test -x /usr/sbin/anacron || run-parts --report /etc/cron.daily 47 6 * * 7 root test -x /usr/sbin/anacron || run-parts --report /etc/cron.weekly 52 6 1 * * root test -x /usr/sbin/anacron || run-parts --report /etc/cron.monthly. .. note:: Comments and Cheetah As Cheetah processes your templates it will consider hash "#" style comments to be actual comments in the template and will strip them from the final config file. If you would like to preserve the comment in the final config file you need to escape the hash character '\#' which will tell Cheetah (and Python) that you do in fact want the comment to appear in the final config file.:: # This is a comment in my template which will be stripped when it's processed through Cheetah \# This comment will appear in the generated config file. bcfg2-1.3.3/doc/server/plugins/generators/tgenshi.txt000066400000000000000000000160611223671746500226310ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-generators-tgenshi-index: ======= TGenshi ======= .. warning:: The TGenshi plugin is deprecated. You should instead use :ref:`server-plugins-generators-cfg-genshi` in the Cfg plugin. This page documents the TGenshi plugin. This plugin works with version 0.4 and newer of the genshi library. The TGenshi plugin allows you to use the `Genshi `_ templating system to create files, instead of the various diff-based methods offered by the Cfg plugin. It also allows you to include the results of probes executed on the client in the created files. To begin, you will need to download and install the Genshi templating engine. To install on CentOS or RHEL, run:: sudo yum install python-genshi Once it is installed, you can enable it by adding ``TGenshi`` to the generators line in ``/etc/bcfg2.conf`` on your Bcfg server. For example:: plugins = Base,Bundler,Cfg,...,TGenshi The TGenshi plugin makes use of a Cfg-like directory structure located in in a TGenshi subdirectory of your repository, usually ``/var/lib/bcfg2/TGenshi``. Each file has a directory containing two file types, template and info. Templates are named according to the genshi format used; template.txt uses the genshi text format, and template.xml uses the XML format. If used with Genshi 0.5 or later the plugin also supports the `new style `_ text template format for files named template.newtxt. One of the advantages of the new format is that it does not use # as a command delimiter, making it easier to utilize for configuration files that use # as a comment character. Only one template format may be used per file served. Info files are identical to those used in ``Cfg``, and ``info.xml`` files are supported. Inside of templates =================== * **metadata** is the client's :ref:`metadata ` * **metadata.Properties** is an xml document of unstructured data (only available when used in conjunction with the :ref:`server-plugins-connectors-properties` plugin) * **name** is the path name specified in bcfg * **path** is the path to the TGenshi template. It starts with a leading slash, and is relative to the Bcfg2 specification root. E.g., ``/Cfg/etc/foo.conf/foo.conf.genshi`` or ``/TGenshi/etc/foo.conf/template.newtxt.H_foo.example.com`` See the genshi `documentation `_ for examples of Genshi syntax. Examples: Old Genshi Syntax --------------------------- Genshi's web pages recommend against using this syntax, as it may disappear from future releases. Group Negation ^^^^^^^^^^^^^^ Templates are also useful for cases where more sophisticated boolean operations than those supported by Cfg are needed. For example, the template:: #if "ypbound" in metadata.groups and "workstation" in metadata.groups client is ypbound workstation #end #if "ubuntu" not in metadata.groups and "desktop" in metadata.groups client is a desktop, but not an ubuntu desktop #end Produces: .. code-block:: xml client is ypbound workstation client is a desktop, but not an ubuntu desktop This flexibility provides the ability to build much more compact and succinct definitions of configuration contents than Cfg can. Troubleshooting =============== When developing a template, you can see what the template would generate on a client with :ref:`bcfg2-info `:: bcfg2-info buildfile E.g.:: bcfg2-info buildfile /etc/foo.conf foo.example.com To generate a file with an altsrc attribute, you can run:: bcfg2-info buildfile /etc/foo/foo.conf --altsrc=/etc/foo.conf \ foo.example.com Sometimes, it's useful to be able to do more in-depth troubleshooting by running the template manually. To do this, run ``bcfg2-info debug``, and, once in the Python interpreter, run:: metadata = self.build_metadata("") path = "" ``path`` should be set to the path to the template file with a leading slash, relative to the Bcfg2 specification root. See `Inside of Templates`_ for examples. Then, run:: import os, Bcfg2.Options from genshi.template import TemplateLoader, NewTextTemplate name = os.path.dirname(path[path.find('/', 1):]) setup = Bcfg2.Options.OptionParser({'repo': Bcfg2.Options.SERVER_REPOSITORY}) setup.parse('--') template = TemplateLoader().load(setup['repo'] + path, cls=NewTextTemplate) print template.generate(metadata=metadata, path=path, name=name).render() This gives you more fine-grained control over how your template is rendered. You can also use this approach to render templates that depend on :ref:`altsrc ` tags by setting ``path`` to the path to the template, and setting ``name`` to the path to the file to be generated, e.g.:: metadata = self.build_metadata("foo.example.com") path = "/Cfg/etc/sysconfig/network-scripts/ifcfg-template/ifcfg-template.genshi" name = "/etc/sysconfig/network-scripts/ifcfg-bond0" File permissions ================ File permissions for entries handled by TGenshi are controlled via the use of :ref:`server-info` files. Note that you **cannot** use both a Permissions entry and a Path entry to handle the same file. Error handling ================ Situations may arise where a templated file cannot be generated due to missing or incomplete information. A TemplateError can be raised to force a bind failure and prevent sending an incomplete file to the client. For example, this template:: {% python from genshi.template import TemplateError grp = None for g in metadata.groups: if g.startswith('ganglia-gmond-'): grp = g break else: raise TemplateError, "Missing group" %}\ will fail to bind if the client is not a member of a group starting with "ganglia-gmond-". The syslogs on the server will contain this message:: bcfg2-server[5957]: Genshi template error: Missing group bcfg2-server[5957]: Failed to bind entry: Path /etc/ganglia/gmond.conf indicating the bind failure and message raised with the TemplateError. FAQs ==== **Question** How do I escape the $ (dollar sign) in a TGenshi text template? For example, if I want to include SVN (subversion) keywords like $Id$ or $HeadURL$ in TGenshi-generated files, or am templating a bourne shell (sh/bash) script or Makefile (make). **Answer** Use $$ (double dollar sign) to output a literal $ (dollarsign) in a TGenshi text template. So instead of $Id$, you'd use $$Id$$. See also Genshi tickets `#282: Document $$ escape convention `_ and `#283: Allow for redefinition of template syntax per-file `_. Examples ======== .. toctree:: :glob: :maxdepth: 1 examples/genshi/* bcfg2-1.3.3/doc/server/plugins/grouping/000077500000000000000000000000001223671746500201045ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/plugins/grouping/grouppatterns.txt000066400000000000000000000053071223671746500235670ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-grouping-grouppatterns: ============= GroupPatterns ============= The GroupPatterns plugin is a connector that can assign clients group membership pased on patterns in client hostnames. Two basic methods are supported: - regular expressions (NamePatterns) - ranges (NameRange) Hosts that match the specification are placed in the group or groups specified by the pattern. Setup ===== #. Enable the GroupPatterns plugin #. Create the ``GroupPatterns/config.xml`` file (similar to the example below). #. Client groups will be augmented based on the specification Pattern Types ============= NamePatterns use regular expressions to match client hostnames. All matching clients are placed in the resulting groups. NamePatterns also have the ability to use regular expression matched groups to dynamically create group names. The first two examples below are NamePatterns. The first adds client hostname to both groups *gp-test1* and *gp-test2*. The second matches the hostname as a group and places the client in a group called *group-*. NameRange patterns allow the use of the application of numeric ranges to host names. The final pattern below matches any of *node1-node32* and places them all into the *rack1* group. Dynamically generated group names are not supported with NameRange. Examples ======== .. code-block:: xml hostname gp-test1 gp-test2 (.*) group-$1 node[[1-32]] rack1 Cluster Example --------------- Functional aspects are extracted from hostname strings, and dynamic groups are created. Expected hostname to group mapping:: xnfs1.example.com -> nfs-server xnfs2.example.com -> nfs-server xlogin1.example.com -> login-server xlogin2.example.com -> login-server xpvfs1.example.com -> pvfs-server xpvfs2.example.com -> pvfs-server xwww.example.com -> www-server GroupPatterns configuration: .. code-block:: xml x(\w[^\d\.]+)\d*\. $1-server Regex explanation: #. ``x`` Match any hostname that begins with "x" #. ``(\w[!^\d|\.]+)`` followed by one or more word characters that are not a decimal digit or "." and save the string to $1 #. ``\d*`` followed by 0 or more decimal digit(s) #. ``\.`` followed by a literal "." bcfg2-1.3.3/doc/server/plugins/grouping/ldap.txt000066400000000000000000000174261223671746500215770ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-grouping-ldap: ==== Ldap ==== .. warning:: This plugin is considered experimental and has known issues (see below). Purpose ------- This plugin makes it possible to fetch data from an LDAP directory, process it and attach it to your metadata. Installation ------------ __ http://www.python-ldap.org/ First, you need to install the `python-ldap library`__. On debian-based systems this is accomplished by:: aptitude install python-ldap To enable the plugin, add "Ldap" to the plugins line in your ``bcfg2.conf``. Then add a new directory called "Ldap" to the root of your Bcfg2 repository and define your queries in a file called ``config.py`` using the information in the next section. Configuration ------------- As processing LDAP search results can get pretty complex, the configuration has to be written in Python. Here is a minimal example to get you started:: from Bcfg2.Server.Plugins.Ldap import LdapConnection, LdapQuery, LdapSubQuery, register_query conn_default = LdapConnection() conn_default.binddn = "uid=example,ou=People,dc=example,dc=com" conn_default.bindpw = "foobat" @register_query class ExampleQuery(LdapQuery): name = "example" base = "ou=People,dc=example,dc=com" scope = "one" attrs = ["cn", "uid"] connection = conn_default def prepare_query(self, metadata): self.filter = "(personalServer=" + metadata.hostname + ")" def process_result(self, metadata): if not self.result: admin_uid = None admin_name = "This server has no admin." return { "admin_uid" : self.result[0][1]["uid"], "admin_name" : self.result[0][1]["cn"] } The first line provides three classes for dealing with connections and queries (details below) and a decorator function for registering your queries with the plugin. In this example our LDAP directory has a number of user objects in it. Each of those may have a personal server they administer. Whenever metadata for this machine is being generated by the Bcfg2 server, the UID and name of the admin are retrieved from LDAP. In your bundles and config templates, you can access this data via the metadata object:: ${metadata.Ldap["example"]["admin_name"]} Class reference --------------- LdapConnection ++++++++++++++ .. class:: LdapConnection This class represents an LDAP connection. Every query must be associated with exactly one connection. .. attribute:: LdapConnection.binddn DN used to authenticate against LDAP (required). .. attribute:: LdapConnection.bindpw Password for the previously mentioned **binddn** (required). .. attribute:: LdapConnection.host Hostname of host running the LDAP server (defaults to "localhost"). .. attribute:: LdapConnection.port Port where LDAP server is listening (defaults to 389). You may pass any of these attributes as keyword arguments when creating the connection object. LdapQuery +++++++++ .. class:: LdapQuery This class defines a single query that may adapt itself depending on the current metadata. .. attribute:: LdapQuery.attrs Can be used to retrieve only a certain subset of attributes. May either be a list of strings (attribute names) or ``None``, meaning all attributes (defaults to ``None``). .. attribute:: LdapQuery.base This is the search base. Only LDAP entries below this DN will be included in your search results (required). .. attribute:: LdapQuery.connection Set this to an instance of the LdapConnection class (required). .. attribute:: LdapQuery.filter LDAP search filter used to narrow down search results (defaults to ``(objectClass=*)``). .. attribute:: LdapQuery.name This will be used as the dictionary key that provides access to the query results from the metadata object (``metadata.Ldap["NAMEGOESHERE"]``) (required). .. attribute:: LdapQuery.scope Set this to one of "base", "one" or "sub" to specify LDAP search depth (defaults to "sub"). .. method:: LdapQuery.is_applicable(self, metadata) You can override this method to indicate whether this query makes sense for a given set of metadata (e.g. you need a query only for a certain bundle or group). (defaults to returning True) .. method:: LdapQuery.prepare_query(self, metadata) Override this method to alter the query prior to execution. This is useful if your filter depends on the current metadata, e.g.:: self.filter = "(cn=" + metadata.hostname + ")" (defaults to doing nothing) .. method:: LdapQuery.process_result(self, metadata) You will probably override this method in every query to reformat the results from LDAP. The raw result is stored in ``self.result``, you must return the altered data. Note that LDAP search results are presented in this structure:: ( ("DN of first entry returned", { "firstAttribute" : 1, "secondAttribute" : 2, } ), ("DN of second entry returned", { "firstAttribute" : 1, "secondAttribute" : 2, } ), ) Therefore, to return just the value of the firstAttribute of the second object returned, you'd write:: return self.result[1][1][0] (defaults to returning ``self.result`` unaltered) LdapSubQuery ++++++++++++ .. class:: LdapSubQuery Sometimes you need more than one query to obtain the data you need (e.g. use the first query to return all websites running on metadata.hostname and another query to find all customers that should have access to those sites). LdapSubQueries are the same as LdapQueries, except for that the methods * ``get_result()`` * ``prepare_query()`` * ``process_result()`` allow any additional keyword arguments that may contain additional data as needed. Note that ``get_result()`` will call ``prepare_query()`` and ``process_result()`` for you, so you shouldn't ever need to invoke these yourself, just override them. Here is another example that uses LdapSubQuery:: class WebSitesQuery(LdapSubQuery): name = "web_sites" filter = "(objectClass=webHostingSite)" attrs = ["dc"] connection = conn_default def prepare_query(self, metadata, base_dn): self.base = base_dn def process_result(self, metadata): [...] # build sites dict from returned dc attributes return sites @register_query class WebPackagesQuery(LdapQuery): name = "web_packages" base = "dc=example,dc=com" attrs = ["customerId"] connection = conn_default def prepare_query(self, metadata): self.filter = "(&(objectClass=webHostingPackage)(cn:dn:=" + metadata.hostname + "))" def process_result(self, metadata): customers = {} for customer in self.result: dn = customer[0] cid = customer[1]["customerId"][0] customers[cid]["sites"] = WebSitesQuery().get_result(metadata, base_dn = dn) return customers This example assumes that we have a number of webhosting packages that contain various sites. We need a first query ("web_packages") to get a list of the packages our customers have and another query for each of those to find out what sites are contained in each package. The magic happens in the second class where ``WebSitesQuery.get_result()`` is called with the additional ``base_dn`` parameter that allows our LdapSubQuery to only search below that DN. .. warning:: Do NOT apply the ``register_query`` decorator to LdapSubQueries. Known Issues ------------ * At this point there is no support for SSL/TLS. bcfg2-1.3.3/doc/server/plugins/grouping/metadata.txt000066400000000000000000000226521223671746500224340ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-grouping-metadata: ======== Metadata ======== The metadata mechanism has two types of information, client metadata and group metadata. The client metadata describes which top level group a client is associated with.The group metadata describes groups in terms of what bundles and other groups they include. Group data and clients' memberships are reflected in the `groups.xml`_ and `clients.xml`_ files, respectively. Usage of Groups in Metadata =========================== Clients are assigned membership of groups in the Metadata descriptions. Clients can be directly assigned to *'profile'* or *'public'* groups. Client membership of all other groups is by those groups being associated with the profile or public groups. This file can be indirectly modified from clients through use of the ``-p`` flag to ``bcfg2``. Clients are associated with profile groups in `clients.xml`_ as shown below. .. _server-plugins-grouping-metadata-clients-xml: clients.xml =========== The ``clients.xml`` file contains the mappings of Profile Groups to clients. The file is just a series of ```` tags, each of which describe one host. A sample file is below: .. code-block:: xml .. xml:schema:: clients.xsd For detailed information on client authentication see :ref:`appendix-guides-authentication` .. _server-plugins-grouping-metadata-clients-database: Clients Database ---------------- .. versionadded:: 1.3.0 It is also possible to store client records in a database rather than writing back to `clients.xml`_. This provides several advantages: * `clients.xml`_ will never be written by the server, removing an area of contention between the user and server. * `clients.xml`_ can be removed entirely for many sites. * The Bcfg2 client list can be queried by other machines without obtaining and parsing `clients.xml`_. * A single client list can be shared amongst multiple Bcfg2 servers. In general, storing clients in the database works almost the same as `clients.xml`_. `groups.xml`_ is parsed identically. If `clients.xml`_ is present, it is parsed, but ```` tags in `clients.xml`_ *do not* assert client existence; they are only used to set client options *if* the client exists (in the database). That is, the two purposes of `clients.xml`_ -- to track which clients exist, and to set client options -- have been separated. With the improvements in `groups.xml`_ parsing in 1.3, client groups can now be set directly in `groups.xml`_ with ```` tags. (See :xml:type:`clientType` for more details.) As a result, `clients.xml`_ is only necessary if you need to set options (e.g., aliases, floating clients, per-client passwords, etc.) on clients. To use the database backend instead of `clients.xml`_, set ``use_database`` in the ``[metadata]`` section of ``bcfg2.conf`` to ``true``. You will also need to configure the :ref:`Global Server Database Settings `. The `clients.xml`_-based model remains the default. groups.xml ========== The ``groups.xml`` file contains Group and Profile definitions. Here's a simple ``groups.xml`` file: .. code-block:: xml A Group tag that does not contain any child tags is a declaration of membership; a Group or Client tag that does contain children is a conditional. So the example above does not assign either the ``rhel5`` or ``rhel6`` groups to machines in the ``mail-server`` group, but conditionally assigns the ``sendmail-server`` or ``postfix-server`` groups depending on the OS of the client. (Presumably in this example the OS groups are set by a probe.) Consequently, a client that is RHEL 5 and a member of the ``mail-server`` profile group would also be a member of the ``apache-server``, ``nfs-client``, ``server``, and ``sendmail-server`` groups; a RHEL 6 client that is a member of the ``mail-server`` profile group would be a member of the ``apache-server``, ``nfs-client``, ``server``, and ``postfix-server`` groups. Client tags in `groups.xml`_ allow you to supplement the profile group declarations in `clients.xml`_ and/or client group assignments with the :ref:`server-plugins-grouping-grouppatterns` plugin. They should be used sparingly. (They are more useful when you are using the database backend for client records.) You can also declare that a group should be negated; this allows you to set defaults and override them efficiently. Negation is applied after other group memberships are calculated, so it doesn't matter how many times a client is assigned to a group or how many times it is negated; a single group negation is sufficient to remove a client from that group. For instance, in the following example, ``foo.example.com`` is **not** a member of ``selinux-enabled``, even though it is a member of the ``foo-server`` and ``every-server`` groups: .. code-block:: xml .. note:: Nested Group conditionals, Client tags, and negated Group tags are all new in 1.3.0. .. xml:schema:: metadata.xsd XInclude ======== .. versionadded:: 0.9.0 `XInclude `_ is a W3C specification for the inclusion of external XML documents into XML source files, allowing complex definitions to be split into smaller, more manageable pieces. The `Metadata`_ plugin supports the use of XInclude specifications to split the `clients.xml`_ and `groups.xml`_ files. This mechanism allows the following specification to produce useful results: .. code-block:: xml Each of the included groups files has the same format. These files are properly validated by ``bcfg2-lint``. This mechanism is useful for composing group definitions from multiple sources, or setting different permissions in an svn repository. You can also optionally include a file that may or may not exist with the ``fallback`` tag: .. code-block:: xml In this case, if ``their-groups.xml`` does not exist, no error will be raised and everything will work fine. (You can also use ``fallback`` to include a different file, or explicit content in the case that the parent include does not exist.) Wildcard XInclude ----------------- .. versionadded:: 1.3.1 Bcfg2 supports an extension to XInclude that allows you to use shell globbing in the hrefs. (Stock XInclude doesn't support this, since the href is supposed to be a URL.) For instance: .. code-block:: xml This would include all ``*.xml`` files in the ``groups`` subdirectory. Note that if a glob finds no files, that is treated the same as if a single included file does not exist. You should use the ``fallback`` tag, described above, if a glob may potentially find no files. Probes ====== The metadata plugin includes client-side probing functionality. This is fully documented :ref:`here `. Metadata Caching ================ .. versionadded:: 1.3.0 Client metadata can be cached in order to improve performance. This is particularly important if you have lots of templates that use metadata from other clients (e.g., with the `MetadataQuery`_ interface described below. See :ref:`server-caching` for a full description of the caching features available. .. _server-plugins-grouping-metadata-clientmetadata: ClientMetadata ============== A special client metadata class is available to :ref:`server-plugins-generators-cfg-genshi` and :ref:`server-plugins-generators-cfg-cheetah`. .. autoclass:: Bcfg2.Server.Plugins.Metadata.ClientMetadata MetadataQuery ------------- .. autoclass:: Bcfg2.Server.Plugins.Metadata.MetadataQuery bcfg2-1.3.3/doc/server/plugins/index.txt000066400000000000000000000046351223671746500201320ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-index: ======= Plugins ======= Plugins are the source of all logic used in building a config. They can perform one of several tasks: #. Generating configuration inventory lists for clients #. Generating configuration entry contents for clients #. Probing client-side state (like hardware inventory, etc) -- the generic client probing mechanism is described at :ref:`server-plugins-probes-index`. #. Automating administrative tasks (e.g. :ref:`server-plugins-generators-sshbase` which automates ssh key management) #. Generating client per-entry installation decision-lists Enabling Plugins ================ In order for the Bcfg2 server to use a plugin, it needs to be listed on the *plugins* line in ``bcfg2.conf``. Default Plugins =============== The `Bcfg2 repository`_ contains the all plugins currently distributed with Bcfg2. .. _Bcfg2 repository: https://github.com/Bcfg2/bcfg2/tree/maint/src/lib/Bcfg2/Server/Plugins Metadata (Grouping) ------------------- .. toctree:: :maxdepth: 1 :glob: grouping/* Each of these plugins has a corresponding subdirectory with the same name in the Bcfg2 repository. Abstract Configuration (Structures) ----------------------------------- .. toctree:: :maxdepth: 1 :glob: structures/bundler/index structures/* Each of these plugins has a corresponding subdirectory with the same name in the Bcfg2 repository. Literal Configuration (Generators) ---------------------------------- .. toctree:: :maxdepth: 1 :glob: generators/* Each of these plugins has a corresponding subdirectory with the same name in the Bcfg2 repository. Connector Plugins ----------------- .. toctree:: :maxdepth: 1 :glob: connectors/* Statistics Plugins ------------------ .. toctree:: :maxdepth: 1 :glob: statistics/* Reporting can be enabled by adding it to the plugins line in ``/etc/bcfg2.conf``. Version Plugins --------------- .. toctree:: :maxdepth: 1 :glob: version/* Miscellaneous Plugins --------------------- .. toctree:: :maxdepth: 1 :glob: misc/* Plugin Roles (in 1.0) ===================== In version 1.0, plugins have been refactored into a series of roles. This are fine-grained plugin capabilities that govern how the server core interacts with plugins. More details can be found in :ref:`server-plugins-plugin-roles` .. toctree:: :hidden: plugin-roles probes/index bcfg2-1.3.3/doc/server/plugins/misc/000077500000000000000000000000001223671746500172055ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/plugins/misc/guppy.txt000066400000000000000000000020741223671746500211150ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-misc-guppy: ===== Guppy ===== This plugin is used to trace memory leaks within the bcfg2-server process using Guppy_. By default the remote debugger is started when this plugin is enabled. The debugger can be disabled in a running process using ``bcfg2-admin xcmd Guppy.Disable`` and enabled using ``bcfg2-admin xcmd Guppy.Enable``. .. _Guppy: http://pypi.python.org/pypi/guppy/0.1.8 Setup ===== - Install the Guppy_ package first. - Add *Guppy* to the **plugins** line in ``bcfg2.conf``. Use cases ========= To attach the console run:: python -c "from guppy import hpy;hpy().monitor()" Example ======= .. code-block:: sh # python -c "from guppy import hpy;hpy().monitor()" *** Connection 1 opened *** lc CID PID ARGV 1 25063 ['/usr/sbin/bcfg2-server', '-D', '/var/run/bcfg2-server.pid'] sc 1 Remote connection 1. To return to Monitor, type or . int Remote interactive console. To return to Annex, type '-'. >>> hp.heap() bcfg2-1.3.3/doc/server/plugins/misc/trigger.txt000066400000000000000000000013561223671746500214160ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-misc-trigger: ======= Trigger ======= Trigger is a plugin that calls external scripts (on the server) at the end of each client run. Setup ===== First, add Trigger to the **plugins** line in ``bcfg2.conf``. Then do the following:: mkdir /var/lib/bcfg2/Trigger echo "#!/bin/sh\necho $1\n" > /var/lib/bcfg2/Trigger/test.sh chmod +x /var/lib/bcfg2/Trigger/test.sh Use cases ========= #. Completing network builds (ie resetting from the build target to the boot PXE target) #. Integration with external systems Trigger Arguments ================= Triggers are run with a series of arguments. #. client hostname #. -p #. client profile #. -g #. group1:group2:..:groupN (all client groups) bcfg2-1.3.3/doc/server/plugins/plugin-roles.txt000066400000000000000000000027551223671746500214440ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-plugin-roles: ============ Plugin Roles ============ * Metadata * Initial metadata construction * Connector data accumulation * ClientMetadata instance delivery * Introspection interface (for bcfg2-info & co) * Connector * Provide additional data for ClientMetadata instances * Probing * send executable probes to clients and receive data responses * Structure * Produce a list of configuration entries that should be included in client configurations * Each structure plugin is produces a list of structures * Core verifies that each bundle listed has been constructed * StructureValidator * Validate a client entry list's internal consistency, modifying if needed * Generator * GoalValidator * Validate client goals, modifying if needed * PullSource * Plugin can provide entry information about clients * PullTarget * Plugin can accept entry data and merge it into the specification * Version * Plugin can read revision information from VCS of choice * Will provide an interface for producing commits made by the bcfg2-server * Decision * ClientRunHooks * Provides hooks executed at the start and end of each client run Configuration of plugins ======================== A single list of plugins (including plugins of all capabilities) is specified upon startup (either via bcfg2.conf or equivalent). All plugins included in the startup list are initialized, and each is enabled in all roles that it supports. bcfg2-1.3.3/doc/server/plugins/probes/000077500000000000000000000000001223671746500175445ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/plugins/probes/current-kernel.txt000066400000000000000000000003171223671746500232460ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-probes-current-kernel: current-kernel ============== Probe the currently running kernel. .. code-block:: sh # PROBE_NAME : current-kernel echo `uname -r` bcfg2-1.3.3/doc/server/plugins/probes/fileprobes.txt000066400000000000000000000041721223671746500224430ustar00rootroot00000000000000.. _server-plugins-probes-fileprobes: ========== FileProbes ========== The FileProbes plugin allows you to probe a client for a file, which is then added to the :ref:`server-plugins-generators-cfg` specification. If the file changes on the client, FileProbes can either update it in the specification or allow Cfg to replace it. FileProbes will not probe a file if there's already a file in Cfg that will apply to the client. So if, for instance, you have a generic file in ``Cfg/etc/foo.conf/foo.conf`` that applies to all hosts, FileProbes will not retrieve ``/etc/foo.conf`` from the client (unless ``update`` is enabled; see Configuration_ below). When a new config file is first probed, an ``info.xml`` file is also written to enforce the permissions from that client. Subsequent probes from other clients will not modify or overwrite the data in ``info.xml``. (This ensures that any manual changes you make to ``info.xml`` for that file are not circumvented.) Configuration ============= FileProbes is configured in ``FileProbes/config.xml``, which might look something like: .. code-block:: xml This will result in ``/etc/foo.conf`` being retrieved from all clients; if it changes on a client, it will be overwritten by the version that was retrieved initially. Clients in the ``blah-servers`` group will be probed for ``/etc/blah.conf``; if it changes on a client, those changes will be written into the Bcfg2 specification. If the file is deleted from a client, it will be rewritten from Bcfg2. ``bar.example.com`` will be probed for ``/var/lib/bar.gz``, which contains non-ASCII characters and so needs to use base64 encoding when transferring the file. The paths probed by FileProbes must also be included as Path entries in your bundles in order to be handled properly by Cfg. Permissions are handled as usual, with ``info.xml`` files in Cfg. bcfg2-1.3.3/doc/server/plugins/probes/group.txt000066400000000000000000000075161223671746500214520ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-probes-group: group ===== Probe used to dynamically set client groups based on OS/distro. .. note:: Some parts of this script may depend on having lsb-release installed. .. code-block:: sh #!/bin/bash OUTPUT="" if [ -e /etc/release ]; then # Solaris OUTPUT="$OUTPUT\ngroup:solaris" elif [ -e /etc/debian_version ]; then # debian based OUTPUT="$OUTPUT\ngroup:deb" if [ -e /etc/lsb-release ]; then # variant . /etc/lsb-release OS_GROUP=$DISTRIB_CODENAME DEBIAN_VERSION=$(echo "$DISTRIB_ID" | tr '[A-Z]' '[a-z]') case "$OS_GROUP" in "lucid") OUTPUT="$OUTPUT\ngroup:${DISTRIB_CODENAME}" OUTPUT="$OUTPUT\ngroup:${DEBIAN_VERSION}" ;; esac else # debian OS_GROUP=`cat /etc/debian_version` OUTPUT="$OUTPUT\ngroup:debian" case "$OS_GROUP" in 5.*) OUTPUT="$OUTPUT\ngroup:lenny" ;; "sid") OUTPUT="$OUTPUT\ngroup:sid" ;; esac fi elif [ -e /etc/redhat-release ]; then # redhat based if [ -x /bin/rpm ]; then OUTPUT="${OUTPUT}\ngroup:rpm" OS_GROUP=`/bin/rpm -q --qf "%{NAME}" --whatprovides redhat-release | grep -vi 'freeing read locks for locker' | sed 's/-release.*//' | tr '[A-Z]' '[a-z]'` REDHAT_VERSION=`/bin/rpm -q --qf "%{VERSION}" --whatprovides redhat-release` case "$OS_GROUP" in "centos" | "fedora" | "sl") OUTPUT="${OUTPUT}\ngroup:${OS_GROUP}" OUTPUT="${OUTPUT}\ngroup:${OS_GROUP}-${REDHAT_VERSION}" ;; "redhat") REDHAT_RELEASE=`/bin/rpm -q --qf "%{RELEASE}" --whatprovides redhat-release| cut -d. -f1` OUTPUT="${OUTPUT}\ngroup:${OS_GROUP}" OUTPUT="${OUTPUT}\ngroup:${OS_GROUP}-${REDHAT_VERSION}" OUTPUT="${OUTPUT}\ngroup:${OS_GROUP}-${REDHAT_RELEASE}" ;; esac fi elif [ -e /etc/gentoo-release ]; then # gentoo OUTPUT="$OUTPUT\ngroup:gentoo" elif [ -x /usr/sbin/system_profiler ]; then # os x ### NOTE: Think about using system_profiler SPSoftwareDataType here OUTPUT="$OUTPUT\ngroup:osx" OSX_VERSION=`sw_vers | grep 'ProductVersion:' | egrep -o '[0-9]+\.[0-9]+'` if [ "$OSX_VERSION" == "10.6" ]; then OUTPUT="$OUTPUT\ngroup:osx-snow" elif [ "$OSX_VERSION" == "10.5" ]; then OUTPUT="$OUTPUT\ngroup:osx-leo" fi echo $OUTPUT else exit 0 fi # get the proper architecture ARCH=`uname -m` case "$ARCH" in "x86_64") if [ "$OS_GROUP" == 'centos' -o "$OS_GROUP" == 'sl' -o "$OS_GROUP" == 'redhat' ]; then OUTPUT="$OUTPUT\ngroup:${ARCH}" else OUTPUT="$OUTPUT\ngroup:amd64" fi ;; "i386" | "i686") OUTPUT="$OUTPUT\ngroup:i386" ;; "sparc64") OUTPUT="$OUTPUT\ngroup:sparc64" ;; esac # output the result of all the group probing # (interpreting the backslashed newlines) echo -e $OUTPUT bcfg2-1.3.3/doc/server/plugins/probes/grub-serial-order.txt000066400000000000000000000025511223671746500236350ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-probes-grub-serial-order: grub-serial-order ================= A basic hardware probe to determine if you should change the default serial ordering in grub.conf. This pre-supposes that you know your hardware is broken. You can tell something is wrong with your hardware if it takes lots of time to iterate through the "Press a key" option and present you with the grub menu. In some cases, I've seen this take as long as 20 minutes. .. code-block:: sh #!/bin/sh # # # We need to modify the order of the --serial line in grub # in order to fix silly hardware bugs. In some cases, having # this in the wrong order causes grub to take an inordinate # amount of time to do anything before it actually auto-picks # the default menu option to boot. # PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH # let's figure out what product type this is os=`uname -s` productname="product-no-dmidecode" if [ $os = "Linux" ] ; then productname=`dmidecode -s system-product-name 2>&1` case $productname in "PowerEdge M600") echo "console serial" ;; *) echo "serial console" ;; esac fi if [ $os = "SunOS" ] ; then # Bcfg2 server is unhappy with null output from probes echo "console" fi bcfg2-1.3.3/doc/server/plugins/probes/index.txt000066400000000000000000000200111223671746500214060ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-probes-index: ====== Probes ====== At times you need to gather information from a client machine before you can generate its configuration. For example, if some of your machines have both a local scratch disk and a system disk while others only have the system disk, you would want to know this information to correctly generate an `/etc/auto.master` autofs config file for each type. Here we will look at how to do this. First, create a ``Probes`` directory in our toplevel repository location:: mkdir /var/lib/bcfg2/Probes This directory will hold any small scripts we want to use to grab information from client machines. These scripts can be in any scripting language; the shebang line (the ``#!/usr/bin/env some_interpreter_binary`` line at the very top of the script) is used to determine the script's interpreter. .. note:: Bcfg2 uses python mkstemp to create the Probe scripts on the client. If your /tmp directory is mounted **noexec**, you will likely need to modify the :envvar:`TMPDIR` environment variable so that the bcfg2 client creates the temporary files in a directory from which it can execute. .. note:: .. versionadded:: 1.3.0 A probe script must exit with a return value of 0. If it exits with a non-0 return value, the client will abort its run. This behavior can be disabled by setting ``exit_on_probe_failure = 0`` in the ``[client]`` section of ``bcfg2.conf``. Now we need to figure out what exactly we want to do. In this case, we want to hand out an ``/etc/auto.master`` file that looks like:: /software /etc/auto.software --timeout 3600 /home /etc/auto.home --timeout 3600 /hometest /etc/auto.hometest --timeout 3600 /nfs /etc/auto.nfs --timeout 3600 /scratch /etc/auto.scratch --timeout 3600 for machines that have a scratch disk. For machines without an extra disk, we want to get rid of that last line:: /software /etc/auto.software --timeout 3600 /home /etc/auto.home --timeout 3600 /hometest /etc/auto.hometest --timeout 3600 /nfs /etc/auto.nfs --timeout 3600 So, from the Probes standpoint we want to create a script that counts the number of SCSI disks in a client machine. To do this, we create a very simple ``Probes/scratchlocal`` script: .. code-block:: bash grep -c Vendor /proc/scsi/scsi Running this on a node with *n* disks will return the number *n+1*, as it also counts the controller as a device. To differentiate between the two classes of machines we care about, we just need to check the output of this script for numbers greater than 2. We do this in the template. .. note:: This example uses :ref:`server-plugins-generators-cfg-cheetah`, but Cheetah templates are **not** required in order for Probes to operate properly. For the template we will want to create a ``Cfg/etc/auto.master`` directory to hold the template of the file in question. Inside of this template we will need to check the result of the Probe script that got run and act accordingly. The ``Cfg/etc/auto.master/auto.master.cheetah`` file looks like:: /software /etc/auto.software --timeout 3600 /home /etc/auto.home --timeout 3600 /hometest /etc/auto.hometest --timeout 3600 /nfs /etc/auto.nfs --timeout 3600 #if int($self.metadata.Probes["scratchlocal"]) > 2 /scratch /etc/auto.scratch --timeout 3600 #end if Any Probe script you run will store its output in ``$self.metadata.Probes["scriptname"]``, so we get to our `scratchlocal` script's output as seen above. (See `Handling Probe Output`_, below, for more information on how this is done.) Note that we had to wrap the output in an `int()` call; the script output is treated as a string, so it needs to be converted before it can be tested numerically. With all of these pieces in place, the following series of events will happen when the client is run: #. Client runs #. Server hands down our ``scratchlocal`` probe script #. Client runs the ``scratchlocal`` probe script and hands its output back up to the server #. Server generates ``/etc/auto.master`` from its template, performing any templating substitutions/actions needed in the process. #. Server hands ``/etc/auto.master`` down to the client #. Client puts file contents in place. Now we have a nicely dynamic ``/etc/auto.master`` that can gracefully handle machines with different numbers of disks. All that's left to do is to add the ``/etc/auto.master`` to a Bundle: .. code-block:: xml Handling Probe Output ===================== Bcfg2 stores output from probes in the ``Probes`` property of a client's metadata object. To access this data in :ref:`server-plugins-generators-cfg-genshi`, for instance, you could do:: ${metadata.Probes['script-name']} This is not the full output of the probe; any lines that start with "group:" have been stripped from the output. The data is a string-like object that has some interesting and salient features: * If the data is a valid XML document, then ``metadata.Probes['script-name'].xdata`` will be an ``lxml.etree._Element`` object representing the XML data. * If the data is a valid JSON document, and either the Python ``json`` or ``simplejson`` module is installed, then ``metadata.Probes['script-name'].json`` will be a data structure representing the JSON data. * If the data is a valid YAML document, and either the Python ``yaml`` or ``syck`` module is installed, then ``metadata.Probes['script-name'].yaml`` will be a data structure representing the YAML data. If these conditions are not met, then the named properties will be ``None``. In all other fashions, the probe data objects should act like strings. Host- and Group-Specific probes =============================== Bcfg2 has the ability to alter probes based on client hostname and group membership. These files work similarly to files in Cfg. If multiple files with the same basename apply to a client, the most specific one is used. Only one instance of a probe is served to a given client, so if a host-specific version and generic version apply, only the client-specific one will be used. If you want to to detect information about the client operating system, the :ref:`server-plugins-probes-ohai` plugin can help. .. _server-plugins-probes-data-storage: Data Storage ============ .. versionadded:: 1.3.0 The Probes plugin stores the output of client probes locally on the Bcfg2 server in order to ensure that probe data and groups are available on server startup (rather than having to wait until all probes have run every time the server is restarted) and to :ref:`bcfg2-info ` and related tools. There are two options for storing this data: ``Probes/probed.xml``, a plain XML file stored in the Bcfg2 specification; or in a database. Advantages and disadvantages of using the database: * The database is easier to query from other machines, for instance if you run ``bcfg2-info`` or ``bcfg2-test`` on a machine that is not your Bcfg2 server. * The database allows multiple Bcfg2 servers to share probe data. * The database is likely to handle probe data writes (which happen on every client run) more quickly, since it can only write the probes whose data has changed. * The database is likely to handle probe data reads (which happen only on server startup) more slowly, since it must query a database rather than the local filesystem. Once the data has been read in initially (from XML file or from the database) it is kept in memory. To use the database-backed storage model, set ``use_database`` in the ``[probes]`` section of ``bcfg2.conf`` to ``true``. You will also need to configure the :ref:`server-database`. The file-based storage model is the default, although that is likely to change in future versions of Bcfg2. Other examples ============== .. toctree:: :maxdepth: 1 current-kernel group vserver grub-serial-order manufacturer producttype serial-console-speed Other Probing plugins ===================== .. toctree:: ohai fileprobes bcfg2-1.3.3/doc/server/plugins/probes/manufacturer.txt000066400000000000000000000020121223671746500227740ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-probes-manufacturer: manufacturer ============ Probe to output some standardized group names based on the manufacturer information. .. code-block:: sh #!/bin/sh # PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH manufacturer=manuf-no-demidecode os=`uname -s` if [ $os = "Linux" ] ; then manufacturer=`dmidecode -s system-manufacturer 2>&1| sed -e 's/[ ]\+$//g'` case $manufacturer in "Dell Inc.") manufacturer="manuf-dell" ;; "Sun Microsystems") manufacturer="manuf-sun" ;; "VMware, Inc.") manufacturer="manuf-vmware" ;; *) manufacturer="manuf-unknown" ;; esac fi if [ $os = "SunOS" ]; then case `uname -i` in SUNW,*) manufacturer="manuf-sun" ;; *) manufacturer="manuf-unknown" ;; esac fi echo group:$manufacturer bcfg2-1.3.3/doc/server/plugins/probes/ohai.txt000066400000000000000000000020771223671746500212330ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-probes-ohai: Ohai ==== .. _Ohai: http://wiki.opscode.com/display/chef/Ohai .. _Ohai-Install: http://wiki.opscode.com/display/chef/Ohai+Installation+and+Use The `Ohai`_ plugin is used to detect information about the client operating system. The data is reported back to the server using JSON. Client prerequisites -------------------- On the client, you need to install `Ohai`_. See `Ohai-Install`_ for more information. Server prerequisites -------------------- If you have python 2.6 or later installed, you can continue on to :ref:`ohai-setup`. Otherwise, you will need to install the python-simplejson module found packaged in most distributions. .. _ohai-setup: Setup ----- To enable the Ohai plugin, you need to first create an ``Ohai`` directory in your Bcfg2 repository (e.g. ``/var/lib/bcfg2/Ohai``). You then need to add **Ohai** to the plugins line in ``bcfg2.conf``. Once this is done, restart the server and start a client run. You will have the JSON output from the client in the ``Ohai`` directory you created previously. bcfg2-1.3.3/doc/server/plugins/probes/producttype.txt000066400000000000000000000040521223671746500226700ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-probes-producttype: producttype =========== A probe to set up dynamic groups based on the producttype and possibly some internal components of the system. Defined products are product-name. Defined component information is has_some_component. In the example below, we can infer that we have Emulex Lightpulse gear and set the group has_hardware_emulex_lightpulse. .. code-block:: sh !/bin/sh # # PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH # let's figure out what product type this is os=`uname -s` productname="product-no-dmidecode" if [ $os = "Linux" ] ; then productname=`dmidecode -s system-product-name 2>&1` case $productname in "PowerEdge M600") productname="product-bladem600" ;; "Sun Fire X4100 M2") productname="product-x4100m2" ;; "Sun Fire X4440") productname="product-x4440" ;; "VMware Virtual Platform") productname="product-vmware-vm" ;; *) productname="product-unknown" ;; esac # check for emulex lightpulse fiber channel HBA check_emulex_lightpulse=`lspci -d 10df: | grep -c LightPulse` if [ $check_emulex_lightpulse -gt 0 ]; then echo group:has_hardware_emulex_lightpulse fi # check for broadcom nics check_broadcom_nic=`lspci -d 14e4: | grep -c NetXtreme` if [ $check_broadcom_nic -gt 0 ]; then echo group:has_hardware_broadcom_nic fi # check for intel pro/1000 MT nics check_intel_pro1000mt_nic=`lspci -d 8086:1010 | wc -l` if [ $check_intel_pro1000mt_nic -gt 0 ]; then echo group:has_hardware_intel_pro1000mt_nic fi fi if [ $os = "SunOS" ] ; then case `uname -i` in SUNW,*) productname=`uname -i` ;; *) productname=product-unknown ;; esac fi echo group:$productname bcfg2-1.3.3/doc/server/plugins/probes/serial-console-speed.txt000066400000000000000000000025011223671746500243200ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-probes-serial-console-speed: serial-console-speed ==================== A probe to tell us what the serial console speed should be for a given piece of hardware. This pre-supposed some knowledge of the hardware because you define the speeds in here instead of attempting to probe bios or something in the hardware in most cases (like x86). .. code-block:: sh #!/bin/sh # # # figure out what serial speed we should tell bcfg2 to use. # since there's no way to probe, we need to set this up by external # knowledge of the system hardware type (and just make sure we # standardize on that serial speed for that hardware class) PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH # let's figure out what product type this is os=`uname -s` productname="product-no-dmidecode" if [ $os = "Linux" ] ; then productname=`dmidecode -s system-product-name 2>&1` case $productname in "PowerEdge M600") echo "115200" ;; *) echo "9600" ;; esac fi if [ $os = "SunOS" ]; then platform=`uname -i` case $platform in SUNW,*) eeprom ttya-mode | sed 's/ttya-mode=//'|awk -F, '{print $1}' ;; *) echo "9600" ;; esac fi bcfg2-1.3.3/doc/server/plugins/probes/vserver.txt000066400000000000000000000010111223671746500217720ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-probes-vserver: vserver ======= Detect if the server is a Linux-VServer host. .. code-block:: sh #!/bin/sh # Test the proc TEST=`cat /proc/self/status|grep s_context| cut -d":" -f2|cut -d" " -f 2` case "$TEST" in "") # Not a vserver kernel echo group:host ;; "0") # Vserver kernel but it is the HOST echo group:host ;; [0-9]*) # Vserver echo group:vserver ;; esac bcfg2-1.3.3/doc/server/plugins/statistics/000077500000000000000000000000001223671746500204445ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/plugins/statistics/reporting.txt000066400000000000000000000005661223671746500232250ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-statistics-reporting: ========= Reporting ========= Reporting can be enabled by adding Reporting to the plugins line in ``/etc/bcfg2.conf``: plugins = Base,Bundler,Cfg,...,Reporting For more information on how to use Reporting to setup reporting, see :ref:`reports-dynamic`. .. note:: This replaces the DBStats plugin. bcfg2-1.3.3/doc/server/plugins/statistics/statistics.txt000066400000000000000000000001421223671746500233740ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-statistics-statistics: ========== Statistics ========== bcfg2-1.3.3/doc/server/plugins/structures/000077500000000000000000000000001223671746500204755ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/plugins/structures/altsrc.txt000066400000000000000000000074511223671746500225350ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-structures-altsrc: ====== altsrc ====== .. versionadded:: 0.9.5 Altsrc is a generic, Bcfg2 server-side mechanism for performing configuration entry name remapping for the purpose of data binding. Altsrc can be used as a parameter for any entry type, and can be used in any structure, including Bundler and Base. Use Cases ========= * Equivalent configuration entries on different architectures with different names * Mapping entries with the same name to different bind results in a configuration (two packages with the same name but different types) * A single configuration entry across multiple specifications (multi-plugin, or multi-repo) Examples ======== * Consider the case of /etc/hosts on linux and /etc/inet/hosts on solaris. These files contain the same data in the same format, and should typically be synchronized, however, exist in different locations. Classically, one would need to create one entry for each in Cfg and perform manual synchronization. Or, you could use symlinks and pray. Altsrc is driven from the bundle side. For example: .. code-block:: xml In this case, when a solaris host gets the 'netinfo' bundle, it will get the first Path entry, which includes an altsrc parameter. This will cause the server to bind the entry as if it were a Path called ``/etc/hosts``. This configuration entry is still called ``/etc/inet/hosts``, and is installed as such. * On encap systems, frequently multiple packages of the same name, but of different types will exist. For example, there might be an openssl encap package, and an openssl rpm package. This can be dealt with using a bundle like: .. code-block:: xml This bundle will bind data for the packages "openssl-encap" and "openssl-rpm", but will be delivered to the client with both packages named "openssl" with different types. * Consider the case where there exist complicated, but completely independent specifications for the same configuration entry but different groups of clients. The following bundle will allow the use of two different templates /etc/firewall-rules-external and /etc/firewall-rules-internal for different clients based on their group membership. .. code-block:: xml ... * Consider the case where a variety of files can be constructed by a single :ref:`Genshi ` or :ref:`Cheetah ` template. It would be possible to copy this template into the proper location for each file, but that requires proper synchronization upon modification and knowing up front what the files will all be called. Instead, the following bundle allows the use of a single template for all proper config file instances. .. code-block:: xml bcfg2-1.3.3/doc/server/plugins/structures/base.txt000066400000000000000000000060221223671746500221500ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-structures-base: ==== Base ==== .. deprecated:: 1.2.0 .. warning:: The Base plugin no longer receives new features/functionality. Please use :ref:`server-plugins-structures-bundler-index` instead. The Base plugin is a structure plugin that provides the ability to add lists of unrelated entries into client configuration entry inventories. Base works much like Bundler in its file format. The main difference between Base and Bundler is that Base files are included in all clients' configuration whereas bundles must be included explicitly in your Metadata. See the :ref:`server-plugins-structures-bundler-index` page for details. If you have lots of unconnected items (for instance: software packages whose configuration wasn't modified, and that are also not depended on by other packages; or single directories or files not belonging to a package), using Bundles in Metadata would clutter or enlarge your ``Metadata/groups.xml`` file, because they all would need to be explicitly specified. ``Base/`` on the other hand is the perfect place to put these items. Without using Base, you would be forced to put them directly into your group definitions in ``groups.xml``, either as many small bundles (substantially enlarging it) or into something like ``Bundler/unrelated-entries.xml``. Using the latter is especially bad if you mix packages and services in your Bundle, since for any updated package in that bundle, the now-related services would be restarted. The Base entries can still be assigned based on group membership, but when they aren't part of a group, each and every client gets the entry. So Base is also a great place to put entries that a large number of your clients will get. For example, you could have a file ``Base/packages.xml`` .. code-block:: xml [...] [...] .. note:: You don't have to reference to the files in Base from anywhere. As long as you include ``Base`` in your ``plugins = ...`` line in ``bcfg2.conf``, these are included automatically. .. note:: Your Base files have to match the pattern ``Base/*.xml`` to be included. The decision when to use Base and when to use Bundler depends on the configuration entry in question, and what you are trying to achieve. Base is mainly used for cases where you don't want/need to explicitly include particular configuration items. Let's say all your machines are various linux distributions. In this case, you may want to manage the ``/etc/hosts`` file using Base instead of Bundler since you will not have to include any Bundles in your Metadata. However, you could alternatively have a base 'linux' group that all the clients inherit which includes a *linux* Bundle with the ``/etc/hosts`` configuration entry. bcfg2-1.3.3/doc/server/plugins/structures/bundler/000077500000000000000000000000001223671746500221305ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/plugins/structures/bundler/index.txt000066400000000000000000000252011223671746500240000ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-structures-bundler-index: ======= Bundler ======= Bundler is used to describe groups of inter-dependent configuration entries, such as the combination of packages, configuration files, and service activations that comprise typical Unix daemons. Bundles are used to add groups of configuration entries to the inventory of client configurations, as opposed to describing particular versions of those entries. For example, a bundle could say that the configuration file ``/etc/passwd`` should be included in a configuration, but will not describe the particular version of ``/etc/passwd`` that a given client will receive. Group and Client tags can be used inside of bundles to differentiate which entries particular clients will recieve; this is useful for the case where entries are named differently across systems; for example, one linux distro may have a package called openssh while another uses the name ssh. Configuration entries nested inside of Group elements only apply to clients who are a member of those groups; multiple nested groups must all apply. Also, groups may be negated; entries included in such groups will only apply to clients who are not a member of said group. The same applies to Client elements. The following is an annotated copy of a bundle: .. code-block:: xml In this bundle, most of the entries are common to all systems. Clients in group **deb** get one extra package and service, while clients in group **rpm** get two extra packages and an extra service. In addition, clients in group **fedora** *and* group **rpm** get one extra package entries, unless they are not in the **fc14** group, in which case, they get an extra package. The client **trust.example.com** gets one extra file that is not distributed to any other clients. Notice that this file doesn't describe which versions of these entries that clients should get, only that they should get them. (Admittedly, this example is slightly contrived, but demonstrates how group entries can be used in bundles) +----------------------------+-------------------------------+ | Group/Hostname | Entry | +============================+===============================+ | all | /etc/ssh/ssh_host_dsa_key | +----------------------------+-------------------------------+ | all | /etc/ssh/ssh_host_rsa_key | +----------------------------+-------------------------------+ | all | /etc/ssh/ssh_host_dsa_key.pub | +----------------------------+-------------------------------+ | all | /etc/ssh/ssh_host_rsa_key.pub | +----------------------------+-------------------------------+ | all | /etc/ssh/ssh_host_key | +----------------------------+-------------------------------+ | all | /etc/ssh/ssh_host_key.pub | +----------------------------+-------------------------------+ | all | /etc/ssh/sshd_config | +----------------------------+-------------------------------+ | all | /etc/ssh/ssh_config | +----------------------------+-------------------------------+ | all | /etc/ssh/ssh_known_hosts | +----------------------------+-------------------------------+ | rpm | Package openssh | +----------------------------+-------------------------------+ | rpm | Package openssh-askpass | +----------------------------+-------------------------------+ | rpm | Service sshd | +----------------------------+-------------------------------+ | rpm and fedora | Package openssh-server | +----------------------------+-------------------------------+ | rpm and fedora and not fc4 | Package openssh-clients | +----------------------------+-------------------------------+ | deb | Package ssh | +----------------------------+-------------------------------+ | deb | Service ssh | +----------------------------+-------------------------------+ | trust.example.com | /etc/ssh/shosts.equiv | +----------------------------+-------------------------------+ Genshi templates ================ Genshi XML templates allow you to use the `Genshi `_ templating system to dynamically generate a bundle. Genshi templates can be specified one of two ways: 1. Add an XML-style genshi template to the Bundler directory with a ``.genshi`` and the associated namespace attribute. 2. Simply add the appropriate namespace attribute to your existing XML bundle. The top-level Bundle tag should look like the following:: Several variables are pre-defined inside templates: +-------------+--------------------------------------------------------+ | Name | Description | +=============+========================================================+ | metadata | :ref:`Client metadata | | | ` | +-------------+--------------------------------------------------------+ | repo | The path to the Bcfg2 repository on the filesystem | +-------------+--------------------------------------------------------+ .. note:: ```` and ```` tags are allowed inside of Genshi templates as of Bcfg2 1.2. However, they do not behave the same as using a Genshi conditional, e.g.:: The conditional is evaluated when the template is rendered, so code inside the conditional is not executed if the conditional fails. A ```` tag is evaluated *after* the template is rendered, so code inside the tag is always executed. This is an important distinction: if you have code that will fail on some groups, you *must* use a Genshi conditional, not a ```` tag. The same caveats apply to ```` tags. See also the :ref:`xml-genshi-reference`. Troubleshooting --------------- To render a bundle for a given client, you can run:: bcfg2-info buildbundle This will render the template; it will not fully bind all of the entries in the bundle. See :ref:`bcfg2-info ` for more details. Altsrc ====== .. toctree:: :maxdepth: 1 ../altsrc Examples ======== In some cases, configuration files need to include the client's hostname in their name. The following template produces such a config file entry. .. code-block:: xml Depending on the circumstance, these configuration files can either be handled by individual entries in :ref:`server-plugins-generators-cfg`, or can be mapped to a single entry by using the :ref:`server-plugins-structures-altsrc` feature. In this example, configuration file names are built using probed results from the client. getmac is a probe that gathers client MAC addresses and returns them in a newline delimited string. .. code-block:: xml .. note:: * The use of the altsrc directive causes all ifcfg files to be handled by the same plugin and entry. * The blocks have only been available in genshi since 0.4 (http://genshi.edgewall.org/ticket/84) If you want a file to be only on a per-client basis, you can use an if declaration. .. code-block:: xml or alternately .. code-block:: xml or yet another way .. code-block:: xml The final form is preferred if there is no code inside the block that would fail on other clients. While these examples are simple, the test in the if block can in fact be any python statement. .. _server-plugins-structures-bundler-index-examples: Other examples ============== Some simple examples of Bundles can be found in the `Bcfg2 example repository`_. .. _Bcfg2 example repository: https://github.com/solj/bcfg2-repo In addition to the example repository, the following is a list of some more complex example Bundles. .. toctree:: :maxdepth: 1 kernel moab nagios ntp snmpd torque yp bcfg2-1.3.3/doc/server/plugins/structures/bundler/kernel.txt000066400000000000000000000062401223671746500241530ustar00rootroot00000000000000.. -*- mode: rst -*- .. vim: ft=rst .. _server-plugins-structures-bundler-kernel: kernel ====== This is a rather complex Bundle for the Linux kernel from a system with a history of complexity. There are two kernel versions present on the systems at all times (the current and the previous), so the package names all contain versioning information. This includes kernel-specific modules for various specialties - ``gm`` for Myrinet boards, ``gpfs`` and ``pvfs`` for storage clients, and ``nvidia`` modules for machines with Nvidia cards. Note that only the ``ia32`` machines have Nvidia cards in them, and thus those entries only exist in that section. It is easy to see that there is duplication of effort between the two architectures - both have the same ``linux`` package entry names, for example. This Bundle could be arranged in many different ways, some of which might be better than this one. Feel free to hack as needed. .. code-block:: xml bcfg2-1.3.3/doc/server/plugins/structures/bundler/moab.txt000066400000000000000000000006411223671746500236100ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-structures-bundler-moab: moab ==== This is a fairly simple Bundle for the Moab workload manager. .. code-block:: xml bcfg2-1.3.3/doc/server/plugins/structures/bundler/nagios.txt000066400000000000000000000042571223671746500241610ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-structures-bundler-nagios: nagios ====== A Bundle for the Nagios service. This Bundle installs all of our local Nagios plugins, takes into account that the SNMP package changed names between SLES 8 and SLES 9, and works on both the Nagios server and the clients. .. code-block:: xml .. note:: You may also want to have a look at the :ref:`NagiosGen ` plugin. bcfg2-1.3.3/doc/server/plugins/structures/bundler/ntp.txt000066400000000000000000000010561223671746500234740ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-structures-bundler-ntp: ntp === Despite its lack of groups, this Bundle controls both ``ntp`` servers and clients. It does this through the use of host-specific entries in the ``Cfg`` repository. It is left as an exercise for the reader to do this better through use of groups. .. code-block:: xml bcfg2-1.3.3/doc/server/plugins/structures/bundler/snmpd.txt000066400000000000000000000005531223671746500240150ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-structures-bundler-snmpd: snmpd ===== A simple bundle for a SNMP daemon with a package, a service and a configuration file. .. code-block:: xml bcfg2-1.3.3/doc/server/plugins/structures/bundler/torque.txt000066400000000000000000000054731223671746500242210ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-structures-bundler-torque: torque ====== = torque.xml = A longer Bundle that includes many group-specific entries. .. code-block:: xml bcfg2-1.3.3/doc/server/plugins/structures/bundler/yp.txt000066400000000000000000000014701223671746500233230ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-structures-bundler-yp: yp == = yp.xml = Note that this Bundle includes **Group** sections. Toplevel elements go to anybody that includes this Bundle, but clients that belong to the **yp-client** and **yp-server** groups get their own specialized treatment too. .. code-block:: xml bcfg2-1.3.3/doc/server/plugins/structures/defaults.txt000066400000000000000000000017571223671746500230570ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-structures-defaults: ========== Defaults ========== The Defaults plugin can be used to populate default attributes for entries. Defaults is *not* a Generator plugin, so it does not actually bind an entry; Defaults are applied after an entry has been bound, and only populate attributes that are not yet set. Like :ref:`server-plugins-generators-rules`, Defaults supports regular expressions in the name attribute. For instance, to make all Service entries use the ``systemd`` tool on Fedora 15 and the ``chkconfig`` tool on Fedora 14, you could do:: If you were to specify a ``type`` attribute for a Service entry in Rules (or a ``type`` attribute for a BoundService entry in Bundler), that would take precendence over the default. bcfg2-1.3.3/doc/server/plugins/structures/deps.txt000066400000000000000000000033451223671746500221760ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-structures-deps: ==== Deps ==== The Deps Plugin allows you to make a series of assertions like "Package X requires Package Y (and optionally also Package Z etc). Note that only configuration entries, like Package, Path, etc can be used. Groupings (like Bundle) are not supported. Here are some examples: .. note:: These particular examples are not extremely useful when using the Packages plugin as Packages will handle the dependency resolution for you. However, there are certainly other use cases for the Deps plugin. Deps/bcfg2.xml ============== .. code-block:: xml This basically causes any configuration specification that includes Package bcfg2 to include python-lxml and isprelink, in a second base clause. Deps/bcfg2-server.xml ===================== .. code-block:: xml This states that the bcfg2-server package (it's a separate package on some distros) depends on a long list of other packages. bcfg2-1.3.3/doc/server/plugins/version/000077500000000000000000000000001223671746500177375ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/plugins/version/bzr.txt000066400000000000000000000016071223671746500213010ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-version-bzr: === Bzr === Why use the Bazaar plugin ========================= The Bazaar plugin is useful if you would like to track changes to your bcfg2 repository using a `Bazaar `_ backend. Currently, it enables you to get revision information out of your repository for reporting purposes. Future plans are to commit changes to the repo which are made by the server. How to enable the Bazaar plugin =============================== Simply add "Bzr" to your plugins line in ``/etc/bcfg2.conf``:: [server] plugins = Base,Bundler,Cfg,...,Bzr Usage notes =========== Unlike other VCS plugins for Bcfg2, the Bazaar plugin checks whether there are uncommitted changes to the repository. If there are, this plugin appends a "+" after the version number. Essentially, this means you're using that version, "plus" some changes. bcfg2-1.3.3/doc/server/plugins/version/cvs.txt000066400000000000000000000011451223671746500212740ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-version-cvs: === CVS === Why use the CVS plugin ========================= The CVS plugin is useful if you would like to track changes to your Bcfg2 repository using a `CVS `_ backend. Currently, it enables you to get revision information out of your repository for reporting purposes. Future plans are to commit changes to the repo which are made by the server. How to enable the CVS plugin =============================== Simply add "Cvs" to your plugins line in ``/etc/bcfg2.conf``:: [server] plugins = Base,Bundler,Cfg,...,Cvs bcfg2-1.3.3/doc/server/plugins/version/darcs.txt000066400000000000000000000014611223671746500215760ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-version-darcs: ===== Darcs ===== This page describes the new Darcs plugin which is experimental. Why use the Darcs plugin ======================== The Darcs plugin is useful if you would like to track changes to your Bcfg2 repository using a `Darcs `_ backend. Currently, it enables you to get revision information out of your repository for reporting purposes. Once the plugin is enabled, every time a client checks in, it will include the current repository revision in the reports/statistics. How to enable the Darcs plugin ============================== You will need to install Darcs on the Bcfg2 server first. Once installed, simply add Darcs to your plugins line in ``/etc/bcfg2.conf``:: [server] plugins = Base,Bundler,Cfg,...,Darcs bcfg2-1.3.3/doc/server/plugins/version/fossil.txt000066400000000000000000000012021223671746500217720ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-version-fossil: ====== Fossil ====== Why use the Fossil plugin ========================= The Fossil plugin is useful if you would like to track changes to your bcfg2 repository using a `Fossil SCM `_ backend. Currently, It enables you to get revision information out of your repository for reporting purposes. Future plans are to commit changes to the repo which are made by the server. How to enable the Fossil plugin =============================== Simply add "Fossil" to your plugins line in ``/etc/bcfg2.conf``:: [server] plugins = Base,Bundler,Cfg,...,Fossil bcfg2-1.3.3/doc/server/plugins/version/git.txt000066400000000000000000000034531223671746500212700ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-version-git: === Git === The Git plugin is useful if you would like to track changes to your bcfg2 repository using a `Git `_ backend. It enables you to get revision information out of your repository for reporting purposes. Once the plugin is enabled, every time a client checks in, it will include the current repository revision in the reports/statistics. Additionally, if the ``GitPython`` library is installed, the Git plugin exposes an additional XML-RPC method call, ``Git.Update``. With no arguments, ``Git.Update`` updates the working copy to the latest version in the remote tracking branch. If the current working copy doesn't have a remote tracking branch, then nothing is done. ``Git.Update`` can also be given a single argument, the name of a git tree-ish (branch, tag, ref, commit, etc.) to check out. When this is done, the new working is updated as well. For example:: bcfg2-admin xcmd Git.Update master This checks out the ``master`` branch and updates it to the latest data from the remote ``master`` (if applicable). If you then run:: bcfg2-admin xcmd Git.Update This updates to the latest remote data without changing branches. Then:: bcfg2-admin xcmd Git.Update dd0bb776c This checks out the specified commit. Subsequently:: bcfg2-admin xcmd Git.Update This does nothing, because the working copy is now in "detached HEAD" state, and there can be no remote tracking branch to update from. To put it another way, once you tell ``Git.Update`` which tree-ish to checkout, it stays on that tree-ish until you tell it otherwise. Enabling the Git plugin ======================= To enable the Git plugin, simply add it to your plugins line in ``/etc/bcfg2.conf``:: [server] plugins = Bundler,Cfg,Metadata,...,Git bcfg2-1.3.3/doc/server/plugins/version/hg.txt000066400000000000000000000012041223671746500210730ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-version-hg: ============== Mercurial (Hg) ============== Why use the Mercurial plugin ============================ The Hg plugin is useful if you would like to track changes to your Bcfg2 repository using `Hg `_ backend. Currently, it enables you to get revision information out of your repository for reporting purposes. How to enable the Mercurial plugin ================================== You will need to install Mercurial on the Bcfg2 server first. Simply add Hg to your plugins line in ``/etc/bcfg2.conf``:: [server] plugins = Base,Bundler,Cfg,...,Hg bcfg2-1.3.3/doc/server/plugins/version/svn.txt000066400000000000000000000033611223671746500213110ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-plugins-version-svn: === Svn === The Svn plugin is useful if you would like to track changes to your Bcfg2 repository using a `Subversion `_ backend. As with the other Version plugins, the Svn plugin enables you to get revision information out of your repository for reporting purposes. Once the plugin is enabled, every time a client checks in, it will include the current repository revision in the reports/statistics. Additionally, if the ``pysvn`` library is installed, the Svn plugin exposes two XML-RPC method calls: * ``Svn.Update`` updates the working copy to the latest version in the repository. * ``Svn.Commit`` commits any changes to the working copy back to the repository. In order for this to work, the user Bcfg2 runs as must be able to commit to the repository non-interactively. Enabling the Svn plugin ======================= Simply add Svn to your plugins line in ``/etc/bcfg2.conf``:: [server] plugins = Bundler,Cfg,Metadata,..,Svn Resolving conflicts ======================= By default, the Svn plugin does not attempt to resolve conflicts when trying to commit or update the repository. This can be changed by adding to ``/etc/bcfg2.conf``, e.g.:: [svn] conflict_resolution = theirs-conflict The possible values of ``conflict_resolution`` are: * ``base`` * ``postpone`` (default) * ``mine-conflict`` * ``theirs-conflict`` * ``mine-full`` * ``theirs-full`` The other possible SVN conflict resolvers (``edit``, ``launch``, ``working``) require manual intervention and so are not possible. Descriptions of each action can be found in the `Version Control with Subversion `_ book. bcfg2-1.3.3/doc/server/selinux.txt000066400000000000000000000217641223671746500170330ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-selinux: ======= SELinux ======= This document describes two related but somewhat disparate concepts: First, how to run Bcfg2 under SELinux; and secondly, how to use Bcfg2 to manage SELinux. .. _server-selinux-policy: Running Bcfg2 under SELinux =========================== .. versionadded:: 1.3.0 Bcfg2 now ships with an SELinux policy that can be used to run both the client and server in enforcing mode. (Most of the helper tools, like ``bcfg2-info`` and ``bcfg2-admin``, will still need to be run unconfined.) It defines the following booleans: +-------------------------------------+-----------------------------------------+----------------------------------------------------------+---------+ | Boolean Name | Description | Plugins Affected | Default | +=====================================+=========================================+==========================================================+=========+ | bcfg2_server_exec_scripts | Allow the Bcfg2 server to execute | :ref:`server-plugins-misc-trigger` and | off | | | scripts in ``unconfined_t``. This | :ref:`server-plugins-connectors-puppetenc`, | | | | ability is limited to scripts in the | and Cfg | | | | ``bcfg2_server_script_exec_t`` context. | :ref:`server-plugins-generators-cfg-validation` | | | | If this boolean is off, then external | | | | | server-side scripts will be run in | | | | | ``bcfg2_server_t``, which is a fairly | | | | | limited context. | | | +-------------------------------------+-----------------------------------------+----------------------------------------------------------+---------+ | bcfg2_server_can_network_connect_db | Allow the Bcfg2 server to connect to | :ref:`server-plugins-statistics-reporting`, the | off | | | databases (e.g., MySQL and PostgreSQL) | :ref:`server-plugins-grouping-metadata-clients-database` | | | | | feature of Metadata, and the database | | | | | :ref:`server-plugins-probes-data-storage` | | | | | feature of Probes | | +-------------------------------------+-----------------------------------------+----------------------------------------------------------+---------+ It also defines the following SELinux types: +----------------------------+-------------------------------------------------+ | Type Name | Description | +============================+=================================================+ | bcfg2_t | The context the Bcfg2 client runs in | +----------------------------+-------------------------------------------------+ | bcfg2_exec_t | The context of the Bcfg2 client script itself | +----------------------------+-------------------------------------------------+ | bcfg2_server_t | The context the Bcfg2 server runs in | +----------------------------+-------------------------------------------------+ | bcfg2_server_exec_t | The context of the Bcfg2 server script itself | +----------------------------+-------------------------------------------------+ | bcfg2_initrc_exec_t | The context of the Bcfg2 client init script | +----------------------------+-------------------------------------------------+ | bcfg2_server_initrc_exec_t | The context of the Bcfg2 server init script | +----------------------------+-------------------------------------------------+ | bcfg2_var_lib_t | The context of most Bcfg2 specification data, | | | with the exception of the executable scripts in | | | ``bcfg2_server_script_exec_t`` | +----------------------------+-------------------------------------------------+ | bcfg2_server_script_t | The context server-side scripts run in. This | | | type is unconfined if the | | | ``bcfg2_server_exec_scripts`` is on. | +----------------------------+-------------------------------------------------+ | bcfg2_server_script_exec_t | The context of the server-side scripts in the | | | Bcfg2 specification | +----------------------------+-------------------------------------------------+ | bcfg2_yum_helper_exec_t | The context of the bcfg2-yum-helper script | +----------------------------+-------------------------------------------------+ | bcfg2_var_run_t | The context of the server pidfile | +----------------------------+-------------------------------------------------+ | bcfg2_lock_t | The context of the client lock file | +----------------------------+-------------------------------------------------+ | bcfg2_conf_t | The context of bcfg2.conf | +----------------------------+-------------------------------------------------+ | bcfg2_tmp_t | The context of temp files created by the Bcfg2 | | | server | +----------------------------+-------------------------------------------------+ If you do run your server in enforcing mode, it is highly recommend that you run ``restorecon -R /var/lib/bcfg2`` every time you update the content in that directory, particularly if you are using plugins that execute arbitrary scripts. .. _server-selinux-entries: Managing SELinux Entries ======================== .. versionadded:: 1.3.0 Bcfg2 has the ability to handle the majority of SELinux entries with the ``SELinux`` entry type, which handles modules (with the :ref:`server-plugins-generators-semodules` plugin), file contexts, users and user mappings, permissive domains, nodes, and interfaces. In addition, ``info.xml`` files and most types of the ``Path`` tag can accept an ``secontext`` attribute to set the context of that entry. The full semantics of each configuration entry is documented with the :ref:`server-plugins-generators-rules` plugin. .. note:: The ``secontext`` attribute takes a *full* context, e.g., "``system_u:object_r:etc_t:s0``"; the ``selinuxtype`` attribute always takes *only* an SELinux type, e.g., "``etc_t``". ``secontext`` (but not ``selinuxtype``) can also accept the special value "``__default__``", which will restore the context on the Path entry in question to the default supplied by the SELinux policy. In its current version, the SELinux support in Bcfg2 is not sufficient to manage MCS/MLS policies. Extra Entries ------------- As it can be very tedious to create a baseline of all existing SELinux entries, you can use ``selinux_baseline.py`` located in the ``tools/`` directory to do that for you. The actual definition of an "extra" entry actually depends on the version of SELinux available; the SELinux APIs have been extremely fluid, so many features available in newer versions are not available in older versions. Newer SELinux versions (e.g., in recent versions of Fedora) can be queried for only entries that have been locally modified; on these versions of SELinux, only locally modified entries will be considered extra. On older SELinux versions (e.g., on RHEL 5), however, that functionality is missing, so *all* SELinux entries will be considered extra, making ``selinux_baseline.py`` quite necessary. ``selinux_baseline.py`` writes a bundle to stdout that contains ``BoundSELinux`` entries for the appropriate SELinux entities. .. _server-selinux-duplicate-entries: Duplicate Entries ----------------- It may be necessary to use `BoundSEFcontext` tags if a single fcontext needs two different SELinux types depending on whether it's a symlink or a plain file. For instance: .. code-block:: xml bcfg2-1.3.3/doc/server/snapshots/000077500000000000000000000000001223671746500166135ustar00rootroot00000000000000bcfg2-1.3.3/doc/server/snapshots/index.txt000066400000000000000000000106511223671746500204660ustar00rootroot00000000000000.. -*- mode: rst -*- .. _server-snapshots-index: =============== Bcfg2 Snapshots =============== .. versionadded:: 1.0.0 This page describes the Snapshots plugin. Snapshots is deprecated, and will be removed in a future release. Before you begin ================ Make sure you have version 0.5 or greater of sqlalchemy. On CentOS/RHEL 5 ---------------- * Download a tarball of SQLAlchemy. * Extract and build the RPM:: tar xzf SQLAlchemy-0.5.6.tar.gz cd SQLAlchemy-0.5.6 python setup.py bdist_rpm * Copy the RPM in ``SQLAlchemy-0.5.6/dist/`` to your Yum repository, and rebuild the repository using ``createrepo``. * Clear the Yum cache:: sudo yum clean all * Install SQLAlchemy:: sudo yum install SQLAlchemy * Manage the package in Bcfg2 as you would any other package. Configuration ============= * A database location needs to be added to ``bcfg2.conf``. Three drivers are currently supported; mysql, postgres, and sqlite. When using the sqlite driver, only the driver and database lines are required. * For MySQL:: [snapshots] driver = mysql database = snapshots user = snapshots password = snapshots host = dbserver * For SQLite:: [snapshots] driver = sqlite database = /var/lib/bcfg2/var/snapshots.sqlite * The database needs to be initialized.:: $ bcfg2-admin snapshots init 2009-03-22 21:40:24,683 INFO sqlalchemy.engine.base.Engine.0x...3e2c PRAGMA table_info("connkeyval") PRAGMA table_info("connkeyval") 2009-03-22 21:40:24,684 INFO sqlalchemy.engine.base.Engine.0x...3e2c () () 2009-03-22 21:40:24,686 INFO sqlalchemy.engine.base.Engine.0x...3e2c PRAGMA table_info("package") PRAGMA table_info("package") 2009-03-22 21:40:24,687 INFO sqlalchemy.engine.base.Engine.0x...3e2c () () ..... COMMIT * The Snapshots plugin needs to be enabled for the bcfg2-server (by adding Snapshots to the plugins line in ``/etc/bcfg2.conf``). Once done, this will cause the the server to store statistics information when clients run. Using the reports interface =========================== All hosts:: $ bcfg2-admin snapshots reports -a ============= ========= ========================================== ============================ Client Correct Revision Time ============= ========= ========================================== ============================ bcfg2client True f46ac7773712bd3c3cfb765ae5d2a3b2a37ac9b7 2009-04-23 11:27:54.378941 ============= ========= ========================================== ============================ List bad entries for a single host:: $ bcfg2-admin snapshots reports -b bcfg2client Bad entries: Package:nscd Package:cupsys File:/etc/ldap.conf List extra entries for a single host:: $ bcfg2-admin snapshots reports -e bcfg2client Extra entries: Package:python-pyxattr Package:librsync1 Package:python-pylibacl Package:gcc-4.2-multilib Package:nxlibs Package:freenx-session-launcher Package:dx-doc Package:dirdiff Package:libhdf4g Package:nxclient Package:freenx-rdp Package:freenx-vnc Package:libxml2-dev Package:mysql-client Package:mysql-client-5.0 Package:libxcompext3 Package:lib32gomp1 Package:dx Package:freenx-media Package:dxsamples Package:gcc-multilib Package:rdiff-backup Package:libdbd-mysql-perl Package:libxcomp3 Package:freenx-server Package:smbfs Package:planner Package:nxagent Package:libc6-dev-i386 Package:libfltk1.1-dev Package:freenx Package:libdx4 Package:libxcompshad3 Service:freenx-server Detailed view of hosts for a particular date:: $ bcfg2-admin snapshots reports --date 2009 5 30 ============= ========= ========================================== ============================ Client Correct Revision Time ============= ========= ========================================== ============================ bcfg2client False 10c1a12c62c57c0861cc453b8d2640c4839a7357 2009-05-29 10:52:34.701056 TODO/Wishlist ============= * Identify per-client changes in correctness over time * Detailed view for a particular date * Track entry changes over time (glibc updated on these dates to these versions) bcfg2-1.3.3/doc/unsorted/000077500000000000000000000000001223671746500151265ustar00rootroot00000000000000bcfg2-1.3.3/doc/unsorted/bcfg2.conf-options.txt000066400000000000000000000005661223671746500212760ustar00rootroot00000000000000.. -*- mode: rst -*- .. _unsorted-bcfg2.conf-options: ========== bcfg2.conf ========== This page documents the various options available in bcfg2.conf. The various sections correspond to the sections in the file itself. components ========== logging ------- Specify an alternate path for the lockfile used by the bcfg2 client. Default value is ``/var/lock/bcfg2.run`` bcfg2-1.3.3/doc/unsorted/dynamic_groups.txt000066400000000000000000000015531223671746500207160ustar00rootroot00000000000000.. -*- mode: rst -*- .. _unsorted-dynamic_groups: ============== Dynamic Groups ============== Bcfg2 supports the use of dynamic groups. These groups are not included in a client's profile group, but instead are derived from the results of probes executed on the client. These dynamic groups need not already exist in ``Metadata/groups.xml``. If a dynamic group is defined in ``Metadata/groups.xml``, clients that include this group will also get all included groups and bundles. Setting up dynamic groups ========================= In order to define a dynamic group, setup a probe that outputs the text based on system properties:: group:groupname This output is processed by the Bcfg2 server, and results in dynamic group membership in groupname for the client. See the :ref:`Probes ` page for a more thorough description of probes. bcfg2-1.3.3/doc/unsorted/emacs_snippet.txt000066400000000000000000000035321223671746500205240ustar00rootroot00000000000000.. -*- mode: rst -*- .. _unsorted-emacs_snippet: ====================== Emacs + YASnippet mode ====================== This page describes using emacs with YASnippet mode with a set of snippets that allow quick composition of bundles and base files. More snippets are under development. #. Download YASnippet from http://code.google.com/p/yasnippet/ #. Install it into your emacs load path (typically ~/.emacs.d/site-lisp) #. Add YASnippet initialization to your .emacs (remember to re-byte-compile it if needed) .. code-block:: cl (require 'yasnippet-bundle) ;;; Bcfg2 snippet (yas/define-snippets 'sgml-mode '( (" $0 " nil) (" $0 " nil) (" $0" nil) (" $0" nil) (" $0" nil) (" $0" nil) (" $0" nil) (" $0" nil) (" $0" nil) ) ) #. One quick M-x eval-current-buffer, and this code is enabled Each of these snippets activates on the opening element, ie , and the snippet will be expanded. The template will be inserted into the text with a set of input prompts, which default to overwrite mode and can be tabbed through. The code above only works for bundles and base, but will be expanded to support other xml files as well. bcfg2-1.3.3/doc/unsorted/howtos.txt000066400000000000000000000013331223671746500172120ustar00rootroot00000000000000.. -*- mode: rst -*- .. _unsorted-howtos: ====== HOWTOs ====== Here are several howtos that describe different aspects of Bcfg2 deployment * :ref:`appendix-guides-authentication` - a description of the Bcfg2 authentication infrastructure * AnnotatedExamples - a description of basic Bcfg2 specification operations * BuildingDebianPackages - How to build debian packages * :ref:`appendix-guides-gentoo` - Issues specific to running Bcfg2 on Gentoo * :ref:`server-plugins-probes-index` - How to use Probes to gather information from a client machine. * :ref:`client-tools-actions` - How to use Actions * :ref:`unsorted-dynamic_groups` - Using dynamic groups * :ref:`client-modes-paranoid` - How to run an update in paranoid mode bcfg2-1.3.3/doc/unsorted/index.txt000066400000000000000000000011231223671746500167730ustar00rootroot00000000000000.. -*- mode: rst -*- .. _unsorted-index: Unsorted Docs ============= These docs have yet to be sorted properly. The content for them can be found at the TitleIndex_ page on Trac. Most should be converted to sphinx. Some may not need any conversion (e.g. The Download page). Once converted and put in the proper place, you can remove the item from the list below. .. _TitleIndex: https://trac.mcs.anl.gov/projects/bcfg2/wiki/TitleIndex * `Plugins/Snapshots` * `PrecompiledPackages` * `SchemaEvolution` * `SecurityDevPlan` * `ServerSideOverview` .. toctree:: :maxdepth: 2 :glob: * bcfg2-1.3.3/doc/unsorted/specification_overview.png000066400000000000000000000170151223671746500224060ustar00rootroot00000000000000PNG  IHDRJ8PLTE̙f::WWuu3J` w(/7>FMUl"Dfƪ3"J1`@wO^n}Ҍ"Df׈33GG\\pp)Rz"31J@`Ow^n}"Df3M"f+3<DMUh|:Wu3Pm33MMff3Mf"+3<DMUh:|Wu3"J1`@wO^n}Ҍ"Df̈ת33MMff:Wu3(P7mETbq3Mf3Uw--DD[[qq3&M3f@MYfs:WuȒׯ33MMff:Wu3M&f3@MYfs:WubKGDf |d pHYsodIDATx[R:[)z<a杹,`>>8k%pDw#BTUq&?M6vǛM=t?N_&1tѱܫ`_& &YƿlOk1L(e^'EB`n_;cl0sF l>szn|` AX! 3v(CQcӁ `Xt($`(C%F"}mlz8a Fa[sΕnYw3>q5}c8DUzś%/! щUen$WhX <0سĻjyDH';/fd 72;hOwk,NDc nT;`Ƥ.5H.lAwc_j  !أP( AѰ?''ueT ؜ػ_{nl0C0! 0! `0l0! `0! `CVؔ vh[00000000000000000000lsXNE0GL, c1%0[`ctĢ0C{F4o^?Oz#zzϟz^|j,q>0N4)ؗ񓧴C-+M_|<2N0;|(ZYV&񞞑^tQpJuO씑_Hyi]U]\:%ej~l . #Q߳ng%yCUΫcSN͉2.Wy;.uYJ2Ko|[:79@q>P<])>7I٧ҷڀRetok]V5j}ПM~Ik1 {g&ǔ95A٫O|%;Z-ͬwYJ gҨu(攛\2[>Sэji;vfnl p V/-{nfўq{o/[(7K+QV]|.uYJ2%+>2?*kL}'S}̄ozQh8])}}UjSQ  ¿ZWU׺N%Yo-52u͹ddk`yBygr/WI!vaYEC0! 0dSJٱ|]8%zԓ<#C;60on <<`""j>?VH#|cV p8HX=6L 7^hcrxCtbz{g+2}C4`>"m 7N ^9E)w@LFލotF/@ Q`FB7&|fƪFglB=9n I."љvQ-=\ I7bPbE `C! `C`C0`C0ĦT#DDo ncwR `VMc0pĄ1`0 N8^ 5(eG3;]~~Ui髋`^Egˎ\@|K.)?k"r[e~/<ηϛi_WEkWM{Ч '\V-(nK*H_ 7k0T][mš2:#8tU.U~G}~;xw5et<97.y'p]ym +v\xEwʨk'Y)KV}ogrWNm%?O>GhuU/LlS(JV.ecp}^dZn̲s62nlZ>e˖FFǗj9΄r/kTˤnE1u/8k" 2oU@ܪd$%}y0|:؋na9kK֭"[Xދ0! `C! `C`Cbp70 kpG`6TL@_h] "E-<# P.i,<3#UfX+0_ii8Rr+̭$V5W%ܓhuvYWYm59 ˚1/`0ڳeQ%q L2x[GS&!-֛sI'J/r|K7>3$$h`$r00n`$BvHGWlu$w_>АJQ@ 6'lUJ I#EJS*F`ўU)6*+hNsasR`IAs TX%(S#lUJ: ţ&'/3ʵ(K7zeiLIuߞr5X4 Ԑ+]) ָ0u<}5<v롼69)TG\L|벬 Z>;0d p?| # p0iNjP7F1>/ino"w2,RkjS ɼAaЦ `jϴ`JvpIc0W&1ǚ+7J4ָ|k6w4y̋n .-7E4"0swܗ,{'oBwh^ 'F3݊&!'ttğ;((pپC*|vntn:|&w/UIeI+=86 S ]  BE;ctla:,_1hCQ+m8ʎ Hڊd vI$oٱlR+0!㏣}DU* a=Iz@h 0 ъyBM-3iuBgF ZcHe?)G?:Ѯ̙~Z 9Nq|b,O~*b"T^T| EE*2+l'v)'5<$,YepcRx3`02`ٕiuf葾mw܋_DXٵs{*N%M cSI MQSI. 7TR0.Jjd^~TRa 1:;ed FTR_qwvƪ ΀X>`NC[fM; 64l=Hظ07iC)q;UT՗sNJ.E>|9Ng^frXQR;inUUSyw[q4k>}1fr2PN=5%Zx՜_ӬY;˫눨b?@&%Sn"'߶>pDowsvȱ3LSﺩ풛?|:sKc$+/Ol4<5הΕ4%2/7:hhLo-z?9^V·Mh|0%G<pI^5utJK:8@To?2d<t/V {`OUvxl04̸QN8 up4%z%zv2_@hAXi,yICJQi i0%3@8p>MӺ(;,I@p뾌x} F'N%줦T4)̀526QA^}ID)I4CpHf?TmïS/zryj3.3f~Mr Ґa"}Hß7 `N`C0x~9_'A~! ,HDCYQ$X%DY{ w Adn/|,}" Xz`VX> `hD #lAक(H}$)6 MX ;)iу>1.7(b p(%'g`lpYeW'&YpFGˤ;n`HhKq 0`0`0v60F  `9"(fb0(Y@hsF(C̀OW7W?Ua#u`0<2F x dd>p v1#Y0! `0! `C! `C!6 + 0!){IENDB`bcfg2-1.3.3/doc/unsorted/vim_snippet.txt000066400000000000000000000037231223671746500202310ustar00rootroot00000000000000.. -*- mode: rst -*- .. _unsorted-vim_snippet: =================== Vim Snippet Support =================== This page describes using vim with snipMate and a set of snippets that allow quick composition of bundles and base files. #. Download snipMate from http://www.vim.org/scripts/script.php?script_id=2540 #. Install it using the install instructions (unzip snipMate.zip -d ~/.vim or equivalent, e.g. $HOME\vimfiles on Windows) #. Add the following to ``~/.vim/snippets/xml.snippets`` .. code-block:: cl # Bundle snippet ${2} # Base snippet ${1} # Group snippet ${2} # ConfigFile snippet # Service snippet # Package snippet # Action snippet # Directory snippet # SymLink snippet # Permissions snippet #. Save and start editing away! Each of these snippets activates on the opening element, ie . After this string is entered, but before entering a space, press , and the snippet will be expanded. The template will be inserted into the text with a set of input prompts, which default to overwrite mode and can be tabbed through. The code above only works for bundles and base, but will be expanded to support other xml files as well. bcfg2-1.3.3/doc/unsorted/viz.png000066400000000000000000000157521223671746500164560ustar00rootroot00000000000000PNG  IHDR &WPLTEsssOOO444iii?(id_<ߌPx%%L 3 f++22(tRNS@fAIDATx흉 gTd֒*u j@X  !`$,Y >\R D.@ʘ^;eCy  xJ,h pBz,J  6' ,Ir* $ yiG‚3 C>ZfNF M,A`DK,x :7<Ă7Bj|0 % k,3҂a˿ d#,Xx XЯ"P]k0.% %n04ծ(kTaBJDqSj0,.}jZ)v -o&xW&5KKIXH],`ʷ@U{AY`O:4Dm$Yh,Zc͈/UY ⃂4@v?-]Ś 4L!77/ ](? Y@g*S&Y? F,XmP!}I̫lG`!yܧ !x $;9h ) m0hD̿4de2f5 ky܀ĵ@CxW!am>4А"4$(G I*Һ2@CА"74$k I):t@CB&!А4$Xb[% @C|[)4UZ, 1 @C<K)4Q, 1* @JakаfhR0Yx*U\,Xr=H,FTŒu 1ރÿALcW<>- ߨ>] HH\~H:ҟ4>h(MLsܧ9]>-h*=d AM.>Yp09Z e8,wFk$ V 巖,J,ggǰlf(7\rdBQNGiB7p-%V$T&DEߓ{ij .LS3\ł[~OnRճ vʄ$POk˜ רjHb3.Kmf* )*K lJ$PC|{/Y@JoY]Mm%kgݨVKp!̀w@[^k`aͥ Tm)K,xaԺj.uˢ0f=ɈqAh ,w}6i;*X ,+,Ti,,˙U;XGG% M 2^,*mGl*vd U4jox{3_w,hv- m#2|j';-&vҾdE XeH]a ޔ;Rn,}p@B\Hw s !qS !O q ,_(Hռ,SK se3_ܒ|M@TI `+]<)Ah)y_=Y 4 |VV r'֔'<֏0-+ue6,^>`aϦXi\>`AϤXiT<>`aIEOXX,Sht4 z]? ޝ`D `ahx;0C4$1"ī`|rG:0$=Rոl.;YֻB-YGA>|*ؒe!yY"ْ4@8xUYּQMB4<[\[cLH4xO-QPH64:k:] CCAdBCħd`rl9 9+-k3dTRPH ֡KʖS YH֧Lʖ+ I֭Mʖ; )`M]baZFtJlM}`!VXXM=|xSN|x@! a$c"XeT, 9Q{Bc^t>u8grOwd e?iY෈j?Ÿw \:r=1*Nd5 6!Ŏ.ejSBƖdϤ='YU$x 4KY$pjd = g ,,gYܲ@&iYPj߳}Va\Yh?-ܳǏlX]P>-$ K,t{>Yx}Z guXیk6DZ0VluoHX}Zfpz'0(0oPj|D2IOOȀtm}8$nn<˲*(E͇YP-#c@hi#w˂s 0O|>R>X?"}ħ֕IGiRR\`!5ţXHOhRT4`!Um6 Bژ`!imJ6XH^,BXC[,4 (4 BN KB,dᴳ ,d'$f& û}A0>z,x+$" b6ޅתGn?xJHȓ͵j-XPcPk9+-O$A!ga!Ch/6/XJ2k8d΂>ᣃ,'àAz̬Y1 59qĂ6 J2f!>[3$p<N`ٲ|НC|NJT(BC,f@kƘ0&Ady)!|AAd `>O5 Srd /fb YB``q!?T E})7F0dǂGYyޗ`#VNOˋ[8G=)'ן n}7%<,MY>?/؛  N 1zt8 faV,1˦ UCꊵkF'$2> /q2{rdA</eNߞ?#y'?}Y` DEh F MeȺH=GX<$J__|,\N W8 rxF7}&6(E<.j'K Y^ȩ>8/]^Yhx -c܈@RxL@Q`d` E\TZ,DT=KqA¿^- G3~nҖe,L /タA EBVX`$+#xAą/Xx~ٕfB0Y -@!,l8 ?tj 5 QH .' 0lB0XpDᦙ֥# RC<k ֻ`/EYd7%X@ .OkTר(~G;&OP?4x M7>nsw4 apVcBNc&/hv{E1e;PK <vG(䑕1]DKqž ^B$7<.HӖ{ES9}Vw{[߷Y\hw •rp"LYoRfanیʸnǴ`q Y0]Ti֏xoV,.< sEWsyyɥ3G!e7mC D>nG,; pݟD<\ӐY `XXH#D;T5eQ&`1b{I]f;`؎ǃBhLSȝOIv  'O~Ŏ,\䁜gWHHO);7BYKm׌_x=j3>WbLj +^,\0C/|M^kJJ6#KPh Y(C$8踱Kq-ѼrĖu.,:3"f>h"^0Dwj d?e2O|-oڄa7Չl~iCS 9[–XHYֱB,lZ:B,lea.* wDY'LQ q Li= HZB"Ra!ڗP 4+>(-7h6$_K@a,Dбϟ@aH,$@Bt) C|)MH?ełàm%R! mޮ \u7vLXX@cu^tY`@$jM `0_GWÝIKW!̼VYp8!sc@T[ݕ @B,xϷkƀwB|b `j Q@JL,ZKnU(ueҭArx&%2VU2Ph+*g^Xx; GƖXXtdc]{MBT/EkyĢ ˎvB/ ̢1*I]ѷl~Hp] ]0!%*C_:+&UNߧ$YI#^Y@ `/ YUbE %%DU5A<5`MFԞX㔎4Ha`CYg&iY@;]` ¤qgaIUMSx2:J~ƀ!O , E\Hw s !qS !O q -__Me "ŷ6)`&j02\d @XԖ󾆒]mE_\eDmeGlDm%S@BHN⳵ IENDB`bcfg2-1.3.3/doc/unsorted/windows.txt000066400000000000000000000076761223671746500174010ustar00rootroot00000000000000.. -*- mode: rst -*- .. _unsorted-windows: ================================= Notes on possible Windows support ================================= * Windows Management Instrumentation (WMI) should be used wherever possible; there is an excellent [http://tgolden.sc.sabren.com/python/wmi.html WMI Python Module] available, which also comes with a [http://tgolden.sc.sabren.com/python/wmi_cookbook.html WMI Cookbook]. * Before Windows 2003 SP1, on 64-bit machines there are [http://msdn2.microsoft.com/en-us/library/aa393067.aspx no API or WMI calls] to get to many 32-bit windows functions (such as the 32-bit registry) from 64-bit programs, and vice versa. There also is no (official) x86_64 native python distributions for Windows pre-Python 2.5. So the choice would be: #. Only support Windows in Python 2.5+ (which wouldn't be that bad because part of the build process would probably be to create stand-alone bcfg2 executables using [http://www.py2exe.org/ py2exe]). For 64-bit support there would have to be some kind of convoluted py2exe build process that built some things with 32-bit python and some things with 64-bit python. #. Wrap external command-line programs such as winreg, which is part of [http://dmst.aueb.gr/dds/sw/outwit/ outwit], and screen scrape. Each external command-line program would need to be compiled into 32 and 64 bit versions. This approach might lead to licensing annoyances and having binary blobs in source control. Services ======== With the exception of 32/64 bit issues, Windows Services support should be pretty trivial; it would differ from \*nix services in that it would be done via WMI API calls and not a 3rd party python module or wrapping a binary. Registry ======== The best way of handling the registry may be to map it into a file-based representation on the server end. The Cfg plugin could then be used to set registry values as needed. Files ===== For a first run there may be some way of utilizing [http://cygwin.com/ cygwin] to make use of the existing \*nix POSIX module for manipulating files. There would probably need to be some changes to deal with the fact that open files can't be manipulated/moved/deleted at all in Windows (other than to do some registry magic that makes the changes on the next reboot). Packages ======== Listing and removal of packages should be pretty easy via WMI. For installation in most cases the admin would need to figure out the correct silent install flags (there is a [http://www.appdeploy.com/ web site] that catalogs a lot of this information), and include that in the bcfg2 server-side XML along with a URL (like with the RPM plugin); the bcfg2 client itself would need to take care of download, perhaps via the [http://linux.duke.edu/projects/urlgrabber/ urlgrabber python module]. Another option would be to utilize one of the existing FLOSS tools for dealing with Windows packages, such as [http://wpkg.org/ WPKG]. Prior FLOSS Art =============== * [http://www.autoitscript.com/autoit3/ AutoIt] - For dealing with packages that don't have a silent install option * [http://www.opensysadmin.com/trac/ticket/4 French Stuff] * [http://ocsinventory.sourceforge.net/ Open Computers and Software Inventory - Next Generation] * [http://www.glpi-project.org/spip.php?lang=en GLPI - Gestionnaire libre de parc informatique] * Javascript thing a colleague of Desai's at ANL wrote - Desai was going to see if this can be released * [http://sial.org/howto/cfengine/windows/ Managing Windows with CFEngine and Perl] * [http://www.dmst.aueb.gr/dds/sw/outwit/ Outwit] - Small unixy utilities for Windows stuff like the registry and clipboard * [http://www.cfengine.org/docs/cfengine-NT/ Porting cfengine to Windows NT] * [http://isg.ee.ethz.ch/tools/realmen/ Real Men Don't Click] - Tobi Oetiker's stuff * [http://isg.ee.ethz.ch/tools/realmen/res/index.en.html More Prior FLOSS Art] * [http://unattended.sourceforge.net/ Unattended] - Bare Metal Installs, Package Management * [http://wpkg.org/ WPKG] - Package Management bcfg2-1.3.3/doc/unsorted/writing_specification.txt000066400000000000000000000242351223671746500222600ustar00rootroot00000000000000.. -*- mode: rst -*- .. _unsorted-writing_specification: =========================== Writing Bcfg2 Specification =========================== Bcfg2 specifications are logically divided in to three areas: * Metadata * Abstract * Literal The metadata portion of the configuration assigns a client to its profile group and to its non-profile groups. The profile group is assigned in ``Metadata/clients.xml`` and the non profile group assignments are in ``Metadata/groups.xml``. The group memberships contained in the metadata are then used to constuct an abstract configuration for the client. An abstract configuration for a client identifies the configuration entities (packages, configuration files, service, etc) that a client requires, but it does not identify them explicitly. For instance an abstract configuration may identify that a client needs the Bcfg2 package with .. code-block:: xml but this does not explicitly identify that an RPM package version 0.9.2 should be loaded from http://rpm.repo.server/bcfg2-0.9.2-0.1.rpm. The abstract configuration is defined in the xml configuration files for the Base and Bundles plugins. A combination of a clients metadata (group memberships) and abstract configuration is then used to generate the clients literal configuration. For instance the above abstract configuration entry may generate a literal configuration of .. code-block:: xml A clients literal configuration is generated by a number of plugins that handle the different configuration entities. .. image:: specification_overview.png Dynamic Groups ============== Dynamic groups are likewise complex, and are covered on their own [wiki:DynamicGroups page] Abstract Configuration (Structures) =================================== A clients Abstract Configuration is the inventory of configuration entities that should be installed on a client. Two plugins provide the basis for the abstract configuration, the Bundler and Base. The plugin Bundler builds descriptions of interrelated configuration entities. These are typically used for the representation of services, or other complex groups of entities. The Base provides a laundry list of configuration entities that need to be installed on hosts. These entities are independent from one another, and can be installed individually without worrying about the impact on other entities. Usage of Groups in Base and Bundles ----------------------------------- Groups are used by the Base and Bundles plugins for selecting Configuration Entity Types for inclusion in a clients abstract configuration. They can be thought of as:: if client is a member of group1 then assign to abstract config Nested groups are conjunctive (logical and).:: if client is a member of group1 and group2 then assign to abstract config Group membership maybe negated. See "Writing Bundles" for an example. Configuration Entity Types -------------------------- Entities in the abstract configuration (and correspondingly in the literal configuration) can have one of several types. In the abstract configuration, each of these entities only has a tag and the name attribute set. The types of Configuration Entities that maybe assigned to the abstract configuration can be seen at :ref:`server-configurationentries`. An example of each entity type is below. .. code-block:: xml Writing Bundles --------------- Bundles consist of a set of configuration entities. These entities are grouped together due to a configuration-time interdependency. Basic services tend to be the simplest example of these. They normally consist of * some software package(s) * some configuration files * an indication that some service should be activated If any of these pieces are installed or updated, all should be rechecked and any associated services should be restarted. All files in the Bundles/ subdirectory of the repository are processed. Each bundle must be defined in its own file and the filename must be the same as the bundle name with a .xml suffix.:: # ls Bundler Glide3.xml LPRng.xml Tivoli-backup.xml Tivoli.xml a2ps.xml abiword.xml account.xml adsm-client.xml amihappy.xml apache-basic.xml apache.xml apache2-basic.xml apt-proxy.xml at.xml atftp-server.xml atftp.xml .... Groups can be used inside of bundles to differentiate which entries particular clients will receive. This is useful for the case where entries are named differently across systems; for example, one linux distro may have a package called openssh while another uses the name ssh. Configuration entries nested inside of Group elements only apply to clients who are a member of those groups; multiply nested groups must all apply. Also, groups may be negated; entries included in such groups will only apply to clients who are not a member of said group. When packages in a bundle are verified by the client toolset, the Paths included in the same bundle are taken into consideration. That is, a package will not fail verification from a Bcfg2 perspective if the package verification only failed because of configuration files that are defined in the same bundle. The following is an annotated copy of a bundle: .. code-block:: xml In this bundle, most of the entries are common to all systems. Clients in group "deb" get one extra package and service, while clients in group "rpm" get two extra packages and an extra service. In addition, clients in group "fedora" and group "rpm" get one extra package entries, unless they are not in the fc4 group, in which case, they get an extra package. Notice that this file doesn't describe which versions of these entries that clients should get, only that they should get them. (Admittedly, this example is slightly contrived, but demonstrates how group entries can be used in bundles) +----------------+-------------------------------+ | Group | Entry | +================+===============================+ | all | /etc/ssh/ssh_host_dsa_key | +----------------+-------------------------------+ | all | /etc/ssh/ssh_host_rsa_key | +----------------+-------------------------------+ | all | /etc/ssh/ssh_host_dsa_key.pub | +----------------+-------------------------------+ | all | /etc/ssh/ssh_host_rsa_key.pub | +----------------+-------------------------------+ | all | /etc/ssh/ssh_host_key | +----------------+-------------------------------+ | all | /etc/ssh/ssh_host_key.pub | +----------------+-------------------------------+ | all | /etc/ssh/sshd_config | +----------------+-------------------------------+ | all | /etc/ssh/ssh_config | +----------------+-------------------------------+ | all | /etc/ssh/ssh_known_hosts | +----------------+-------------------------------+ | rpm | Package openssh | +----------------+-------------------------------+ | rpm | Package openssh-askpass | +----------------+-------------------------------+ | rpm | Service sshd | +----------------+-------------------------------+ | rpm and fedora | Package openssh-server | +----------------+-------------------------------+ | rpm and fedora | Package openssh-clients | | and not fc4 | | +----------------+-------------------------------+ | deb | Package ssh | +----------------+-------------------------------+ | deb | Service ssh | +----------------+-------------------------------+ Bundle Tag ^^^^^^^^^^ .. xml:type:: BundleType :nochildren: As mentioned above the Configuration Entity Tags may only have the name attribute in Bundle definitions. Group and Client Tags ^^^^^^^^^^^^^^^^^^^^^ .. xml:type:: BundlerGroupType :nochildren: An abstract group may contain any of the Configuration Entity types and other groups. Literal Configuration (Generators) ================================== A Generator is a Bcfg2 piece of code that is run to generate the literal configuration for a host using a combination of the hosts metadata and abstract configuration. A Generator can take care of a particular configuration element. Any time this element is requested by the client, the server dynamically generates it either by crunching data and creating new information or by reading a file off of disk and passes it down to the client for installation. Usage of Groups in Generators ----------------------------- Similar to Abstract Configuration plugins, groups are used by generator plugins for selecting Configuration Entities for inclusion in a clients literal configuration. They can be thought of as:: if client is a member of group1 then assign to abstract config Nested groups are conjunctive (logical and).:: if client is a member of group1 and group2 then assign to abstract config How the groups are configured is specific to the plugin, but here are two common methods: * xml configuration file (Pkgmgr, Rules) * file name encoding (Cfg, SSHBase) Details are included on each plugin's page. bcfg2-1.3.3/examples/000077500000000000000000000000001223671746500143345ustar00rootroot00000000000000bcfg2-1.3.3/examples/Bundler/000077500000000000000000000000001223671746500157275ustar00rootroot00000000000000bcfg2-1.3.3/examples/Bundler/sgenshi-dirvish.genshi000066400000000000000000000011071223671746500222330ustar00rootroot00000000000000 client: nfs-host tree: /export/homes/${user.text} exclude: *~ .nfs* bcfg2-1.3.3/examples/Cfg/000077500000000000000000000000001223671746500150335ustar00rootroot00000000000000bcfg2-1.3.3/examples/Cfg/etc/000077500000000000000000000000001223671746500156065ustar00rootroot00000000000000bcfg2-1.3.3/examples/Cfg/etc/cron.d/000077500000000000000000000000001223671746500167715ustar00rootroot00000000000000bcfg2-1.3.3/examples/Cfg/etc/cron.d/dirvish/000077500000000000000000000000001223671746500204415ustar00rootroot00000000000000bcfg2-1.3.3/examples/Cfg/etc/cron.d/dirvish/dirvish000066400000000000000000000002721223671746500220350ustar00rootroot00000000000000# /etc/cron.d/dirvish: crontab fragment for dirvish SHELL=/bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root # run every night 4 22 * * * root /etc/dirvish/dirvish-cronjob bcfg2-1.3.3/examples/Cfg/etc/dirvish/000077500000000000000000000000001223671746500172565ustar00rootroot00000000000000bcfg2-1.3.3/examples/Cfg/etc/dirvish/dirvish-cronjob/000077500000000000000000000000001223671746500223605ustar00rootroot00000000000000bcfg2-1.3.3/examples/Cfg/etc/dirvish/dirvish-cronjob/dirvish-cronjob000066400000000000000000000020021223671746500253770ustar00rootroot00000000000000#! /bin/sh # # daily cron job for the dirvish package # # NOTE: This is the sample cron job included in Debian. You may need to # change the executable paths if running on a different OS. if [ ! -x /usr/sbin/dirvish-expire ]; then exit 0; fi if [ ! -s /etc/dirvish/master.conf ]; then exit 0; fi mount_check() { mntout=`tempfile -p mount` mount $1 >$mntout 2>&1 if [ ! -d $1/lost+found ]; then # only works for "real" filesystems :-) # (Yes, I know about reiserfs.) echo "'mount $1' failed?! Stopping." echo "mount output:" cat $mntout rm -f $mntout exit 2 fi if stat $1 | grep 'Inode: 2[^0-9]' >/dev/null; then # ditto rm -f $mntout return 0 # ok fi echo "$1 isn't inode 2 ?! Mount must have failed; stopping." echo '' stat $1 echo "mount output:" cat $mntout rm -f $mntout umount $1 exit 2 } ## Example of how to mount and umount a backup partition... # mount_check /backup /usr/sbin/dirvish-expire --quiet && /usr/sbin/dirvish-runall --quiet rc=$? # umount /backup || rc=$? exit $rc bcfg2-1.3.3/examples/Properties/000077500000000000000000000000001223671746500164705ustar00rootroot00000000000000bcfg2-1.3.3/examples/Properties/dirvish.xml000066400000000000000000000003031223671746500206560ustar00rootroot00000000000000 user1homedir user2homedir user3homedir bcfg2-1.3.3/examples/README000066400000000000000000000004651223671746500152210ustar00rootroot00000000000000This directory contains example files for various Bcfg2 generators and plugins. The documentation for the examples is available in the Bcfg2 wiki or the manual. Those files may not work in your environment. Others can be obsolete. The purpose is to give you a entry point for your own configuration. bcfg2-1.3.3/examples/TGenshi/000077500000000000000000000000001223671746500156755ustar00rootroot00000000000000bcfg2-1.3.3/examples/TGenshi/etc/000077500000000000000000000000001223671746500164505ustar00rootroot00000000000000bcfg2-1.3.3/examples/TGenshi/etc/dirvish/000077500000000000000000000000001223671746500201205ustar00rootroot00000000000000bcfg2-1.3.3/examples/TGenshi/etc/dirvish/master.conf/000077500000000000000000000000001223671746500223375ustar00rootroot00000000000000bcfg2-1.3.3/examples/TGenshi/etc/dirvish/master.conf/template.newtxt000066400000000000000000000005561223671746500254330ustar00rootroot00000000000000bank: /backup image-default: %Y-%m-%d log: bzip2 index: bzip2 xdev: 1 exclude: lost+found/ *~ .nfs* Runall: {% for user in metadata.Properties['dirvish.xml'].data.find('users') %}\ homes/${user.tag} {% end %}\ expire-default: +2 weeks expire-rule: # MIN HR DOM MON DOW STRFTIME_FMT * * * * 1 +6 weeks * * 1-7 * 1 +6 months * * 1-7 1,4,7,10 1 never bcfg2-1.3.3/examples/TGenshi/etc/motd/000077500000000000000000000000001223671746500174135ustar00rootroot00000000000000bcfg2-1.3.3/examples/TGenshi/etc/motd/template.newtxt000066400000000000000000000015171223671746500225050ustar00rootroot00000000000000------------------------------------------------------------------------ GOALS FOR SERVER MANAGED BY BCFG2 ------------------------------------------------------------------------ Hostname is ${metadata.hostname} Groups: {% for group in metadata.groups %}\ * ${group} {% end %}\ {% if metadata.categories %}\ Categories: {% for category in metadata.categories %}\ * ${category} {% end %}\ {% end %}\ {% if metadata.Probes %}\ Probes: {% for probe, value in metadata.Probes.iteritems() %}\ * ${probe} \ ${value} {% end %}\ {% end %}\ ------------------------------------------------------------------------ ITOPS MOTD ------------------------------------------------------------------------ Please create a Ticket for any system level changes you need from IT. bcfg2-1.3.3/examples/TGenshi/tmp/000077500000000000000000000000001223671746500164755ustar00rootroot00000000000000bcfg2-1.3.3/examples/TGenshi/tmp/bar/000077500000000000000000000000001223671746500172415ustar00rootroot00000000000000bcfg2-1.3.3/examples/TGenshi/tmp/bar/template.txt000066400000000000000000000005251223671746500216170ustar00rootroot00000000000000[communication] protocol = xmlrpc/ssl #if metadata.uuid != None user = $metadata.uuid #end #choose #when metadata.password is not None password = $metadata.password #end #when metadata.password is None password = GlobalPassword #end #end [client] drivers = Action,Chkconfig,POSIX,YUMng [components] bcfg2 = https://config.example.com:6789 bcfg2-1.3.3/examples/TGenshi/tmp/foo/000077500000000000000000000000001223671746500172605ustar00rootroot00000000000000bcfg2-1.3.3/examples/TGenshi/tmp/foo/template.xml000066400000000000000000000020411223671746500216120ustar00rootroot00000000000000 ${name}
    Name:${name}
    Hostname:${metadata.hostname}
    Toolset:${metadata.hostname}
    UUID:${metadata.uuid}
    Password:${metadata.password}
    Bundles:
    ${bundle}
    Groups:
    ${group}
    Categories:
    ${category}
    Probes:
    ${probe}${metadata.probes[probe]}
    bcfg2-1.3.3/examples/TemplateHelper/000077500000000000000000000000001223671746500172475ustar00rootroot00000000000000bcfg2-1.3.3/examples/TemplateHelper/include.py000066400000000000000000000073641223671746500212560ustar00rootroot00000000000000""" IncludeHelper makes it easier to include group- and host-specific files in a template. Synopsis: {% python import os include = metadata.TemplateHelper['include'] custom = include.IncludeHelper(metadata, path).files(os.path.basename(name)) %}\ {% for file in custom %}\ ########## Start ${include.describe_specificity(file)} ########## {% include ${file} %} ########## End ${include.describe_specificity(file)} ########## {% end %}\ This would let you include files with the same base name; e.g. in a template for ''foo.conf'', the include files would be called ''foo.conf.G_.genshi_include''. If a template needs to include different files in different places, you can do that like so: inc = metadata.TemplateHelper['include'].IncludeHelper(metadata, path) custom_bar = inc.files("bar") custom_baz = inc.files("baz") This would result in two different sets of custom files being used, one drawn from ''bar.conf.G_.genshi_include'' and the other from ''baz.conf.G_.genshi_include''. """ import os import re __export__ = ["IncludeHelper", "get_specificity", "describe_specificity"] class IncludeHelper(object): def __init__(self, metadata, path): """ Constructor. The template path can be found in the ''path'' variable that is set for all Genshi templates. """ self.metadata = metadata self.path = path def get_basedir(self): return os.path.dirname(self.path) def files(self, fname, groups=None): """ Return a list of files to include for this host. Files are found in the template directory based on the following patterns: * ''.H_.genshi_include'': Host-specific files * ''.G_.genshi_include'': Group-specific files * ''.genshi_include'': Non-specific includes Note that there is no numeric priority on the group-specific files; all matching files are returned by ``IncludeHelper.files()``. If you wish to only include files for a subset of groups, pass the ``groups`` keyword argument. Host-specific files are always included in the return value. """ files = [] hostfile = os.path.join(self.get_basedir(), "%s.H_%s.genshi_include" % (fname, self.metadata.hostname)) if os.path.isfile(hostfile): files.append(hostfile) allfile = os.path.join(self.get_basedir(), "%s.genshi_include" % fname) if os.path.isfile(allfile): files.append(allfile) if groups is None: groups = sorted(self.metadata.groups) for group in groups: filename = os.path.join(self.get_basedir(), "%s.G_%s.genshi_include" % (fname, group)) if os.path.isfile(filename): files.append(filename) return files SPECIFICITY_RE = re.compile(r'(G|H)_(.*)\.genshi_include') def get_specificity(fname): """ Get a tuple of (, ) describing the specificity of the given file. Specificity types are "host", "group", or "all". The parameter will be either a hostname, a group name, or None (for "all"). """ match = SPECIFICITY_RE.search(fname) if match: if match.group(1) == "G": stype = "group" else: stype = "host" return (stype, match.group(2)) return ("all", None) def describe_specificity(fname): """ Get a string describing the specificity of the given file """ (stype, param) = get_specificity(fname) if stype != "all": return "%s-specific configs for %s" % (stype, param) else: return "Generic configs for all clients" bcfg2-1.3.3/examples/bcfg2-lint.conf000066400000000000000000000012571223671746500171370ustar00rootroot00000000000000[lint] plugins=InfoXML,Comments,RequiredAttrs,Validate,MergeFiles [errors] no-infoxml=error paranoid-false=error properties-schema-not-found=silent inconsistent-bundle-name=error keywords-not-found=error comments-not-found=error [InfoXML] required_attrs = owner,group,perms,paranoid [Comments] global_keywords = Id genshibundler_comments = Properties,Probes,Description properties_comments = Template,Format genshi_comments = Maintainer,Properties,Probes,Description cheetah_comments = Maintainer,Properties,Probes,Description cfg_comments = cfg_keywords = probe_comments = Maintainer,Purpose,Groups,Other Output [Validate] schema=/usr/share/bcfg2/schemas [MergeFiles] threshold=85 bcfg2-1.3.3/examples/bcfg2.conf000066400000000000000000000002331223671746500161640ustar00rootroot00000000000000[communication] protocol = xmlrpc/ssl password = foobat # certificate = /etc/bcfg2.key # key = /etc/bcfg2.key [components] bcfg2 = https://localhost:6789 bcfg2-1.3.3/examples/bcfg2.confHostbase000066400000000000000000000015231223671746500176600ustar00rootroot00000000000000[server] repository = /var/lib/bcfg2 plugins = Bundler,Rules,Metadata,SSHbase,Cfg [statistics] sendmailpath = /usr/sbin/sendmail [communication] protocol = xmlrpc/ssl password = foobat key = /etc/bcfg2.key [components] bcfg2 = https://localhost:6789 [hostbase] # postgresql, mysql, sqlite3 or ado_mssql database_engine = mysql # Or path to database file if using sqlite3. database_name = # Not used with sqlite3. database_user = # Not used with sqlite3. database_password = # Set to empty string for localhost. Not used with sqlite3. database_host = # Set to empty string for default. Not used with sqlite3. database_port = 3306 # enter an NIS group name you'd like to give access to edit hostbase records ##authorized_group = support # default mx record for new hosts added to the database default_mx = mailserver.yourdomain.net priority = 30 bcfg2-1.3.3/examples/brpt.sqlite000066400000000000000000006400001223671746500165260ustar00rootroot00000000000000SQLite format 3@ uu ?9.6*4%+"%"     oo  5sAAjoeyhagedorn@mcs.anl.govsha1$a1043$f0904cb26ef42eb5259cf3dcb80dc6c01506ed8c2006-06-21 16:42:01.9958142006-06-19 15:10:18.850029 joey/1.10+delete_logentry o3'%%tableauth_messageauth_messageCREATE TABLE "auth_message" ( "id" integer NOT NULL PRIMARY KEY, "user_id" integer NOT NULL, "message" text NOT NULL ) !!ctableauth_groupauth_groupCREATE TABLE "auth_group" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(80) NOT NULL UNIQUE )3G!indexsqlite_autoindex_auth_group_1auth_groupJctableauth_userauth_userCREATE TABLE "auth_user" ( "id" integer NOT NULL PRIMARY KEY, "username" varchar(30) NOT NULL UNIQUE, "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL, "email" varchar(75) NOT NULL, "password" varchar(128) NOT NULL, "is_staff" bool NOT NULL, "is_active" bool NOT NULL, "is_superuser" bool NOT NULL, "last_login" datetime NOT NULL, "date_joined" datetime NOT NULL ) $5t1Eindexsqlite_autoindex_auth_user_1auth_user++Stableauth_permissionauth_permissionCREATE TABLE "auth_permission" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL, "content_type_id" integer NOT NULL, "codename" varchar(100) NOT NULL, UNIQUE ("content_type_id", "codename") )=Q+indexsqlite_autoindex_auth_permission_1auth_permission<99tableauth_group_permissionsauth_group_permissions CREATE TABLE "auth_group_permissions" ( "id" integer NOT NULL PRIMARY KEY, "group_id" integer NOT NULL REFERENCES "auth_group" ("id"), "permission_id" integer NOT NULL REFERENCES "auth_permission" ("id"), UNIQUE ("group_id", "permission_id") )K _9indexsqlite_autoindex_auth_group_permissions_1auth_group_permissions     "c --ctableauth_user_groupsauth_user_groups CREATE TABLE "auth_user_groups" ( "id" integer NOT NULL PRIMARY KEY, "user_id" integer NOT NULL REFERENCES "auth_user" ("id"), "group_id" integer NOT NULL REFERENCES "auth_group" ("id"), UNIQUE ("user_id", "group_id") )? S-indexsqlite_autoindex_auth_user_groups_1auth_user_groupsE AAtableauth_user_user_permissionsauth_user_user_permissionsCREATE TABLE "auth_user_user_permissions" ( "id" integer NOT NULL PRIMARY KEY, "user_id" integer NOT NULL REFERENCES "auth_user" ("id"), "permission_id" integer NOT NULL REFERENCES "auth_permission" ("id"), UNIQUE ("user_id", "permission_id") )S gAindexsqlite_autoindex_auth_user_user_permissions_1auth_user_user_permissions    vYE(~Z<" pingreportspingreasonreportsreasonmetadatareportsmetadata"##performancereportsperformanceclientreportsclient badreportsbad modifiedreportsmodified extrareportsextra !!repositoryreportsrepository" ##interactionreportsinteractionlog entryadminlogentrysitesitessitesessionsessionssession(%%#content typecontenttypescontenttype!!permissionauthpermissionuserauthusergroupauthgroupmessageauthmessage j)R;}reportspingreportsreasonreportsmetadata#reportsperformancereportsclientreportsbad reportsmodified reportsextra !reportsrepository #reportsinteraction adminlogentrysitessitesessionssession%#contenttypescontenttype!authpermission authuserauthgroupauthmessage MQ33Ctabledjango_content_typedjango_content_typeCREATE TABLE "django_content_type" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(100) NOT NULL, "app_label" varchar(100) NOT NULL, "model" varchar(100) NOT NULL, UNIQUE ("app_label", "model") )EY3indexsqlite_autoindex_django_content_type_1django_content_typeD))Ctabledjango_sessiondjango_sessionCREATE TABLE "django_session" ( "session_key" varchar(40) NOT NULL PRIMARY KEY, "session_data" text NOT NULL, "expire_date" datetime NOT NULL );O)indexsqlite_autoindex_django_session_1django_session,##tabledjango_sitedjango_siteCREATE TABLE "django_site" ( "id" integer NOT NULL PRIMARY KEY, "domain" varchar(100) NOT NULL, "name" varchar(50) NOT NULL ) hhMIAM9Af081f2e5fbec289a16c5f9ec883d0cf9KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMmI3ZmJiMWY5OTAwZmM0ZWYxOWE0YTNkYzI4 ZTFjNmU= 2006-07-05 16:42:02.305576 $Mf081f2e5fbec289a16c5f9ec883d0cf9 ##example.comexample.com  cc;--)tabledjango_admin_logdjango_admin_logCREATE TABLE "django_admin_log" ( "id" integer NOT NULL PRIMARY KEY, "action_time" datetime NOT NULL, "user_id" integer NOT NULL REFERENCES "auth_user" ("id"), "content_type_id" integer NULL REFERENCES "django_content_type" ("id"), "object_id" text NULL, "object_repr" varchar(200) NOT NULL, "action_flag" smallint unsigned NOT NULL, "change_message" text NOT NULL )33[tablereports_interactionreports_interactionCREATE TABLE "reports_interaction" ( "id" integer NOT NULL PRIMARY KEY, "client_id" integer NOT NULL, "timestamp" datetime NOT NULL, "state" varchar(32) NOT NULL, "repo_revision" integer NOT NULL, "client_version" varchar(32) NOT NULL, "pingable" bool NOT NULL, "goodcount" integer NOT NULL, "totalcount" integer NOT NULL )   oj##tablereports_badreports_badCREATE TABLE "reports_bad" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(128) NOT NULL, "kind" varchar(16) NOT NULL, "critical" bool NOT NULL, "reason_id" integer NOT NULL )11itablereports_repositoryreports_repositoryCREATE TABLE "reports_repository" ( "id" integer NOT NULL PRIMARY KEY, "timestamp" datetime NOT NULL )p''tablereports_extrareports_extraCREATE TABLE "reports_extra" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(128) NOT NULL, "kind" varchar(16) NOT NULL, "critical" bool NOT NULL, "reason_id" integer NOT NULL )y--%tablereports_modifiedreports_modified CREATE TABLE "reports_modified" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(128) NOT NULL, "kind" varchar(16) NOT NULL, "critical" bool NOT NULL, "reason_id" integer NOT NULL )  ZBCTahlmqszDE\gknrv]^jpu_`io{FAHKd|@GJMNPRUVY[tewWyfbXSQI~xc}LOQe}L#QZ=2-,+*22-)'$#)(,#22-   *))tablereports_reasonreports_reason!CREATE TABLE "reports_reason" ( "id" integer NOT NULL PRIMARY KEY, "owner" text NOT NULL, "current_owner" text NOT NULL, "group" text NOT NULL, "current_group" text NOT NULL, "perms" text NOT NULL, "current_perms" text NOT NULL, "status" text NOT NULL, "current_status" text NOT NULL, "to" text NOT NULL, "current_to" text NOT NULL, "version" text NOT NULL, "current_version" text NOT NULL, "current_exists" bool NOT NULL, "current_diff" text NOT NULL ) ))Itablereports_clientreports_client#CREATE TABLE "reports_client" ( "id" integer NOT NULL PRIMARY KEY, "creation" datetime NOT NULL, "name" varchar(128) NOT NULL, "current_interaction_id" integer NULL REFERENCES "reports_interaction" ("id") )   gI339tablereports_performancereports_performance$CREATE TABLE "reports_performance" ( "id" integer NOT NULL PRIMARY KEY, "metric" varchar(128) NOT NULL, "value" numeric(32, 16) NOT NULL )^--otablereports_metadatareports_metadata&CREATE TABLE "reports_metadata" ( "id" integer NOT NULL PRIMARY KEY, "client_id" integer NOT NULL REFERENCES "reports_client" ("id"), "timestamp" datetime NOT NULL )E==tablereports_bad_interactionsreports_bad_interactions'CREATE TABLE "reports_bad_interactions" ( "id" integer NOT NULL PRIMARY KEY, "bad_id" integer NOT NULL REFERENCES "reports_bad" ("id"), "interaction_id" integer NOT NULL REFERENCES "reports_interaction" ("id"), UNIQUE ("bad_id", "interaction_id") )Oc=indexsqlite_autoindex_reports_bad_interactions_1reports_bad_interactions(      ?QAA-tablereports_extra_interactionsreports_extra_interactions)CREATE TABLE "reports_extra_interactions" ( "id" integer NOT NULL PRIMARY KEY, "extra_id" integer NOT NULL REFERENCES "reports_extra" ("id"), "interaction_id" integer NOT NULL REFERENCES "reports_interaction" ("id"), UNIQUE ("extra_id", "interaction_id") )S gAindexsqlite_autoindex_reports_extra_interactions_1reports_extra_interactions*c!GGEtablereports_modified_interactionsreports_modified_interactions,CREATE TABLE "reports_modified_interactions" ( "id" integer NOT NULL PRIMARY KEY, "modified_id" integer NOT NULL REFERENCES "reports_modified" ("id"), "interaction_id" integer NOT NULL REFERENCES "reports_interaction" ("id"), UNIQUE ("modified_id", "interaction_id") )Y"mGindexsqlite_autoindex_reports_modified_interactions_1reports_modified_interactions-   @pL( rDmE)j@(3+Can add interaction add_interaction)5+Can delete log entrydelete_logentry)5+Can change log entrychange_logentry#/%Can add log entryadd_logentry +#Can delete sitedelete_site +#Can change sitechange_site%Can add siteadd_site&1)Can delete sessiondelete_session&1)Can change sessionchange_session +#Can add sessionadd_session/;1Can delete content typedelete_contenttype/;1Can change content typechange_contenttype) 5+Can add content typeadd_contenttype, 7/Can delete permissiondelete_permission, 7/Can change permissionchange_permission& 1)Can add permissionadd_permission +#Can delete userdelete_user +#Can change userchange_user%Can add useradd_user"-%Can delete groupdelete_group"-%Can change groupchange_group'Can add groupadd_group&1)Can delete messagedelete_message&1)Can change messagechange_message +#Can add messageadd_message VxJh>$xNvV1)!Can add reasonadd_reason(03+Can delete metadatadelete_metadata(/3+Can change metadatachange_metadata".-%Can add metadataadd_metadata.-91Can delete performancedelete_performance.,91Can change performancechange_performance(+3+Can add performanceadd_performance$*/'Can delete clientdelete_client$)/'Can change clientchange_client()!Can add clientadd_client')!Can delete bad delete_bad&)!Can change bad change_bad%#Can add bad add_bad($3+Can delete modified delete_modified(#3+Can change modified change_modified""-%Can add modified add_modified"!-%Can delete extra delete_extra" -%Can change extra change_extra'Can add extra add_extra,7/Can delete repository delete_repository,7/Can change repository change_repository&1)Can add repository add_repository.91Can delete interaction delete_interaction.91Can change interaction change_interaction 66H]r 6Ohz#add_message)change_message)delete_messageadd_group%change_group%delete_groupadd_user#change_user#delete_user )add_permission /change_permission /delete_permission +add_contenttype 1change_contenttype1delete_contenttype#add_session)change_session)delete_sessionadd_site#change_site#delete_site%add_logentry+change_logentry :Pi&<J[l}$#delete_ping6#change_ping5add_ping4'delete_reason3'change_reason2!add_reason1+delete_metadata0+change_metadata/+ add_interaction1 change_interaction1 delete_interaction) add_repository/ change_repository/ delete_repository add_extra% change_extra % delete_extra!% add_modified"+ change_modified#+ delete_modified$  add_bad%! change_bad&! delete_bad'!add_client('change_client)'delete_client*+add_performance+1change_performance,1delete_performance-%add_metadata.   rr#KK[tablereports_performance_interactionreports_performance_interaction2CREATE TABLE "reports_performance_interaction" ( "id" integer NOT NULL PRIMARY KEY, "performance_id" integer NOT NULL REFERENCES "reports_performance" ("id"), "interaction_id" integer NOT NULL REFERENCES "reports_interaction" ("id"), UNIQUE ("performance_id", "interaction_id") )]$qKindexsqlite_autoindex_reports_performance_interaction_1reports_performance_interaction3 %EEviewreports_current_interactionsreports_current_interactionsCREATE VIEW reports_current_interactions AS SELECT x.client_id AS client_id, reports_interaction.id AS interaction_id FROM (select client_id, MAX(timestamp) as timer FROM reports_interaction GROUP BY client_id) x, reports_interaction WHERE reports_interaction.client_id = x.client_id AND reports_interaction.timestamp = x.timer  ..FIs&%%mtablereports_pingreports_ping5CREATE TABLE "reports_ping" ( "id" integer NOT NULL PRIMARY KEY, "client_id" integer NOT NULL REFERENCES "reports_client" ("id"), "starttime" datetime NOT NULL, "endtime" datetime NOT NULL, "status" varchar(4) NOT NULL )(33'tablereports_interactionreports_interactionCREATE TABLE reports_interaction (id integer PRIMARY KEY, client_id integer, timestamp datetime, state varchar(32), repo_revision integer, client_version varchar(32), goodcount integer, totalcount integer)'))) tablereports_clientreports_client#CREATE TABLE "reports_client" ( "id" integer NOT NULL PRIMARY KEY, "creation" datetime NOT NULL, "name" varchar(128) NOT NULL, "current_interaction_id" integer NULL REFERENCES "reports_interaction" ("id"), "expiration" datetime NULL ) *G3'indexreports_interaction_client_idreports_interaction7CREATE INDEX reports_interaction_client_id on reports_interaction (client_id)   ..4*+UAKindexreports_extra_interactions_client_idreports_extra_interactions8CREATE INDEX reports_extra_interactions_client_id on reports_extra_interactions(interaction_id)6,[GWindexreports_modified_interactions_client_idreports_modified_interactions:CREATE INDEX reports_modified_interactions_client_id on reports_modified_interactions(interaction_id)-W)Gindexreports_client_current_interaction_idreports_client;CREATE INDEX reports_client_current_interaction_id on reports_client (current_interaction_id)I.iKkindexreports_performance_interaction_performance_idreports_performance_interactionCREATE INDEX reports_performance_interation_interaction_id on reports_performance_interaction (interaction_id) Gk"*3<ENWakuuk "+4=FOXajs|  Q  P  O  N     ,  =CiD}      -  >DjD}      .  ?EkD}      /  @G~  H=NY o!z"#$%4u5  Q     Y_A$kM/`B"Y#,+ libncurses5-devpackageUnknown +% bridge-utilspackageUnknown* libidn11packageUnknown) binutilspackageUnknown( libcurl3packageUnknown '% libcurl3-devpackageUnknown& libsysfs1packageUnknown% libatm1packageUnknown$! pkg-configpackageUnknown #% libidn11-devpackageUnknown"! libssl-devpackageUnknown)!7 python2.3-twisted-binpackageUnknown  iproutepackageUnknown libsmapi2packageUnknown cpp-3.3packageUnknown libxft2packageUnknown libxmu6packageUnknown portmappackageUnknown whatamipackageUnknown libc6-devpackageUnknown libx11-6packageUnknown libmagic1packageUnknown libsm6packageUnknown libice6packageUnknown(5 linux-kernel-headerspackageUnknown mailxpackageUnknown filepackageUnknown xfsprogspackageUnknown reportbugpackageUnknown AC@|=A= =! Thu Jan 5 06:29:46 2006cleanunknown$Revision$= =! Thu Jan 5 06:30:47 2006cleanunknown$Revision$= =! Thu Jan 5 06:29:32 2006cleanunknown$Revision$= =! Thu Jan 5 06:32:59 2006cleanunknown$Revision$= =! Thu Jan 5 06:33:44 2006cleanunknown$Revision$= =! Thu Jan 5 06:39:31 2006cleanunknown$Revision$MM= =! Thu Jan 5 06:35:50 2006dirtyunknown$Revision${|D =/ Wed Nov 23 06:30:57 2005cleanunknown$Revision: 1.40 $dd= =! Thu Jan 5 06:34:13 2006dirtyunknown$Revision${|D =/ Wed Nov 23 06:29:37 2005cleanunknown$Revision: 1.40 $dd= =!  Thu Jan 5 06:32:00 2006cleanunknown$Revision$66= =! Thu Jan 5 06:32:59 2006cleanunknown$Revision$66= =! Thu Jan 5 06:36:07 2006cleanunknown$Revision$66= =! Thu Jan 5 06:32:56 2006cleanunknown$Revision$66= =! Thu Jan 5 06:29:31 2006cleanunknown$Revision$66 kC9u/k= =! EThu Jan 5 06:37:01 2006dirtyunknown$Revision$  D =/ EMon Nov 28 06:26:21 2005cleanunknown$Revision: 1.40 $= =! DThu Jan 5 06:40:33 2006dirtyunknown$Revision$  D =/ DMon Nov 28 06:29:43 2005cleanunknown$Revision: 1.40 $= =! CThu Jan 5 06:37:15 2006dirtyunknown$Revision$  D =/ CMon Nov 28 06:26:04 2005cleanunknown$Revision: 1.40 $= =! BThu Jan 5 06:39:06 2006dirtyunknown$Revision$  D =/ BMon Nov 28 06:32:53 2005cleanunknown$Revision: 1.40 $D =/ ATue Oct 18 10:16:25 2005cleanunknown$Revision: 1.37 $  = =! >Thu Jan 5 06:30:28 2006cleanunknown$Revision$##= =! =Thu Jan 5 06:40:32 2006cleanunknown$Revision$##= =! Thu Jan 5 06:32:06 2006cleanunknown$Revision$= =! Thu Jan 5 06:42:07 2006cleanunknown$Revision$= =! Thu Jan 5 06:35:10 2006cleanunknown$Revision$ ^d/_+[&^07A12006-06-20 12:27:49.982608ccn251.mcs.anl.gov06A12006-06-20 12:27:49.746685ccn241.mcs.anl.gov05A12006-06-20 12:27:49.514135ccn242.mcs.anl.gov04A12006-06-20 12:27:49.247140ccn243.mcs.anl.gov33A72006-06-20 12:27:49.005553cct10m-67.mcs.anl.gov22A52006-06-20 12:27:48.747426cct9m-67.mcs.anl.gov21A52006-06-20 12:27:48.497518cct8m-67.mcs.anl.gov20A52006-06-20 12:27:48.258119cct7m-67.mcs.anl.gov2/A52006-06-20 12:27:47.988235cct6m-67.mcs.anl.gov2.A52006-06-20 12:27:47.720366cct5m-67.mcs.anl.gov2-A52006-06-20 12:27:47.434229cct4m-67.mcs.anl.gov2,A52006-06-20 12:27:47.181935cct3m-67.mcs.anl.gov2+A52006-06-20 12:27:46.940713cct2m-67.mcs.anl.gov2*A52006-06-20 12:27:46.698707cct1m-67.mcs.anl.gov3)A72006-06-20 12:27:46.440763cct10m-fe.mcs.anl.gov2(A52006-06-20 12:27:46.190723cct9m-fe.mcs.anl.gov2'A52006-06-20 12:27:45.932524cct8m-fe.mcs.anl.gov2&A52006-06-20 12:27:45.674438cct7m-fe.mcs.anl.gov Yj8p>~NY/JA/2006-06-20 12:31:22.763838ccn17.mcs.anl.gov/IA/2006-06-20 12:30:55.235513ccn16.mcs.anl.gov/HA/2006-06-20 12:30:45.138430ccn15.mcs.anl.gov/GA/2006-06-20 12:30:34.887905ccn13.mcs.anl.gov/FA/2006-06-20 12:30:24.160255ccn12.mcs.anl.gov.EA-2006-06-20 12:29:55.528569ccn7.mcs.anl.gov.DA-2006-06-20 12:29:27.388909ccn5.mcs.anl.gov.CA-2006-06-20 12:28:51.062654ccn4.mcs.anl.gov.BA-2006-06-20 12:28:13.204424ccn2.mcs.anl.gov.AA-2006-06-20 12:27:54.635153ccn1.mcs.anl.gov0@A12006-06-20 12:27:54.389107ccn244.mcs.anl.gov0?A12006-06-20 12:27:54.077101ccn256.mcs.anl.gov0>A12006-06-20 12:27:52.775187ccn237.mcs.anl.gov0=A12006-06-20 12:27:51.487556ccn240.mcs.anl.gov0<A12006-06-20 12:27:51.237287ccn245.mcs.anl.gov0;A12006-06-20 12:27:50.995877ccn254.mcs.anl.gov0:A12006-06-20 12:27:50.754362ccn253.mcs.anl.gov09A12006-06-20 12:27:50.496817ccn252.mcs.anl.gov08A12006-06-20 12:27:50.246302ccn250.mcs.anl.gov Vxoe\RH>5+! {rh^TJ@6,"}sj`VLB9/%LKJIHGFEDCBA@?B>=<;:;98765432>10/.-,9+*)7(6'&%$#"! +$ z   B   9 7~2}|{zyxw N!%/9CMqg]SI5+!W_hqz#-7{?AIRZclt}     z  S  B  1      \  % \ y        [   s      =  m   J&e7$%&+'\  A  v    .  t  `  }   LM  N !O!"P"#Q## $R$$u$$$$I$ TkJ.T1c="qTF stracepackageUnknownE! ntp-simplepackageUnknownD ntpdatepackageUnknownC gm-utilspackageUnknownB mpishpackageUnknownA gmpackageUnknown@ cpp-3.2packageUnknown? tftppackageUnknown#>+ libmpich1.0-devpackageUnknown4=M kernel-image-2.6.9-chiba-selfishpackageUnknown< mpich-binpackageUnknown; gcc-3.2packageUnknown4:M gm-driver-2.6.9-chiba-selfish-uppackageUnknown9# libmpich1.0packageUnknown 8% gcc-3.2-basepackageUnknown?7c kernel-image-2.6.10-rc2-mm3-v0.7.32-9-mode1packageUnknown?6c kernel-image-2.6.10-rc2-mm3-v0.7.32-9-mode4packageUnknown75S kernel-image-2.6.9-chiba-selfish-uppackageUnknown4 patchpackageUnknown3 mpichpackageUnknown2! zlib1g-devpackageUnknown1 gccpackageUnknown%0/ python2.3-twistedpackageUnknown/ makepackageUnknown. gcc-3.3packageUnknown- cvspackageUnknown QPd)x=Q8 3! d2006-01-05 06:31:20cleanunknown$Revision$##8 3! c2006-01-05 06:31:17cleanunknown$Revision$##8 3! b2006-01-05 06:31:14cleanunknown$Revision$##8 3! a2006-01-05 06:31:12cleanunknown$Revision$##8 3! `2006-01-05 06:31:05cleanunknown$Revision$##8 3! _2006-01-05 06:31:04cleanunknown$Revision$##8 3! ^2006-01-05 06:31:02cleanunknown$Revision$##8 3! ]2006-01-05 06:31:02cleanunknown$Revision$##8 3! \2006-01-05 06:31:00cleanunknown$Revision$##8 3! [2006-01-05 06:30:57cleanunknown$Revision$##8 3! Z2006-01-05 06:30:53cleanunknown$Revision$##8 3! Y2006-01-05 06:30:47cleanunknown$Revision$8 3! X2006-01-05 06:30:37cleanunknown$Revision$##8 3! W2006-01-05 06:30:34cleanunknown$Revision$##8 3! V2006-01-05 06:30:30cleanunknown$Revision$##8 3! U2006-01-05 06:30:28cleanunknown$Revision$## X8AJS\enx.$ (1:CLU^hr|&0:DNXblt} & & & $ & U & D & 3 & & & $ $ H$$ $$$%$&$'E $ / $ _ $ $ $ V $ n $ $ %S%&T&&v&&L&&"&]&!~&#&%&'F&5&D&}& &e & &" & & &{ & & & &' &^ &{ & & & & &  & a & & T & & &  & > & o & & 'U''(V()W)*X*+Y+++++O++ DpW5]<uL/ aDb hdparmpackageUnknowna# debootstrappackageUnknown` valgrindpackageUnknown_ gdbpackageUnknown%^/ libdbd-mysql-perlpackageUnknown$]- libmysqlclient12packageUnknown!\' libplrpc-perlpackageUnknown[ libpq3packageUnknown&Z1 libnet-daemon-perlpackageUnknownY libedit2packageUnknownX# libselinux1packageUnknown"W) libdbd-pg-perlpackageUnknownV iperfpackageUnknownU# libdbi-perlpackageUnknown T% mysql-commonpackageUnknownS! traceroutepackageUnknown)R7 kernel-image-2.6.12.6packageUnknown2QI kernel-image-2.6.11.10-mcs-x86packageUnknownP condorserviceUnknownO arpingpackageUnknownN libnet1packageUnknownM autoconfpackageUnknownL# automake1.4packageUnknownK m4packageUnknownJ libtoolpackageUnknown!I' autotools-devpackageUnknown%H/ module-init-toolspackageUnknown#G+ gm-route-clientpackageUnknown ^f2c0b.^1*A12006-06-20 14:24:09.247594ccn103.mcs.anl.gov1)A12006-06-20 14:24:08.556136ccn164.mcs.anl.gov1(A12006-06-20 14:24:07.864745ccn172.mcs.anl.gov1'A12006-06-20 14:24:07.182307ccn180.mcs.anl.gov1&A12006-06-20 14:24:06.483981ccn160.mcs.anl.gov0%A/2006-06-20 14:24:05.783075ccn44.mcs.anl.gov1$A12006-06-20 14:24:05.100015ccn162.mcs.anl.gov0#A/2006-06-20 14:24:04.375435ccn99.mcs.anl.gov1"A12006-06-20 14:24:03.684795ccn116.mcs.anl.gov0!A/2006-06-20 14:24:02.976457ccn69.mcs.anl.gov1 A12006-06-20 14:24:02.152244ccn149.mcs.anl.gov1A12006-06-20 14:24:01.461132ccn178.mcs.anl.gov1A12006-06-20 14:24:00.769428ccn238.mcs.anl.gov0A/2006-06-20 14:24:00.094813ccn33.mcs.anl.gov1A12006-06-20 14:23:59.420278ccn140.mcs.anl.gov1A12006-06-20 14:23:58.728936ccn112.mcs.anl.gov0A/2006-06-20 14:23:58.046858ccn68.mcs.anl.gov0A/2006-06-20 14:23:57.346922ccn87.mcs.anl.gov N&:CLU^gpy )3=GQ[eoy0&!)2;DMV_hqz + + L+&+P+`+ q+!+"+#+$+%+4+5 +. +i + + + +% +] + + + + +N +~ + 2 + d + + + + + + + ( + @ + + ,,Z,, @-[-.\.. %/]// )0^00 *1_12`22~22W22,2b2!2#2%2'Q252D2}2 2t 2 2/ 2 2 2 2 2 U(2<FPZdnx$.8BLV`jt~ (1:CLU^gpy  9 j 7 7 9 6 9 X 6 T 6 / 6 6 6 6 6 6  6 * 6 E 6 7e777777]777717$7%7&&7'V7475 7; 7y 7 75 7h 7 7 7$ 7 ; 7 p 7 7 7 ' 7 l 7 7 7 \ 7 y 7 7 8f88 .9h99999!9`999949$9%9&)9'Y 9 > 9 s 9 9 9 + 9 p 9 _ 9 | 9 9 :i:: 2 Xxph`XPH@7.$zpf\RH>4*  vlbXND:0&=<;:9876543210/.-,+*)('&%$#"!      ~}|~{}z|y{xzwyvxuwtvsurtqsproqnpmolnkmjlikhjgifh N(2<FPZdnx",6>GOXajr{ '1;EOYcmw>4>5 >? >~ > > > >; >n > > > >+ >Y > > B > w > > > > > >  > , > F > > 0 > ^ > ?o?@p@@ @ BAqABrBBBBB7BwBBBBFB$B%B&0B'`B4B5 BL B B BB B{ B B B9 B N B B B B 4 B y B B B B  B j B B B CsC _e1d0a-_1NA12006-06-20 14:24:36.088486ccn240.mcs.anl.gov0MA/2006-06-20 14:24:35.382826ccn92.mcs.anl.gov1LA12006-06-20 14:24:34.683562ccn139.mcs.anl.gov0KA/2006-06-20 14:24:33.950281ccn23.mcs.anl.gov1JA12006-06-20 14:24:33.240402ccn167.mcs.anl.gov0IA/2006-06-20 14:24:32.199453ccn71.mcs.anl.gov1HA12006-06-20 14:24:31.501396ccn145.mcs.anl.gov1GA12006-06-20 14:24:30.525547ccn184.mcs.anl.gov1FA12006-06-20 14:24:29.834335ccn174.mcs.anl.gov1EA12006-06-20 14:24:29.118499ccn179.mcs.anl.gov0DA/2006-06-20 14:24:28.418757ccn41.mcs.anl.gov0CA/2006-06-20 14:24:27.726671ccn46.mcs.anl.gov1BA12006-06-20 14:24:26.977654ccn194.mcs.anl.gov0AA/2006-06-20 14:24:26.253168ccn39.mcs.anl.gov1@A12006-06-20 14:24:25.547383ccn123.mcs.anl.gov1?A12006-06-20 14:24:24.837232ccn106.mcs.anl.gov0>A/2006-06-20 14:24:23.588322ccfs1.mcs.anl.gov1=A12006-06-20 14:24:22.882858ccn232.mcs.anl.gov U!*4>GPYbkt}  *4>HR\fpz (2<FPZdnx y j j 7k k :l l Fm m G mG mtn n Io o Sp q q4vq5 q qU q  q A q q q q r s t tot4yt5 t! tY t t t t t t t ; t w t uu v w x  y  z  zzz zDzzzzz4}z5 z& z^ z z z z zX z z z zJ zw z + z Z z z z z z z @ z |{  | } ~  `Rj0H`8n 3! D2006-01-05 06:29:46cleanunknown$Revision$##8m 3! C2006-01-05 06:29:32cleanunknown$Revision$8l 3! B2006-01-05 06:29:31cleanunknown$Revision$668k 3! A2006-01-03 16:26:27cleanunknown$Revision$##8j 3! @2006-01-03 16:26:26cleanunknown$Revision$##8i 3! ?2006-01-03 16:26:26cleanunknown$Revision$##8h 3! >2006-01-03 06:34:16cleanunknown$Revision$##8g 3! =2005-12-26 06:26:08cleanunknown$Revision$##8f 3! <2005-12-22 06:27:20cleanunknown$Revision$##8e 3! ;2005-12-19 17:51:58cleanunknown$Revision$##8d 3! :2005-12-19 11:40:37cleanunknown$Revision$##8c 3! 92005-12-19 11:40:05cleanunknown$Revision$##8b 3! 82005-12-19 11:36:01cleanunknown$Revision$##8a 3! 72005-12-14 06:25:51cleanunknown$Revision$8` 3! 62005-12-10 06:30:50cleanunknown$Revision$8_ 3! 52006-01-05 06:30:38dirtyunknown$Revision$  ,{5q+g(k,=, =! OThu Jan 5 06:30:34 2006cleanunknown$Revision$##=+ =! NThu Jan 5 06:40:18 2006cleanunknown$Revision$##=* =! MThu Jan 5 06:30:28 2006cleanunknown$Revision$##=) =! LThu Jan 5 06:42:38 2006cleanunknown$Revision$##=( =! KThu Jan 5 06:42:05 2006cleanunknown$Revision$##=' =! JThu Jan 5 06:39:04 2006dirtyunknown$Revision$  D& =/ JMon Nov 28 06:29:41 2005cleanunknown$Revision: 1.40 $=% =! IThu Jan 5 06:32:56 2006dirtyunknown$Revision$  D$ =/ IMon Nov 28 06:33:34 2005cleanunknown$Revision: 1.40 $=# =! HThu Jan 5 06:40:00 2006dirtyunknown$Revision$  D" =/ HMon Nov 28 06:35:50 2005cleanunknown$Revision: 1.40 $=! =! GThu Jan 5 06:31:08 2006dirtyunknown$Revision$  D =/ GMon Nov 28 06:35:07 2005cleanunknown$Revision: 1.40 $= =! FThu Jan 5 06:30:28 2006dirtyunknown$Revision$  D =/ FMon Nov 28 06:35:07 2005cleanunknown$Revision: 1.40 $ N&0;FQ\gr}%/9CMWaku !,7AKU_is}      \    w 4 5 , f        J             Q       , k    : $ % &- ']  D  y    a  ~  ! 4 5 0 k    \      "  #  '  (    - l    ; $ % &. '^ 4 5 C M#-7AKValw "-8CNYdoz *5@KValw     Z   5  9  ;  < * i *s  =  +  G  V   > y    P    ' $ % & 'J 4 5 / j    & ^    O     * G a      3  e      [    X  r     M     ?  A  C  K *q I\>w]> pQ2fI libxt6PackageUnknown libdm0PackageUnknown % xemacs21-binPackageUnknown# xfslibs-devPackageUnknown(5 xemacs21-mulesupportPackageUnknown! fontconfigPackageUnknown libxpm4PackageUnknown xemacs21PackageUnknown libtiff4PackageUnknown libapr0PackageUnknown# libxrender1PackageUnknown portmapServiceUnknown apacheServiceUnknown2 I kernel-headers-2.6.12.5-p3-mcsPackageUnknown  blktoolPackageUnknown  valgrindPackageUnknown  gdbPackageUnknown  portmapPackageUnknown % pvfs2-serverServiceUnknown libxml2PackageUnknown") python2.3-lxmlPackageUnknown# python-lxmlPackageUnknown! libxslt1.1PackageUnknown gnuplotPackageUnknown % libfreetype6PackageUnknown! libpng12-0PackageUnknown hostPackageUnknown % libgd2-noxpmPackageUnknown~# gnuplot-noxPackageUnknown (K U_i(?^ 3/ 52005-12-01 06:38:32cleanunknown$Revision: 1.40 $8] 3! 42006-01-05 06:30:00dirtyunknown$Revision$  ?\ 3/ 42005-12-01 06:38:16cleanunknown$Revision: 1.40 $8[ 3! 32006-01-05 06:36:07dirtyunknown$Revision$  ?Z 3/ 32005-12-01 06:36:49cleanunknown$Revision: 1.40 $8Y 3! 22005-12-19 11:43:35dirtyunknown$Revision$  ?X 3/ 22005-12-01 06:35:33cleanunknown$Revision: 1.40 $8W 3! 12006-01-05 06:30:32dirtyunknown$Revision$  ?V 3/ 12005-12-01 06:35:26cleanunknown$Revision: 1.40 $8U 3! 02006-01-05 06:29:49dirtyunknown$Revision$  ?T 3/ 02005-12-01 06:35:18cleanunknown$Revision: 1.40 $8S 3! /2006-01-05 06:34:23dirtyunknown$Revision$  ?R 3/ /2005-12-01 06:33:59cleanunknown$Revision: 1.40 $8Q 3! .2006-01-05 06:36:45dirtyunknown$Revision$  ?P 3/ .2005-12-01 06:32:43cleanunknown$Revision: 1.40 $8O 3! -2006-01-05 06:36:04dirtyunknown$Revision$  G  !+5?IS]gq| !,7BMXblv$/:EP[fq|   6 v    D 4 5 K     H z   8 f   M        i   K    9 y    H $ % &5 'f 4 5 N     K }   ; i     ; T r       P  _d1b.a._1`A12006-06-20 14:24:49.330066ccn117.mcs.anl.gov0_A/2006-06-20 14:24:48.638701ccn66.mcs.anl.gov1^A12006-06-20 14:24:47.964251ccn113.mcs.anl.gov1]A12006-06-20 14:24:47.289748ccn114.mcs.anl.gov0\A/2006-06-20 14:24:46.615854ccn95.mcs.anl.gov0[A/2006-06-20 14:24:45.690672cct6m.mcs.anl.gov0ZA/2006-06-20 14:24:44.991374ccn27.mcs.anl.gov0YA/2006-06-20 14:24:44.041849ccn20.mcs.anl.gov1XA12006-06-20 14:24:43.342237ccn125.mcs.anl.gov1WA12006-06-20 14:24:42.561288ccn124.mcs.anl.gov1VA12006-06-20 14:24:41.860188ccn190.mcs.anl.gov1UA12006-06-20 14:24:41.185721ccn152.mcs.anl.gov0TA/2006-06-20 14:24:40.477400ccn29.mcs.anl.gov1SA12006-06-20 14:24:39.769310ccn142.mcs.anl.gov0RA/2006-06-20 14:24:38.853569ccn31.mcs.anl.gov1QA12006-06-20 14:24:38.170434ccn181.mcs.anl.gov1PA12006-06-20 14:24:37.480264ccn144.mcs.anl.gov1OA12006-06-20 14:24:36.814153ccn148.mcs.anl.gov O$/:EP[fpz$.8BLV`jt$/:EP[fq|   :     g     %  l        L  M  U  W Y [ ] _ a  N  P  Q    < |    J $ % &8 'i 4 5 P     M    > k     = V t       R     =     h     '  n ^m< xGS"^/]A/2006-06-20 12:33:03.689945ccn72.mcs.anl.gov/\A/2006-06-20 12:33:02.224338ccn71.mcs.anl.gov/[A/2006-06-20 12:33:01.074344ccn70.mcs.anl.gov/ZA/2006-06-20 12:32:59.959618ccn67.mcs.anl.gov/YA/2006-06-20 12:32:58.835695ccn65.mcs.anl.gov.XA-2006-06-20 12:32:18.960734ccn9.mcs.anl.gov/WA/2006-06-20 12:32:17.204893ccn32.mcs.anl.gov/VA/2006-06-20 12:32:15.545700ccn30.mcs.anl.gov/UA/2006-06-20 12:32:13.971375ccn29.mcs.anl.gov/TA/2006-06-20 12:32:11.238685ccn31.mcs.anl.gov/SA/2006-06-20 12:32:07.339068ccn28.mcs.anl.gov/RA/2006-06-20 12:32:07.010523ccn26.mcs.anl.gov/QA/2006-06-20 12:32:04.985893ccn25.mcs.anl.gov/PA/2006-06-20 12:32:02.615604ccn27.mcs.anl.gov/OA/2006-06-20 12:32:00.996594ccn24.mcs.anl.gov/NA/2006-06-20 12:31:59.090256ccn23.mcs.anl.gov/MA/2006-06-20 12:31:55.266913ccn22.mcs.anl.gov/LA/2006-06-20 12:31:51.885215ccn21.mcs.anl.gov/KA/2006-06-20 12:31:50.423183ccn20.mcs.anl.gov Y (08@HPX`hpx (08@HPX`hpx (1:CLU^gpy      #&),/46:;>@BDFHJLNQ R!U"V#Y$Z%]&^'_(c)f*j+l,o-s.u/x0|1~23456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXY N"+4=FOXajs| '09BKT]fpz$.8BLV`jt~[\]^_ ` abcdefg!h%i(j*k,l0m3n7o:p<q?rCsEtHuLvOwQxUyWzZ{^|a}c~eh k m q u w z ~                                   exph`XPH@80( xph`XPH@80( xph`XPH@80( e)d(c(b(a'`'_'^&]%\%[%Z$Y#X#W#V"U!T!S!R QPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&% $ # " !                  ^xph`XPH@80' |sjaXOF=4+"wne\SJA8/& CHBHAG@G?G>G=F<F;F:E9E8E7D6D5C4C3C2B1B0B/A.A-A,@+@*@)?(?'?&>%>$>#="=!= <<<;;;:::9998887776 6 6 5 5 54443332221~1}1|0{0z0y/x/w/v.u.t.s-r-q-p,o,n,m+l+k+j*i*h*g)f) HCGDH=; =! _Thu Jan 5 06:30:27 2006cleanunknown$Revision$##=: =! ]Thu Jan 5 06:31:02 2006cleanunknown$Revision$##=9 =! \Thu Jan 5 06:40:10 2006cleanunknown$Revision$##=8 =! [Thu Jan 5 06:37:10 2006cleanunknown$Revision$##=7 =! ZThu Jan 5 06:42:30 2006cleanunknown$Revision$##=6 =! YThu Jan 5 06:31:04 2006cleanunknown$Revision$##=5 =! XThu Jan 5 06:39:17 2006dirtyunknown$Revision$  D4 =/ XThu Dec 1 06:30:59 2005cleanunknown$Revision: 1.40 $=3 =! WThu Jan 5 06:30:00 2006cleanunknown$Revision$##=2 =! VThu Jan 5 06:33:13 2006cleanunknown$Revision$##=1 =! UThu Jan 5 06:40:54 2006cleanunknown$Revision$##=0 =! TThu Jan 5 06:40:47 2006cleanunknown$Revision$##=/ =! SThu Jan 5 06:37:29 2006cleanunknown$Revision$##=. =! QThu Jan 5 06:39:29 2006cleanunknown$Revision$##=- =! PThu Jan 5 06:42:06 2006cleanunknown$Revision$## (K U_i(?N 3/ -2005-12-01 06:32:25cleanunknown$Revision: 1.40 $8M 3! ,2006-01-05 06:39:17dirtyunknown$Revision$  ?L 3/ ,2005-12-01 06:30:59cleanunknown$Revision: 1.40 $8K 3! +2006-01-05 06:30:39dirtyunknown$Revision$  ?J 3/ +2005-12-01 06:30:46cleanunknown$Revision: 1.40 $8I 3! *2006-01-05 06:29:59dirtyunknown$Revision$  ?H 3/ *2005-12-01 06:30:42cleanunknown$Revision: 1.40 $8G 3! )2006-01-05 06:32:11dirtyunknown$Revision$  ?F 3/ )2005-12-01 06:30:29cleanunknown$Revision: 1.40 $8E 3! (2006-01-05 06:37:17dirtyunknown$Revision$  ?D 3/ (2005-12-01 06:30:11cleanunknown$Revision: 1.40 $8C 3! '2006-01-05 06:32:59dirtyunknown$Revision$  ?B 3/ '2005-12-01 06:29:54cleanunknown$Revision: 1.40 $8A 3! &2006-01-05 06:39:03dirtyunknown$Revision$  ?@ 3/ &2005-12-01 06:29:51cleanunknown$Revision: 1.40 $8? 3! %2006-01-05 06:30:47dirtyunknown$Revision$  @L\ l0|@9 3! 2006-01-05 06:42:32cleanunknown$Revision$##9 3! 2006-01-05 06:42:30cleanunknown$Revision$##9 3! 2006-01-05 06:42:29cleanunknown$Revision$##9 3! 2006-01-05 06:42:23cleanunknown$Revision$##9 3! 2006-01-05 06:42:22cleanunknown$Revision$##9 3! 2006-01-05 06:42:18cleanunknown$Revision$##9 3! 2006-01-05 06:42:15cleanunknown$Revision$##9 3! 2006-01-05 06:42:13cleanunknown$Revision$##9 3! 2006-01-05 06:42:10cleanunknown$Revision$##9 3! 2006-01-05 06:42:07cleanunknown$Revision$9 3! 2006-01-05 06:42:06cleanunknown$Revision$##9 3! 2006-01-05 06:42:05cleanunknown$Revision$##9 3! 2006-01-05 06:42:02cleanunknown$Revision$##9 3! 2006-01-05 06:41:57cleanunknown$Revision$##9 3! 2006-01-05 06:41:52cleanunknown$Revision$##9 3! 2006-01-05 06:40:57cleanunknown$Revision$## ctZ>oL1jE'c!~' libmailutils0packageUnknown} libgsasl7packageUnknown$|- edg-crl-upgradedserviceUnknown{ grisserviceUnknownz lam4packageUnknown y% ext3rminatorpackageUnknownx libxml2packageUnknown"w) python2.3-lxmlpackageUnknownv# python-lxmlpackageUnknownu! libxslt1.1packageUnknown%t/ mpich2-system-mpdpackageUnknown s% mysql-clientpackageUnknownr gawkpackageUnknownq gnuplotpackageUnknownp hostpackageUnknown o% libgd2-noxpmpackageUnknownn# gnuplot-noxpackageUnknownm# gnuplot-x11packageUnknownl cpp-4.0packageUnknown k% gcc-4.0-basepackageUnknownj g77-3.3packageUnknown)i7 kernel-image-2.6.11.8packageUnknownh nttcppackageUnknowng g77packageUnknown%f/ nfs-kernel-serverpackageUnknowne# libg2c0-devpackageUnknownd! mpd-systemserviceUnknownc! rsh-clientpackageUnknown ;g4j7k8n;19A12006-06-20 14:22:30.011499ccn135.mcs.anl.gov08A/2006-06-20 14:22:29.111946ccn62.mcs.anl.gov17A12006-06-20 14:22:28.000396ccn129.mcs.anl.gov06A/2006-06-20 14:22:25.882948ccn93.mcs.anl.gov15A12006-06-20 14:22:23.913051ccn212.mcs.anl.gov14A12006-06-20 14:22:21.627362ccn206.mcs.anl.gov13A12006-06-20 14:22:17.593653ccn210.mcs.anl.gov12A12006-06-20 14:22:15.978545ccn229.mcs.anl.gov11A12006-06-20 14:22:14.398637ccn226.mcs.anl.gov10A12006-06-20 14:22:11.850485ccn204.mcs.anl.gov1/A12006-06-20 14:22:09.439502ccn214.mcs.anl.gov1.A12006-06-20 14:22:05.747430ccn208.mcs.anl.gov1-A12006-06-20 14:22:01.974297ccn200.mcs.anl.gov/,A-2006-06-20 14:21:57.932505ccn9.mcs.anl.gov1+A12006-06-20 14:21:55.678676ccn222.mcs.anl.gov1*A12006-06-20 14:21:52.784532ccn213.mcs.anl.gov1)A12006-06-20 14:21:49.356481ccn202.mcs.anl.gov1(A12006-06-20 14:21:47.406959ccn207.mcs.anl.gov1'A12006-06-20 14:21:45.464275ccn215.mcs.anl.gov (K U_i(?> 3/ %2005-12-01 06:29:48cleanunknown$Revision: 1.40 $8= 3! $2006-01-05 06:32:04dirtyunknown$Revision$  ?< 3/ $2005-12-01 06:27:42cleanunknown$Revision: 1.40 $8; 3! #2006-01-05 06:30:27dirtyunknown$Revision$ ?: 3/ #2005-12-01 06:27:08cleanunknown$Revision: 1.40 $89 3! "2006-01-05 06:29:46dirtyunknown$Revision$  ?8 3/ "2005-12-01 06:26:31cleanunknown$Revision: 1.40 $87 3! !2006-01-05 06:32:55dirtyunknown$Revision$  ?6 3/ !2005-12-01 06:26:28cleanunknown$Revision: 1.40 $85 3!  2006-01-05 06:39:24dirtyunknown$Revision$  ?4 3/  2005-12-01 06:26:14cleanunknown$Revision: 1.40 $83 3! 2006-01-05 06:33:47dirtyunknown$Revision$  ?2 3/ 2005-12-01 06:25:29cleanunknown$Revision: 1.40 $81 3! 2006-01-05 06:40:00dirtyunknown$Revision$  ?0 3/ 2005-11-28 06:35:50cleanunknown$Revision: 1.40 $8/ 3! 2006-01-05 06:30:28dirtyunknown$Revision$  Wm< xGQW0pA12006-06-20 12:33:36.086341ccn107.mcs.anl.gov0oA12006-06-20 12:33:34.745893ccn109.mcs.anl.gov0nA12006-06-20 12:33:32.883783ccn103.mcs.anl.gov0mA12006-06-20 12:33:30.430217ccn102.mcs.anl.gov0lA12006-06-20 12:33:28.412773ccn101.mcs.anl.gov0kA12006-06-20 12:33:26.370681ccn100.mcs.anl.gov/jA/2006-06-20 12:33:19.355191ccn98.mcs.anl.gov/iA/2006-06-20 12:33:17.432930ccn96.mcs.anl.gov/hA/2006-06-20 12:33:16.090882ccn95.mcs.anl.gov/gA/2006-06-20 12:33:14.509221ccn94.mcs.anl.gov/fA/2006-06-20 12:33:13.076608ccn92.mcs.anl.gov/eA/2006-06-20 12:33:12.785423ccn90.mcs.anl.gov/dA/2006-06-20 12:33:12.559829ccn89.mcs.anl.gov/cA/2006-06-20 12:33:11.177307ccn86.mcs.anl.gov/bA/2006-06-20 12:33:09.856415ccn83.mcs.anl.gov/aA/2006-06-20 12:33:08.486026ccn82.mcs.anl.gov/`A/2006-06-20 12:33:06.620459ccn80.mcs.anl.gov/_A/2006-06-20 12:33:05.364796ccn78.mcs.anl.gov/^A/2006-06-20 12:33:05.042036ccn74.mcs.anl.gov HCGDH=J =! pThu Dec 22 06:27:20 2005cleanunknown$Revision$##=I =! oThu Jan 5 06:34:03 2006cleanunknown$Revision$##=H =! nThu Jan 5 06:37:04 2006cleanunknown$Revision$##=G =! mMon Dec 26 06:26:08 2005cleanunknown$Revision$##=F =! lThu Jan 5 06:34:17 2006cleanunknown$Revision$##=E =! kThu Jan 5 06:42:47 2006cleanunknown$Revision$##DD =/ jTue Oct 25 16:10:19 2005cleanunknown$Revision: 1.37 $  =C =! iThu Jan 5 06:36:07 2006cleanunknown$Revision$##=B =! hThu Jan 5 06:42:10 2006cleanunknown$Revision$##=A =! gThu Jan 5 06:34:21 2006cleanunknown$Revision$##=@ =! fThu Jan 5 06:40:28 2006cleanunknown$Revision$##=? =! cThu Jan 5 06:37:16 2006cleanunknown$Revision$##=> =! bThu Jan 5 06:33:56 2006cleanunknown$Revision$##== =! aThu Jan 5 06:32:06 2006cleanunknown$Revision$##=< =! `Thu Jan 5 06:33:30 2006cleanunknown$Revision$## \ypg^ULC:1( }tkbYPG>5,#xof]TKB90' fffeeedddcccbbbaaa ` ` ` _ __^^^]]]\\\~[}[|[{ZzZyZxYwYvYuXtXsXrWqWpWoVnVmVlUkUjUiThTgTfSeSdScRbRaR`Q_Q^Q]P\P[PZOYOXOWNVNUNTMSMRMQLPLOLNKMKLKKJJJIJHJGIFIEIDH I&&0:DNXblv  *4>HR\fpz$.8BLV`jt~                        ! # & * - / 1 2 7 9 < @ B F G J N P T V Z \ ` c g j l o r w x |                   Fj8p> vDyF0A12006-06-20 12:34:13.886701ccn121.mcs.anl.gov0A12006-06-20 12:34:12.121759ccn141.mcs.anl.gov0A12006-06-20 12:34:10.797156ccn140.mcs.anl.gov0A12006-06-20 12:34:09.568469ccn139.mcs.anl.gov0A12006-06-20 12:34:07.233575ccn138.mcs.anl.gov0~A12006-06-20 12:34:05.390771ccn135.mcs.anl.gov0}A12006-06-20 12:34:03.434325ccn134.mcs.anl.gov0|A12006-06-20 12:34:01.344660ccn136.mcs.anl.gov0{A12006-06-20 12:33:59.218329ccn137.mcs.anl.gov0zA12006-06-20 12:33:57.163972ccn111.mcs.anl.gov0yA12006-06-20 12:33:54.611890ccn112.mcs.anl.gov0xA12006-06-20 12:33:51.984731ccn113.mcs.anl.gov0wA12006-06-20 12:33:48.634579ccn114.mcs.anl.gov0vA12006-06-20 12:33:45.953418ccn133.mcs.anl.gov0uA12006-06-20 12:33:44.247895ccn132.mcs.anl.gov0tA12006-06-20 12:33:42.057868ccn131.mcs.anl.gov0sA12006-06-20 12:33:40.691830ccn130.mcs.anl.gov0rA12006-06-20 12:33:39.308809ccn104.mcs.anl.gov0qA12006-06-20 12:33:37.560890ccn106.mcs.anl.gov OCGK O=Y =! Thu Jan 5 06:32:59 2006cleanunknown$Revision$##=X =! ~Mon Dec 19 11:40:05 2005cleanunknown$Revision$##=W =! }Tue Jan 3 06:34:16 2006cleanunknown$Revision$##=V =! |Thu Jan 5 06:33:13 2006cleanunknown$Revision$##=U =! {Thu Jan 5 06:33:16 2006cleanunknown$Revision$##=T =! zThu Jan 5 06:39:12 2006cleanunknown$Revision$##=S =! yThu Jan 5 06:36:17 2006cleanunknown$Revision$##=R =! xThu Jan 5 06:42:15 2006cleanunknown$Revision$##=Q =! wThu Jan 5 06:42:13 2006cleanunknown$Revision$##=P =! vThu Jan 5 06:30:00 2006cleanunknown$Revision$##=O =! uThu Jan 5 06:33:24 2006cleanunknown$Revision$##=N =! tThu Jan 5 06:33:36 2006cleanunknown$Revision$##=M =! sThu Jan 5 06:30:28 2006cleanunknown$Revision$##=L =! rThu Jan 5 06:33:56 2006cleanunknown$Revision$##=K =! qThu Jan 5 06:39:37 2006cleanunknown$Revision$## @@@@@>h =! Thu Jan 5 06:42:02 2006cleanunknown$Revision$##>g =! Thu Jan 5 06:30:53 2006cleanunknown$Revision$##>f =! Thu Jan 5 06:36:35 2006cleanunknown$Revision$##>e =! Thu Jan 5 06:40:35 2006cleanunknown$Revision$##>d =! Thu Jan 5 06:42:48 2006cleanunknown$Revision$##>c =! Thu Jan 5 06:33:17 2006cleanunknown$Revision$##>b =! Thu Jan 5 06:40:51 2006cleanunknown$Revision$##>a =! Thu Jan 5 06:40:09 2006cleanunknown$Revision$##>` =! Thu Jan 5 06:42:32 2006cleanunknown$Revision$##>_ =! Thu Jan 5 06:36:45 2006cleanunknown$Revision$##>^ =! Thu Jan 5 06:39:03 2006cleanunknown$Revision$##>] =! Thu Jan 5 06:39:03 2006cleanunknown$Revision$##>\ =! Thu Jan 5 06:43:02 2006cleanunknown$Revision$##>[ =! Thu Jan 5 06:36:33 2006cleanunknown$Revision$##>Z =! Thu Jan 5 06:40:19 2006cleanunknown$Revision$## 7g4h5i6j70A12006-06-20 12:34:43.160717ccn156.mcs.anl.gov0A12006-06-20 12:34:41.695139ccn155.mcs.anl.gov0A12006-06-20 12:34:39.812588ccn128.mcs.anl.gov0A12006-06-20 12:34:37.760161ccn127.mcs.anl.gov0A12006-06-20 12:34:35.923184ccn154.mcs.anl.gov0A12006-06-20 12:34:35.540098ccn151.mcs.anl.gov0A12006-06-20 12:34:33.800115ccn123.mcs.anl.gov0A12006-06-20 12:34:32.300130ccn124.mcs.anl.gov0A12006-06-20 12:34:30.927177ccn125.mcs.anl.gov0 A12006-06-20 12:34:29.410821ccn126.mcs.anl.gov0 A12006-06-20 12:34:27.961425ccn149.mcs.anl.gov0 A12006-06-20 12:34:26.570383ccn148.mcs.anl.gov0 A12006-06-20 12:34:24.988994ccn146.mcs.anl.gov0 A12006-06-20 12:34:23.289610ccn147.mcs.anl.gov0A12006-06-20 12:34:21.615620ccn142.mcs.anl.gov0A12006-06-20 12:34:20.082919ccn145.mcs.anl.gov0A12006-06-20 12:34:18.526295ccn115.mcs.anl.gov0A12006-06-20 12:34:16.810259ccn116.mcs.anl.gov0A12006-06-20 12:34:15.344659ccn122.mcs.anl.gov Zypg^ULC:1( }tkbYPG>5,#xoe[QG=3) yxwvutsrqponmlkjihgf~e~d~c}b|a|`|_|^{]{\{[zZzYzXyWyVyUxTxSxRwQwPwOvNvMvLuKuJuItHtGtFsEsDsCrBrAr@q?q>q=p<p;p:o9o8o7n6n5n4m3m2m1l0l/l.k-k,k+j*j)j(i'i&i%h$h#h"g!g g S '1;EOY}si_UKA7-#cks{ #+3;CKS[cks{                                             ! %(+-0359?CGKP!T#X%\'a(b)e*h+k,n-q.t/w0{1}23456789:;<=> @@@@@>w =! Thu Jan 5 06:32:59 2006cleanunknown$Revision$##>v =! Thu Jan 5 06:42:55 2006cleanunknown$Revision$##>u =! Thu Jan 5 06:30:27 2006cleanunknown$Revision$##>t =! Thu Jan 5 06:30:27 2006cleanunknown$Revision$##>s =! Thu Jan 5 06:36:52 2006cleanunknown$Revision$##>r =! Thu Jan 5 06:42:56 2006cleanunknown$Revision$##>q =! Thu Jan 5 06:33:25 2006cleanunknown$Revision$##>p =! Thu Jan 5 06:35:15 2006cleanunknown$Revision$##>o =! Thu Jan 5 06:37:22 2006cleanunknown$Revision$##>n =! Thu Jan 5 06:36:07 2006cleanunknown$Revision$##>m =! Thu Jan 5 06:33:43 2006cleanunknown$Revision$##>l =! Thu Jan 5 06:37:24 2006cleanunknown$Revision$##>k =! Thu Jan 5 06:39:24 2006cleanunknown$Revision$##>j =! Thu Jan 5 06:39:41 2006cleanunknown$Revision$##>i =! Thu Jan 5 06:41:57 2006cleanunknown$Revision$## 7g4h5i6j70)A12006-06-20 12:35:18.055534ccn173.mcs.anl.gov0(A12006-06-20 12:35:16.784274ccn174.mcs.anl.gov0'A12006-06-20 12:35:15.979908ccn175.mcs.anl.gov0&A12006-06-20 12:35:10.384768ccn169.mcs.anl.gov0%A12006-06-20 12:35:09.214767ccn171.mcs.anl.gov0$A12006-06-20 12:35:03.748638ccn166.mcs.anl.gov0#A12006-06-20 12:35:01.908120ccn168.mcs.anl.gov0"A12006-06-20 12:35:00.567203ccn198.mcs.anl.gov0!A12006-06-20 12:34:59.200374ccn197.mcs.anl.gov0 A12006-06-20 12:34:57.902398ccn196.mcs.anl.gov0A12006-06-20 12:34:56.536118ccn195.mcs.anl.gov0A12006-06-20 12:34:54.986484ccn193.mcs.anl.gov0A12006-06-20 12:34:53.638222ccn161.mcs.anl.gov0A12006-06-20 12:34:52.347086ccn163.mcs.anl.gov0A12006-06-20 12:34:50.855892ccn165.mcs.anl.gov0A12006-06-20 12:34:49.384198ccn160.mcs.anl.gov0A12006-06-20 12:34:47.549769ccn158.mcs.anl.gov0A12006-06-20 12:34:46.200585ccn159.mcs.anl.gov0A12006-06-20 12:34:44.684744ccn157.mcs.anl.gov e@y9p(e> =! Thu Jan 5 06:42:59 2006cleanunknown$Revision$##> =! Thu Jan 5 06:31:00 2006cleanunknown$Revision$##> =! Thu Jan 5 06:32:11 2006dirtyunknown$Revision$  E =/ Thu Dec 1 06:30:29 2005cleanunknown$Revision: 1.40 $> =! Thu Jan 5 06:39:59 2006cleanunknown$Revision$##> =! Tue Jan 3 16:26:26 2006cleanunknown$Revision$##E =/ Wed Nov 23 06:34:12 2005cleanunknown$Revision: 1.40 $  >~ =! Thu Jan 5 06:29:46 2006cleanunknown$Revision$##E} =/ Wed Oct 5 10:56:06 2005cleanunknown$Revision: 1.36 $  >| =! Mon Dec 19 17:51:58 2005cleanunknown$Revision$##>{ =! Thu Jan 5 06:29:49 2006cleanunknown$Revision$##>z =! Thu Jan 5 06:29:48 2006cleanunknown$Revision$##>y =! Thu Jan 5 06:42:42 2006cleanunknown$Revision$##>x =! Thu Jan 5 06:29:49 2006cleanunknown$Revision$## O",6@JT^hr|  +6ALWbmx$.8BLV`jt~      "  R m    >     $ % & '9 4w 5  V   R        A W      &  T      D    S  i        n    @     $ % & '< 4x 5  X   S      L )2;DMV_hqz  *4>HR\fpz$.8BLV`jt~o9p;q>rAsDtGuKvNwPxTyVzY{\|`~d j o p s v y |                                                   " % ) + 7g4h5i6j70<A12006-06-20 12:39:01.412178ccn220.mcs.anl.gov0;A12006-06-20 12:38:45.261843ccn219.mcs.anl.gov0:A12006-06-20 12:38:43.548727ccn211.mcs.anl.gov09A12006-06-20 12:38:30.501503ccn207.mcs.anl.gov08A12006-06-20 12:38:01.240610ccn210.mcs.anl.gov07A12006-06-20 12:37:27.482917ccn208.mcs.anl.gov06A12006-06-20 12:37:24.228616ccn187.mcs.anl.gov05A12006-06-20 12:37:23.044637ccn189.mcs.anl.gov04A12006-06-20 12:37:21.888003ccn186.mcs.anl.gov03A12006-06-20 12:36:52.872566ccn204.mcs.anl.gov02A12006-06-20 12:36:20.701007ccn206.mcs.anl.gov01A12006-06-20 12:35:59.813104ccn203.mcs.anl.gov00A12006-06-20 12:35:58.663805ccn178.mcs.anl.gov0/A12006-06-20 12:35:57.505791ccn182.mcs.anl.gov0.A12006-06-20 12:35:56.356386ccn179.mcs.anl.gov0-A12006-06-20 12:35:55.241118ccn176.mcs.anl.gov0,A12006-06-20 12:35:54.025853ccn199.mcs.anl.gov0+A12006-06-20 12:35:18.566555ccn202.mcs.anl.gov0*A12006-06-20 12:35:18.320260ccn201.mcs.anl.gov YnH) |W4yX>"yY} libjpeg62PackageUnknown|# gnuplot-x11PackageUnknown{ dpkg-devPackageUnknown"z) kernel-packagePackageUnknowny! rsh-clientPackageUnknownx# debootstrapPackageUnknownw nttcpPackageUnknownv ucfPackageUnknownu! postgresqlPackageUnknownt! libperl5.8PackageUnknown%s/ postgresql-clientPackageUnknownr unzipPackageUnknownq rlsServiceUnknownp! postgresqlServiceUnknowno tftpPackageUnknown n% ext3rminatorPackageUnknown"m) libqthreads-12PackageUnknown*l9 kernel-image-2.6.11.12PackageUnknownk hdparmPackageUnknownj mailutilsPackageUnknown"i) guile-1.6-libsPackageUnknownh cpp-4.0PackageUnknowng bonnie++PackageUnknown#f+ libguile-ltdl-1PackageUnknown%e/ nfs-kernel-serverPackageUnknown d% gcc-4.0-basePackageUnknown c% libreadline5PackageUnknown!b' libmailutils0PackageUnknown @L\ l0|@9~ 3! 2006-01-05 06:40:54cleanunknown$Revision$##9} 3! 2006-01-05 06:40:51cleanunknown$Revision$##9| 3! 2006-01-05 06:40:47cleanunknown$Revision$##9{ 3! 2006-01-05 06:40:44cleanunknown$Revision$##9z 3! 2006-01-05 06:40:42cleanunknown$Revision$##9y 3! 2006-01-05 06:40:35cleanunknown$Revision$##9x 3! 2006-01-05 06:40:32cleanunknown$Revision$##9w 3! 2006-01-05 06:40:28cleanunknown$Revision$##9v 3! 2006-01-05 06:40:19cleanunknown$Revision$##9u 3! 2006-01-05 06:40:18cleanunknown$Revision$##9t 3! 2006-01-05 06:40:14cleanunknown$Revision$##9s 3! 2006-01-05 06:40:10cleanunknown$Revision$##9r 3! 2006-01-05 06:40:09cleanunknown$Revision$##9q 3! 2006-01-05 06:40:07cleanunknown$Revision$##9p 3! 2006-01-05 06:39:59cleanunknown$Revision$##9o 3! 2006-01-05 06:39:54cleanunknown$Revision$## (K U_i(?. 3/ 2005-11-28 06:35:07cleanunknown$Revision: 1.40 $8- 3! 2006-01-05 06:31:08dirtyunknown$Revision$  ?, 3/ 2005-11-28 06:35:07cleanunknown$Revision: 1.40 $8+ 3! 2006-01-05 06:32:56dirtyunknown$Revision$  ?* 3/ 2005-11-28 06:33:34cleanunknown$Revision: 1.40 $8) 3! 2006-01-05 06:39:06dirtyunknown$Revision$  ?( 3/ 2005-11-28 06:32:53cleanunknown$Revision: 1.40 $8' 3! 2006-01-05 06:30:25dirtyunknown$Revision$  ?& 3/ 2005-11-28 06:32:21cleanunknown$Revision: 1.40 $8% 3! 2006-01-05 06:39:58dirtyunknown$Revision$  ?$ 3/ 2005-11-28 06:32:18cleanunknown$Revision: 1.40 $8# 3! 2006-01-05 06:36:45dirtyunknown$Revision$  ?" 3/ 2005-11-28 06:32:02cleanunknown$Revision: 1.40 $8! 3! 2006-01-05 06:36:45dirtyunknown$Revision$  ? 3/ 2005-11-28 06:30:48cleanunknown$Revision: 1.40 $8 3! 2006-01-05 06:40:33dirtyunknown$Revision$  V~=l+a V> =! Thu Jan 5 06:36:45 2006dirtyunknown$Revision$  E =/ Thu Dec 1 06:32:43 2005cleanunknown$Revision: 1.40 $> =! Mon Jan 9 13:57:52 2006cleanunknown$Revision$##> =! Thu Jan 5 06:29:59 2006cleanunknown$Revision$##> =! Thu Jan 5 06:42:23 2006cleanunknown$Revision$##> =! Thu Jan 5 06:29:49 2006dirtyunknown$Revision$  E =/ Thu Dec 1 06:35:18 2005cleanunknown$Revision: 1.40 $> =! Thu Jan 5 06:30:00 2006dirtyunknown$Revision$  E =/ Thu Dec 1 06:38:16 2005cleanunknown$Revision: 1.40 $> =! Thu Jan 5 06:32:55 2006dirtyunknown$Revision$  E =/ Thu Dec 1 06:26:28 2005cleanunknown$Revision: 1.40 $> =! Thu Jan 5 06:36:35 2006cleanunknown$Revision$##> =! Thu Jan 5 06:39:29 2006cleanunknown$Revision$##> =! Thu Jan 5 06:39:54 2006cleanunknown$Revision$## T~tj`VLB8.$zpf\RH>4*  vlbXND:0&MLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!      ~}|{z O~sh]RG<1&{peZOD9/$wmcXMB7,!      ;B>~}|{7z6yxwvutsrqpon+mlkjizhgfedcba`_^]\[ZYXWVUT;SRQPONM>LK Ah6l9p= sA0_A/2006-06-20 14:23:00.632898ccn65.mcs.anl.gov0^A/2006-06-20 14:22:59.825212ccn72.mcs.anl.gov1]A12006-06-20 14:22:59.143054ccn185.mcs.anl.gov1\A12006-06-20 14:22:58.434342ccn199.mcs.anl.gov0[A/2006-06-20 14:22:57.734802ccn73.mcs.anl.gov1ZA12006-06-20 14:22:57.018507ccn126.mcs.anl.gov0YA/2006-06-20 14:22:56.127420cct3m.mcs.anl.gov0XA/2006-06-20 14:22:55.295040ccn85.mcs.anl.gov0WA/2006-06-20 14:22:54.612254ccn24.mcs.anl.gov1VA12006-06-20 14:22:53.929026ccn239.mcs.anl.gov1UA12006-06-20 14:22:53.237666ccn237.mcs.anl.gov1TA12006-06-20 14:22:52.443815ccn130.mcs.anl.gov0SA/2006-06-20 14:22:50.997341ccn22.mcs.anl.gov1RA12006-06-20 14:22:50.297913ccn163.mcs.anl.gov0QA/2006-06-20 14:22:49.554755ccn37.mcs.anl.gov0PA/2006-06-20 14:22:48.823835ccn78.mcs.anl.gov0OA/2006-06-20 14:22:48.108511ccn47.mcs.anl.gov1NA12006-06-20 14:22:47.408988ccn165.mcs.anl.gov1MA12006-06-20 14:22:46.725210ccn133.mcs.anl.gov @L\ l0|@9^ 3! 2006-01-05 06:39:03cleanunknown$Revision$##9] 3! 2006-01-05 06:39:03cleanunknown$Revision$##9\ 3! 2006-01-05 06:39:02cleanunknown$Revision$##9[ 3! 2006-01-05 06:39:01cleanunknown$Revision$##9Z 3! 2006-01-05 06:37:29cleanunknown$Revision$##9Y 3! 2006-01-05 06:37:24cleanunknown$Revision$##9X 3! 2006-01-05 06:37:22cleanunknown$Revision$##9W 3! 2006-01-05 06:37:16cleanunknown$Revision$##9V 3! 2006-01-05 06:37:10cleanunknown$Revision$##9U 3! 2006-01-05 06:37:07cleanunknown$Revision$##9T 3! 2006-01-05 06:37:04cleanunknown$Revision$##9S 3! 2006-01-05 06:37:01cleanunknown$Revision$##9R 3! 2006-01-05 06:36:57cleanunknown$Revision$##9Q 3! 2006-01-05 06:36:53cleanunknown$Revision$##9P 3! 2006-01-05 06:36:52cleanunknown$Revision$##9O 3! 2006-01-05 06:36:46cleanunknown$Revision$## f2d0`,1pA12006-06-20 14:25:01.015673ccn187.mcs.anl.gov1oA12006-06-20 14:25:00.290113ccn141.mcs.anl.gov1nA12006-06-20 14:24:59.607190ccn176.mcs.anl.gov1mA12006-06-20 14:24:58.890726ccn158.mcs.anl.gov1lA12006-06-20 14:24:58.207601ccn161.mcs.anl.gov1kA12006-06-20 14:24:57.491499ccn118.mcs.anl.gov1jA12006-06-20 14:24:56.817096ccn146.mcs.anl.gov1iA12006-06-20 14:24:56.100788ccn100.mcs.anl.gov0hA/2006-06-20 14:24:55.301345ccn52.mcs.anl.gov1gA12006-06-20 14:24:54.593375ccn196.mcs.anl.gov0fA/2006-06-20 14:24:53.661025ccn21.mcs.anl.gov1eA12006-06-20 14:24:52.962480ccn153.mcs.anl.gov1dA12006-06-20 14:24:52.144966ccn115.mcs.anl.gov0cA/2006-06-20 14:24:51.420615ccn67.mcs.anl.gov0bA/2006-06-20 14:24:50.720909ccn34.mcs.anl.gov1aA12006-06-20 14:24:50.047249ccn186.mcs.anl.gov @i6k9n< s@1rA12006-06-20 14:23:16.981441ccsto4.mcs.anl.gov0qA/2006-06-20 14:23:16.225418ccn60.mcs.anl.gov0pA/2006-06-20 14:23:14.934181ccn88.mcs.anl.gov0oA/2006-06-20 14:23:13.935953ccn84.mcs.anl.gov1nA12006-06-20 14:23:12.614390ccn138.mcs.anl.gov0mA/2006-06-20 14:23:11.601207cct1m.mcs.anl.gov1lA12006-06-20 14:23:10.918232ccn193.mcs.anl.gov1kA12006-06-20 14:23:10.235016ccn233.mcs.anl.gov1jA12006-06-20 14:23:08.835847ccsto2.mcs.anl.gov0iA/2006-06-20 14:23:07.903150cct7m.mcs.anl.gov0hA/2006-06-20 14:23:07.203564ccn77.mcs.anl.gov0gA/2006-06-20 14:23:06.512857ccn82.mcs.anl.gov1fA12006-06-20 14:23:05.804437ccn230.mcs.anl.gov1eA12006-06-20 14:23:04.880616ccsto5.mcs.anl.gov1dA12006-06-20 14:23:04.205774ccn191.mcs.anl.gov1cA12006-06-20 14:23:03.506223ccn192.mcs.anl.gov1bA12006-06-20 14:23:02.815127ccn231.mcs.anl.gov0aA/2006-06-20 14:23:01.965425ccn75.mcs.anl.gov0`A/2006-06-20 14:23:01.290832ccn43.mcs.anl.gov GeJ,{^:vY<gGa libgsasl7PackageUnknown$`- edg-crl-upgradedServiceUnknown_ grisServiceUnknown%^/ nfs-kernel-serverServiceUnknown)]7 kernel-image-2.6.12.6PackageUnknown\! nfs-commonPackageUnknown[ sysstatPackageUnknownZ updatePackageUnknownY condorServiceUnknownX sysstatServiceUnknown W% mysql-clientPackageUnknownV mailxPackageUnknown U% mysql-serverPackageUnknownT mysqlServiceUnknown%S/ libdbd-mysql-perlPackageUnknown!R' libplrpc-perlPackageUnknownQ libpq3PackageUnknown&P1 libnet-daemon-perlPackageUnknown"O) libdbd-pg-perlPackageUnknownN# libdbi-perlPackageUnknown M% mysql-commonPackageUnknownL cpp-3.2PackageUnknownK gcc-3.2PackageUnknownJ filePackageUnknownI autoconfPackageUnknown H% gcc-3.2-basePackageUnknownG# automake1.4PackageUnknownF m4PackageUnknownE libtoolPackageUnknown M(3>IT_ju&0:DNXcny)4?JU`kv " B Y       '  V     G    T  k        q    B     $ % & '> 4{ 5 # [   U       # C Z       )  X     J    U  l        r    C OvkaVK@6+  }rg\QF;0%zodYND9.# t~}|{zyxBwvuts;rqponmlkj>ihgSfe7d6cba`_^]\[ZY+XWVUTSRQzPONMLKJtIHGFEDC;BA>@6?>=+<;:98 O#.9DOZep{  +6ALWbmw$/:EP[fq|     $ % & '? 4| 5 $ \   V       $ D [       *  Y      V         t    G     $ % & 'A 4~ 5 ' `   Y       & E ]       -  ]     [~=G Q[? 3/ 2005-11-28 06:29:43cleanunknown$Revision: 1.40 $8 3! 2006-01-05 06:30:43dirtyunknown$Revision$  ? 3/ 2005-11-28 06:29:41cleanunknown$Revision: 1.40 $8 3! 2006-01-05 06:33:17dirtyunknown$Revision$  ? 3/ 2005-11-28 06:29:41cleanunknown$Revision: 1.40 $8 3! 2006-01-05 06:39:04dirtyunknown$Revision$  ? 3/ 2005-11-28 06:29:41cleanunknown$Revision: 1.40 $8 3! 2006-01-05 06:37:01dirtyunknown$Revision$  ? 3/ 2005-11-28 06:26:21cleanunknown$Revision: 1.40 $8 3! 2006-01-05 06:37:15dirtyunknown$Revision$  ? 3/ 2005-11-28 06:26:04cleanunknown$Revision: 1.40 $8 3! 2006-01-05 06:31:18dirtyunknown$Revision$  ? 3/ 2005-11-28 06:26:03cleanunknown$Revision: 1.40 $? 3/ 2005-11-26 00:42:27cleanunknown$Revision: 1.40 $? 3/  2005-11-23 06:34:12cleanunknown$Revision: 1.40 $  T~tj`VLB8.$zpf\RH>4*  vlbXND:0&          ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?> :w/^L :E! =/ Thu Dec 1 06:30:46 2005cleanunknown$Revision: 1.40 $> =! Thu Jan 5 06:30:27 2006dirtyunknown$Revision$ E =/ Thu Dec 1 06:27:08 2005cleanunknown$Revision: 1.40 $> =! Thu Jan 5 06:39:24 2006dirtyunknown$Revision$  E =/ Thu Dec 1 06:26:14 2005cleanunknown$Revision: 1.40 $> =! Thu Jan 5 06:30:47 2006dirtyunknown$Revision$  E =/ Thu Dec 1 06:29:48 2005cleanunknown$Revision: 1.40 $> =! Thu Jan 5 06:39:03 2006dirtyunknown$Revision$  E =/ Thu Dec 1 06:29:51 2005cleanunknown$Revision: 1.40 $E =/ Sat Nov 26 00:42:27 2005cleanunknown$Revision: 1.40 $> =! Thu Jan 5 06:37:17 2006dirtyunknown$Revision$  E =/ Thu Dec 1 06:30:11 2005cleanunknown$Revision: 1.40 $> =! Thu Jan 5 06:36:07 2006dirtyunknown$Revision$  E =/ Thu Dec 1 06:36:49 2005cleanunknown$Revision: 1.40 $ @L\ l0|@9N 3! 2006-01-05 06:36:46cleanunknown$Revision$##9M 3! 2006-01-05 06:36:45cleanunknown$Revision$##9L 3! 2006-01-05 06:36:45cleanunknown$Revision$##9K 3! 2006-01-05 06:36:45cleanunknown$Revision$##9J 3! 2006-01-05 06:36:35cleanunknown$Revision$##9I 3! 2006-01-05 06:36:35cleanunknown$Revision$##9H 3! 2006-01-05 06:36:35cleanunknown$Revision$##9G 3! 2006-01-05 06:36:35cleanunknown$Revision$##9F 3! 2006-01-05 06:36:33cleanunknown$Revision$##9E 3! 2006-01-05 06:36:17cleanunknown$Revision$##9D 3! 2006-01-05 06:36:17cleanunknown$Revision$##9C 3! 2006-01-05 06:36:17cleanunknown$Revision$##9B 3! 2006-01-05 06:36:17cleanunknown$Revision$##9A 3! 2006-01-05 06:36:08cleanunknown$Revision$##9@ 3! 2006-01-05 06:36:07cleanunknown$Revision$##9? 3! 2006-01-05 06:36:07cleanunknown$Revision$66 EiL$zW1k8iE!D' autotools-devPackageUnknownC libmagic1PackageUnknownB lam4PackageUnknownA! traceroutePackageUnknown@ libnet1PackageUnknown? arpingPackageUnknown> patchPackageUnknown= pvfs2PackageUnknown0<E kernel-image-2.6.12.5-p3-mcsPackageUnknown0;E pvfs2-driver-2.6.12.5-p3-mcsPackageUnknown!:' libpango1.0-0PackageUnknown9# libatk1.0-0PackageUnknown!8' libgtk2.0-binPackageUnknown&71 libpango1.0-commonPackageUnknown#6+ mozilla-firefoxPackageUnknown 5% libglib2.0-0PackageUnknown4 libidl0PackageUnknown3# libgtk2.0-0PackageUnknown$2- libgtk2.0-commonPackageUnknown1# xlibmesa-glPackageUnknown0! emacs21-elPackageUnknown%// libhtml-tree-perlPackageUnknown. gm-devPackageUnknown- libxft1PackageUnknown!,' mpich2-systemPackageUnknown,+= kernel-source-2.4.29-rc2PackageUnknown#*+ libgenders-perlPackageUnknown :j7k8n;n:1A12006-06-20 14:23:42.081758ccn109.mcs.anl.gov0A/2006-06-20 14:23:41.373380ccn79.mcs.anl.gov0A/2006-06-20 14:23:40.682588ccn83.mcs.anl.gov1A12006-06-20 14:23:39.983336ccn104.mcs.anl.gov0A/2006-06-20 14:23:39.274642ccn57.mcs.anl.gov0A/2006-06-20 14:23:27.682644ccfs2.mcs.anl.gov1A12006-06-20 14:23:26.974253ccn128.mcs.anl.gov0~A/2006-06-20 14:23:26.283383ccn49.mcs.anl.gov1}A12006-06-20 14:23:25.559050ccn131.mcs.anl.gov0|A/2006-06-20 14:23:24.859184ccn80.mcs.anl.gov1{A12006-06-20 14:23:24.159599ccn150.mcs.anl.gov1zA12006-06-20 14:23:23.460028ccn159.mcs.anl.gov1yA12006-06-20 14:23:22.576590ccn132.mcs.anl.gov1xA12006-06-20 14:23:21.471312ccn147.mcs.anl.gov1wA12006-06-20 14:23:20.736714ccn137.mcs.anl.gov1vA12006-06-20 14:23:20.028878ccn136.mcs.anl.gov0uA/2006-06-20 14:23:19.337598ccn50.mcs.anl.gov0tA/2006-06-20 14:23:18.621455ccn30.mcs.anl.gov0sA/2006-06-20 14:23:17.930031ccn35.mcs.anl.gov N_=kN$cI, nN) libxtrap6PackageUnknown(! python-devPackageUnknown'! mpich2-mpdPackageUnknown& gendersPackageUnknown% libdps1PackageUnknown$ libxp6PackageUnknown #% xlibmesa-gluPackageUnknown" libxi6PackageUnknown! rcsPackageUnknown # sss-wrapperPackageUnknown!' xbase-clientsPackageUnknown! mpich2-devPackageUnknown xlibsPackageUnknown libxmuu1PackageUnknown libxtst6PackageUnknown'3 libhtml-tagset-perlPackageUnknown libxv1PackageUnknown pdshPackageUnknown#+ sss-bcm-clientsPackageUnknown'3 libhtml-parser-perlPackageUnknown&1 python-elementtreePackageUnknown fingerPackageUnknown! libxrandr2PackageUnknown# libwww-perlPackageUnknown! libdb3-devPackageUnknown# mpich2-libsPackageUnknown# libxcursor1PackageUnknown# liburi-perlPackageUnknown  mpdServiceUnknown @iH$ Ak4z@7 S kernel-image-2.6.9-chiba-selfish-upPackageUnknown4 M kernel-image-2.6.9-chiba-selfishPackageUnknown  libx11-6PackageUnknown# + libmpich1.0-devPackageUnknown mpich-binPackageUnknown g77-3.3PackageUnknown4M gm-driver-2.6.9-chiba-selfish-upPackageUnknown'3 kernel-image-2.4.28PackageUnknown# libmpich1.0PackageUnknown! xlibs-dataPackageUnknown$- gm-driver-2.4.28PackageUnknown?c kernel-image-2.6.10-rc2-mm3-v0.7.32-9-mode1PackageUnknown?c kernel-image-2.6.10-rc2-mm3-v0.7.32-9-mode4PackageUnknown)7 kernel-image-2.6.11.8PackageUnknown2~I kernel-image-2.6.11.10-mcs-x86PackageUnknown'}3 pvfs2-driver-2.4.28PackageUnknown| g77PackageUnknown"{) xfree86-commonPackageUnknownz# libg2c0-devPackageUnknowny mpichPackageUnknown x% pvfs2-clientServiceUnknownw libedit2PackageUnknownv# libselinux1PackageUnknownu iperfPackageUnknown 8g4h5k8j8/OA/2006-06-20 12:40:59.367525ccn66.mcs.anl.gov2NA52006-06-20 12:40:59.050559ccfs1-fe.mcs.anl.gov0MA12006-06-20 12:40:58.636975ccn188.mcs.anl.gov0LA12006-06-20 12:40:57.358510ccn185.mcs.anl.gov0KA12006-06-20 12:40:55.842348ccn192.mcs.anl.gov0JA12006-06-20 12:40:54.589773ccn181.mcs.anl.gov0IA12006-06-20 12:40:53.287053ccn167.mcs.anl.gov0HA12006-06-20 12:40:51.942343ccn144.mcs.anl.gov/GA/2006-06-20 12:40:48.301044ccn93.mcs.anl.gov/FA/2006-06-20 12:40:46.817021ccn69.mcs.anl.gov0EA12006-06-20 12:40:30.940298ccn215.mcs.anl.gov0DA12006-06-20 12:40:30.713887ccn216.mcs.anl.gov0CA12006-06-20 12:40:14.515890ccn213.mcs.anl.gov0BA12006-06-20 12:39:57.061332ccn224.mcs.anl.gov0AA12006-06-20 12:39:51.479045ccn223.mcs.anl.gov0@A12006-06-20 12:39:45.019474ccn222.mcs.anl.gov0?A12006-06-20 12:39:44.675669ccn221.mcs.anl.gov0>A12006-06-20 12:39:23.338536ccn217.mcs.anl.gov0=A12006-06-20 12:39:06.731887ccn218.mcs.anl.gov Fk9tBxEyF1&A12006-06-20 14:21:43.565360ccn219.mcs.anl.gov1%A12006-06-20 14:21:41.935227ccn220.mcs.anl.gov1$A12006-06-20 14:21:38.302187ccn205.mcs.anl.gov1#A12006-06-20 14:21:35.137344ccn217.mcs.anl.gov1"A12006-06-20 14:21:33.471899ccn227.mcs.anl.gov1!A12006-06-20 14:21:31.431605ccn203.mcs.anl.gov1 A12006-06-20 14:21:29.457814ccn218.mcs.anl.gov1A12006-06-20 14:21:27.783812ccn223.mcs.anl.gov0A/2006-06-20 14:21:25.910039ccn15.mcs.anl.gov0A/2006-06-20 14:21:23.994672ccn12.mcs.anl.gov0A/2006-06-20 14:21:22.087455ccn13.mcs.anl.gov0A/2006-06-20 14:21:20.038872ccn16.mcs.anl.gov/A-2006-06-20 14:21:17.723680ccn2.mcs.anl.gov/A-2006-06-20 14:21:15.733187ccn3.mcs.anl.gov/A-2006-06-20 14:21:13.412210ccn8.mcs.anl.gov0A/2006-06-20 14:21:07.430271ccn10.mcs.anl.gov0A/2006-06-20 14:21:05.542010ccn14.mcs.anl.gov/A-2006-06-20 14:21:03.435149ccn5.mcs.anl.gov0A/2006-06-20 14:20:59.967814ccn11.mcs.anl.gov WwY;eJ0 t\5~W%t/ module-init-toolsPackageUnknown#s+ gm-route-clientPackageUnknownr stracePackageUnknownq gm-utilsPackageUnknownp! ntp-simplePackageUnknowno ntpdatePackageUnknownn mpishPackageUnknown%m/ mpich2-system-mpdPackageUnknownl gmPackageUnknownk! mpd-systemServiceUnknownj cpp-3.3PackageUnknowni! zlib1g-devPackageUnknownh libc6-devPackageUnknowng gccPackageUnknown%f/ python2.3-twistedPackageUnknowne makePackageUnknownd bzip2PackageUnknownc gcc-3.3PackageUnknownb cppPackageUnknowna opensslPackageUnknown` cvsPackageUnknown#_+ libncurses5-devPackageUnknown ^% bridge-utilsPackageUnknown!]' python2.3-devPackageUnknown\ libidn11PackageUnknown[ binutilsPackageUnknownZ libcurl3PackageUnknown Y% libcurl3-devPackageUnknownX libsysfs1PackageUnknown(W5 linux-kernel-headersPackageUnknown Ow6^SO>/ =! Thu Jan 5 06:40:44 2006cleanunknown$Revision$##>. =! Thu Jan 5 06:40:14 2006cleanunknown$Revision$##>- =! Thu Jan 5 06:40:42 2006cleanunknown$Revision$##>, =! Sat Dec 10 06:30:50 2005cleanunknown$Revision$>+ =! Thu Jan 5 06:36:45 2006cleanunknown$Revision$##>* =! Thu Jan 5 06:32:59 2006dirtyunknown$Revision$  E) =/ Thu Dec 1 06:29:54 2005cleanunknown$Revision: 1.40 $>( =! Thu Jan 5 06:29:59 2006dirtyunknown$Revision$  E' =/ Thu Dec 1 06:30:42 2005cleanunknown$Revision: 1.40 $E& =/ Mon Dec 5 06:25:21 2005dirtyunknown$Revision: 1.37 $E% =/ Mon Oct 24 06:27:13 2005cleanunknown$Revision: 1.37 $  >$ =! Thu Jan 5 06:33:47 2006dirtyunknown$Revision$  E# =/ Thu Dec 1 06:25:29 2005cleanunknown$Revision: 1.40 $>" =! Thu Jan 5 06:30:39 2006dirtyunknown$Revision$  @L\ l0|@9> 3! 2006-01-05 06:36:07cleanunknown$Revision$##9= 3! 2006-01-05 06:36:07cleanunknown$Revision$##9< 3! 2006-01-05 06:36:07cleanunknown$Revision$##9; 3! 2006-01-05 06:36:07cleanunknown$Revision$##9: 3! 2006-01-05 06:36:07cleanunknown$Revision$##99 3! 2006-01-05 06:35:15cleanunknown$Revision$##98 3! 2006-01-05 06:35:10cleanunknown$Revision$97 3! 2006-01-05 06:35:10cleanunknown$Revision$##96 3! 2006-01-05 06:35:09cleanunknown$Revision$##95 3! 2006-01-05 06:34:31cleanunknown$Revision$##94 3! 2006-01-05 06:34:27cleanunknown$Revision$##93 3! 2006-01-05 06:34:22cleanunknown$Revision$##92 3! 2006-01-05 06:34:21cleanunknown$Revision$##91 3! 2006-01-05 06:34:17cleanunknown$Revision$##90 3! 2006-01-05 06:34:06cleanunknown$Revision$##9/ 3! 2006-01-05 06:34:03cleanunknown$Revision$## S&0:DNXblv  *4>HR\fpz$.8BLV`jt~         4 5 : ; ? A D I L M Q S X Y _ d f i k n q u y { ~                                                 " T~tj`VLB8.$zpf\RH>4*  vlbXND:0&!      ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPON RqJ!gL1_@oRV libatm1PackageUnknownU! pkg-configPackageUnknown T% libidn11-devPackageUnknownS! libssl-devPackageUnknown)R7 python2.3-twisted-binPackageUnknownQ iproutePackageUnknown%P/ python2.2-xmlbasePackageUnknownO python2.2PackageUnknown!N' python2.2-xmlPackageUnknownM bcfg2ServiceUnknown!L' stop-bootlogdServiceUnknownK rmnologinServiceUnknownJ cronServiceUnknownI rsyncServiceUnknownH makedevServiceUnknownG inetdServiceUnknownF klogdServiceUnknownE sysklogdServiceUnknownD singleServiceUnknown#C+ amihappy-clientPackageUnknownB http-tinyPackageUnknownA libkrb53PackageUnknown@ cpp-2.95PackageUnknown'?3 libmsyslog-mcs-perlPackageUnknown%>/ python2.3-libxml2PackageUnknown!=' libaudiofile0PackageUnknown< tcpdumpPackageUnknown%;/ chiba-stage-slavePackageUnknown&:1 emacs21-bin-commonPackageUnknown Fq )2;DNXblv  *{q4<ENV_hpy ; ; ; f ; 2;;;;2;q;;; ;?;V;h; w;!;";#;$;%;4;5 ;G ; ; ; ; ;C ;v ; ; ; ;3 ;b ; ; I ; ~ ; ; ; ; ; ;  ; / ; I ; <k<< 4=l== 6>m>>>>(>g>>>>8>T>f> u>!>">#>$ KOc(w;K9. 3! 2006-01-05 06:33:56cleanunknown$Revision$##9- 3! 2006-01-05 06:33:56cleanunknown$Revision$##9, 3! 2006-01-05 06:33:56cleanunknown$Revision$##9+ 3! 2006-01-05 06:33:50cleanunknown$Revision$##9* 3! 2006-01-05 06:33:44cleanunknown$Revision$8) 3! 2006-01-05 06:33:43cleanunknown$Revision$##8( 3! ~2006-01-05 06:33:40cleanunknown$Revision$##8' 3! }2006-01-05 06:33:36cleanunknown$Revision$##8& 3! |2006-01-05 06:33:30cleanunknown$Revision$##8% 3! {2006-01-05 06:33:29cleanunknown$Revision$##8$ 3! z2006-01-05 06:33:25cleanunknown$Revision$##8# 3! y2006-01-05 06:33:24cleanunknown$Revision$##8" 3! x2006-01-05 06:33:17cleanunknown$Revision$##8! 3! w2006-01-05 06:33:16cleanunknown$Revision$##8 3! v2006-01-05 06:33:13cleanunknown$Revision$##8 3! u2006-01-05 06:33:13cleanunknown$Revision$## N|qf[PE:/$ti^TI?4)zodYNC8-" ,+z*)('&%$#"! 2     &~}|{zyxwvutsrqponmlkjihgfedcba`_ TvT 6+#Can delete pingdelete_ping 5+#Can change pingchange_ping4%Can add pingadd_ping$3/'Can delete reasondelete_reason$2/'Can change reasonchange_reason V~=s2h V>= =! Thu Jan 5 06:30:43 2006dirtyunknown$Revision$  E< =/ Mon Nov 28 06:29:41 2005cleanunknown$Revision: 1.40 $>; =! Thu Jan 5 06:36:45 2006dirtyunknown$Revision$  E: =/ Mon Nov 28 06:32:02 2005cleanunknown$Revision: 1.40 $>9 =! Thu Jan 5 06:31:18 2006dirtyunknown$Revision$  E8 =/ Mon Nov 28 06:26:03 2005cleanunknown$Revision: 1.40 $>7 =! Thu Jan 5 06:42:51 2006cleanunknown$Revision$##>6 =! Thu Jan 5 06:42:22 2006cleanunknown$Revision$##>5 =! Thu Jan 5 06:39:58 2006dirtyunknown$Revision$  E4 =/ Mon Nov 28 06:32:18 2005cleanunknown$Revision: 1.40 $>3 =! Thu Jan 5 06:34:06 2006cleanunknown$Revision$##>2 =! Thu Jan 5 06:42:18 2006cleanunknown$Revision$##>1 =! Thu Jan 5 06:31:02 2006cleanunknown$Revision$##>0 =! Thu Jan 5 06:31:17 2006cleanunknown$Revision$## Ij7p> vD|I0bA12006-06-20 12:43:55.013025ccn108.mcs.anl.gov/aA/2006-06-20 12:43:53.821941ccn84.mcs.anl.gov/`A/2006-06-20 12:43:52.622712ccn75.mcs.anl.gov/_A/2006-06-20 12:43:51.407010ccn73.mcs.anl.gov/^A/2006-06-20 12:43:50.168385ccn88.mcs.anl.gov/]A/2006-06-20 12:43:49.924364ccn76.mcs.anl.gov/\A/2006-06-20 12:43:48.691670ccn77.mcs.anl.gov/[A/2006-06-20 12:43:47.508669ccn79.mcs.anl.gov/ZA/2006-06-20 12:43:45.680814ccn68.mcs.anl.gov/YA/2006-06-20 12:43:30.321705ccn19.mcs.anl.gov/XA/2006-06-20 12:43:24.307358ccn18.mcs.anl.gov/WA/2006-06-20 12:43:14.505349ccn14.mcs.anl.gov/VA/2006-06-20 12:42:46.622021ccn11.mcs.anl.gov/UA/2006-06-20 12:42:07.912326ccn10.mcs.anl.gov.TA-2006-06-20 12:41:35.697255ccn6.mcs.anl.gov0SA12006-06-20 12:41:34.489720ccn118.mcs.anl.gov0RA12006-06-20 12:41:33.301090ccn117.mcs.anl.gov.QA-2006-06-20 12:41:02.478199ccn8.mcs.anl.gov/PA/2006-06-20 12:41:00.527714ccn91.mcs.anl.gov Otj`UJ@5*  ~sh^SH=2'}rg]RG<2({zyxw>vuts9rqp7o6nmlk2jihgfed+cba&`_$^ ]\[ZzYXWVUTSRQPONBMLKJI;HGFEDCB>A@?>9=<;7:698765432+10/$. - I!+5@KValw'2=HS]gq{ !,7BMXcny     d % 4 5 A     = p    - [   C  x         .  H   1  `   . m   0 o    = 4 5 E     A t   1 `   G  |        1 p    > O~sh]RH=2(}rg\RG<2(|qf[QF;0%JzIHGFEtDCBA@?>=<;B:9876;543210/>.-,+9*)('7&6%$#"! +$ zt     B~;}| Ovk`UJ@5+  uj_UJ?4) }rg\QF;0%     q~}|{zyxBwvutsr;qponmlk>jihg9fedc7b6a`_^2]\[ZYXW+VUTSR&QPO$N MLK  =h5i6l9o=0LA/2006-06-20 14:22:46.017385ccn32.mcs.anl.gov0KA/2006-06-20 14:22:45.317999ccn42.mcs.anl.gov1JA12006-06-20 14:22:44.593312ccn189.mcs.anl.gov0IA/2006-06-20 14:22:43.902071ccn48.mcs.anl.gov1HA12006-06-20 14:22:43.227371ccn198.mcs.anl.gov1GA12006-06-20 14:22:42.378222ccn195.mcs.anl.gov1FA12006-06-20 14:22:41.694980ccn197.mcs.anl.gov0EA/2006-06-20 14:22:40.762276cct4m.mcs.anl.gov1DA12006-06-20 14:22:40.062501ccn171.mcs.anl.gov0CA/2006-06-20 14:22:38.929966cct2m.mcs.anl.gov1BA12006-06-20 14:22:37.805791ccsto1.mcs.anl.gov1AA12006-06-20 14:22:37.131190ccn170.mcs.anl.gov1@A12006-06-20 14:22:36.431833ccn175.mcs.anl.gov1?A12006-06-20 14:22:35.757249ccn183.mcs.anl.gov1>A12006-06-20 14:22:35.099317ccn134.mcs.anl.gov1=A12006-06-20 14:22:34.355085ccn102.mcs.anl.gov1<A12006-06-20 14:22:33.221130ccn107.mcs.anl.gov1;A12006-06-20 14:22:31.443286ccn168.mcs.anl.gov0:A/2006-06-20 14:22:30.744177ccn81.mcs.anl.gov ^rE mE$mJ {^ autofsserviceUnknown# stagemasterserviceUnknown tftpd-hpaserviceUnknown % dhcp3-serverserviceUnknown apache2serviceUnknown# netbootmondserviceUnknown'3 systemimager-serverserviceUnknown % bcfg2-serverserviceUnknown famserviceUnknown mdadmserviceUnknown dpkg-devpackageUnknown") kernel-packagepackageUnknown exim4serviceUnknown ! postgresqlpackageUnknown ! libperl5.8packageUnknown% / postgresql-clientpackageUnknown  unzippackageUnknown  rlsserviceUnknown! postgresqlserviceUnknown % mysql-serverpackageUnknown mysqlserviceUnknown updatepackageUnknown") libqthreads-12packageUnknown*9 kernel-image-2.6.11.12packageUnknown mailutilspackageUnknown") guile-1.6-libspackageUnknown#+ libguile-ltdl-1packageUnknown % libreadline5packageUnknown O~si^SH>4) {qg\RG<1&wlaVK@5* hgfe2dcba`_^]\[ZYXWVUT&SRQPONMLKJIHGFEDCBAq@?>=<;:987654B3210/.-,S+9*)('7&%$#"!  0f2e2e2c00A/2006-06-20 14:23:56.630255ccn56.mcs.anl.gov1A12006-06-20 14:23:55.939155ccn105.mcs.anl.gov0A/2006-06-20 14:23:55.148239ccn51.mcs.anl.gov1A12006-06-20 14:23:54.173569ccsto3.mcs.anl.gov1A12006-06-20 14:23:53.490797ccn155.mcs.anl.gov0A/2006-06-20 14:23:52.366404ccn96.mcs.anl.gov0A/2006-06-20 14:23:51.641862ccn58.mcs.anl.gov0A/2006-06-20 14:23:50.950837ccn53.mcs.anl.gov0A/2006-06-20 14:23:50.226205ccn97.mcs.anl.gov1A12006-06-20 14:23:49.493604ccn157.mcs.anl.gov0A/2006-06-20 14:23:48.568751cct5m.mcs.anl.gov0 A/2006-06-20 14:23:47.861305ccn36.mcs.anl.gov1 A12006-06-20 14:23:47.161398ccn177.mcs.anl.gov0 A/2006-06-20 14:23:46.428567ccn54.mcs.anl.gov0 A/2006-06-20 14:23:45.746308ccn59.mcs.anl.gov1 A12006-06-20 14:23:45.021187ccn108.mcs.anl.gov0A/2006-06-20 14:23:44.296637ccn94.mcs.anl.gov1A12006-06-20 14:23:43.588736ccn101.mcs.anl.gov0A/2006-06-20 14:23:42.889238ccn91.mcs.anl.gov Ovk`UK@5* }rg\RG<1'{peZOD:/$76q543210/B.-,+*;)('&%$>#"! 76+z     q~}|{zyBxwvutsrqSp9onml7kji H)4?JU`kv'2=HS^it$/:EP[fq|        ;   4  O  m  )n *p *r 4 5 4 5 k   -  r 4 5 5 R    x    % ]     W    I v   ?  { ( a  !    P ) b   Z    L z   F  * c  G  U#+4<EMV^gox )3=GQ[eoy &/8AJS\enw S E e _ DtDEuEFvFGwGHxHH OIyIJzJK{KL|LL DMNOPQRSS&*S'Z S S& S S S , S q S S  S Y S TUVV WW X X X gY Y Z Z [ [ \ \ ] ]  ^ ^ _ _ ` ` a a b b c c d d &e f f - fT fg g /h h J Pti_TI>3(}rg\RG<1&{pf[PF;1&;>62+~}|&{zyxwvu;ts>rq6po+nmlkjihgfeBdcba`_;^]\[ZY>XWVUT7S6RQPO2NMLKJIHGF+EDCB&A@?z>=<;:98 Vw/e$a VEK =/ Mon Nov 28 06:32:21 2005cleanunknown$Revision: 1.40 $>J =! Thu Jan 5 06:34:22 2006cleanunknown$Revision$##>I =! Thu Jan 5 06:32:59 2006cleanunknown$Revision$##>H =! Thu Jan 5 06:31:12 2006cleanunknown$Revision$##>G =! Thu Jan 5 06:30:57 2006cleanunknown$Revision$##>F =! Thu Jan 5 06:32:59 2006cleanunknown$Revision$##>E =! Thu Jan 5 06:32:06 2006cleanunknown$Revision$##>D =! Thu Jan 5 06:33:56 2006cleanunknown$Revision$##>C =! Thu Jan 5 06:36:17 2006cleanunknown$Revision$##>B =! Thu Jan 5 06:33:17 2006dirtyunknown$Revision$  EA =/ Mon Nov 28 06:29:41 2005cleanunknown$Revision: 1.40 $E@ =/ Mon Oct 24 10:43:57 2005cleanunknown$Revision: 1.37 $  >? =! Thu Jan 5 06:36:45 2006dirtyunknown$Revision$  E> =/ Mon Nov 28 06:30:48 2005cleanunknown$Revision: 1.40 $ G $,4<DLT\dlt} (1:CLU^gpy'*.1278=AEIMO!S#W%['`(d)g*i+m,p-r.v/y0z123456789:;<=>?@ABCEFGHIJKLMNOPQRSTUVWXYZ[\]^ PxndZPF<1&~sh^SH=2'|rh]RH=2'WV$UTSRQPONMLKJI;HGF>E6DCB2A@+?>&=<;:9876543210/;.-,>+*6)(+'&%$#"!   2&      D*5@KValw'2=HS^it$/:EP[fq| n    ) a    P      _   O  B     > q   . ]    a       N  0  #  f E r H u K y M | c     n    2  M   !   "   #   $   8  j T~tj`VLB8.$zpf\RH>4*  vlbXND:0&utsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#" =k9m:o< p=0uA12006-06-20 12:44:43.340871ccn170.mcs.anl.gov0tA12006-06-20 12:44:42.134899ccn190.mcs.anl.gov0sA12006-06-20 12:44:29.224621ccn209.mcs.anl.gov0rA12006-06-20 12:44:28.066897ccn153.mcs.anl.gov0qA12006-06-20 12:44:26.859883ccn152.mcs.anl.gov0pA12006-06-20 12:44:25.686110ccn150.mcs.anl.gov0oA12006-06-20 12:44:23.511466ccn129.mcs.anl.gov0nA12006-06-20 12:44:22.312268ccn143.mcs.anl.gov0mA12006-06-20 12:44:21.063332ccn120.mcs.anl.gov/lA/2006-06-20 12:44:19.880535ccn97.mcs.anl.gov0kA12006-06-20 12:44:18.709069ccn119.mcs.anl.gov0jA12006-06-20 12:44:18.484179ccn249.mcs.anl.gov0iA12006-06-20 12:44:18.240126ccn110.mcs.anl.gov0hA12006-06-20 12:44:16.998962ccn105.mcs.anl.gov0gA12006-06-20 12:44:15.766558ccn180.mcs.anl.gov/fA/2006-06-20 12:44:14.542266ccn99.mcs.anl.gov/eA/2006-06-20 12:44:13.326890ccn87.mcs.anl.gov/dA/2006-06-20 12:44:11.753565ccn85.mcs.anl.gov.cA-2006-06-20 12:43:56.328616ccn3.mcs.anl.gov Pti^SH=2'|rh]RG<1&|rh^TJ@6,! '&%$#"!+ &z     ~}|9{zy7xwv2utsrqpo&n$mlkjihgfedcba`_9^]\7[ZYX *~=z9v5r*EZ =/ Tue Oct 25 02:35:17 2005cleanunknown$Revision: 1.37 $  >Y =! Thu Jan 5 06:42:38 2006cleanunknown$Revision$##>X =! Thu Jan 5 06:40:57 2006cleanunknown$Revision$##>W =! Thu Jan 5 06:33:29 2006cleanunknown$Revision$##>V =! Wed Dec 14 06:25:51 2005cleanunknown$Revision$>U =! Thu Jan 5 06:39:19 2006cleanunknown$Revision$##>T =! Thu Jan 5 06:39:01 2006cleanunknown$Revision$##>S =! Thu Jan 5 06:36:07 2006cleanunknown$Revision$##>R =! Thu Jan 5 06:37:07 2006cleanunknown$Revision$##>Q =! Thu Jan 5 06:36:08 2006cleanunknown$Revision$##>P =! Thu Jan 5 06:36:53 2006cleanunknown$Revision$##>O =! Thu Jan 5 06:36:45 2006cleanunknown$Revision$##>N =! Thu Jan 5 06:36:17 2006cleanunknown$Revision$##>M =! Thu Jan 5 06:30:37 2006cleanunknown$Revision$##>L =! Thu Jan 5 06:30:25 2006dirtyunknown$Revision$  OvlaVK@5* {pf[PE:/$xmcXMB7,! vutsqr qponmlkjihgfeBdcbaS`9_^]7\[ZYXWVUTSRQP ONM$LK J IHGF EDCBAq@ ?>=<;:98765;43210/. ->,+* )6(2 ]~6s2o']Eh =/ Thu Dec 1 06:38:32 2005cleanunknown$Revision: 1.40 $>g =! Thu Jan 5 06:39:43 2006cleanunknown$Revision$##>f =! Thu Jan 5 06:36:04 2006dirtyunknown$Revision$  Ee =/ Thu Dec 1 06:32:25 2005cleanunknown$Revision: 1.40 $>d =! Thu Jan 5 06:31:20 2006cleanunknown$Revision$##>c =! Tue Jan 3 16:26:26 2006cleanunknown$Revision$##>b =! Thu Jan 5 06:40:07 2006cleanunknown$Revision$##>a =! Thu Jan 5 06:35:09 2006cleanunknown$Revision$##>` =! Thu Jan 5 06:36:57 2006cleanunknown$Revision$##>_ =! Thu Jan 5 06:37:01 2006cleanunknown$Revision$##>^ =! Thu Jan 5 06:32:04 2006dirtyunknown$Revision$  E] =/ Thu Dec 1 06:27:42 2005cleanunknown$Revision: 1.40 $>\ =! Tue Jan 3 16:26:27 2006cleanunknown$Revision$##>[ =! Thu Jan 5 06:41:52 2006cleanunknown$Revision$## =g4h5k9p=0A12006-06-20 12:46:46.599619ccn234.mcs.anl.gov0A12006-06-20 12:46:45.408520ccn162.mcs.anl.gov/A/2006-06-20 12:46:44.243154ccn63.mcs.anl.gov/A/2006-06-20 12:46:43.076632ccn44.mcs.anl.gov/A/2006-06-20 12:46:41.877870ccn41.mcs.anl.gov/A/2006-06-20 12:46:40.581098ccn49.mcs.anl.gov/A/2006-06-20 12:46:35.240797ccn61.mcs.anl.gov/A/2006-06-20 12:46:33.875128ccn57.mcs.anl.gov0A12006-06-20 12:46:18.026511ccn214.mcs.anl.gov0A12006-06-20 12:46:01.751815ccn212.mcs.anl.gov0~A12006-06-20 12:46:00.553833ccn194.mcs.anl.gov0}A12006-06-20 12:45:25.041616ccn200.mcs.anl.gov0|A12006-06-20 12:45:23.825660ccn191.mcs.anl.gov0{A12006-06-20 12:45:23.108394ccn183.mcs.anl.gov0zA12006-06-20 12:45:19.444844ccn184.mcs.anl.gov0yA12006-06-20 12:45:18.253458ccn177.mcs.anl.gov0xA12006-06-20 12:45:17.045793ccn172.mcs.anl.gov0wA12006-06-20 12:45:15.814212ccn164.mcs.anl.gov0vA12006-06-20 12:44:44.025010ccn205.mcs.anl.gov 0L\ l09 3! 2006-01-09 13:57:52cleanunknown$Revision$##9 3! 2006-01-05 06:43:02cleanunknown$Revision$##9 3! 2006-01-05 06:42:59cleanunknown$Revision$##9 3! 2006-01-05 06:42:56cleanunknown$Revision$##9 3! 2006-01-05 06:42:55cleanunknown$Revision$##9 3! 2006-01-05 06:42:51cleanunknown$Revision$##9 3! 2006-01-05 06:42:48cleanunknown$Revision$##9 3! 2006-01-05 06:42:47cleanunknown$Revision$##9 3! 2006-01-05 06:42:43cleanunknown$Revision$##9 3! 2006-01-05 06:42:42cleanunknown$Revision$##9 3! 2006-01-05 06:42:38cleanunknown$Revision$##9 3! 2006-01-05 06:42:38cleanunknown$Revision$## @L\ l0|@9n 3! 2006-01-05 06:39:49cleanunknown$Revision$##9m 3! 2006-01-05 06:39:47cleanunknown$Revision$##9l 3! 2006-01-05 06:39:43cleanunknown$Revision$##9k 3! 2006-01-05 06:39:41cleanunknown$Revision$##9j 3! 2006-01-05 06:39:41cleanunknown$Revision$##9i 3! 2006-01-05 06:39:37cleanunknown$Revision$##9h 3! 2006-01-05 06:39:31cleanunknown$Revision$MM9g 3! 2006-01-05 06:39:29cleanunknown$Revision$##9f 3! 2006-01-05 06:39:29cleanunknown$Revision$##9e 3! 2006-01-05 06:39:29cleanunknown$Revision$##9d 3! 2006-01-05 06:39:24cleanunknown$Revision$##9c 3! 2006-01-05 06:39:19cleanunknown$Revision$##9b 3! 2006-01-05 06:39:13cleanunknown$Revision$##9a 3! 2006-01-05 06:39:12cleanunknown$Revision$##9` 3! 2006-01-05 06:39:09cleanunknown$Revision$##9_ 3! 2006-01-05 06:39:03cleanunknown$Revision$## POc(w<P8 3! t2006-01-05 06:33:13cleanunknown$Revision$##8 3! s2006-01-05 06:32:59cleanunknown$Revision$##8 3! r2006-01-05 06:32:59cleanunknown$Revision$668 3! q2006-01-05 06:32:59cleanunknown$Revision$##8 3! p2006-01-05 06:32:59cleanunknown$Revision$##8 3! o2006-01-05 06:32:59cleanunknown$Revision$##8 3! n2006-01-05 06:32:59cleanunknown$Revision$##8 3! m2006-01-05 06:32:59cleanunknown$Revision$8 3! l2006-01-05 06:32:59cleanunknown$Revision$##8 3! k2006-01-05 06:32:56cleanunknown$Revision$##8 3! j2006-01-05 06:32:56cleanunknown$Revision$668 3! i2006-01-05 06:32:06cleanunknown$Revision$8 3! h2006-01-05 06:32:06cleanunknown$Revision$##8 3! g2006-01-05 06:32:06cleanunknown$Revision$##8 3! f2006-01-05 06:32:01cleanunknown$Revision$##8 3! e2006-01-05 06:32:00cleanunknown$Revision$66 F  !,7BMXcny)4?JU`kv&0:EP[fq|    ? r   / ^     5 O l       E  z    2  w    b        b        +  1  3   $ c    5 4 5 = |    9 l   ) W   ?  t     ?  *4>HR\fpz$.8BLV`jt~                                             $ ' ( , 0 3 6 8 = > C E H K O R U W [ Fe#-7AKU_iyoes{#,5>GPYbkt~ 2 2 2 2 V 2 E 2 4 2  2 2 20 2g 2} 2 2 2 2 2  2 k 2 2 e 2 2  2  2 B 2 v 2 2 3a33 ,4b444 5c56d66666\666606R6d6 s6!6"6#6$6%6465 6: 6x 6 6 6 64 6g 6 6 6 6# 6U 6 6 : T~tj`VLB8.$zpf\RH>4*  vlbXND:0&IHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!      ~}|{zyxwv IgI< libg2c0PackageUnknown; libldap2PackageUnknown: libsmapi2PackageUnknown9 libxft2PackageUnknown8 libxmu6PackageUnknown7 whatamiPackageUnknown O~sh^SH=3) |rg\QF<1&xmbXNC8-" 2    +  &$ ~} |z{zyxwtvutsrqponmlkjBihgf;edcba`_^>]\[ZYSX9WVUT6SRQPONML+K JIH$GF :'2=HS^it$/:EP[fq|     C    @   I     K     L      Y     &  k     )  n     1  v  8  ~   #    "  5    '  <    7  q        A  }    D OwlaVK@5+  |qf[PE:/$vlaWLA6+  cba`7_^]\2[ZYXWVUTS&RQPONMLKJIHGFEBDCBA7@?>=<;:9876543210/.-B,+*)(;'&%$#"!> S96 ]w6l+h ]>v =!  Thu Jan 5 06:32:59 2006cleanunknown$Revision$##>u =!  Thu Jan 5 06:34:27 2006cleanunknown$Revision$##>t =!  Thu Jan 5 06:36:07 2006cleanunknown$Revision$##Es =/ Tue Oct 25 16:12:18 2005cleanunknown$Revision: 1.37 $  >r =! Thu Jan 5 06:36:46 2006cleanunknown$Revision$##>q =! Thu Jan 5 06:39:03 2006cleanunknown$Revision$##>p =! Thu Jan 5 06:36:46 2006cleanunknown$Revision$##>o =! Thu Jan 5 06:39:49 2006cleanunknown$Revision$##>n =! Thu Jan 5 06:33:40 2006cleanunknown$Revision$##Em =/ Tue Oct 25 16:09:41 2005cleanunknown$Revision: 1.37 $  >l =! Thu Jan 5 06:33:50 2006cleanunknown$Revision$##>k =! Thu Jan 5 06:34:23 2006dirtyunknown$Revision$  Ej =/ Thu Dec 1 06:33:59 2005cleanunknown$Revision: 1.40 $>i =! Thu Jan 5 06:30:38 2006dirtyunknown$Revision$  ApS)\6f@#dA!9' esound-commonPackageUnknown*89 libstdc++2.10-glibc2.2PackageUnknown7 emacs21PackageUnknown+6; kernel-image-2.4.29-rc1PackageUnknown'53 kernel-image-2.4.27PackageUnknown4! libungif4gPackageUnknown3 mpisyncPackageUnknown$2- chiba-qm-scriptsPackageUnknown1 libaudio2PackageUnknown0! libpcap0.8PackageUnknown/ screenPackageUnknown. g++PackageUnknown- xaw3dgPackageUnknown, mdadmPackageUnknown#++ python-libxslt1PackageUnknown$*- libmysqlclient12PackageUnknown!)' apache2-utilsPackageUnknown(# libgnutls10PackageUnknown"') python-libxml2PackageUnknown& gcc-2.95PackageUnknown% libartsc0PackageUnknown&$1 python2.3-libxslt1PackageUnknown(#5 libfrontier-rpc-perlPackageUnknown" g++-3.3PackageUnknown!! libgcrypt7PackageUnknown! ' python2.3-xmlPackageUnknown$- libaudiofile-devPackageUnknown%/ chiba-mayor-slavePackageUnknown Q~tj`VLB8.$yncXMB7,! uj_TJ?4) /  _  . ~- }, |+ {* z y  x) w( v' ut t& s% r$ q# p" o! n m  l k j9 i h gX f e d c b a ` _ ^ ] \ [ Z Y X  W  V2 U& T S R Q P O N M L K J I H G F E2D&CBA@?>=<;:9876 5 423& Jj8p> vD|J/A/2006-06-20 12:47:11.693018ccn37.mcs.anl.gov/A/2006-06-20 12:47:10.468890ccn56.mcs.anl.gov/A/2006-06-20 12:47:09.256319ccn35.mcs.anl.gov/A/2006-06-20 12:47:09.019987ccn40.mcs.anl.gov/A/2006-06-20 12:47:07.845974ccn43.mcs.anl.gov/A/2006-06-20 12:47:06.612610ccn42.mcs.anl.gov/A/2006-06-20 12:47:05.396781ccn38.mcs.anl.gov/A/2006-06-20 12:47:04.200252ccn33.mcs.anl.gov/A/2006-06-20 12:47:03.956067ccn55.mcs.anl.gov/A/2006-06-20 12:47:02.765395ccn53.mcs.anl.gov/A/2006-06-20 12:47:01.549212ccn50.mcs.anl.gov/A/2006-06-20 12:47:00.340813ccn54.mcs.anl.gov/A/2006-06-20 12:46:59.167155ccn52.mcs.anl.gov/A/2006-06-20 12:46:58.009612ccn51.mcs.anl.gov/ A/2006-06-20 12:46:56.835864ccn64.mcs.anl.gov/ A/2006-06-20 12:46:55.602903ccn62.mcs.anl.gov/ A/2006-06-20 12:46:54.278644ccn60.mcs.anl.gov/ A/2006-06-20 12:46:53.129296ccn59.mcs.anl.gov/ A/2006-06-20 12:46:51.930931ccn58.mcs.anl.gov 1~=z9v5r1> =! Thu Jan 5 06:35:10 2006cleanunknown$Revision$##> =! Thu Jan 5 06:30:27 2006cleanunknown$Revision$##> =! Thu Jan 5 06:36:17 2006cleanunknown$Revision$##> =! Thu Jan 5 06:32:59 2006cleanunknown$Revision$##> =! Thu Jan 5 06:31:05 2006cleanunknown$Revision$##> =! Thu Jan 5 06:29:59 2006cleanunknown$Revision$##> =! Thu Jan 5 06:39:13 2006cleanunknown$Revision$##>~ =! Thu Jan 5 06:36:35 2006cleanunknown$Revision$##>} =! Thu Jan 5 06:36:07 2006cleanunknown$Revision$##>| =! Thu Jan 5 06:33:13 2006cleanunknown$Revision$##>{ =! Thu Jan 5 06:34:31 2006cleanunknown$Revision$##>z =! Thu Jan 5 06:42:43 2006cleanunknown$Revision$##>y =! Thu Jan 5 06:36:07 2006cleanunknown$Revision$##>x =!  Thu Jan 5 06:39:09 2006cleanunknown$Revision$##>w =!  Mon Dec 19 11:36:01 2005cleanunknown$Revision$## J']gq{%/9CMWakuSI?5+! wmcYOE;1'                                     a b e h m p s t v z }                  <GJg D  |b  ]  L~tj`VLB8.$zpf\RH>4*  vlbXND:0&     ~}|{zyxwvutsrqpon m l k j i h g f e d c b a `_^]\[ZYXWVUTSRQPONMLKJ <j8o:n;o<0.A12006-06-20 12:48:20.478944ccn230.mcs.anl.gov0-A12006-06-20 12:48:20.232967ccn228.mcs.anl.gov0,A12006-06-20 12:48:13.764662ccn229.mcs.anl.gov0+A12006-06-20 12:48:03.923430ccn227.mcs.anl.gov0*A12006-06-20 12:47:57.969425ccn226.mcs.anl.gov0)A12006-06-20 12:47:57.717877ccn225.mcs.anl.gov/(A/2006-06-20 12:47:56.569195ccn81.mcs.anl.gov0'A12006-06-20 12:47:56.310764ccn248.mcs.anl.gov0&A12006-06-20 12:47:56.058957ccn246.mcs.anl.gov1%A32006-06-20 12:47:25.886772ccsched.mcs.anl.gov2$A52006-06-20 12:47:25.653578ccfs2-67.mcs.anl.gov0#A12006-06-20 12:47:25.426055ccn255.mcs.anl.gov/"A/2006-06-20 12:47:24.252171ccn34.mcs.anl.gov/!A/2006-06-20 12:47:23.070465ccn39.mcs.anl.gov/ A/2006-06-20 12:47:17.947043ccn45.mcs.anl.gov/A/2006-06-20 12:47:16.777564ccn46.mcs.anl.gov/A/2006-06-20 12:47:15.607874ccn47.mcs.anl.gov/A/2006-06-20 12:47:14.331509ccn48.mcs.anl.gov/A/2006-06-20 12:47:12.851370ccn36.mcs.anl.gov \~=s8n&\> =! ,Mon Dec 19 11:43:35 2005dirtyunknown$Revision$  E =/ ,Thu Dec 1 06:35:33 2005cleanunknown$Revision: 1.40 $> =! +Thu Jan 5 06:29:46 2006dirtyunknown$Revision$  E =/ +Thu Dec 1 06:26:31 2005cleanunknown$Revision: 1.40 $> =! *Thu Jan 5 06:30:32 2006dirtyunknown$Revision$  E =/ *Thu Dec 1 06:35:26 2005cleanunknown$Revision: 1.40 $> =! (Mon Dec 19 11:40:37 2005cleanunknown$Revision$##8 =! %Thu Jan 5 06:34:12 2006dirty $Revision$> =! "Thu Jan 5 06:42:29 2006cleanunknown$Revision$##> =! !Thu Jan 5 06:39:41 2006cleanunknown$Revision$##E =/  Tue Oct 25 16:09:27 2005cleanunknown$Revision: 1.37 $  > =! Thu Jan 5 06:39:47 2006cleanunknown$Revision$##> =! Thu Jan 5 06:30:27 2006cleanunknown$Revision$##> =! Thu Jan 5 06:29:59 2006cleanunknown$Revision$## +ccmw !+3<ENW`ir{     A l  %  S        #  6   +  5  oIsE[!|#%'[5D} _    ( x  VyT6c;|]8zV!5' esound-commonpackageUnknown4! libungif4gpackageUnknown3 mpisyncpackageUnknown2 libaudio2packageUnknown1 xaw3dgpackageUnknown0 mdadmpackageUnknown#/+ python-libxslt1packageUnknown".) python-libxml2packageUnknown- gcc-2.95packageUnknown, libartsc0packageUnknown&+1 python2.3-libxslt1packageUnknown(*5 libfrontier-rpc-perlpackageUnknown!)' python2.3-xmlpackageUnknown$(- libaudiofile-devpackageUnknown%'/ chiba-mayor-slavepackageUnknown &% libzzip-0-12packageUnknown!%' libartsc0-devpackageUnknown$# libesd0-devpackageUnknown #% libaudio-devpackageUnknown" g++-2.95packageUnknown%!/ libstdc++2.10-devpackageUnknown  libesd0packageUnknown") emacs21-commonpackageUnknown!' libxml2-utilspackageUnknown txt2manpackageUnknown % bcfg2-xmlrpcserviceUnknown# mayor_slaveserviceUnknown on~m l~kuji;hg f;ed <~xmbWMB7,! uk`VKA6+  ~?>=<;:9287&6543210/.-2,+&*)('&%$#"!2 &< ; S : 9 8 7   6 5 y E ;     4  3  e  2   $ 1 0 aB"#<+ amihappy-clientpackageUnknown; http-tinypackageUnknown: cpp-2.95packageUnknown'93 libmsyslog-mcs-perlpackageUnknown%8/ python2.3-libxml2packageUnknown!7' libaudiofile0packageUnknown&61 emacs21-bin-commonpackageUnknown -kkt} (1:CLU^gpy@ABCEFGHIJKLMNOPQRSTUVWXYZ[\]^_` abcdefg h#i&j)k.l/m2 ZjF#mN+ kK'Z+6; pvfs2-driver-2.4.29-rc2PackageUnknown5 libxaw7PackageUnknown$4- xemacs21-supportPackageUnknown3 libsm6PackageUnknown2 libice6PackageUnknown1 xfsprogsPackageUnknown!0' libcompfaceg1PackageUnknown/ reportbugPackageUnknown. lynxPackageUnknown- uuid-devPackageUnknown,! rsh-serverPackageUnknown"+) emacsen-commonPackageUnknown*! libdm0-devPackageUnknown) libxext6PackageUnknown (% pvfs2-clientPackageUnknown' ssl-certPackageUnknown!&' apache-commonPackageUnknown% bwmPackageUnknown $% mime-supportPackageUnknown# xinetdPackageUnknown" bcPackageUnknown! libsasl2PackageUnknown % libsmapi-devPackageUnknown!' xemacs21-mulePackageUnknown defomaPackageUnknown&1 ttf-bitstream-veraPackageUnknown") libfontconfig1PackageUnknown(5 xemacs21-basesupportPackageUnknown ,ffox#,5>GPYbkt~` abcdefg"h$i'j+k-l1m4n6o8p=q@rBsFtIuJvMwRxSyXz[{]|_~f i l n r t x { }       9~=z9> =! 5Thu Jan 5 06:39:02 2006cleanunknown$Revision$##> =! 4Thu Jan 5 06:36:35 2006cleanunknown$Revision$##> =! 2Thu Jan 5 06:30:30 2006cleanunknown$Revision$##> =! 1Thu Jan 5 06:32:56 2006cleanunknown$Revision$##> =! 0Thu Jan 5 06:39:29 2006cleanunknown$Revision$##> =! /Thu Jan 5 06:31:14 2006cleanunknown$Revision$##> =! .Thu Jan 5 06:32:01 2006cleanunknown$Revision$## g405A12006-06-20 12:48:27.613736ccn236.mcs.anl.gov04A12006-06-20 12:48:26.350394ccn238.mcs.anl.gov03A12006-06-20 12:48:26.123496ccn235.mcs.anl.gov02A12006-06-20 12:48:25.015717ccn239.mcs.anl.gov01A12006-06-20 12:48:23.898886ccn233.mcs.anl.gov00A12006-06-20 12:48:22.775515ccn232.mcs.anl.gov0/A12006-06-20 12:48:21.642542ccn231.mcs.anl.govbcfg2-1.3.3/examples/report-configuration.xml000066400000000000000000000025501223671746500212400ustar00rootroot00000000000000 bcfg2-1.3.3/gentoo/000077500000000000000000000000001223671746500140115ustar00rootroot00000000000000bcfg2-1.3.3/gentoo/bcfg2-1.3.0.ebuild000066400000000000000000000035701223671746500166240ustar00rootroot00000000000000# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Header: $ EAPI=4 PYTHON_DEPEND="*:2.6" SUPPORT_PYTHON_ABIS="1" # ssl module required. RESTRICT_PYTHON_ABIS="2.5" inherit distutils eutils DESCRIPTION="configuration management tool" HOMEPAGE="http://bcfg2.org" SRC_URI="ftp://ftp.mcs.anl.gov/pub/bcfg/${P}.tar.gz" LICENSE="BSD-2" SLOT="0" KEYWORDS="~amd64 ~x86 ~amd64-linux ~x86-linux ~x64-solaris" IUSE="doc cheetah genshi server" DEPEND="dev-python/setuptools doc? ( dev-python/sphinx )" RDEPEND="app-portage/gentoolkit cheetah? ( dev-python/cheetah ) genshi? ( dev-python/genshi ) server? ( virtual/fam dev-python/lxml dev-python/python-daemon || ( dev-python/pyinotify dev-libs/libgamin[python] ) )" PYTHON_MODNAME="Bcfg2" distutils_src_install_post_hook() { if ! use server; then rm -f "$(distutils_get_intermediate_installation_image)${EPREFIX}/usr/sbin/bcfg2-"* fi } src_compile() { distutils_src_compile if use doc; then einfo "Building Bcfg2 documentation" PYTHONPATH="build-$(PYTHON -f --ABI)" \ sphinx-build doc doc_output || die fi } src_install() { distutils_src_install --record=PY_SERVER_LIBS --install-scripts "${EPREFIX}/usr/sbin" if ! use server; then # Remove files only necessary for a server installation rm -rf "${ED}usr/share/bcfg2" || die rm -rf "${ED}usr/share/man/man8" || die else newinitd "${FILESDIR}/${PN}-server-1.2.0.rc" bcfg2-server fi insinto /etc doins examples/bcfg2.conf if use doc; then # install the sphinx documentation pushd doc_output > /dev/null insinto /usr/share/doc/${PF}/html doins -r [a-z]* _images _static || die "Failed to install documentation" popd > /dev/null fi } pkg_postinst () { distutils_pkg_postinst if use server; then einfo "If this is a new installation, you probably need to run:" einfo " bcfg2-admin init" fi } bcfg2-1.3.3/gentoo/files/000077500000000000000000000000001223671746500151135ustar00rootroot00000000000000bcfg2-1.3.3/gentoo/files/bcfg2-server.rc000066400000000000000000000011431223671746500177270ustar00rootroot00000000000000#!/sbin/runscript # # bcfg2-server - bcfg configuration daemon # depend () { need net } start () { ebegin "Starting bcfg2-server" start-stop-daemon --start --quiet \ --pidfile /var/run/bcfg2-server/bcfg2-server.pid \ --startas /usr/sbin/bcfg2-server -- -D /var/run/bcfg2-server.pid eend $? "Failed to start bcfg2-server" } stop () { ebegin "Stopping bcfg2-server" start-stop-daemon --stop --quiet \ --pidfile /var/run/bcfg2-server/bcfg2-server.pid \ --signal INT eend $? "Failed to stop bcfg2-server" } bcfg2-1.3.3/man/000077500000000000000000000000001223671746500132715ustar00rootroot00000000000000bcfg2-1.3.3/man/bcfg2-admin.8000066400000000000000000000126551223671746500154440ustar00rootroot00000000000000.TH "BCFG2-ADMIN" "8" "March 18, 2013" "1.3" "Bcfg2" .SH NAME bcfg2-admin \- Perform repository administration tasks . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .\" Man page generated from reStructuredText. . .SH SYNOPSIS .sp \fBbcfg2\-admin\fP [\-C \fIconfigfile\fP] \fImode\fP [\fImode args\fP] [\fImode options\fP] .SH DESCRIPTION .sp \fBbcfg2\-admin\fP is used to perform Bcfg2 repository administration. .SH OPTIONS .INDENT 0.0 .TP .BI \-C \ configfile Specify alternate bcfg2.conf location. .TP .BI \-E \ encoding Specify the encoding of config files. .TP .BI \-Q \ path Specify the path to the server repository. .TP .BI \-S \ server Manually specify the server location (as opposed to using the value in bcfg2.conf). This should be in the format "\fI\%https://server:port\fP" .TP .B \-d Enable debugging output. .TP .B \-h Print usage information. .TP .BI \-o \ logfile Writes a log to the specified path. .TP .BI \-\-ssl\-key\fB= key Specify the path to the SSL key. .TP .B \-v Enable verbose output. .TP .BI \-x \ password Use \(aqpassword\(aq for client communication. .UNINDENT .SH MODES .INDENT 0.0 .TP .B backup Create an archive of the entire Bcfg2 repository. .TP .B bundle \fIaction\fP Display details about the available bundles (See BUNDLE OPTIONS below). .TP .B client \fIaction\fP \fIclient\fP [attribute=value] Add, edit, or remove clients entries in metadata (See CLIENT OPTIONS below). .TP .B compare \fIold\fP \fInew\fP Compare two client configurations. Can be used to verify consistent behavior between releases. Determine differences between files or directories (See COMPARE OPTIONS below). .TP .B init Initialize a new repository (interactive). .TP .B minestruct \fIclient\fP [\-f xml\-file] [\-g groups] Build structure entries based on client statistics extra entries (See MINESTRUCT OPTIONS below). .TP .B perf Query server for performance data. .TP .B pull \fIclient\fP \fIentry\-type\fP \fIentry\-name\fP Install configuration information into repo based on client bad entries (See PULL OPTIONS below). .TP .B reports [init|load_stats|purge|scrub|update] Interact with the dynamic reporting system (See REPORTS OPTIONS below). .TP .B snapshots [init|dump|query|reports] Interact with the Snapshots database (See SNAPSHOTS OPTIONS below). .TP .B syncdb Sync the Django ORM with the configured database. .TP .B tidy Remove unused files from repository. .TP .B viz [\-H] [\-b] [\-k] [\-o png\-file] Create a graphviz diagram of client, group and bundle information (See VIZ OPTIONS below). .TP .B xcmd Provides a XML\-RPC Command Interface to the bcfg2\-server. .UNINDENT .SS BUNDLE OPTIONS .INDENT 0.0 .TP .B mode One of the following. .INDENT 7.0 .TP .B \fIlist\-xml\fP List all available xml bundles .TP .B \fIlist\-genshi\fP List all available genshi bundles .TP .B \fIshow\fP Interactive dialog to get details about the available bundles .UNINDENT .UNINDENT .SS CLIENT OPTIONS .INDENT 0.0 .TP .B mode One of the following. .INDENT 7.0 .TP .B \fIadd\fP Add a client .TP .B \fIdel\fP Delete a client .TP .B \fIlist\fP List all client entries .UNINDENT .TP .B client Specify the client\(aqs name. .TP .B attribute=value Set attribute values when adding a new client. Allowed attributes are \(aqprofile\(aq, \(aquuid\(aq, \(aqpassword\(aq, \(aqlocation\(aq, \(aqsecure, and \(aqaddress\(aq. .UNINDENT .SS COMPARE OPTIONS .INDENT 0.0 .TP .B old Specify the location of the old configuration file. .TP .B new Specify the location of the new configuration file. .UNINDENT .SS MINESTRUCT OPTIONS .INDENT 0.0 .TP .B client Client whose metadata is to be searched for extra entries. .TP .B \-g \fIgroups\fP Hierarchy of groups in which to place the extra entries in. .TP .B \-f \fIoutputfile\fP Specify the xml file in which to write the extra entries. .UNINDENT .SS PULL OPTIONS .INDENT 0.0 .TP .B client Specify the name of the client to search for. .TP .B entry type Specify the type of the entry to pull. .TP .B entry name Specify the name of the entry to pull. .UNINDENT .SS REPORTS OPTIONS .INDENT 0.0 .TP .B load_stats [\-s] [\-c] [\-03] Load statistics data. .TP .B purge [\-\-client [n]] [\-\-days [n]] [\-\-expired] Purge historic and expired data. .TP .B scrub Scrub the database for duplicate reasons and orphaned entries. .TP .B update Apply any updates to the reporting database. .UNINDENT .SS SNAPSHOTS OPTIONS .INDENT 0.0 .TP .B init Initialize the snapshots database. .TP .B query Query the snapshots database. .TP .B dump Dump some of the contents of the snapshots database. .TP .B reports [\-a] [\-b] [\-e] [\-\-date=MM\-DD\-YYYY] Generate reports for clients in the snapshots database. .UNINDENT .SS VIZ OPTIONS .INDENT 0.0 .TP .B \-H Include hosts in diagram. .TP .B \-b Include bundles in diagram. .TP .BI \-o \ Write to outfile file instead of stdout. .TP .B \-k Add a shape/color key. .UNINDENT .SH SEE ALSO .sp \fIbcfg2\-info(8)\fP, \fIbcfg2\-server(8)\fP .\" Generated by docutils manpage writer. . bcfg2-1.3.3/man/bcfg2-build-reports.8000066400000000000000000000027161223671746500171440ustar00rootroot00000000000000.TH "BCFG2-BUILD-REPORTS" "8" "March 18, 2013" "1.3" "Bcfg2" .SH NAME bcfg2-build-reports \- Generate state reports for Bcfg2 clients . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .\" Man page generated from reStructuredText. . .SH SYNOPSIS .sp \fBbcfg2\-build\-reports\fP [\fI\-A\fP] [\fI\-c\fP] [\fI\-s\fP] .SH DESCRIPTION .sp \fBbcfg2\-build\-reports\fP is used to build all client state reports. See the Bcfg2 manual for report setup information. .SH OPTIONS .INDENT 0.0 .TP .B \-A Displays all data. .TP .BI \-c \ configfile Specify an alternate report configuration path. The default is \fBrepo/etc/reports\-configuration.xml\fP. .TP .B \-h Print usage information. .TP .BI \-s \ statsfile Use an alternative path for the statistics file. The default is \fBrepo/etc/statistics.xml\fP. .UNINDENT .SH SEE ALSO .sp \fIbcfg2(1)\fP, \fIbcfg2\-server(8)\fP .\" Generated by docutils manpage writer. . bcfg2-1.3.3/man/bcfg2-crypt.8000066400000000000000000000115401223671746500155050ustar00rootroot00000000000000.TH "BCFG2-CRYPT" "8" "March 18, 2013" "1.3" "Bcfg2" .SH NAME bcfg2-crypt \- Bcfg2 encryption and decryption utility . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .\" Man page generated from reStructuredText. . .SH SYNOPSIS .sp \fBbcfg2\-crypt\fP [\-C \fIconfigfile\fP] [\-\-decrypt|\-\-encrypt] [\-\-cfg|\-\-properties] [\-\-stdout] [\-\-remove] [\-\-xpath \fIxpath\fP] [\-p \fIpassphrase\-or\-name\fP] [\-v] [\-I] \fIfilename\fP [\fIfilename\fP...] .SH DESCRIPTION .sp \fBbcfg2\-crypt\fP performs encryption and decryption of Cfg and Properties files. It\(aqs often sufficient to run \fBbcfg2\-crypt\fP with only the name of the file you wish to encrypt or decrypt; it can usually figure out what to do. .SH OPTIONS .INDENT 0.0 .TP .BI \-C \ configfile Specify alternate bcfg2.conf location. .TP .B \-\-decrypt, \-\-encrypt Select encryption or decryption mode for the given file(s). This is usually unnecessary, as \fBbcfg2\-crypt\fP can often determine which is necessary based on the contents of each file. .TP .B \-\-cfg An XML file should be encrypted in its entirety rather than element\-by\-element. This is only necessary if the file is an XML file whose name ends with \fI.xml\fP and whose top\-level tag is \fI\fP. See [MODES] below for details. .TP .B \-\-properties Process a file as an XML Properties file, and encrypt the text of each element separately. This is necessary if, for example, you\(aqve used a different top\-level tag than \fIProperties\fP in your Properties files. See [MODES] below for details. .TP .B \-\-stdout Print the resulting file to stdout instead of writing it to a file. .TP .B \-\-remove Remove the plaintext file after it has been encrypted. Only meaningful for Cfg files. .TP .BI \-\-xpath \ xpath Encrypt the character content of all elements that match the specified XPath expression. The default is \fI*[@encrypted]\fP or \fI*\fP; see [MODES] below for more details. Only meaningful for Properties files. .TP .BI \-p \ passphrase Specify the name of a passphrase specified in the \fI[encryption]\fP section of \fIbcfg2.conf\fP. See [SELECTING PASSPHRASE] below for more details. .TP .B \-v Be verbose. .TP .B \-I When encrypting a Properties file, interactively select the elements whose data should be encrypted. .TP .B \-h Print usage information. .UNINDENT .SH MODES .sp \fBbcfg2\-crypt\fP can encrypt Cfg files or Properties files; they are handled very differently. .INDENT 0.0 .TP .B Cfg When \fBbcfg2\-crypt\fP is used on a Cfg file, the entire file is encrypted. This is the default behavior on files that are not XML, or that are XML but whose top\-level tag is not \fI\fP. This can be enforced by use of the \fI\-\-cfg\fP option. .TP .B Properties When \fBbcfg2\-crypt\fP is used on a Properties file, it encrypts the character content of elements matching the XPath expression given by \fI\-\-xpath\fP. By default the expression is \fI*[@encrypted]\fP, which matches all elements with an \fIencrypted\fP attribute. If you are encrypting a file and that expression doesn\(aqt match any elements, then the default is \fI*\fP, which matches everything. When \fBbcfg2\-crypt\fP encrypts the character content of an element, it also adds the \fIencrypted\fP attribute, set to the name of the passphrase used to encrypt that element. When it decrypts an element it does not remove \fIencrypted\fP, though; this lets you easily and efficiently run \fBbcfg2\-crypt\fP against a single Properties file to encrypt and decrypt it without needing to specify a long list of options. See the online Bcfg2 docs on Properties files for more information on how this works. .UNINDENT .SH SELECTING PASSPHRASE .sp The passphrase used to encrypt or decrypt a file is discovered in the following order. .INDENT 0.0 .IP 1. 3 The passphrase given on the command line using \fI\-p\fP is used. .IP 2. 3 If exactly one passphrase is specified in \fIbcfg2.conf\fP, it will be used. .IP 3. 3 If operating in Properties mode, \fIbcfg2.conf\fP will attempt to read the name of the passphrase from the encrypted elements. .IP 4. 3 If decrypting, all passphrases will be tried sequentially. .IP 5. 3 If no passphrase has been determined at this point, an error is produced and the file being encrypted or decrypted is skipped. .UNINDENT .SH SEE ALSO .sp \fIbcfg2\-server(8)\fP .\" Generated by docutils manpage writer. . bcfg2-1.3.3/man/bcfg2-info.8000066400000000000000000000064441223671746500153060ustar00rootroot00000000000000.TH "BCFG2-INFO" "8" "March 18, 2013" "1.3" "Bcfg2" .SH NAME bcfg2-info \- Creates a local version of the Bcfg2 server core for state observation . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .\" Man page generated from reStructuredText. . .SH SYNOPSIS .sp \fBbcfg2\-info\fP [\-C \fIconfigfile\fP] [\-E \fIencoding\fP] [\-Q \fIrepository path\fP] [\-h] [\-p] [\-x \fIpassword\fP] [\fImode\fP] [\fImode args\fP] [\fImode options\fP] .SH DESCRIPTION .sp \fBbcfg2\-info\fP instantiates an instance of the Bcfg2 core for data examination and debugging purposes. .SH OPTIONS .INDENT 0.0 .TP .BI \-C \ configfile Specify alternate bcfg2.conf location. .TP .BI \-E \ encoding Specify the encoding of config files. .TP .BI \-Q \ path Specify the path to the server repository. .TP .B \-d Enable debugging output. .TP .B \-h Print usage information. .TP .BI \-p \ profile Specify a profile. .TP .BI \-x \ password Use \(aqpassword\(aq for client communication. .UNINDENT .SH MODES .INDENT 0.0 .TP .B build \fIhostname\fP \fIfilename\fP Build config for hostname, writing to filename. .TP .B buildall \fIdirectory\fP Build configs for all clients in directory. .TP .B buildallfile \fIdirectory\fP \fIfilename\fP [\fIhostnames\fP] Build config file for all clients in directory. .TP .B buildbundle \fIfilename\fP \fIhostname\fP Build bundle for hostname (not written to disk). If filename is a bundle template, it is rendered. .TP .B builddir \fIhostname\fP \fIdirname\fP Build config for hostname, writing separate files to dirname. .TP .B buildfile [\-\-altsrc=*altsrc*] \fIfilename\fP \fIhostname\fP Build config file for hostname (not written to disk). .TP .B bundles Print out group/bundle information. .TP .B clients Print out client/profile information. .TP .B config Print out the configuration of the Bcfg2 server. .TP .B debug Shell out to native python interpreter. .TP .B event_debug Display filesystem events as they are processed. .TP .B groups List groups. .TP .B help Print the list of available commands. .TP .B mappings [\fIentry type\fP] [\fIentry name\fP] Print generator mappings for optional type and name. .TP .B packageresolve \fIhostname\fP \fIpackage\fP [\fIpackage\fP...] Resolve the specified set of packages. .TP .B packagesources \fIhostname\fP Show package sources. .TP .B profile \fIcommand\fP \fIargs\fP Profile a single bcfg2\-info command. .TP .B quit Exit bcfg2\-info command line. .TP .B showentries \fIhostname\fP \fItype\fP Show abstract configuration entries for a given host. .TP .B showclient \fIclient1\fP \fIclient2\fP Show metadata for given hosts. .TP .B update Process pending file events. .TP .B version Print version of this tool. .UNINDENT .SH SEE ALSO .sp \fIbcfg2(1)\fP, \fIbcfg2\-server(8)\fP .\" Generated by docutils manpage writer. . bcfg2-1.3.3/man/bcfg2-lint.8000066400000000000000000000112461223671746500153150ustar00rootroot00000000000000.TH "BCFG2-LINT" "8" "March 18, 2013" "1.3" "Bcfg2" .SH NAME bcfg2-lint \- Check Bcfg2 specification for validity, common mistakes, and style . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .\" Man page generated from reStructuredText. . .SH SYNOPSIS .sp \fBbcfg2\-lint\fP [\fIoptions\fP] [\fIplugin\fP [\fIplugin\fP...]] .SH DESCRIPTION .sp \fBbcfg2\-lint\fP checks the Bcfg2 specification for schema validity, common mistakes, and other criteria. It can be quite helpful in finding typos or malformed data. .sp \fBbcfg2\-lint\fP exits with a return value of 2 if errors were found, and 3 if warnings (but no errors) were found. Any other non\-0 exit value denotes some failure in the script itself. .sp \fBbcfg2\-lint\fP is a rewrite of the older bcfg2\-repo\-validate tool. .SH OPTIONS .INDENT 0.0 .TP .BI \-C \ configfile Specify alternate bcfg2.conf location. .TP .BI \-Q \ path Specify the path to the server repository. .TP .B \-v Be verbose. .TP .B \-\-lint\-config Specify path to bcfg2\-lint.conf (default \fB/etc/bcfg2\-lint.conf\fP). .TP .B \-\-stdin Rather than operating on all files in the Bcfg2 specification, only validate a list of files supplied on stdin. This mode is particularly useful in pre\-commit hooks. .sp This makes a few assumptions: .sp Metadata files will only be checked if a valid chain of XIncludes can be followed all the way from clients.xml or groups.xml. Since there are multiple formats of metadata stored in Metadata/ (i.e., clients and groups), there is no way to determine which sort of data a file contains unless there is a valid chain of XIncludes. It may be useful to always specify all metadata files should be checked, even if not all of them have changed. .sp Property files will only be validated if both the property file itself and its matching schema are included on stdin. .UNINDENT .SH PLUGINS .sp In addition to the plugins listed below, Bcfg2 server plugins may have their own \fIbcfg2\-lint\fP functionality, which is enabled automatically when the server plugin is enabled. See \fIbcfg2\-lint.conf(5)\fP for more information on lint plugin configuration. .INDENT 0.0 .TP .B Comments Check the specification for VCS keywords and any comments that are required. By default, this only checks that the \fI$Id$\fP keyword is included and expanded in all files. You may specify VCS keywords to check and comments to be required in the config file. (For instance, you might require that every file have a "Maintainer" comment.) .sp In XML files, only comments are checked for the keywords and comments required. .TP .B Genshi Ensure that all Genshi templates are valid and compile properly. .TP .B GroupNames Ensure that all groups called by name in Metadata, Rules, Bundler, GroupPatterns, and Cfg are valid. .TP .B InfoXML Check that certain attributes are specified in \fIinfo.xml\fP files. By default, requires that \fIowner\fP, \fIgroup\fP, and \fImode\fP are specified. Can also require that an \fIinfo.xml\fP exists for all Cfg files, and that paranoid mode be enabled for all files. .TP .B MergeFiles Suggest that similar probes and config files be merged into single probes or TGenshi templates. .TP .B RequiredAttrs Check that all entries have the appropriate required attributes, and that the attributes are in a valid format. This goes above and beyond the validation offered by an XML schema. .TP .B Validate Validate the Bcfg2 specification against the XML schemas. .sp Property files are freeform XML, but if a \fI.xsd\fP file with a matching filename is provided, then schema validation will be performed on property files individually as well. For instance, if you have a property file named \fIntp.xml\fP then by placing a schema for that file in \fIntp.xsd\fP schema validation will be performed on \fIntp.xml\fP. .UNINDENT .SH BUGS .sp \fBbcfg2\-lint\fP may not handle some deprecated plugins as well as it handles newer ones. For instance, there may be some places where it expects all of your configuration files to be handled by Cfg rather than by a mix of Cfg and TGenshi or TCheetah. .SH SEE ALSO .sp \fIbcfg2(1)\fP, \fIbcfg2\-server(8)\fP, \fIbcfg2\-lint.conf(5)\fP .\" Generated by docutils manpage writer. . bcfg2-1.3.3/man/bcfg2-lint.conf.5000066400000000000000000000113251223671746500162340ustar00rootroot00000000000000.TH "BCFG2-LINT.CONF" "5" "March 18, 2013" "1.3" "Bcfg2" .SH NAME bcfg2-lint.conf \- Configuration parameters for bcfg2-lint . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .\" Man page generated from reStructuredText. . .SH DESCRIPTION .sp bcfg2\-lint.conf includes configuration parameters for bcfg2\-lint. .SH FILE FORMAT .sp The file is INI\-style and consists of sections and options. A section begins with the name of the sections in square brackets and continues until the next section begins. .sp Options are specified in the form "name=value". .sp The file is line\-based each newline\-terminated line represents either a comment, a section name or an option. .sp Any line beginning with a hash (#) is ignored, as are lines containing only whitespace. .sp The file consists of one \fI[lint]\fP section, up to one \fI[errors]\fP section, and then any number of plugin\-specific sections, documented below. (Note that this makes it quite feasible to combine your \fIbcfg2\-lint.conf\fP into your \fIbcfg2.conf(5)\fP file, if you so desire). .SH GLOBAL OPTIONS .sp These options apply to \fIbcfg2\-lint\fP generally, and must be in the \fI[lint]\fP section. .INDENT 0.0 .TP .B plugins A comma\-delimited list of plugins to run. By default, all plugins are run. This can be overridden by listing plugins on the command line. See \fIbcfg2\-lint(8)\fP for a list of the available plugins. .UNINDENT .SH ERROR HANDLING .sp Error handling is configured in the \fI[errors]\fP section. Each option should be the name of an error and one of \fIerror\fP, \fIwarning\fP, or \fIsilent\fP, which tells \fBbcfg2\-lint\fP how to handle the warning. Error names and their defaults can be displayed by running \fBbcfg2\-lint\fP with the \fI\-\-list\-errors\fP option. .SH PLUGIN OPTIONS .sp These options apply only to a single plugin. Each option should be in a section named for its plugin; for instance, options for the InfoXML plugin would be in a section called \fI[InfoXML]\fP. .sp If a plugin is not listed below, then it has no configuration. .sp In many cases, the behavior of a plugin can be configured by modifying how errors from it are handled. See ERROR HANDLING, above. .SS Comments .sp The \fIComments\fP plugin configuration specifies which VCS keywords and comments are required for which file types. The valid types of file are \fIglobal\fP (all file types), \fIbundler\fP (non\-templated bundle files), \fIgenshibundler\fP (templated bundle files), \fIproperties\fP (property files), \fIcfg\fP (non\-templated Cfg files), \fIgenshi\fP or \fIcheetah\fP (templated Cfg files), \fIinfoxml\fP (info.xml files), and \fIprobe\fP (probe files). .sp The specific types (i.e., types other than "global") all supplement global; they do not override it. The exception is if you specify an empty option, e.g.: .INDENT 0.0 .INDENT 3.5 cfg_keywords = .UNINDENT .UNINDENT .sp By default, the \fI$Id$\fP keyword is checked for and nothing else. .sp Multiple keywords or comments should be comma\-delimited. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 \fI_keywords\fP .UNINDENT .UNINDENT .UNINDENT .sp Ensure that files of the specified type have the given VCS keyword. Do \fInot\fP include the dollar signs. I.e.: .INDENT 0.0 .INDENT 3.5 infoxml_keywords = Revision .UNINDENT .UNINDENT .sp \fInot\fP .INDENT 0.0 .INDENT 3.5 infoxml_keywords = $Revision$ .INDENT 0.0 .IP \(bu 2 \fI_comments\fP .UNINDENT .UNINDENT .UNINDENT .sp Ensure that files of the specified type have a comment containing the given string. In XML files, only comments are checked. In plain text files, all lines are checked since comment characters may vary. .SS InfoXML .INDENT 0.0 .TP .B required_attrs A comma\-delimited list of attributes to require on \fI\fP tags. Default is "owner,group,mode". .UNINDENT .SS MergeFiles .INDENT 0.0 .TP .B threshold The threshold at which MergeFiles will suggest merging config files and probes. Default is 75% similar. .UNINDENT .SS Validate .INDENT 0.0 .TP .B schema The full path to the XML Schema files. Default is \fB/usr/share/bcfg2/schema\fP. This can be overridden with the \fI\-\-schema\fP command\-line option. .UNINDENT .SH SEE ALSO .sp \fIbcfg2\-lint(8)\fP .\" Generated by docutils manpage writer. . bcfg2-1.3.3/man/bcfg2-report-collector.8000066400000000000000000000033071223671746500176450ustar00rootroot00000000000000.TH "BCFG2-REPORT-COLLECTOR" "8" "July 27, 2013" "1.3" "Bcfg2" .SH NAME bcfg2-report-collector \- Reports collection daemon . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .\" Man page generated from reStructuredText. . .SH SYNOPSIS .sp \fBbcfg2\-report\-collector\fP [\fIoptions\fP] .SH DESCRIPTION .sp \fBbcfg2\-report\-collector\fP runs a daemon to collect logs from the LocalFilesystem \fIBcfg2 Reports\fP transport object and add them to the Reporting storage backend. .SH OPTIONS .INDENT 0.0 .TP .BI \-C \ configfile Specify alternate bcfg2.conf location. .TP .BI \-D \ pidfile Daemonize, placing the program pid in \fIpidfile\fP. .TP .BI \-E \ encoding Specify the encoding of config files. .TP .BI \-Q \ path Specify the path to the server repository. .TP .BI \-W \ configfile Specify the path to the web interface configuration file. .TP .B \-d Enable debugging output. .TP .B \-h Print usage information. .TP .BI \-o \ path Set path of file log .TP .B \-v Run in verbose mode. .TP .B \-\-version Print the version and exit .UNINDENT .SH SEE ALSO .sp \fIbcfg2\-server(8)\fP, \fIbcfg2\-reports(8)\fP .\" Generated by docutils manpage writer. . bcfg2-1.3.3/man/bcfg2-reports.8000066400000000000000000000073631223671746500160520ustar00rootroot00000000000000.TH "BCFG2-REPORTS" "8" "March 18, 2013" "1.3" "Bcfg2" .SH NAME bcfg2-reports \- Query reporting system for client status . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .\" Man page generated from reStructuredText. . .SH SYNOPSIS .sp \fBbcfg2\-reports\fP [\-a] [\-b \fINAME\fP] [\-c] [\-d] [\-e \fINAME\fP] [\-h] [\-m \fINAME\fP] [\-s \fINAME\fP] [\-x \fINAME\fP] [\-\-badentry=\fIKIND,NAME\fP] [\-\-extraentry=\fIKIND,NAME\fP] [\-\-fields=\fIARG1,ARG2,...\fP] [\-\-modifiedentry=\fIKIND,NAME\fP] [\-\-sort=\fIARG1,ARG2,...\fP] [\-\-stale] [\-v] .SH DESCRIPTION .sp \fBbcfg2\-reports\fP allows you to retrieve data from the database about clients, and the states of their current interactions. It also allows you to change the expired/unexpired states. The utility runs as a standalone application. It does, however, use the models from \fBsrc/lib/Bcfg2/Reporting/models.py\fP. .SH OPTIONS .INDENT 0.0 .TP .B \-h Print usage information. .UNINDENT .SH MODES .sp The following are various modes available for \fBbcfg2\-reports\fP. .SS Single\-Host Modes .INDENT 0.0 .TP .BI \-b, \-\-bad \ hostname Shows bad entries from the current interaction of \fIhostname\fP. .TP .BI \-e, \-\-extra \ hostname Shows extra entries from the current interaction of \fIhostname\fP. .TP .BI \-m, \-\-modified \ hostname Shows modified entries from the current interaction of \fIhostname\fP. .TP .BI \-s, \-\-show \ hostname Shows bad, modified, and extra entries from the current interaction of \fIhostname\fP. .TP .BI \-t, \-\-total \ hostname Shows total number of managed and good entries from the current interaction of \fIhostname\fP. .TP .BI \-x, \-\-expire \ hostname Toggles expired/unexpired state of \fIhostname\fP. .TP .B \-a, \-\-all Show all hosts, including expired hosts. .UNINDENT .SS Host Selection Modes .INDENT 0.0 .TP .B \-a, \-\-all Show all hosts, including expired hosts. .TP .B \-c, \-\-clean Show only clean hosts. .TP .B \-d, \-\-dirty Show only dirty hosts. .TP .B \-\-stale Show hosts that haven\(aqt run in the last 24 hours. .UNINDENT .SS Entry Modes .sp The following mode flags require either a comma\-delimited list of any number of \fI:\fP arguments describing entries, or the \fI\-\-file\fP option. .INDENT 0.0 .TP .BI \-\-badentry\fB= entrylist Shows only hosts whose current interaction has bad entries matching the given entry or entries. .TP .BI \-\-extraentry\fB= entrylist Shows only hosts whose current interaction has extra entries matching the given entry or entries. .TP .BI \-\-entrystatus\fB= entry Shows the status of the single entry (given by \fI:\fP) on all hosts. .TP .BI \-\-modifiedentry\fB= entrylist Shows only hosts whose current interaction has modified entries matching the given entry or entries. .UNINDENT .SS Entry Options .sp The following options can be used with the above Entry Modes. .INDENT 0.0 .TP .BI \-\-fields\fB= fields Only display the listed fields. Takes a comma\-delimited list of field names .TP .BI \-\-file\fB= file Read \fI:\fP pairs from the specified file instead of the command line. .UNINDENT .SH SEE ALSO .sp \fIbcfg2(1)\fP, \fIbcfg2\-server(8)\fP .\" Generated by docutils manpage writer. . bcfg2-1.3.3/man/bcfg2-server.8000066400000000000000000000035611223671746500156560ustar00rootroot00000000000000.TH "BCFG2-SERVER" "8" "July 27, 2013" "1.3" "Bcfg2" .SH NAME bcfg2-server \- Server for client configuration specifications . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .\" Man page generated from reStructuredText. . .SH SYNOPSIS .sp \fBbcfg2\-server\fP [\-d] [\-v] [\-C \fIconfigfile\fP] [\-D \fIpidfile\fP] [\-E \fIencoding\fP] [\-Q \fIrepo path\fP] [\-S \fIserver url\fP] [\-o \fIlogfile\fP] [\-x \fIpassword\fP] [\-\-ssl\-key=\fIssl key\fP] .SH DESCRIPTION .sp \fBbcfg2\-server\fP is the daemon component of Bcfg2 which serves configurations to clients based on the data in its repository. .SH OPTIONS .INDENT 0.0 .TP .BI \-C \ configfile Specify alternate bcfg2.conf location. .TP .BI \-D \ pidfile Daemonize, placing the program pid in \fIpidfile\fP. .TP .BI \-E \ encoding Specify the encoding of config files. .TP .BI \-Q \ path Specify the path to the server repository. .TP .BI \-S \ server Manually specify the server location (as opposed to using the value in bcfg2.conf). This should be in the format "\fI\%https://server:port\fP" .TP .B \-d Enable debugging output. .TP .B \-v Run in verbose mode. .TP .B \-h Print usage information. .TP .BI \-\-ssl\-key\fB= key Specify the path to the SSL key. .UNINDENT .SH SEE ALSO .sp \fIbcfg2(1)\fP, \fIbcfg2\-lint(8)\fP .\" Generated by docutils manpage writer. . bcfg2-1.3.3/man/bcfg2.1000066400000000000000000000126631223671746500143460ustar00rootroot00000000000000.TH "BCFG2" "1" "March 18, 2013" "1.3" "Bcfg2" .SH NAME bcfg2 \- Bcfg2 client tool . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .\" Man page generated from reStructuredText. . .SH SYNOPSIS .sp \fBbcfg2\fP [\fIoptions\fP] .SH DESCRIPTION .sp \fBbcfg2\fP runs the Bcfg2 configuration process on the current host. This process consists of the following steps. .INDENT 0.0 .IP \(bu 2 Fetch and execute probes .IP \(bu 2 Upload probe results .IP \(bu 2 Fetch the client configuration .IP \(bu 2 Check the current client state .IP \(bu 2 Attempt to install the desired configuration .IP \(bu 2 Upload statistics about the Bcfg2 execution and client state .UNINDENT .SH OPTIONS .INDENT 0.0 .TP .B \-B Configure everything except the given bundle(s). .TP .BI \-C \ configfile Specify alternate bcfg2.conf location. .TP .BI \-D \ drivers Specify a comma\-delimited set of Bcfg2 tool drivers. \fINOTE: only drivers listed will be loaded. (e.g., if you do not include POSIX, you will be unable to verify/install Path entries).\fP .TP .BI \-E \ encoding Specify the encoding of config files. .TP .B \-I Run bcfg2 in interactive mode. The user will be prompted before each change. .TP .B \-O Omit lock check. .TP .B \-P Run bcfg2 in paranoid mode. Diffs will be logged for configuration files marked as paranoid by the Bcfg2 server. .TP .B \-Q Run bcfg2 in "bundle quick" mode, where only entries in a bundle are verified or installed. This runs much faster than \-q, but doesn\(aqt provide statistics to the server at all. In order for this option to work, the \-b option must also be provided. This option is incompatible with \-r. .TP .BI \-R \ retrycount Specify the number of times that the client will attempt to retry network communication. .TP .BI \-S \ server Manually specify the server location (as opposed to using the value in bcfg2.conf). This should be in the format "\fI\%https://server:port\fP" .TP .B \-Z Do not configure independent entries. .TP .BI \-b \ bundles Run only the specified colon\-delimited set of bundles. .TP .BI \-c \ cachefile Cache a copy of the configuration in cachefile. .TP .BI \-\-ca\-cert\fB= cacert Specifiy the path to the SSL CA certificate. .TP .B \-d Enable debugging output. .TP .B \-e When in verbose mode, display extra entry information. .TP .BI \-f \ path Configure from a file rather than querying the server. .TP .B \-h Print usage information. .TP .B \-k Run in bulletproof mode. This currently only affects behavior in the debian toolset; it calls apt\-get update and clean and dpkg \-\-configure \-\-pending. .TP .BI \-l \ decisionmode Run the client in the specified decision list mode ("whitelist" or "blacklist"), or "none", which can be used in order to override the decision list mode specified in bcfg2.conf). This approach is needed when particular changes are deemed "high risk". It gives the ability tocentrally specify these changes, but only install them on clients when administrator supervision is available. Because collaborative configuration is one of the remaining hard issues in configuration management, these issues typically crop up in environments with several administrators and much configuration variety. (This setting will be ignored if the \-f option is also specified). .TP .B \-n Run bcfg2 in dry\-run mode. No changes will be made to the system. .TP .BI \-o \ logfile Writes a log to the specified path. .TP .BI \-p \ profile Assert a profile for the current client. .TP .B \-q Run bcfg2 in quick mode. Package checksum verification won\(aqt be performed. This mode relaxes the constraints of correctness, and thus should only be used in safe conditions. .TP .BI \-r \ mode Cause bcfg2 to remove extra configuration elements it detects. Mode is one of "all", "Services", "Packages", or "Users". "all" removes all extra entries. "Services", "Packages", and "Users" remove only the extra configuration elements of the respective type. ("Services" actually just disables extra services, since they can\(aqt be removed, and "Users" removes extra POSIXUser and POSIXUser entries.) .TP .BI \-s \ servicemode Set bcfg2 interaction level for services. Default behavior is to modify all services affected by reconfiguration. "build" mode attempts to stop all services started. "disabled" suppresses all attempts to modify services. .TP .BI \-\-ssl\-cert\fB= cert Specify the path to the SSL certificate. .TP .BI \-\-ssl\-cns\fB= CNs Colon\-delimited list of acceptable SSL server Common Names. .TP .BI \-\-ssl\-key\fB= key Specify the path to the SSL key. .TP .BI \-u \ user Attempt to authenticate as \(aquser\(aq. .TP .BI \-t \ timeout Set the timeout (in seconds) for client communication. Default is 90 seconds. .TP .B \-v Run bcfg2 in verbose mode. .TP .BI \-x \ password Use \(aqpassword\(aq for client communication. .TP .B \-z Only configure independent entries, ignore bundles. .UNINDENT .SH SEE ALSO .sp \fIbcfg2\-server(8)\fP, \fIbcfg2\-info(8)\fP .\" Generated by docutils manpage writer. . bcfg2-1.3.3/man/bcfg2.conf.5000066400000000000000000000532511223671746500152740ustar00rootroot00000000000000.TH "BCFG2.CONF" "5" "July 19, 2013" "1.3" "Bcfg2" .SH NAME bcfg2.conf \- Configuration parameters for Bcfg2 . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .\" Man page generated from reStructuredText. . .SH DESCRIPTION .sp bcfg2.conf includes configuration parameters for the Bcfg2 server and client. .SH FILE FORMAT .sp The file is INI\-style and consists of sections and options. A section begins with the name of the sections in square brackets and continues until the next section begins. .sp Options are specified in the form "name=value". .sp The file is line\-based each newline\-terminated line represents either a comment, a section name or an option. .sp Any line beginning with a hash (#) is ignored, as are lines containing only whitespace. .SH SERVER OPTIONS .sp These options are only necessary on the Bcfg2 server. They are specified in the \fB[server]\fP section of the configuration file. .INDENT 0.0 .TP .B repository Specifies the path to the Bcfg2 repository containing all of the configuration specifications. The repository should be created using the \fIbcfg2\-admin init\fP command. .TP .B filemonitor The file monitor used to watch for changes in the repository. The default is the best available monitor. The following values are valid: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C inotify gamin fam pseudo .ft P .fi .UNINDENT .UNINDENT .TP .B fam_blocking . Whether the server should block at startup until the file monitor backend has processed all events. This can cause a slower startup, but ensure that all files are recognized before the first client is handled. .TP .B ignore_files A comma\-separated list of globs that should be ignored by the file monitor. Default values are: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C *~ *# #* *.swp *.swpx *.swx SCCS \&.svn 4913 \&.gitignore .ft P .fi .UNINDENT .UNINDENT .TP .B listen_all This setting tells the server to listen on all available interfaces. The default is to only listen on those interfaces specified by the bcfg2 setting in the components section of \fBbcfg2.conf\fP. .TP .B plugins A comma\-delimited list of enabled server plugins. Currently available plugins are: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C Account Base Bundler Bzr Cfg Cvs Darcs DBStats Decisions Deps Editor FileProbes Fossil Git GroupPatterns Guppy Hg Hostbase Ldap Metadata NagiosGen Ohai Packages Pkgmgr POSIXCompat Probes Properties PuppetENC Reporting Rules SEModules ServiceCompat Snapshots SSHbase SSLCA Statistics Svn TCheetah TemplateHelper TGenshi Trigger .ft P .fi .UNINDENT .UNINDENT .sp Descriptions of each plugin can be found in their respective sections below. .TP .B prefix Specifies a prefix if the Bcfg2 installation isn\(aqt placed in the default location (e.g. \fB/usr/local\fP). .TP .B backend Specifies which server core backend to use. Current available options are: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C cherrypy builtin best .ft P .fi .UNINDENT .UNINDENT .sp The default is \fIbest\fP, which is currently an alias for \fIbuiltin\fP. More details on the backends can be found in the official documentation. .TP .B user The username or UID to run the daemon as. Default is \fI0\fP. .TP .B group The group name or GID to run the daemon as. Default is \fI0\fP. .TP .B vcs_root Specifies the path to the root of the VCS working copy that holds your Bcfg2 specification, if it is different from \fIrepository\fP. E.g., if the VCS repository does not hold the bcfg2 data at the top level, you may need to set this option. .TP .B umask The umask to set for the server. Default is \fI0077\fP. .UNINDENT .SH SERVER PLUGINS .sp This section has a listing of all the plugins currently provided with Bcfg2. .SS Account Plugin .sp The account plugin manages authentication data, including the following. .INDENT 0.0 .IP \(bu 2 \fB/etc/passwd\fP .IP \(bu 2 \fB/etc/group\fP .IP \(bu 2 \fB/etc/security/limits.conf\fP .IP \(bu 2 \fB/etc/sudoers\fP .IP \(bu 2 \fB/root/.ssh/authorized_keys\fP .UNINDENT .SS Base Plugin .sp The Base plugin is a structure plugin that provides the ability to add lists of unrelated entries into client configuration entry inventories. Base works much like Bundler in its file format. This structure plugin is good for the pile of independent configs needed for most actual systems. .SS Bundler Plugin .sp The Bundler plugin is used to describe groups of inter\-dependent configuration entries, such as the combination of packages, configuration files, and service activations that comprise typical Unix daemons. Bundles are used to add groups of configuration entries to the inventory of client configurations, as opposed to describing particular versions of those entries. .SS Bzr Plugin .sp The Bzr plugin allows you to track changes to your Bcfg2 repository using a GNU Bazaar version control backend. Currently, it enables you to get revision information out of your repository for reporting purposes. .SS Cfg Plugin .sp The Cfg plugin provides a repository to describe configuration file contents for clients. In its simplest form, the Cfg repository is just a directory tree modeled off of the directory tree on your client machines. .SS Cvs Plugin (experimental) .sp The Cvs plugin allows you to track changes to your Bcfg2 repository using a Concurrent version control backend. Currently, it enables you to get revision information out of your repository for reporting purposes. .SS Darcs Plugin (experimental) .sp The Darcs plugin allows you to track changes to your Bcfg2 repository using a Darcs version control backend. Currently, it enables you to get revision information out of your repository for reporting purposes. .SS DBStats Plugin .sp Direct to database statistics plugin. .SS Decisions Plugin .sp The Decisions plugin has support for a centralized set of per\-entry installation decisions. This approach is needed when particular changes are deemed "\fIhigh risk\fP"; this gives the ability to centrally specify these changes, but only install them on clients when administrator supervision is available. .SS Defaults Plugin .sp The Defaults plugin can be used to populate default attributes for entries. Defaults is \fInot\fP a Generator plugin, so it does not actually bind an entry; Defaults are applied after an entry has been bound, and only populate attributes that are not yet set. .SS Deps Plugin .sp The Deps plugin allows you to make a series of assertions like "Package X requires Package Y (and optionally also Package Z etc.)" .SS Editor Plugin .sp The Editor plugin attempts to allow you to partially manage configuration for a file. Its use is not recommended and not well documented. .SS FileProbes Plugin .sp The FileProbes plugin allows you to probe a client for a file, which is then added to the Cfg specification. If the file changes on the client, FileProbes can either update it in the specification or allow Cfg to replace it. .SS Fossil Plugin .sp The Fossil plugin allows you to track changes to your Bcfg2 repository using a Fossil SCM version control backend. Currently, it enables you to get revision information out of your repository for reporting purposes. .SS Git Plugin .sp The Git plugin allows you to track changes to your Bcfg2 repository using a Git version control backend. Currently, it enables you to get revision information out of your repository for reporting purposes. .SS GroupPatterns Plugin .sp The GroupPatterns plugin is a connector that can assign clients group membership based on patterns in client hostnames. .SS Guppy Plugin .sp The Guppy plugin is used to trace memory leaks within the bcfg2\-server process using Guppy. .SS Hg Plugin (experimental) .sp The Hg plugin allows you to track changes to your Bcfg2 repository using a Mercurial version control backend. Currently, it enables you to get revision information out of your repository for reporting purposes. .SS Hostbase Plugin .sp The Hostbase plugin is an IP management system built on top of Bcfg2. .SS Ldap Plugin .sp The Ldap plugin makes it possible to fetch data from an LDAP directory, process it and attach it to your metadata. .SS Metadata Plugin .sp The Metadata plugin is the primary method of specifying Bcfg2 server metadata. .SS NagiosGen Plugin .sp The NagiosGen plugin dynamically generates Nagios configuration files based on Bcfg2 data. .SS Ohai Plugin (experimental) .sp The Ohai plugin is used to detect information about the client operating system. The data is reported back to the server using JSON. .SS Packages Plugin .sp The Packages plugin is an alternative to Pkgmgr for specifying package entries for clients. Where Pkgmgr explicitly specifies package entry information, Packages delegates control of package version information to the underlying package manager, installing the latest version available from through those channels. .SS Pkgmgr Plugin .sp The Pkgmgr plugin resolves the Abstract Configuration Entity "Package" to a package specification that the client can use to detect, verify and install the specified package. .SS POSIXCompat Plugin .sp The POSIXCompat plugin provides a compatibility layer for 1.3 POSIX Entries so that they are compatible with older clients. .SS Probes Plugin .sp The Probes plugin gives you the ability to gather information from a client machine before you generate its configuration. This information can be used with the various templating systems to generate configuration based on the results. .SS Properties Plugin .sp The Properties plugin is a connector plugin that adds information from properties files into client metadata instances. .SS PuppetENC Plugin .sp The PuppetENC plugin is a connector plugin that adds support for Puppet External Node Classifiers. .SS Reporting Plugin .sp The Reporting plugin enables the collection of data for use with Bcfg2\(aqs dynamic reporting system. .SS Rules Plugin .sp The Rules plugin provides literal configuration entries that resolve the abstract configuration entries normally found in the Bundler and Base plugins. The literal entries in Rules are suitable for consumption by the appropriate client drivers. .SS SEModules Plugin .sp The SEModules plugin provides a way to distribute SELinux modules via Bcfg2. .SS ServiceCompat Plugin .sp The ServiceCompat plugin converts service entries for older clients. .SS Snapshots Plugin .sp The Snapshots plugin stores various aspects of a client’s state when the client checks in to the server. .SS SSHbase Plugin .sp The SSHbase generator plugin manages ssh host keys (both v1 and v2) for hosts. It also manages the ssh_known_hosts file. It can integrate host keys from other management domains and similarly export its keys. .SS SSLCA Plugin .sp The SSLCA plugin is designed to handle creation of SSL privatekeys and certificates on request. .SS Statistics .sp The Statistics plugin is deprecated (see Reporting). .SS Svn Plugin .sp The Svn plugin allows you to track changes to your Bcfg2 repository using a Subversion backend. Currently, it enables you to get revision information out of your repository for reporting purposes. .SS TCheetah Plugin .sp The TCheetah plugin allows you to use the cheetah templating system to create files. It also allows you to include the results of probes executed on the client in the created files. .SS TGenshi Plugin .sp The TGenshi plugin allows you to use the Genshi templating system to create files. It also allows you to include the results of probes executed on the client in the created files. .SS Trigger Plugin .sp The Trigger plugin provides a method for calling external scripts when clients are configured. .SH CACHING OPTIONS .sp These options are specified in the \fB[caching]\fP section. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B client_metadata The following four caching modes are available for client metadata: .INDENT 7.0 .IP \(bu 2 off: No caching of client metadata objects is performed. This is the default. .IP \(bu 2 initial: Only initial metadata objects are cached. Initial metadata objects are created only from the data in the Metadata plugin, before additional groups from other plugins are merged in. .IP \(bu 2 cautious: Final metadata objects are cached, but each client’s cache is cleared at the start of each client run, immediately after probe data is received. Cache is also cleared as in aggressive mode. \fIon\fP is a synonym for cautious. .IP \(bu 2 aggressive: Final metadata objects are cached. Each plugin is responsible for clearing cache when appropriate. .UNINDENT .UNINDENT .UNINDENT .UNINDENT .SH CLIENT OPTIONS .sp These options only affect client functionality. They can be specified in the \fB[client]\fP section. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B decision Specify the server decision list mode (whitelist or blacklist). (This settiing will be ignored if the client is called with the \-f option). .TP .B drivers Specify tool driver set to use. This option can be used to explicitly specify the client tool drivers you want to use when the client is run. .TP .B paranoid Run the client in paranoid mode. .TP .B profile Assert the given profile for the host. .UNINDENT .UNINDENT .UNINDENT .SH COMMUNICATION OPTIONS .sp Specified in the \fB[communication]\fP section. These options define settings used for client\-server communication. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B ca The path to a file containing the CA certificate. This file is required on the server, and optional on clients. However, if the cacert is not present on clients, the server cannot be verified. .TP .B certificate The path to a file containing a PEM formatted certificate which signs the key with the ca certificate. This setting is required on the server in all cases, and required on clients if using client certificates. .TP .B key Specifies the path to a file containing the SSL Key. This is required on the server in all cases, and required on clients if using client certificates. .TP .B password Required on both the server and clients. On the server, sets the password clients need to use to communicate. On a client, sets the password to use to connect to the server. .TP .B protocol Communication protocol to use. Defaults to xmlrpc/ssl. .TP .B retries A client\-only option. Number of times to retry network communication. Default is 3 retries. .TP .B retry_delay A client\-only option. Number of seconds to wait in between retrying network communication. Default is 1 second. .TP .B serverCommonNames A client\-only option. A colon\-separated list of Common Names the client will accept in the SSL certificate presented by the server. .TP .B timeout A client\-only option. The network communication timeout. .TP .B user A client\-only option. The UUID of the client. .UNINDENT .UNINDENT .UNINDENT .SH COMPONENT OPTIONS .sp Specified in the \fB[components]\fP section. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B bcfg2 URL of the server. On the server this specifies which interface and port the server listens on. On the client, this specifies where the client will attempt to contact the server. .sp e.g. \fIbcfg2 = https://10.3.1.6:6789\fP .TP .B encoding Text encoding of configuration files. Defaults to UTF\-8. .TP .B lockfile The path to the client lock file, which is used to ensure that only one Bcfg2 client runs at a time on a single client. .UNINDENT .UNINDENT .UNINDENT .SH LOGGING OPTIONS .sp Specified in the \fB[logging]\fP section. These options control the server logging functionality. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B debug Whether or not to enable debug\-level log output. Default is false. .TP .B path Server log file path. .TP .B syslog Whether or not to send logging data to syslog. Default is true. .TP .B verbose Whether or not to enable verbose log output. Default is false. .UNINDENT .UNINDENT .UNINDENT .SH MDATA OPTIONS .sp Specified in the \fB[mdata]\fP section. These options affect the default metadata settings for Paths with type=\(aqfile\(aq. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B owner Global owner for Paths (defaults to root) .TP .B group Global group for Paths (defaults to root) .TP .B mode Global permissions for Paths (defaults to 644) .TP .B secontext Global SELinux context for Path entries (defaults to \fI__default__\fP, which restores the expected context) .TP .B paranoid Global paranoid settings for Paths (defaults to false) .TP .B sensitive Global sensitive settings for Paths (defaults to false) .TP .B important Global important settings for Paths. Defaults to false. .UNINDENT .UNINDENT .UNINDENT .SH PACKAGES OPTIONS .sp The following options are specified in the \fB[packages]\fP section. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B resolver Enable dependency resolution. Default is 1 (true). .TP .B metadata Enable metadata processing. Default is 1 (true). If metadata is disabled, it’s implied that resolver is also disabled. .TP .B yum_config The path at which to generate Yum configs. No default. .TP .B apt_config The path at which to generate APT configs. No default. .TP .B gpg_keypath The path on the client where RPM GPG keys will be copied before they are imported on the client. Default is \fB/etc/pki/rpm\-gpg\fP. .TP .B version Set the version attribute used when binding Packages. Default is auto. .UNINDENT .UNINDENT .UNINDENT .sp The following options are specified in the \fB[packages:yum]\fP section. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B use_yum_libraries By default, Bcfg2 uses an internal implementation of Yum’s dependency resolution and other routines so that the Bcfg2 server can be run on a host that does not support Yum itself. If you run the Bcfg2 server on a machine that does have Yum libraries, however, you can enable use of those native libraries in Bcfg2 by setting this to 1. .TP .B helper Path to bcfg2\-yum\-helper. By default, Bcfg2 looks first in $PATH and then in \fB/usr/sbin/bcfg2\-yum\-helper\fP for the helper. .UNINDENT .UNINDENT .UNINDENT .sp The following options are specified in the \fB[packages:pulp]\fP section. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B username The username of a Pulp user that will be used to register new clients and bind them to repositories. .TP .B password The password of a Pulp user that will be used to register new clients and bind them to repositories. .UNINDENT .UNINDENT .UNINDENT .sp All other options in the \fB[packages:yum]\fP section will be passed along verbatim to the Yum configuration if you are using the native Yum library support. .SH PARANOID OPTIONS .sp These options allow for finer\-grained control of the paranoid mode on the Bcfg2 client. They are specified in the \fB[paranoid]\fP section of the configuration file. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B path Custom path for backups created in paranoid mode. The default is in \fB/var/cache/bcfg2\fP. .TP .B max_copies Specify a maximum number of copies for the server to keep when running in paranoid mode. Only the most recent versions of these copies will be kept. .UNINDENT .UNINDENT .UNINDENT .SH SNAPSHOTS OPTIONS .sp Specified in the \fB[snapshots]\fP section. These options control the server snapshots functionality. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B driver sqlite .TP .B database The name of the database to use for statistics data. .sp e.g.: \fB$REPOSITORY_DIR/etc/bcfg2.sqlite\fP .UNINDENT .UNINDENT .UNINDENT .SH SSLCA OPTIONS .sp These options are necessary to configure the SSLCA plugin and can be found in the \fB[sslca_default]\fP section of the configuration file. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B config Specifies the location of the openssl configuration file for your CA. .TP .B passphrase Specifies the passphrase for the CA’s private key (if necessary). If no passphrase exists, it is assumed that the private key is stored unencrypted. .TP .B chaincert Specifies the location of your ssl chaining certificate. This is used when pre\-existing certifcate hostfiles are found, so that they can be validated and only regenerated if they no longer meet the specification. If you’re using a self signing CA this would be the CA cert that you generated. .UNINDENT .UNINDENT .UNINDENT .SH DATABASE OPTIONS .sp Server\-only, specified in the \fB[database]\fP section. These options control the database connection of the server. .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B engine The database engine used by the statistics module. One of the following: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C postgresql mysql sqlite3 ado_mssql .ft P .fi .UNINDENT .UNINDENT .TP .B name The name of the database to use for statistics data. If \(aqdatabase_engine\(aq is set to \(aqsqlite3\(aq this is a file path to the sqlite file and defaults to \fB$REPOSITORY_DIR/etc/brpt.sqlite\fP. .TP .B user User for database connections. Not used for sqlite3. .TP .B password Password for database connections. Not used for sqlite3. .TP .B host Host for database connections. Not used for sqlite3. .TP .B port Port for database connections. Not used for sqlite3. .TP .B options Various options for the database connection. The value is expected as multiple key=value pairs, separated with commas. The concrete value depends on the database engine. .UNINDENT .UNINDENT .UNINDENT .SH REPORTING OPTIONS .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .TP .B config Specifies the location of the reporting configuration (default is /etc/bcfg2\-web.conf. .TP .B time_zone Specifies a time zone other than that used on the system. (Note that this will cause the Bcfg2 server to log messages in this time zone as well). .TP .B web_debug Turn on Django debugging. .UNINDENT .UNINDENT .UNINDENT .SH SEE ALSO .sp \fIbcfg2(1)\fP, \fIbcfg2\-server(8)\fP .\" Generated by docutils manpage writer. . bcfg2-1.3.3/misc/000077500000000000000000000000001223671746500134515ustar00rootroot00000000000000bcfg2-1.3.3/misc/apache/000077500000000000000000000000001223671746500146725ustar00rootroot00000000000000bcfg2-1.3.3/misc/apache/bcfg2.conf000066400000000000000000000012541223671746500165260ustar00rootroot00000000000000 # # If the root is changed update the static content alias as well # WSGIScriptAlias /bcfg2 "/usr/share/bcfg2/reports.wsgi" WSGISocketPrefix /var/run/apache2/wsgi WSGIDaemonProcess Bcfg2.Server.Reports processes=1 threads=10 WSGIProcessGroup Bcfg2.Server.Reports # # Manually set this to override the static content # #SetEnv bcfg2.media_url /bcfg2/site_media/ # # This should have the same prefix as WSGIScriptAlias # Alias "/bcfg2/site_media/" "/usr/share/bcfg2/site_media/" Options None AllowOverride None Order allow,deny Allow from all bcfg2-1.3.3/misc/bcfg2-selinux.spec000066400000000000000000000134061223671746500170010ustar00rootroot00000000000000%global __python python %{!?py_ver: %global py_ver %(%{__python} -c 'import sys;print(sys.version[0:3])')} %global pythonversion %{py_ver} %{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} %{!?_initrddir: %global _initrddir %{_sysconfdir}/rc.d/init.d} %global selinux_policyver %(%{__sed} -e 's,.*selinux-policy-\\([^/]*\\)/.*,\\1,' /usr/share/selinux/devel/policyhelp 2>/dev/null || echo 0.0.0) %global selinux_types %(%{__awk} '/^#[[:space:]]*SELINUXTYPE=/,/^[^#]/ { if ($3 == "-") printf "%s ", $2 }' /etc/selinux/config 2>/dev/null) %global selinux_variants %([ -z "%{selinux_types}" ] && echo mls strict targeted || echo %{selinux_types}) # For -pre or -rc releases, remove the initial # characters from the appropriate line below. # # Don't forget to change the Release: tag below to something like 0.1 #%%global _rc 1 #%%global _pre 2 %global _pre_rc %{?_pre:.pre%{_pre}}%{?_rc:.rc%{_rc}} Name: bcfg2-selinux Version: 1.3.3 Release: 1%{?_pre_rc}%{?dist} Summary: Bcfg2 Client and Server SELinux policy %if 0%{?suse_version} Group: System/Management Conflicts: selinux-policy = 2.20120725 %else Group: Applications/System # the selinux reference policy 2.20120725 (3.11.1 in RH versioning) # contains a bogus bcfg2 module Conflicts: selinux-policy = 3.11.1 %endif License: BSD URL: http://bcfg2.org Source0: ftp://ftp.mcs.anl.gov/pub/bcfg/%{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildArch: noarch BuildRequires: checkpolicy, selinux-policy-devel, hardlink BuildRequires: /usr/share/selinux/devel/policyhelp Requires: selinux-policy >= %{selinux_policyver} Requires: %{name} = %{version}-%{release} Requires(post): /usr/sbin/semodule, /sbin/restorecon, /sbin/fixfiles, bcfg2 Requires(postun): /usr/sbin/semodule, /sbin/restorecon, /sbin/fixfiles, bcfg2 %description Bcfg2 helps system administrators produce a consistent, reproducible, and verifiable description of their environment, and offers visualization and reporting tools to aid in day-to-day administrative tasks. It is the fifth generation of configuration management tools developed in the Mathematics and Computer Science Division of Argonne National Laboratory. It is based on an operational model in which the specification can be used to validate and optionally change the state of clients, but in a feature unique to bcfg2 the client's response to the specification can also be used to assess the completeness of the specification. Using this feature, bcfg2 provides an objective measure of how good a job an administrator has done in specifying the configuration of client systems. Bcfg2 is therefore built to help administrators construct an accurate, comprehensive specification. Bcfg2 has been designed from the ground up to support gentle reconciliation between the specification and current client states. It is designed to gracefully cope with manual system modifications. Finally, due to the rapid pace of updates on modern networks, client systems are constantly changing; if required in your environment, Bcfg2 can enable the construction of complex change management and deployment strategies. This package includes the Bcfg2 server and client SELinux policy. %prep %setup -q -n %{name}-%{version}%{?_pre_rc} %build cd redhat/selinux for selinuxvariant in %{selinux_variants}; do make NAME=${selinuxvariant} -f /usr/share/selinux/devel/Makefile mv bcfg2.pp bcfg2.pp.${selinuxvariant} make NAME=${selinuxvariant} -f /usr/share/selinux/devel/Makefile clean done cd - %install for selinuxvariant in %{selinux_variants}; do install -d %{buildroot}%{_datadir}/selinux/${selinuxvariant} install -p -m 644 redhat/selinux/bcfg2.pp.${selinuxvariant} \ %{buildroot}%{_datadir}/selinux/${selinuxvariant}/bcfg2.pp done /usr/sbin/hardlink -cv %{buildroot}%{_datadir}/selinux %clean [ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} || exit 2 %files %defattr(-,root,root,0755) %doc redhat/selinux/* %{_datadir}/selinux/*/bcfg2.pp %post for selinuxvariant in %{selinux_variants}; do /usr/sbin/semodule -s ${selinuxvariant} -i \ %{_datadir}/selinux/${selinuxvariant}/bcfg2.pp &> /dev/null || : done /sbin/fixfiles -R bcfg2 restore || : if rpm -q bcfg2-server >& /dev/null; then /sbin/fixfiles -R bcfg2-server restore || : fi /sbin/restorecon -R %{_localstatedir}/cache/bcfg2 || : /sbin/restorecon -R %{_localstatedir}/lib/bcfg2 || : %postun if [ $1 -eq 0 ] ; then for selinuxvariant in %{selinux_variants}; do /usr/sbin/semodule -s ${selinuxvariant} -r bcfg2 &> /dev/null || : done /sbin/fixfiles -R bcfg2 restore || : if rpm -q bcfg2-server >& /dev/null; then /sbin/fixfiles -R bcfg2-server restore || : fi [ -d %{_localstatedir}/cache/bcfg2 ] && \ /sbin/restorecon -R %{_localstatedir}/cache/bcfg2 || : [ -d %{_localstatedir}/lib/bcfg2 ] && \ /sbin/restorecon -R %{_localstatedir}/lib/bcfg2 || : fi %changelog * Thu Nov 07 2013 Sol Jerome 1.3.3-1 - New upstream release * Mon Jul 01 2013 Sol Jerome 1.3.2-1 - New upstream release * Thu Mar 21 2013 Sol Jerome 1.3.1-1 - New upstream release * Fri Mar 15 2013 Sol Jerome 1.3.0-0.0 - New upstream release * Tue Jan 29 2013 Sol Jerome 1.3.0-0.0rc2 - New upstream release * Wed Jan 09 2013 Sol Jerome 1.3.0-0.0rc1 - New upstream release * Tue Oct 30 2012 Sol Jerome 1.3.0-0.0pre2 - New upstream release * Fri Sep 14 2012 Chris St. Pierre 1.3.0-0.2pre1 - Broke bcfg2-selinux into its own specfile bcfg2-1.3.3/misc/bcfg2.spec000066400000000000000000000676151223671746500153270ustar00rootroot00000000000000# Fedora 13+ and EL6 contain these macros already; only needed for EL5 %if 0%{?rhel} && 0%{?rhel} <= 5 %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()") %define python_version %(%{__python} -c 'import sys;print(sys.version[0:3])') %endif # openSUSE macro translation %if 0%{?suse_version} %global python_version %{py_ver} %{!?_initrddir: %global _initrddir %{_sysconfdir}/rc.d/init.d} # openSUSE < 11.2 %if %{suse_version} < 1120 %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()") %endif %endif # For -pre or -rc releases, remove the initial # characters from the appropriate line below. # # Don't forget to change the Release: tag below to something like 0.1 #%%global _rc 1 #%%global _pre 2 %global _pre_rc %{?_pre:.pre%{_pre}}%{?_rc:.rc%{_rc}} Name: bcfg2 Version: 1.3.3 Release: 1%{?_pre_rc}%{?dist} Summary: A configuration management system %if 0%{?suse_version} # http://en.opensuse.org/openSUSE:Package_group_guidelines Group: System/Management %else Group: Applications/System %endif License: BSD URL: http://bcfg2.org Source0: ftp://ftp.mcs.anl.gov/pub/bcfg/%{name}-%{version}.tar.gz # Used in %%check Source1: http://www.w3.org/2001/XMLSchema.xsd %if %{?rhel}%{!?rhel:10} <= 5 || 0%{?suse_version} # EL5 and OpenSUSE require the BuildRoot tag BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) %endif BuildArch: noarch BuildRequires: python BuildRequires: python-devel BuildRequires: python-lxml BuildRequires: python-boto %if 0%{?suse_version} BuildRequires: python-M2Crypto BuildRequires: python-Genshi BuildRequires: python-gamin BuildRequires: python-pyinotify BuildRequires: python-python-daemon BuildRequires: python-CherryPy >= 3 %else # ! suse_version BuildRequires: python-daemon BuildRequires: python-inotify %if "%{_vendor}" == "redhat" && 0%{!?rhel:1} && 0%{!?fedora:1} # by default, el5 doesn't have the %%rhel macro, provided by this # package; EPEL build servers install buildsys-macros by default, but # explicitly requiring this may help builds in other environments BuildRequires: buildsys-macros %else # vendor != redhat || rhel defined %if 0%{?rhel} && 0%{?rhel} < 6 BuildRequires: python-ssl %else # rhel > 5 # EL5 lacks python-mock, so test suite is disabled BuildRequires: python-sqlalchemy BuildRequires: python-nose BuildRequires: mock BuildRequires: m2crypto BuildRequires: Django BuildRequires: python-genshi BuildRequires: python-cheetah BuildRequires: pylibacl BuildRequires: libselinux-python BuildRequires: python-pep8 BuildRequires: python-cherrypy >= 3 BuildRequires: python-mock BuildRequires: pylint %endif # rhel > 5 %endif # vendor != redhat || rhel defined %endif # ! suse_version %if 0%{?mandriva_version} # mandriva seems to behave differently than other distros and needs # this explicitly. BuildRequires: python-setuptools %endif %if 0%{?mandriva_version} == 201100 # mandriva 2011 has multiple providers for libsane, so (at least when # building on OBS) one must be chosen explicitly: "have choice for # libsane.so.1 needed by python-imaging: libsane1 sane-backends-iscan" BuildRequires: libsane1 %endif # RHEL 5 and 6 ship with sphinx 0.6, but sphinx 1.0 is available with # a different package name in EPEL. %if "%{_vendor}" == "redhat" && 0%{?rhel} <= 6 && 0%{?fedora} == 0 BuildRequires: python-sphinx10 # python-sphinx10 doesn't set sys.path correctly; do it for them %global pythonpath %(find %{python_sitelib} -name Sphinx*.egg) %else BuildRequires: python-sphinx >= 1.0 %endif BuildRequires: python-docutils %if 0%{?fedora} >= 16 BuildRequires: systemd-units %endif Requires: python-lxml %if 0%{?rhel} && 0%{?rhel} < 6 Requires: python-ssl %endif Requires: libselinux-python %if 0%{?fedora} >= 16 Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units %else Requires(post): /sbin/chkconfig Requires(preun): /sbin/chkconfig Requires(preun): /sbin/service Requires(postun): /sbin/service %endif %if "%{_vendor}" != "redhat" # fedora and rhel (and possibly other distros) do not know this tag. Recommends: cron %endif %description Bcfg2 helps system administrators produce a consistent, reproducible, and verifiable description of their environment, and offers visualization and reporting tools to aid in day-to-day administrative tasks. It is the fifth generation of configuration management tools developed in the Mathematics and Computer Science Division of Argonne National Laboratory. It is based on an operational model in which the specification can be used to validate and optionally change the state of clients, but in a feature unique to bcfg2 the client's response to the specification can also be used to assess the completeness of the specification. Using this feature, bcfg2 provides an objective measure of how good a job an administrator has done in specifying the configuration of client systems. Bcfg2 is therefore built to help administrators construct an accurate, comprehensive specification. Bcfg2 has been designed from the ground up to support gentle reconciliation between the specification and current client states. It is designed to gracefully cope with manual system modifications. Finally, due to the rapid pace of updates on modern networks, client systems are constantly changing; if required in your environment, Bcfg2 can enable the construction of complex change management and deployment strategies. This package includes the Bcfg2 client software. %package server Summary: Bcfg2 Server %if 0%{?suse_version} Group: System/Management %else Group: System Environment/Daemons %endif Requires: bcfg2 = %{version}-%{release} Requires: python-lxml >= 1.2.1 %if 0%{?suse_version} Requires: python-pyinotify Requires: python-python-daemon %else Requires: python-inotify Requires: python-daemon %endif Requires: /usr/sbin/sendmail Requires: /usr/bin/openssl Requires: graphviz Requires: python-nose %if %{_vendor} == redhat %if 0%{?fedora} >= 16 Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units Requires(post): systemd-sysv %else Requires(post): /sbin/chkconfig Requires(preun): /sbin/chkconfig Requires(preun): /sbin/service Requires(postun): /sbin/service %endif %endif %description server Bcfg2 helps system administrators produce a consistent, reproducible, and verifiable description of their environment, and offers visualization and reporting tools to aid in day-to-day administrative tasks. It is the fifth generation of configuration management tools developed in the Mathematics and Computer Science Division of Argonne National Laboratory. It is based on an operational model in which the specification can be used to validate and optionally change the state of clients, but in a feature unique to bcfg2 the client's response to the specification can also be used to assess the completeness of the specification. Using this feature, bcfg2 provides an objective measure of how good a job an administrator has done in specifying the configuration of client systems. Bcfg2 is therefore built to help administrators construct an accurate, comprehensive specification. Bcfg2 has been designed from the ground up to support gentle reconciliation between the specification and current client states. It is designed to gracefully cope with manual system modifications. Finally, due to the rapid pace of updates on modern networks, client systems are constantly changing; if required in your environment, Bcfg2 can enable the construction of complex change management and deployment strategies. This package includes the Bcfg2 server software. %package server-cherrypy Summary: Bcfg2 Server - CherryPy backend %if 0%{?suse_version} Group: System/Management %else Group: System Environment/Daemons %endif Requires: bcfg2 = %{version}-%{release} Requires: bcfg2-server = %{version}-%{release} # cherrypy 3.3 actually doesn't exist yet, but 3.2 has bugs that # prevent it from working: # https://bitbucket.org/cherrypy/cherrypy/issue/1154/assertionerror-in-recv-when-ssl-is-enabled Requires: python-cherrypy > 3.3 %description server-cherrypy Bcfg2 helps system administrators produce a consistent, reproducible, and verifiable description of their environment, and offers visualization and reporting tools to aid in day-to-day administrative tasks. It is the fifth generation of configuration management tools developed in the Mathematics and Computer Science Division of Argonne National Laboratory. It is based on an operational model in which the specification can be used to validate and optionally change the state of clients, but in a feature unique to bcfg2 the client's response to the specification can also be used to assess the completeness of the specification. Using this feature, bcfg2 provides an objective measure of how good a job an administrator has done in specifying the configuration of client systems. Bcfg2 is therefore built to help administrators construct an accurate, comprehensive specification. Bcfg2 has been designed from the ground up to support gentle reconciliation between the specification and current client states. It is designed to gracefully cope with manual system modifications. Finally, due to the rapid pace of updates on modern networks, client systems are constantly changing; if required in your environment, Bcfg2 can enable the construction of complex change management and deployment strategies. This package includes the Bcfg2 CherryPy server backend. %package web Summary: Bcfg2 Web Reporting Interface %if 0%{?suse_version} Group: System/Management Requires: python-django >= 1.2 Requires: python-django-south >= 0.7 %else Group: System Tools Requires: Django >= 1.2 Requires: Django-south >= 0.7 Requires: bcfg2-server %endif Requires: httpd %if "%{_vendor}" == "redhat" Requires: mod_wsgi %global apache_conf %{_sysconfdir}/httpd %else Requires: apache2-mod_wsgi %global apache_conf %{_sysconfdir}/apache2 %endif %description web Bcfg2 helps system administrators produce a consistent, reproducible, and verifiable description of their environment, and offers visualization and reporting tools to aid in day-to-day administrative tasks. It is the fifth generation of configuration management tools developed in the Mathematics and Computer Science Division of Argonne National Laboratory. It is based on an operational model in which the specification can be used to validate and optionally change the state of clients, but in a feature unique to bcfg2 the client's response to the specification can also be used to assess the completeness of the specification. Using this feature, bcfg2 provides an objective measure of how good a job an administrator has done in specifying the configuration of client systems. Bcfg2 is therefore built to help administrators construct an accurate, comprehensive specification. Bcfg2 has been designed from the ground up to support gentle reconciliation between the specification and current client states. It is designed to gracefully cope with manual system modifications. Finally, due to the rapid pace of updates on modern networks, client systems are constantly changing; if required in your environment, Bcfg2 can enable the construction of complex change management and deployment strategies. This package includes the Bcfg2 reports web frontend. %package doc Summary: Documentation for Bcfg2 %if 0%{?suse_version} Group: Documentation/HTML %else Group: Documentation %endif %description doc Bcfg2 helps system administrators produce a consistent, reproducible, and verifiable description of their environment, and offers visualization and reporting tools to aid in day-to-day administrative tasks. It is the fifth generation of configuration management tools developed in the Mathematics and Computer Science Division of Argonne National Laboratory. It is based on an operational model in which the specification can be used to validate and optionally change the state of clients, but in a feature unique to bcfg2 the client's response to the specification can also be used to assess the completeness of the specification. Using this feature, bcfg2 provides an objective measure of how good a job an administrator has done in specifying the configuration of client systems. Bcfg2 is therefore built to help administrators construct an accurate, comprehensive specification. Bcfg2 has been designed from the ground up to support gentle reconciliation between the specification and current client states. It is designed to gracefully cope with manual system modifications. Finally, due to the rapid pace of updates on modern networks, client systems are constantly changing; if required in your environment, Bcfg2 can enable the construction of complex change management and deployment strategies. This package includes the Bcfg2 documentation. %package examples Summary: Examples for Bcfg2 Group: Documentation %description examples Bcfg2 helps system administrators produce a consistent, reproducible, and verifiable description of their environment, and offers visualization and reporting tools to aid in day-to-day administrative tasks. It is the fifth generation of configuration management tools developed in the Mathematics and Computer Science Division of Argonne National Laboratory. It is based on an operational model in which the specification can be used to validate and optionally change the state of clients, but in a feature unique to bcfg2 the client's response to the specification can also be used to assess the completeness of the specification. Using this feature, bcfg2 provides an objective measure of how good a job an administrator has done in specifying the configuration of client systems. Bcfg2 is therefore built to help administrators construct an accurate, comprehensive specification. Bcfg2 has been designed from the ground up to support gentle reconciliation between the specification and current client states. It is designed to gracefully cope with manual system modifications. Finally, due to the rapid pace of updates on modern networks, client systems are constantly changing; if required in your environment, Bcfg2 can enable the construction of complex change management and deployment strategies. This package includes the examples files for Bcfg2. %prep %setup -q -n %{name}-%{version}%{?_pre_rc} # The pylint and pep8 unit tests fail on RH-derivative distros %if "%{_vendor}" == "redhat" mv testsuite/Testsrc/test_code_checks.py \ testsuite/Testsrc/test_code_checks.py.disable_unit_tests awk ' BEGIN {line=0} /class Test(Pylint|PEP8)/ {line=FNR+1} FNR==line {sub("True","False")} {print $0} ' testsuite/Testsrc/test_code_checks.py.disable_unit_tests \ > testsuite/Testsrc/test_code_checks.py %endif # Fixup some paths %{__perl} -pi -e 's@/etc/default@%{_sysconfdir}/sysconfig@g' tools/bcfg2-cron %{__perl} -pi -e 's@/usr/lib/bcfg2@%{_libexecdir}@g' debian/bcfg2.cron.daily %{__perl} -pi -e 's@/usr/lib/bcfg2@%{_libexecdir}@g' debian/bcfg2.cron.hourly # Get rid of extraneous shebangs for f in `find src/lib -name \*.py` do sed -i -e '/^#!/,1d' $f done sed -i "s/apache2/httpd/g" misc/apache/bcfg2.conf %build %{__python} setup.py build %{?pythonpath: PYTHONPATH="%{pythonpath}"} \ %{__python} setup.py build_sphinx %install %if 0%{?rhel} == 5 || 0%{?suse_version} # EL5 and OpenSUSE require the buildroot to be cleaned manually rm -rf %{buildroot} %endif %{__python} setup.py install -O1 --skip-build --root=%{buildroot} --prefix=/usr install -d %{buildroot}%{_bindir} install -d %{buildroot}%{_sbindir} install -d %{buildroot}%{_initrddir} install -d %{buildroot}%{_sysconfdir}/cron.daily install -d %{buildroot}%{_sysconfdir}/cron.hourly install -d %{buildroot}%{_sysconfdir}/sysconfig install -d %{buildroot}%{_libexecdir} install -d %{buildroot}%{_localstatedir}/cache/%{name} install -d %{buildroot}%{_localstatedir}/lib/%{name} %if 0%{?suse_version} install -d %{buildroot}/var/adm/fillup-templates %endif mv %{buildroot}%{_bindir}/bcfg2* %{buildroot}%{_sbindir} %if 0%{?fedora} < 16 # Install SysV init scripts for everyone but new Fedoras install -m 755 redhat/scripts/bcfg2.init \ %{buildroot}%{_initrddir}/bcfg2 install -m 755 redhat/scripts/bcfg2-server.init \ %{buildroot}%{_initrddir}/bcfg2-server install -m 755 redhat/scripts/bcfg2-report-collector.init \ %{buildroot}%{_initrddir}/bcfg2-report-collector %endif install -m 755 debian/bcfg2.cron.daily \ %{buildroot}%{_sysconfdir}/cron.daily/bcfg2 install -m 755 debian/bcfg2.cron.hourly \ %{buildroot}%{_sysconfdir}/cron.hourly/bcfg2 install -m 755 tools/bcfg2-cron \ %{buildroot}%{_libexecdir}/bcfg2-cron install -m 644 debian/bcfg2.default \ %{buildroot}%{_sysconfdir}/sysconfig/bcfg2 install -m 644 debian/bcfg2-server.default \ %{buildroot}%{_sysconfdir}/sysconfig/bcfg2-server %if 0%{?suse_version} install -m 755 debian/bcfg2.default \ %{buildroot}/var/adm/fillup-templates/sysconfig.bcfg2 install -m 755 debian/bcfg2-server.default \ %{buildroot}/var/adm/fillup-templates/sysconfig.bcfg2-server ln -s %{_initrddir}/bcfg2 %{buildroot}%{_sbindir}/rcbcfg2 ln -s %{_initrddir}/bcfg2-server %{buildroot}%{_sbindir}/rcbcfg2-server %endif touch %{buildroot}%{_sysconfdir}/%{name}.{cert,conf,key} # systemd install -d %{buildroot}%{_unitdir} install -p -m 644 redhat/systemd/%{name}.service \ %{buildroot}%{_unitdir}/%{name}.service install -p -m 644 redhat/systemd/%{name}-server.service \ %{buildroot}%{_unitdir}/%{name}-server.service # Webserver install -d %{buildroot}%{apache_conf}/conf.d install -p -m 644 misc/apache/bcfg2.conf \ %{buildroot}%{apache_conf}/conf.d/wsgi_bcfg2.conf # mandriva cannot handle %ghost without the file existing, # so let's touch a bunch of empty config files touch %{buildroot}%{_sysconfdir}/bcfg2.conf %if 0%{?rhel} == 5 # Required for EL5 %clean rm -rf %{buildroot} %endif %if 0%{?rhel} != 5 # EL5 lacks python-mock, so test suite is disabled %check # Downloads not allowed in koji; fix .xsd urls to point to local files sed -i "s@schema_url = .*\$@schema_url = 'file://`pwd`/`basename %{SOURCE1}`'@" \ testsuite/Testschema/test_schema.py sed "s@http://www.w3.org/2001/xml.xsd@file://$(pwd)/schemas/xml.xsd@" \ %{SOURCE1} > `basename %{SOURCE1}` %{__python} setup.py test %endif %post %if 0%{?fedora} >= 18 %systemd_post bcfg2.service %else if [ $1 -eq 1 ] ; then # Initial installation %if 0%{?suse_version} %fillup_and_insserv -f bcfg2 %else %if 0%{?fedora} >= 16 /bin/systemctl daemon-reload >/dev/null 2>&1 || : %else /sbin/chkconfig --add bcfg2 %endif %endif fi %endif %post server %if 0%{?fedora} >= 18 %systemd_post bcfg2-server.service %else if [ $1 -eq 1 ] ; then # Initial installation %if 0%{?suse_version} %fillup_and_insserv -f bcfg2-server %else %if 0%{?fedora} >= 16 /bin/systemctl daemon-reload >/dev/null 2>&1 || : %else /sbin/chkconfig --add bcfg2-server %endif %endif fi %endif %preun %if 0%{?fedora} >= 18 %systemd_preun bcfg2.service %else if [ $1 -eq 0 ]; then # Package removal, not upgrade %if 0%{?suse_version} %stop_on_removal bcfg2 %else %if 0%{?fedora} >= 16 /bin/systemctl --no-reload disable bcfg2.service > /dev/null 2>&1 || : /bin/systemctl stop bcfg2.service > /dev/null 2>&1 || : %else /sbin/service bcfg2 stop &>/dev/null || : /sbin/chkconfig --del bcfg2 %endif %endif fi %endif %preun server %if 0%{?fedora} >= 18 %systemd_preun bcfg2-server.service %else if [ $1 -eq 0 ]; then # Package removal, not upgrade %if 0%{?suse_version} %stop_on_removal bcfg2-server %stop_on_removal bcfg2-report-collector %else %if 0%{?fedora} >= 16 /bin/systemctl --no-reload disable bcfg2-server.service > /dev/null 2>&1 || : /bin/systemctl stop bcfg2-server.service > /dev/null 2>&1 || : %else /sbin/service bcfg2-server stop &>/dev/null || : /sbin/chkconfig --del bcfg2-server %endif %endif fi %endif %postun %if 0%{?fedora} >= 18 %systemd_postun bcfg2.service %else %if 0%{?fedora} >= 16 /bin/systemctl daemon-reload >/dev/null 2>&1 || : %endif if [ $1 -ge 1 ] ; then # Package upgrade, not uninstall %if 0%{?suse_version} %insserv_cleanup %else %if 0%{?fedora} >= 16 /bin/systemctl try-restart bcfg2.service >/dev/null 2>&1 || : %else /sbin/service bcfg2 condrestart &>/dev/null || : %endif %endif fi %endif %postun server %if 0%{?fedora} >= 18 %systemd_postun bcfg2-server.service %else %if 0%{?fedora} >= 16 /bin/systemctl daemon-reload >/dev/null 2>&1 || : %endif if [ $1 -ge 1 ] ; then # Package upgrade, not uninstall %if 0%{?fedora} >= 16 /bin/systemctl try-restart bcfg2-server.service >/dev/null 2>&1 || : %else /sbin/service bcfg2-server condrestart &>/dev/null || : %endif fi %if 0%{?suse_version} if [ $1 -eq 0 ]; then # clean up on removal. %insserv_cleanup fi %endif %endif %if 0%{?fedora} || 0%{?rhel} %triggerun -- bcfg2 < 1.2.1-1 /usr/bin/systemd-sysv-convert --save bcfg2 >/dev/null 2>&1 || : /bin/systemctl --no-reload enable bcfg2.service >/dev/null 2>&1 || : /sbin/chkconfig --del bcfg2 >/dev/null 2>&1 || : /bin/systemctl try-restart bcfg2.service >/dev/null 2>&1 || : %triggerun server -- bcfg2-server < 1.2.1-1 /usr/bin/systemd-sysv-convert --save bcfg2-server >/dev/null 2>&1 || : /bin/systemctl --no-reload enable bcfg2-server.service >/dev/null 2>&1 || : /sbin/chkconfig --del bcfg2-server >/dev/null 2>&1 || : /bin/systemctl try-restart bcfg2-server.service >/dev/null 2>&1 || : %endif %files %if 0%{?rhel} == 5 || 0%{?suse_version} # Required for EL5 and OpenSUSE %defattr(-,root,root,-) %endif %doc COPYRIGHT LICENSE README %{_mandir}/man1/bcfg2.1* %{_mandir}/man5/bcfg2.conf.5* %ghost %attr(600,root,root) %config(noreplace,missingok) %{_sysconfdir}/bcfg2.cert %ghost %attr(0600,root,root) %config(noreplace,missingok) %{_sysconfdir}/bcfg2.conf %if 0%{?fedora} >= 16 %config(noreplace) %{_unitdir}/%{name}.service %else %{_initrddir}/bcfg2 %endif %if 0%{?fedora} || 0%{?rhel} %config(noreplace) %{_sysconfdir}/sysconfig/bcfg2 %else %config(noreplace) %{_sysconfdir}/default/bcfg2 %endif %{_sysconfdir}/cron.daily/bcfg2 %{_sysconfdir}/cron.hourly/bcfg2 %{_sbindir}/bcfg2 %{_libexecdir}/bcfg2-cron %dir %{_localstatedir}/cache/%{name} %{python_sitelib}/Bcfg2*.egg-info %dir %{python_sitelib}/Bcfg2 %{python_sitelib}/Bcfg2/__init__.py* %{python_sitelib}/Bcfg2/Client %{python_sitelib}/Bcfg2/Compat.py* %{python_sitelib}/Bcfg2/Logger.py* %{python_sitelib}/Bcfg2/Options.py* %{python_sitelib}/Bcfg2/Proxy.py* %{python_sitelib}/Bcfg2/Utils.py* %{python_sitelib}/Bcfg2/version.py* %if 0%{?suse_version} %{_sbindir}/rcbcfg2 %config(noreplace) /var/adm/fillup-templates/sysconfig.bcfg2 %endif %files server %if 0%{?rhel} == 5 || 0%{?suse_version} %defattr(-,root,root,-) %endif %ghost %attr(600,root,root) %config(noreplace) %{_sysconfdir}/bcfg2.key %if 0%{?fedora} >= 16 %config(noreplace) %{_unitdir}/%{name}-server.service %else %{_initrddir}/bcfg2-server %{_initrddir}/bcfg2-report-collector %endif %config(noreplace) %{_sysconfdir}/sysconfig/bcfg2-server %{_sbindir}/bcfg2-* %dir %{_localstatedir}/lib/%{name} %{python_sitelib}/Bcfg2/Cache.py* %{python_sitelib}/Bcfg2/Encryption.py* %{python_sitelib}/Bcfg2/SSLServer.py* %{python_sitelib}/Bcfg2/Statistics.py* %{python_sitelib}/Bcfg2/settings.py* %{python_sitelib}/Bcfg2/Server %{python_sitelib}/Bcfg2/Reporting %{python_sitelib}/Bcfg2/manage.py* %exclude %{python_sitelib}/Bcfg2/Server/CherryPyCore.py %dir %{_datadir}/bcfg2 %{_datadir}/bcfg2/schemas %{_datadir}/bcfg2/xsl-transforms %{_datadir}/bcfg2/Hostbase %if 0%{?suse_version} %{_sbindir}/rcbcfg2-server %config(noreplace) /var/adm/fillup-templates/sysconfig.bcfg2-server %endif %{_mandir}/man5/bcfg2-lint.conf.5* %{_mandir}/man8/bcfg2*.8* %doc tools/* %files server-cherrypy %if 0%{?rhel} == 5 || 0%{?suse_version} %defattr(-,root,root,-) %endif %{python_sitelib}/Bcfg2/Server/CherryPyCore.py %files web %if 0%{?rhel} == 5 || 0%{?suse_version} %defattr(-,root,root,-) %endif %{_datadir}/bcfg2/reports.wsgi %{_datadir}/bcfg2/site_media %config(noreplace) %{apache_conf}/conf.d/wsgi_bcfg2.conf %files doc %if 0%{?rhel} == 5 || 0%{?suse_version} %defattr(-,root,root,-) %endif %doc build/sphinx/html/* %files examples %if 0%{?rhel} == 5 || 0%{?suse_version} %defattr(-,root,root,-) %endif %doc examples/* %changelog * Thu Nov 07 2013 Sol Jerome 1.3.3-1 - New upstream release * Sun Aug 04 2013 John Morris - 1.3.2-2 - Reconcile divergences with Fedora specfile, as requested by upstream (equally large changes made in Fedora version to reconcile with this file) - Python macro cleanups - Accommodations for OpenSUSE - Macros for pre and rc releases - %%check section - Move BRs to top of file - Rearrange lines to match Fedora - Group: tag tweaks - Startup/shutdown changes - Separate examples package - Remove %%{__install} macros; RH has backed away from those - Add fedora systemd units, both f16 and f18 variants :P - Changes to %%post* scripts - Rearrange %%files sections * Mon Jul 01 2013 Sol Jerome 1.3.2-1 - New upstream release * Thu Mar 21 2013 Sol Jerome 1.3.1-1 - New upstream release * Fri Mar 15 2013 Sol Jerome 1.3.0-0.0 - New upstream release * Tue Jan 29 2013 Sol Jerome 1.3.0-0.0rc2 - New upstream release * Wed Jan 09 2013 Sol Jerome 1.3.0-0.0rc1 - New upstream release * Tue Oct 30 2012 Sol Jerome 1.3.0-0.0pre2 - New upstream release * Wed Oct 17 2012 Chris St. Pierre 1.3.0-0.2pre1 - Split bcfg2-selinux into its own specfile * Fri Sep 14 2012 Chris St. Pierre 1.3.0-0.1pre1 - Added -selinux subpackage * Fri Aug 31 2012 Sol Jerome 1.3.0-0.0pre1 - New upstream release * Wed Aug 15 2012 Chris St. Pierre 1.2.3-0.1 - Added tools/ as doc for bcfg2-server subpackage * Sat Feb 18 2012 Christopher 'm4z' Holm <686f6c6d@googlemail.com> 1.2.1 - Added Fedora and Mandriva compatibilty (for Open Build Service). - Added missing dependency redhat-lsb. * Tue Feb 14 2012 Christopher 'm4z' Holm <686f6c6d@googlemail.com> 1.2.1 - Added openSUSE compatibility. - Various changes to satisfy rpmlint. * Thu Jan 27 2011 Chris St. Pierre 1.2.0pre1-0.0 - Added -doc sub-package * Mon Jun 21 2010 Fabian Affolter - 1.1.0rc3-0.1 - Changed source0 in order that it works with spectool * Fri Feb 2 2007 Mike Brady 0.9.1 - Removed use of _libdir due to Red Hat x86_64 issue. * Fri Dec 22 2006 Jeffrey C. Ollie - 0.8.7.1-5 - Server needs client library files too so put them in main package * Wed Dec 20 2006 Jeffrey C. Ollie - 0.8.7.1-4 - Yes, actually we need to require openssl * Wed Dec 20 2006 Jeffrey C. Ollie - 0.8.7.1-3 - Don't generate SSL cert in post script, it only needs to be done on the server and is handled by the bcfg2-admin tool. - Move the /etc/bcfg2.key file to the server package - Don't install a sample copy of the config file, just ghost it - Require gamin-python for the server package - Don't require openssl - Make the client a separate package so you don't have to have the client if you don't want it * Wed Dec 20 2006 Jeffrey C. Ollie - 0.8.7.1-2 - Add more documentation * Mon Dec 18 2006 Jeffrey C. Ollie - 0.8.7.1-1 - First version for Fedora Extras * Fri Sep 15 2006 Narayan Desai - 0.8.4-1 - Initial log bcfg2-1.3.3/misc/bcfg2_logo.png000066400000000000000000000157111223671746500161670ustar00rootroot00000000000000PNG  IHDR`GfӅtEXtSoftwareAdobe ImageReadyqe<kIDATx]YlG6 &$#Q&d2SL^(ʢ B6QREq ,Yb 6ް1|个o߾ uZ}vWשΩsNN{nȑ G\8rv!ۑ#lG9rvȑC#Gَ9d;rȑC#lG9Ӕt_9لQgF" vڵk7WLOUb>u 8(alޡ5+Wz{{_n#ӧIƄ!0+I}nbi}q/uGQ%"OE X?cmj5efzҥK/^!@͛McZZZzz:P>u4;wp£Dkhf T*cCb,|ܞ)y=эi ( .===fќ߼y?.-o߾A(KCM>Ф{GyY_>6h/>#9iSP6)tsGGGgg'{|Zw%ǣq{ʤʙ3gfeeeggD֘u|miqq4TN4X7dS،j:eLk"yݪEZ 1D_}(߾HLSx=0''qbZ#5bg~̤Q {6*Zb_-KRlywFP7`( ͅ ,x(G K/]n]Հu_gS=ُmmJQTI#kQil#Mɛd7#Da "vg![:B%%gG) G 4q2cƌai7jԯ"BڪLk^ь {oմy`O;`{Μ9-BCϨY ()B6it?D}P]v;v * =M2E %%%b N0`wuuu Ǣ"0(ҨCI,2p đ5ĘH;ϯFix/mDBꞣaFAEϔYcʆǏZ-hllܻw/ZsժUO=& 4"_T w܉弚 ZĤ4k֬ŋK4W֨Ґ Ab4/ 7onnnF!Zڛ/2{쁫OCh(+%?\гbF l(Fw~(zdƳu ϲ{D3P۷o{Ѹ@٘ݐgŸ'/A7|Pl( 8_ CmC)޷vW ܣG~P.|z;atA_m1܉sNLJ1qknh/#W8 _G}oܸH-ͼ &7|xf(l4G(xлК֊e`٣t6dhp ^P|LaMDAl2(|iKHt!g)%ǑNúp۶m|a ^Okk!b0,z1hFHP#;Bx΢pH#0ӧOSiqfC:&xNpd7W_*b7~@*Gnkk믿lٲN—1BA`'MDTնnH;BOG6qU oƫejC-/r;ݶ e!?mΛ7ơMW~Y~0[·Odd!4<"mC[,Yd͚57mD]+pر$ Ą#O(O?>taH~p8??p^ҋ xPΟ?/ ;-G"}A["JABqҥK *##%'OS_EE w=mNw2@[}Cѕ)I:91vh8 T/B, hbt Вmitkjwtŋ?8a'x %|Ə%k0Բ x"1LQ[k^P+29{7nL.\555;4L8` _C[u:1332kj|#}͘W^^>b5N: ej B{-[^kb<~-Ba@P2y=cヰ/EqDa%`4 Y@=+ FsƤ`bLlOXK=Y0`ݹfNٱ-uL34# ?j` yx뭷Щ_: %48Q:O/;G'D_#_H!| .aR A8 jhh0@sKMX3`{T֊úLOOu= {rkpgXS44"2?ٳtA@A[ b9x*-)OGKKB%Z #\Oo ز`{hyZC#D`}I]']ehkf=#Aَƨl@SU?,..+V 0:ЀcDFJ+C* f=w/#ۈK=uv~v0/3x跷"jSX*X۹VO֣em _g/܁Uoaaaـޕ@BUC1 \V9i\f  $v%. hhKn$8i#л%2\~&U e˖UUU566Jҽ&y MhiΠxAbٳC1 ~q68͛7OxuRaԁFxp'dvȅ0BPzy k 4ΗQ)7|؅%M?`^^a {_|EX/ c )1GIE7`+/ctRTSQczɔApsz_>ߡڅ,B6ZO8j8=d|`)&]t:1|`e_G ~f# 9 WK,y.f3&Ѣo6E< P Z,d2r?9sV\ \wԈ5lguF6415:m^ ,/YUD6tx1}%&ӫu܊O8ܼ`uy%Cf}ޛ%6#~!7eSSӅCgX]]]/ n{[1i2e Z|%4|rQ_P'ʃ F=]ػwo]] N(y%`q1na@#iΝ+,,evJkc… 6xz%*bf)2Ajx%K=9z-nj.\XYYm6t$UCjS/­7%gFvkkkiiiH}l7";=BF^4KwwwQQQHu7v6_sis;::b"m9աnG7ugGxt aÆ/f4w؁p.1r$]V gؕD[Kb(B ,X.`h7>}+DuZ x؞a5ϦqEEP$Gձ$%>s*g͚؟FЬ79_AJ\cf02 ΡAZ{.pQUՙ8z =* m|?c%{GM`ICq)aD6{c_z5ĦFaQz9Y:D}䐭M6!fhYf=sE4wzn?[S^(1ߧ^AaܼKOp/X(c?O?^7CVvwwìe֟%^7 .s:;PKcO÷`kr)^zW^%z ;n2F!5 ˄Rn?ۄmZ@r=䣘r\Gy%0-[?HlUV\yȑF}w%#55 -'.m<"ˊ+___υ‹ܢ*eyy .Db=;F:?#01-`p{#DKWco$#u yKK l8?!/,/2R<:F6@1. խ ܶk^.]O /,GһI$OeffBK ?(bNighyr%mƜ~Ź';f烅l3B]P$z~N_ahUeEoSrl-ԿRd@^xzHU3v:d; 7ޏsDyhʌu3q8Jnk4Gc8E,@ hO+\7wu2vñՐ՘d;z<6k^QW͎.$Zjb\#ozE7Ln i倆Bkl{J\lhY;=ǖ! &.^<{V5635 # 6'Teht Od$O'[k2bhy28gjF:4c:۶(BQf}V!25b"!/uLV lnAS%O1Ѡ&T> />L@CpFGJpbN%ٻCM"tw\9*п4dcy>FN8+IJ9OJgR/ehpjhTbJ $>r4k9&pvȑC#Gَ?Cȑَ9d;rȑC#Gَ9d;rv!ۑ#lG9rvȑC#Gَ9d;rQl?#dڟIENDB`bcfg2-1.3.3/misc/bcfg2_logo_with_icon.png000066400000000000000000000240261223671746500202310ustar00rootroot00000000000000PNG  IHDR"`6;2tEXtSoftwareAdobe ImageReadyqe<'IDATx}il׵^lHJ"XX%k$Ydxl'4^A`0?1? 0Y{ƎXϙ,%3cdb)=mϒ}!E$K޻kc_ު^Hj!(n{|,s5]ׅM64䰻&ld 3lɆM60&f6d 3lafM60&f6d&lafM6lɆM64 /niJueMSlTcMH$6Atry<F6 t+ЉtX1:I>.Qit||< Bh4jeee>x$5mN-Od<7,of"P2JRTxSsmjkb|i!FGGGFF8.үbNXYYY]] bt* $+d1|"y_ʕQHƉ),!OQ.8#.7ؽ͑;8~A| c@ޅj;`Ok|v4Ⱥd2isE)&B(Dfdkb^jd)7K8,` tfHn:HalNQ#.@Zpr궫ZUcyI0{@H$`h2gØ̋ MTC̈rJ ` ث-0Ɲ)ÛnPXyX%-us]CC`TSH~e3gϋ0ϼd0WbT+͌"smTYr*# -^q_a}ѱ( ˇd{}h]ybChfIC/ufwRgfXꜚpeN! #q}W9!2/9hV̅p:34[t䢨82pRUU5-6::J,[b 0-|<ҷ\CitS8ECh-jKbW{¥Wk[L<,Ff8"6ſtۍ7Q@L nl0ĉb&cLaJY?#4<ǯ\lGK%hlkk0 6+ 8p`hh6-Z ;f,>y`IK:ܹs… 81ڂ#YbŚ5kn&Y*/ VLμJN,=Gixݕ=ynm6,!^"֗3#vj-ɋɧ +g0r^z::b )IF<+Ez T I|l3 )f'n7%BM%{4JSd܃lhjjj}1 W+rx;.c8ACɄRP Y 3=& _&V'"v菊NkehaGdAY9`&o4 |=oI0hh8#G6o KdDG|կ~uIBkOx2D\E YOS+Wf`Gd07<>flaR[~ߝ;wm!ܷjx/MQ7_Vq.[txyJPXi+x5`*2ȼ>` j-Q,RKrג劧azlaݸq]`G$xi~ /(U_׿noo{knn""f)իW!@/Jͳ]`q(@d)lÇ={r8^Qyܶi&Q5hf2䠓lwR|#ʁKc 40`1q_{-k! |4q O-=s?34F-.q?a}!xjiH 'nxO H`rlذ_ yQ`__< !sݻ%|I3k޽O3f$x@ۂZ .\o>ZM|4i [HBЍDɄACHTTXߘW[C5_h,.zkt%cK#/:n3LK2Mrzj0`@ ǖ8֭[!Ì u+#XmM c~]5c-guuCe 3]$=OblF[MD"PɮW .Dxw_-P:::cc?+x9s?N0bǣqNG[`BM|&h o/M ]16زS t7!ǀ1GbpTojG|_Xl>JLE8оȼ[ln7(Q>E!O3?~ï4g(~v,2lnM3zb :tPm+ㅂ;t3 {IWƌa? c80K.( r '4;j$޽</3iFϟ/PZ`A:N|B"q! (?xbJEod%wX07GmPi P׮]Q)񫳥PDZq(=銊0bX]YUY!Qc3=*:;(1'y#M/i4񚔢dQF}.^x ڵ,/\@.C2+,y"jMqⱟyž2%ۆLHS[EOnc[Q0x6\(͖uTER50~p0gߖ1FXEyӼy(pY9xaE}!XHsxF&N$86&B,ɡz|2?X8Rš5k oz7o$Kft,nkk#_yω5ٍn1dvv4݆~@miaFXeUV(#{EyqY+rqt6} p=LnJ0~kNOMc2Fw>9r<[{kdǷl! *'NH[0OTcZ"3:9 V^Ml1TI`l|EQEB/=\ ]]]Fʕ+0SxY;T~;P8tWW&-9c"V<ӌrc||%b/޽cB}}cOBEniUeqF^|lX?)G9BI2 c7o޻wogg'h ,j&9|˖S$9<:y$j+(!&h/._˖-@T/COalb 4攬.oP|iQPC(!Ξ cq#JŲ<> {poH;ԑ"iOZ8 r*)<%Qhy}{RGGsE655ZLO~Zh׿ucQBexȐx z%y6mڄJ;MScISdwYy*d)#fPih-k 9|2綍?'al :bÉޑT V:42!Lc#dMGh_XE&f+~'ge&ĪE.91|cI@P"cAK.Y/Ciƍ>P_M|hJBӺHDkKDK0X軪 ri'R{79tq@.ǢE^t?K,ٶm[ss3LL g.7-r>+Ǒ0:&FYc70Gׁ16G92vZH |<.T ̒"ҮE]|2 `}DT:ٯ5uQ@J{LK\v{ժU{1In1efzTvV;*>S0Hx$-DO6nܗ|+$!3D. chhiƸ'd}m1}ЊOZwBP-E*&:BSxb)nLׄ&"sst$wt97XW͛tNjJ")ҝJ>(0l$)LԩSgΜOىà̌`ڻw/p[CC_ ޻woBI;ZQJ[f"/ x”---r[hP2$r ќk [H.kv҇QfG 0zL:EfНH'%z3ohrKu_N%=xXuM@\$*nD15ű);Ӻu֮]hVv޺uWTTG7x^¿f_xpʴ|PS  4"2<IDy{kH"6H`f-F(Zr%r522Immm:]8>y$;39ӼAIl,):/:nss34uaQSX3^zS"K? $2r- {ma_%ÚFbNLnn`Nyв4ߋF+"\Ox#>ӧOaF}0[h1QAwo[(3(}+Jf9(xjQ#\aPyfl gE[yD9sD/63xN"mZQ< <1fΘr"TD jmm()?… qٳJqwi}}}o_0(^USƒ?-XU`mZe7lh{hb 7߼x" t IBǔ\E,O4g690URbmH#ϵ/#=-  .ٍFK/tAMoժUwt 쵓7ywVַڵkޖ+_! )֤uc[`6n*l%Ү2:1TV*{Y] kW"Xi=c>w#)(ik֬A ̌|ldFh۷o۶ҥK.\PV)aVhÊ V\PlA˷)c(ڹf& Q$PpzWF[mj:N?㞔߅B+'fΟ%Sm0zl\YZk|ԝ h/v:܄1erd~\qj̉K/f]ڗɹO҃upNܩT(>jfd@j-M+Uew4gT[p;d*:y*ҷ"s:<7c#)gsSeV=^WKl`kc;J/˜_ THK2\%{>@|>X̦wvL4սedls-7)?;/9+"!D2s%E 1-×_N ov}*ɦ fb"xNq_i~sQqUy] cިv/&zYKg\|c<96ܱtXQȥle&rfM78. xK bcfg2-1.3.3/misc/logos/bcfg2_logo_300px.jpg000066400000000000000000000402151223671746500202350ustar00rootroot00000000000000JFIFddDuckyZAdobeds,     !1 AQa"q2B# RSV3$tEU&brC4T5Wcd%e7)  !1AQaq"2BR#br3S5cs$DC4T6 ?DM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4Dv]3O'[}|si[g0AMVWS-(4vSb6:BgsZ1`01Zw+av]Qsu79-ӈMO; s&{fϱk*-J,AN6@Z-D{|qaX{MPx'H^ȥIOU@f6`feX ߏG˱خ'7r3Bb7~~ )PT%>G\FS7DprZ ~}=_f5f@겼1:/= gOZ TWZGxiY&&Q f(b-!mGm g>=\{4L,xgK>5d~+IZ~y^Nn-p r2cwb!{*.IOE'"z>@|"&x.+b?oD_v) ?9+B(PP>Dh x?]gD=vOPG8 "4E- p$苇wWGuâ.crtnۡ`D_"RҟP"4|tEDM4DDMjo:u _'3:YZYmڑp(,6-)ĭGlmZD٢+ZXIt ,a9u/ۻ3#܇wݔlWړXM~^Yʕ2qqX^%jŧSҭm]{ éSɇbв'fM%p  Ö͘|``L*dAqNu%D$c'TΘK~\Kke:O922Nq^HI涾#jRK'?uU.p\3K72p0kq`Ύ} W\'a*J^%Pch6Ο"apwmTR[=\REvwEZJS6 :R$9,ySTӒ7hVҎ~o"11`*`훻};v&);2(r^s4aO)PJ^C{4o#xzhR\mJmiQ9S:q4@6#MdYA~aRVrXbTmobUP^mAu#:"׽.$Ԅ`„) ['D]W'?s;d! ?K8OFM'h7*fWϥD">׻nfG&2¬ėK!冒{-n9Y L\yi6"n79&AgV8$ߩw3$Jԧ\?EZBY#nA' QɁƘNk΢2B'e`2vY S ʉqdSeDhcsh;[ǫ4QJ~PGB) /95[3 !7;y :"S'hS>QVeHe'뱚 5r!2%ݱoyfߒKN0VtޛyU7zp7p0697Z-?k3왈4UcX6+\j%?2K:̧/6k"qx-QE |iŬ s˷.c"f{Wܶǁ)p6̨V6! ;j!˧wU ,w-ǔRGI<WmI}5!C<]akZLmci/1[#QdFl5ʗRU[?\WFN|TI7սU,X֟Z di6`O0[X,R4ϔT[;`c=bY&Q*sEN'Ckoy3u<*loˬ>2z:pZuEу1P't+j2ȽaZSBTګ׺>8玭p X'Pp,R?M~)?(v%:f}T1~*}=j+Nifڹt|clY/яHLTlz)t) OC]Emo/'O kp[zc<%̨"Li(K6m`)*JH #Q'Qza7Qr4Em5A<Ur1|)H8rhr[HQm-={,Udج G}|:^Sae)BJwstEϷ:ԼK^p(ȍ%mgEkv~?=?Mu]+#SbVSoe'["'62)XB}Vی;@D}h_^_;'皘չ2aP.#bdXW}OJ/~l#x5U/o,ǣV4۴6wVRI'd6 jf&OrKCAK9n7NI9cDWA=UGKu-m˩GvE4KU?Z {o v?oOuE 8Lj{C|cEE̪T[ȿɡYn{7j-3J% Ͷ'DPw|k{rjIe4e8͈P0S?]i);e) iJF/c;cfu3n~ iwl*B%Bۉ2?(gmDWJi^QA}zzK3e]mx*HۖHp9''N¢]ŮioK!ÔcJI%*mć"_i2ߏgwtmjd8s>b3 *qJaH:"M9]iymuO,!:Wԇ lhrԒ hmn鑚挒͏Iҵ5&K#t.?)؋ KZ!j'wyiye.2Ÿ+rx7'#^u߰ʑ:%~i+RNM\3!ce4 nv&ͪexs &.q;ܠ+f.1k7" caHds\ *(*;6ZG+nm*vٌBc0/,ߑ4\L,/;iJ9/}#/K.#V&ɕtS]t)P$2jl)DwÈ$DyPwڻ1ul\Ahtf0-;`mxnt㳥.spQ5}!r% @sa7^~"h)ɽ`/,,XhXv84(@9-eJ̈Q\> @2 YG@E=9QÌyaF D0;.]/H]u钊kj.-Am;xugefX9TJyM+O|ĘDYsЬx{O4.MSJTMCm56&8\l|Kd]n`l))&?+U{;\RGEJ]Fu:v"6.֮f]:W|fz%rcCI)KcydjtH_E3Cl{a!X{[P ? ѤjbEMef&e{L~^ {o b&Z|v~ 2Vkp<RR "؇qYYܥ[eʒT9MyN%*Xv'DX=xC18-4;*V2"+S'̺Du-=ReJ遾fauBlZ|yEJIC)ȚlˀWա0 .JTO3icV \*ykS2ONO0MGCP*^oIdƈ<]lN 3ln/H}u[QRĒtEcYCz#HRvBJ+ ݫk);ch XI䆙u2Y!#DZw7>˖; ߓ %]X0Jehk۸xTͦ**1FJGp>+r{\RU4E"AA!U)+T(0&ZPNL&>4E*#ٳv_ ڛr m[1Qc5Ka{mJˢ,}`p0v5| ׹GHÈnɉTڃALgu A wى~auVe(b殊 >k"hW?ûͭ2&^r WLvrB@ƍE,PNj3Ȝci=duݞ=>=z_F Ϧ>p=p\Mݷ'pc5;hvWVqDOm.h6DfNFׂ;W생1Ǜzq[۶iJgIoG쓭jQS>|QnT7t Y|i\yTu>G|T,DIԵʳeuESD.^Uk9;R<+`cI= hn~C )x\vREc4S_œ%+I;;l<>:s::|n } ¼MƱwAs%ĈUR{vŘ!8>!!C胢#Y4M6}o^4i$Ga p#)fGɝ)D [ A+?ev LKCyurM+C@AjřpjMʛ'-I~ۙZ{x3ݓ8{!/m-6'#Ej>]1#G <9OePj˱n*AsRv>dYG:׹.lߣy\wqhX\Zc² }GJkVۓ:"}ż?>\ynC2d5zԂW%˜ ֭dZoKVvi>yIX'8#SZXwZ ^0h4F{4W߸]͒j*j3MeR M 'SQY; ):4<,^vn(#}A(}m 5XmAݰnۛK;#r/:tT/L~H])o.B~%%M RƔ )l. }w](ϴ"mk 7lqq&7PǪEl6 Xz 4DS:Ì=m1Rr|YB:Ma,|RFOE| qfs셧՚#4"c럁LqKS^;m^'(3o;Н /݅G1ITBWҒK8mdZLbؘ.x׹erxIe{%g}?#\&T{b5p`TeȵnFMm,n8~bF XYu;3^ӨjfG@n[ynmj[('?{B;9V}AȶdR^l0s #m]PCo'b{%VݎHmAoeTNph%x {?x='hSOS.[9{!q"B]Gn*s M,J> xo:Jմ˃HcM&HBLT&\dYT{]50ŭkqp{6a^.D(zSJ2IVj$ 1UꌴN.n1#I^(Ҹdq|/(yL @_sS/sn@eeY-vSΐ< GH%6 :Ưs6ks> 6yaR|ܜf W;)zEH|Uީ̀S[~'tuD}z G통R*8+j0ܧm.>6Ih#pm&qLf'hh(="H8*׊ >+Ta %`ʤ鞟]J@$Ov˸k'#ǮqĈ#locA#p05aV|qjI2-᥷>򾓬7TDntl%J"yw,et[KŢ61"#j%m{y 芝{ʡrNKO]@Ӱ+lmlLK>($PE|"8/0~ ¤0P.bc+P`xc6(I{qލl!WI"']%kCT)܍"eWQhy/bVb8ˈPOQJR@#|EGqDe#yCʡ!I WBe$&S=^%+>EmOs-&(ʼnR*dHRZjd^6p.wV NNjo; 4.T|<$2)`hwٜ9/=aHf__m˘UdRm6PRwWBHl 8b [S9ݖ7o'*Q>ܤ~]a02l8aV! &i,#`\e:;KN{ƋψrxMgMk7nc 6-cfӐwVYM'mc._S KCHRBRոJenul6S9\I>6/4~82a cc#cZ.s8.1x׍ˍxzE s-:kEJ%G:Hvg?%y^h}!KB^Lbuc@:9ʕ#`T"/}۶:8VB .+&PD46@R P+xx*ܴDD_;/b܆qSr+E^W%aD4Wꕶ~4sL1G~2d0=6w\vF"6VkpөN56]?xFбèJj̠1Ʀ)p[ID|*z$םҡ Ndrf:i\*ʚbBl?^"9] ,UvEmk:ǫaW h!JSUӸTr%@ TUJk*&Ltn6XkX%] "->y;Souo<(q0=F <?߭mZHZl뜽K<5tI\'ѴٴcDItI-z$|gߴD1YkJ$ᶺΩfCr1oYu.ZZpt0v0&z6[mAB'4D=Z+TUY|$Ȳ#pM,4b^D_ a阑}$I5//jK^/+˹^3US7ea$4K: K{΢N̍V`F Ѳg$bzWK.m5-p@[mO.W*0 rم7˳qec1Y BK;edQۤЄ/<)6tEg:"K' 9 O[wLs֡6?2RtEÁ))`[TsEZ^}k'gye8mQ$~bmzneO.;c-Ͼ-GKZTI'(˜zl:ڭ[qEeIz*ozjۥE$ېxk+1J7  [*,~bGv~'5]\@\[}e.zKvm9,]piUW:Oq$ZOܬ"W)U.]Y\qbbd< J>h&4nagXկmlK5r-ʘI'cDV;2l,gHUZ"όC"n6JTRwtEtEڴHP+0r&oڳ`Ա{UxZY%h`JSmmJ*eI@*"L I}xIk^ŲWhd%{opX?z?t#q0QKm<4K\aqUXCvmkŠ| G[ے^?8) ]=xU:;r_tGzl}ӭ:ykJR9 đ:;2_uɞ纬8eG Ȃe>3Q#"lW&3BkZuҴRwU0>k.$W|K::Zh*-/*q7` Bq &5..3Vv%dZ_=9}XMbjLşZ<PN!IP?if-{#(740{,=FU/1,ͷk$ uQֈ(46+<ݓq^k\.)ĩ)X~aő|Ab>#]VHO8EsUh[]Tԇr|rND꽰wݎ+X(SDi %*jB_oo0':jnU\ivq6/au&O*D@xoki7J^>Rr&Kڮڙ3Xt~~B~ O䷿Z5[tD]'=pQ8^]697EkyQKj{d%,Ҝ@ɩxrgJl, g(-6ۼ<_\su {l^wST.gn!U̓s&E~2ږ \\էRGEJ)[ui$ui×M^hm9o%Amml 6bSd\nq.mٳL[TW0u䃰s᫖OWԶDLt`"i \ ǵ^([ZDo:ph@v*kGRvCOgРGk=8OK )8qgSJmNFd4kg4 﫲^y'5qaǡmP%Ia0+q55$ #lăq Xkn4i;V) 4< ɎI عݵ{]#> f]"ʙ zxMVdvOrtmHe0M; 8*z3gPZ nLoh#afi)n)G|Xzڪ{{nJmIȏQj6{J>] ߻S9~8ծVWOL* 'JJIbsh^ìxd9}APقh$-~_жA8/I;{QmXqFaTJqs݊ĠY#o |NVff,/!tt+js ԧ80:. 48cXgWbpy:͔Y5(~=>U;k̻c=ITq )5~=L U;k{ێA3'TYr]Y1|i^(ttT*h "v2uOܺOQƺo9oW5#==;[HkqglUr9 (n6˝/Ci7OKj>/-?E+Z[Ȣaޘno`&4 4cu!}2F' l'N1D}~]x*@JKu8y%!(JRf3j \~ KPPK%X;yJO{Tj%N%=-ңySOO!?{0BՁ|e~ZЉ1vwSY"~ܾ< B7TbxJ ם<{G_ep-v7>?sR?/3}%8$Տ=0p\e y R!@ÿˮ?rKpΈw\MQiZrZJV$6A?'ףM6/!彄moBÀey%$Ts5gPhrÿd5hHNèWJs$j??ņ #%׏2 ?Ry:fԽufRI>_:a)'3hY|SƏ~sVKM1D=Jl~ZsAWwi*oi-ݧ0|< Aw]uaYVzksiYR֏N$]˼ՔVѲTJ0VF ^K${f '98[}fTK$@'Ǣ+5Q:׸\WJi6=UdWk[e(c)<>c\5£DNuS~ҩw :Ŧt;l zWu}Sn[|v.+G=M 7wmػ|R?op.PBkZ?} PǔFiE pyGlXRwLZ5}]ǒAwy$]4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DD_bcfg2-1.3.3/misc/logos/bcfg2_logo_300px.png000066400000000000000000000647451223671746500202570ustar00rootroot00000000000000PNG  IHDR,s"?tEXtSoftwareAdobe ImageReadyqe<iIDATx}鞴9%眃A0xAQkDEEQ1(0AATI%g؜wBwsfj{g6)ofzgϩ0 \%\+\%\€..p p VKK%\%\€..a p p VKK%\% X..a p plX56lsӠW.`Ȏ.aR kƒwL'G?/V}.>kX5X!.`8<*;]Q*}AAԚмq=CKh٬1$TͰe0h4YVU$R9h͆)I2u{-<}u{}[X zwk].U\A~a!lxYͬ fu@& kkupð+* m36\t!YFV,Z ibfɢhx.X36M=;x9|3orxA6CKo~߰n^TDůG;acy3.T_x|>` zr*}W&+y pE^Ĩ$6|鳡S{aƭK?v bOY` h-9>L ˫i0or06u87wHHp +l#1Š`Kob,JQQq ر@6#*TAUUDprB 2H8!ļ^~vlyM Jm(aTK+h!l"`6??knX'栨q`'\l"a t81[ŠlղWߝ /?>p VEJwu)RH lTIƀp*$hjuׁ:]0gꍒ㷬j={ͅ ±c#1\񒜜 7pCb v\38p"Zmp@ePL0b\8p@˥+:o> }o6/?%{i)W맄VWKӦMAӀEnF='* :+bW\4jlԏxIvκ=p 'U*,%2BzB infx!pm|n8.."BxCjLF1Ρ(T?r  0 c뭷ނq@dT€Ib7Ql4(vաSڬ6  ʉ@ EC.)O"89y ]Qry<ؽ7s{?xʌhUU 5iW^ KՃԊ?هemEVM-15Dh{W blǒ+ً?)&hsṔ: YPlL4\ջ3[׭ ёLwH'nn|v/@Fr"NIZ5<^8>idٹIUk#p9B5o(ףxY^ 4 ]?:lڥ*Y'!M@>^5Uh6TLxDmC,+i,o 5-8 mꅬpZ2ю1+ɬtS{Z3(*BP)v#R@Aeft}gY ޣ[RB, DuOgȴlPTl*=!J>)ŰW;d`9u@}8yuH"mf&(Ȉ "޷^i{T}VamwNi+XirKKF(",uMܼ?汀E.=W[ xn2 Xw#H NW]~*)oj -' ^$V))jA3{MT;/ʄ+>"?H/- bj:ՓkN-㠩ˬ CfH+6q`FǕ˰QTTs%^  `2P9 t<{{ٲsMիW7555!***Zyr?~j۶m֭[k 6i|jW蚑qv=L@-7 <כD4R,@%ŏE֮];O>-.-ZhS&FcǎݻkŊ=z1C'볬hYYCzNK#Ptp8Ȑ @ŸU=eRWX )XMSRfPP+9>#Hx +1'{Law"Nء`S>(x0Io*"E> kTw][Y7gmTUٔ7Bq8TN*NV+9 rNG*NOO/J۽{-[C@!-SdD00*}mڴiv:th&M4c΃%''pt7Ð!C1K.];k֬Ybg҄$0` 7nʕ5hРZ}XV߂X.>7x$f$+.n#FAy8inn.|K/ロk5k6W?5، , H<P# hݯ_;oZWRbN璞Uxٴ;w}͚3gΜIU)ˆD:oc r`EJv]eJaBvM)#l|Ƈ+[*(~EC2YQYYCPiy}€dZcb\ aj ȶ.nr#U>-z*dXN˕Xz4aZU+226l@K.1i`룏>>[)jό `eXM>bLdM~雮+bqիa֭pA"V~ "k9rpÐmzg/[li5hKM\zrtXuDBh>ǍwSfZ8"'N`N`EZ:Ì[N:%]ve>;L2{̙K%)DD#X>3!!㇎9bHFξE+"ر])c-.ٳ,\跷yg>[ MA,g'1 "]EA꧚Ip4"O@ a*dDW XeePܧdC}Sns ;a#04v%" %% &aH5n15|Og{衇>>ۓϯ 90`EnuyHI<|Pj3>#07pi+| TlPEj28\"!F 4ʅD?hR5vNS\"\01R|o-EE_7qTUCտC5C%8dQՓ&GFF6moMÇu]7WMet`E0 WT8wСR,O!P} `v ]GR$O@@NZn|׌Y舘_'IN:~g oZ⢱&ĦD'*?~pxd|s&pfD9A*e3POݺ><ڿ[ѣoիg1cnl{S^˪4`֨и#x[ΊDp9a@D,V) ^PHGd"/ 4 1E9VR$6 $,!8A`"UobݩpkGBs㲧&1WD]7NKZ5[j3@ oI"*A?ZEL1Vuq :: ^xhonݺ~C}. n1P jp—۴iӎG_Q UeQ \ vn"y q<Ų)[Q]>dUlcLVZ E5Aij>;{xݖ~I5bbG!"WEe]׭v#}tO iz3=_zntOTHf>Azxo㱟EpLvVz>|xG;g/(s['E۬AF(ݰůoPzZ:!ܔ*a "x 6:run@) SkgpuA@X_lcqMq8Tr8U4E@;QL8)l\qzEMgjiI-m{Y{%Xtة_ BuyeDV9 viN {RDZ\r;"Fw˖-$}sڴiwBJ?`. *v)ԩSB\]CRғag#lOVI!=ܳwߝ%-ThR5܁_W ѢEFSN}F eu:vv_ %+v^ hgNC iN'sH"!3_ktCi 6i8Pjj2LjNK9XHA ,.z4 s-\g}ϯ/Uo k+?! )݇lRFб^* m1@S#0w01@~(  -b ]C> e," P!3P@d+^tcC;4VޗnjieIe2`5=́~UH+VL+184ݐZd?!&14 @vӣG|uKc[hWWo6񃝏&2ҏ!kc7n<7n\iӦ 4\}lw3Xg(bO?}Ǘ_~9Sұ) W^ } v>'l/qr KMu{dΞ={b۶m'̳/udQWuw^6y"m0p'Lİ3gv`x 6l-XbY6"y:cx=ኼKK3HBf-!ła-#1{,Z+'[LcTN$rV6kƫ, N(Դ[3;)CeBAHE' ETvThh\ ؞|\ 4H:KQk#V|$6 YdOtU=.EMjH~>4zٵ9` wG&ubkOAN>}^7߬ݽ{).kB)4E>wyu?f"(WXs} :rʐԇ]v)ʩm… !,_" 'h;J?c*mt知; 裏k7Cwy}po[f]n>|tis-[KqZbqk{z uVK_// G4X \Ѯ}{/tF3k{Gݎ i #X%0rrwZyBQ$B%m1X(+/4Le@}%~"ţg,KsTw"mO$.oV! 6 j+7Z-6-Lz!xJ馛A,ƺ*3$F(bE `4[oENdnsNJvܸjCFh_ #ΣOZ2'N>X>`܋D{/ޕ0~MXs}d\p"g'b]C"Ќ='ʸgn=zwyg. .v~}RSSX7gVlV[%٥wpW P 5hРd|az3>ϟxwp f9eQ߲hXtƱ֢xv&?w 68o2@"FEE)(T&cR)5 G6z-2IGa& T'4thP7"5xW\#;`3/gurԅpbY2)/G.ҁǟ+ɮ]طjR||P1YӬ^YRLj% 2777#0J}Wm ;^:A;:"ohw(¾qر(jv m&$!adݼ=y`/Yot~rK g2Ͼ?czYyR_kw_~/s]DnA:ub3=W#E=CI樋.֍cg9+F5ua9ے-=/E8#{vb,SQCVꤦYf rw)%NkabhIlc59[CE/ܑp0 nbz;u+{~+jV^}<)cunx?#\qee 2 ="fU=ɴ`eNBM o *ٹm,mqV+9Bhg W,3 %eCRL!X )W_HzˁWW>.HGP&5m2|#3E],c@DtA+R=7mڴ~=+b@fΜ9_rx?I}%WH>}Wb_xB"WRkݺuCɬAдqz3{r JG;=pEݧg6HmBH2ϺuV/ X/مE]sF^9f2 Vd|ҿFL0laNAB,"hQ-q“? )͓qQUt'1Ly;Dc批j4eEBxXU b;]טق+Kn{Wh\.Mgl2hȾvmjWC^AC^ﭴG0aIb&GLxݻ|jժAaЫzA+~>Я_n%])K,Ƹ[q#K2)ՙP-> ɓ'ӧO<; ֕vBIe Jgl2,-l^]yh@\/n"ۥ$`H Th }ʿ@qo޽{`߷#K u֭vⳢٚ4i\-O2I]]p,wW22ή]2񷴓g0}[CXP:PQ˓j'ֽ1ȍL/MQ85ZAqHbt`ؘjW1l*̧0egFtSգ" 1Rܘ16H\9 = >*n\EȾX$S͇7pz}Nvt4싏Fbg*m>YO'=bús,n)n6QDWj1?dCe[#;ݻ ,]Q:tP`B6D3g\,eXߖvm۶q2\} N`.((tN@_ P~y(:= qк0-+Vl@`ӢofW_,T4PR!hdSc2Ћ"-=(v" G8#J1,j͔HSԂ>xǎg`[=iJ&[=$'ֆ8)c ?kLd+ظHa@e]1T`*}X !Z1W2# @Ϙ_E A/2Y$c@]w5￁9 1EM"R۷o7u@ v%&_bdKtĉc˖-m36P|ۚQ' @viqK,a-u .rQCXGv횓ޔCIY `(_H ٪ѱh&MKvIl^2 5pvQ 8Ə#B&{,}dݻ v1ӧ(Cq[%EʱP2/8|M[4 `8]fQ́, ' {}cG*3Qְ1&$'e38|b`=De&MÄŌ#RmܞXEA`ɘDHU3MёΙL]*csNFQsۜȺ,6:j|W?7mTa_gӦMO2Xׯ_4&E4V!#<6^#elmڴiȍQV#Q}ʕ+w$VA1˰0#33OIJ#sJ0,ncފLXHam@C؁i k"&e #OMz' rm)[|CMMo֚ZY=P2ҫƅhW> ˎ,7VOdع{~ dX4xAɰ;~ﶁ8cF$QAA%>.'}x6) _l3Cĺ]QB⢘B8A4ƍX^=|{mSɯ״LrcuzR.ZV0X<QSHJ.9CĤGe%6Y~?ФIڦk~P"=e֭t6ҤKbo BSk`4D݋u+G Ai}nI'1 փXJeaPVݛMfAؠ|&Oa5k'NRA(޾]~8B9PD;c&}5"kd7KmRU4xm!F]3ıw"vT.@N\ 3F16`E]3]u@ Xf:zs LBΔ/Lo:6Shv&VdaE0k8̂`Ht^ Nm>^WF<Õ{D(3[0v@eܹ)I%f$O$"sz衇f(O|i9 jaeϣbIiE%B")YŰHF8 ,xk/ iԨQ } 03@E~۷oO>̓"3ӃG}ښ5kB$EOU^-SPɳe˖g^E%ӑ1c<82$%WbO QӧOg.]0~Wi_D#(vi/.-NesVNςx}Q04\.^f\d_Ih%_aWKDN.fwe@~$Js[(nIA~H6HF0"3b҃g 1(}6#;V@$7 3&LxQiΏiGplUədxᦛSLRY<+:\V9k&b1!QO];hbg]G$Eq("TP\2%m`y `.Xjvvv$bSb7MƎ0 ŏX+%ݖ!5fѿ VK=z]'ź"u.oO#; 7$5,+,EDW e{Y>wgh儁׶k [Âa*0-([Eh wPکCMO^?#E>؊m?vqi 9y-+([ک ȃ*%#l_.0a`HPhs-_zIAEtXUUdHH| }?o8^?yddҢə,R8'w p rх0(PbTTYRDsXxz.!"x;Y yAsSO]TAHTᅒLmVbឧ={^i8gd"Ŧ=f #;4az26mB~L86]GOm)̫8)[%&*GUPB c4F+pe]KNHemc38eVz^I! @&^pED2 e|\XFbs!`e=(lǐV.&菢RjeBjep0niVE |WIbuD+<b'/"ʹ0,!7pE7yʟ4|q 0ϲe˪'NT d@Ų8: An60-W!ȓ +Ī.)I9nȌO_cnI=p(e"Y(V~hPL{ۓ r7e*X;> ]F׈fnMͶ:&P +h^_eN/*%ahۆд~}Ը4]{} ;*8n@l!]M~: D1 g>ZI'9V? ["a+]de[5n ~ɉb^h|8*.8Xy8UV~Pfxl p:T8VG@IY/P=9ϒ.V׮][}Wa';#ΛHOv>ksa^Veb*ZNT(?J'$:t7߼W^=o?vӎ̪ղۮxeժU%3 V zi)lLEb+`.vRïՍGf bRnkiU9bhxOAm/,,=QkZFMa+\gG:K0ˌ;\ЯK{hT4G MUk>RD_Ы %G>rŶ`kϞ=)N޲e?4iS һw=)S,¾PwXFr~Ԛ5kcdVTҩ}(Ηaq@ Int@xv{=% D^qeǴbeU1a<݌UjZQhr!Cc_!1ҥ?sʗUVmח4f/,ɪs& +'6 UիSHϤ5Jn EF"d6 Tgfra&=͠)bȴ@衸jz3̲ߩp0[]u8܉{3ziZiP5P=rȉG}K"ħ(nݺʚbpB05']B~p7oޜMtgdd@_ŽJ+ui'Q|G;ڦMcI0UKOOgYZPB=C>)S(7aaH oC0EŮh0Vw7hРȚxoQYt(y1<<x!2 %[&/ʢs(i LQ$IqDQ nHk@MWw[ RйA-8|Ё{ҢErf˕wU> ]Y;)֍AmCZfAQaWx0sG]\W;)v,X|j( PJ;MFz WJ߾}/"'e0m5S,)W\G e[2 ưhҥ : Yj*Oц@23a"47"L\s;ӽ{{LE&|ĶMNݻwo YlFc/.wPxZtkc)B;v4ޠ6_gU/]ԃNd|=;)O[U^a\+dUۀ̦F+ڈ"2%}<>Cr8 XbW3f 18C  59D.[@Fx,@ljɃ-@B P$pp:?\L@@ηXзk׮> 6v*UVZh`Ϟ=90FȽ[f͚51A GIGPL!A5O]KG%!!AYQ_BFIT?G:uTh( .!`-@aUB Y{ۚ G{t$>?ܛ5eR, xX6Oիәp 6q}͝"ti \:֫ Ǐ=ĉoˠds/π=PRyd l7M5q7E`z xeJ(C7~EwRnC%$E&>?;< 5y#* #"[ZLydžl:XVBH(:DrI2s f=nt„ 8[סXjџ(:D˖-E:-V%psa;:|pu0X;eeV?/Mi751=m$š_"X8r#馛&s"V%XL1cBlF)djCrSyl}l@^i>@OJbh\\Q|u Xhy+T)vUZV&U q;]^!]s/<}RdrB*aVTMLȒ8#K i@Z\"gY1t$f|0db9C9eʔ[ƝP2hS(wuuFb)\@;T2%Cݛ }WAIftҝh_^G&8;2͛7o'NxkT$?^ CE bPr_WA~^ ~\mfбՅnMCH8hZ7;w:E^UVŮq\JV!wɰnH Ċg4X0}DT,@>8]5Z8s)ȋiUŅ=]ΥT1L=\sNARY0,Su."aY}b)m?GIQҫȌ'sm;r_FsY_ 7GDIJ΍,k}7Q]bvFJ;RѼ}(onCPw׮]l78Xy'+}[`&iba"٨ S-2 I3gjsÆ $K`_CLľ},**KS舠gwq'9X ت]ŒpӦMM_'LeXn!X=ԫ-|U=\wuuJhy.I-*X㻑%J".MZC/ Z"VN>K+Nj0)8Urm tR^.L^sªlr2MgV$Z? <)VrJ^$eGs4 N_Mn(sLt/so}y'0S@M5W:jѢE~x8 L"!#j_b/r!g)%P?]ؖ2u8G6$I&ڝ'nK/Y ZS_Er}?8{LU\3_^"1& x(Q&U =}?x!>ς@ +5ii7Gn8f< OBav@;:eh&$bQ1},`\^w2#0' ?fîg[-m_Y##3435aDK}>Vv?9ubiWMN9/c=j̘7HռN˖-[O8+WZ,E`Y4C UW_ޮ];t`,VZ_|%K8`M5M6mQxg} B7=+JE}GWT+KUEE6d:GO#&JJ$ˢdݺunݺnIJJjD%!9s&!PYs\m\8>؄3 aUf+XbM.#]rɥ3-Z7<">^Z-㲚`JQ˪.a4b61|)aL=V&PDk  [l>N1cnD#Cq/FlS9PˡA'k"#`ZN- -ƼQ^BlX^?a?r+@:/%ϤIf80Zyf R X,% &矿`PzH<$!e}zj4jЋYmFЮ R3$6VO!666F>c? S E,$ ؤF'sG9Su qAtoMÿFM$i=DF,"VbgYfǺfl) Z p~Jvr )l993(^q[/db҄0tfO X"R+ - A>NE"(ߒ4ʰtdk&31֊}#hjz 5tI!S՟C/Cя-|╸F5AU8KVbPEqgDҞx i)? .NdKcttnEOb lV-&^IEJ`nȨ=H]zMH^^TWeNspĐ`F%Clhҥ~6?m8~ {/!/e^Ľ +zk ,_-PR}=zȊ,^87,bs;Șj -Tɬ`ޏ0?"'_G]Jv>c]=zԺBkI+3Õyȑ#'CĞ^e~nG1|n,Ts-£UV"믿~bvv ɈSu t%%쪫^~sMP*mō#e-"G^b"У%!?n6me}l zfU,B X*@_ի%=?hn(:lXUDl1PRX&h;ԔK$O61f/3#,q,;c@)TT{ d-G_6ô'#UJ8c)\ᯛ)x UADـ>s7dXs `,WMoqp~fmQ2a翸% \HXvKOO?.R'-̟9s3x Mf޽Ak&$"7|;$#EݧD"DUŲ@ } 2f͚v-$NεdAz=den\8Ix6թ?-:_(9Rnӎ-4PZ*%RGQmfCAp9 ?c镲=,̦*,U*m VOeW9.ͪR!LB,Δ\C)AD^(rL`* F4B9xZThfd"GATd/T&'Ue bC?0agx,T"l,K,YۣGg̘q?2!ғ2#m"%G%&B *-ZӧO`'Rș}fs t6l3όEXvcI.%O⾄/%-6G?jӦMC@OfiQ'f舉9vm0E¹tYWTek4O'm܋G DC""&&M6j㻈1puR,ލ?eyh8(R?` ɎK顚GAH'.f#DBMURe)SXh{ֽ{?}j'=T$O-{a5&*ӝ={\[o۱c^>*V=IbQF-?~-۷@`CL,1ϞjF,r#:th)S>>}BIL'슘_a$VL$⛫xM7c JZÓQwM4&dWm6NXLj'zFGTm۶xoժU{ڕю/97ٲf2LW*ƹ׹^M"=&͊‹g&[;k-ڣHwΦ4tp}Ȳ\YJ8ע]L<'r{bPKu*kӎ@L>ti naH'vLc,}\\*2;H:''A19(ܼy+WnAֲES|x$'XY)۵ٳg/ĺvѽPҽ{. H,BS [n_,D淔Vz!DvUd IktaKV'W ϒmD(dnj{0Lz5$_+W!c߿ߧ~:"O~ZPP`$ҸYE\b醧ID[bUd,>,|v?3>TȺi Qrva*I$mWRPBNcQ(f  =(LCBl SgR7ʆo H UN3v|w,)8X>l}c[ DJ QKƁԷo8%̄K`n?`A]x̙r$ݔd P5h r^QkzqڵSEs&1pRRjB28px;.]y͚5nX> ~q1k珧-s;J/F*_Β6 4Y(3D2pV^ziN:@vTv|Ȩ8@QY6mھp?,X3$u\r%bq9o-[m]vYC ĿgV.ٲeq1ʖU3f0瞪@9jY&"XMQn+&L"1xj;0$5~L6? #I 2TSGÃ99 ;I.1) & ¤ VZٟ,R-@@kf*D 8)V&!xP,eBӰ^˫fF')MJ, q96:555Y,Q\$dzyLX[ڡB!9iHRzt-R{#?N[cQ,eJVVƼlRL$^@J]9U`Ȏ*osKb?d2XD‡^`NDyq|6nIT(%a?Emr8h0xٹ:m]2rnS&`'QO[I"|3Vy#B:Ą񎨪c\dC!"YL$3XkS9\Jr FŢ/n(۰r7Lx.2OE瑨5XBSY Z'HlNdrƉU Vֱ"GРQ^v? >Yke),/=U^FrQ_dr*k#M89h hOՒ 61m r ȴ@gP!Z' E=a mK 琙υeY&?ݪF9חYR~8W^-G r0TB\?* X~3@N] ny}6) Q+Cq]0Z~N`d)Jdy_}ţȦ1 AiIIl^NyCL<޴BCP+g$ 9uyMkt&p76D@>!?o@s rdSSZܗZ SG~r-AO'{W!=ϟ=[9I0⁕Yf 0'=g5a#H 'I[0ADC/}dB0VNNv3?Ūeoc5ߧ+rҨQʼQv[t¯!-S~Jߣv ۆD76;Un}Vv.LqCǠcee#Y-d@ " b[ir]bVn?L$"X<\- JUO‰~,zҙuw43Bnx(}# 4Wzԡf: #O ax6Sxss08|}VAѯV׃3\%\v  pWioq{bbX<] 3-ǡ0*zAβ}1%8C@YB(Z#H;B"dX&`q]2`l(!E'0`7᥸Rr7XG'<2w1RW;sge]ZQj+ "k"DTZ4Fm 4UAFђ "%`j1 $ƦG+*ݲ 2׽;󛻳.9f}wL׻_qH% aE$9v lwZrR *#ь#5v8`ii!q ng[WrX-t;ި\9CI")/6n܈ 6N4m_2 Z[SÇ͔W"QB>j0K6UqPsd!?:B n"*%4H~m,O.Vz+2Ēb*-H1SinI+R85G{R_dhxiP.}Ԕ{1wdVnpA0]SzEB%R"y|smw 5wQlvꧠ^DXN*Lѓ:A4'-bP%m>g&HbIb6Cf27I7]dN6 MMM &ӫ"}단q*1 [F_Qƀb㨝UѯϋuFQika2Lֈ%mx^q)CSz-*WGm8JFPq4ougqpzMxlGEC 9uQ( |og5D?UwՂa<_Q+Ux HG.y/Ly׵Nc2!RJ C*S‚tk/=- ])ۤBʨ*?^{X}:k/>Tt2X-@v̯_c? |ܩ2pwɿA(1ܾ)~0fMԷ4ŗ^&];|q[j/Y!NC%Fq_SSt{Ou1z |3`A2]؇xxVϩ;=;%?SO0wu1Z7u,+~J#EC&1H'E(-/mPezW&%6r.XUfR!UB.G[3eY'/8.zۜ%MeM)50 Y s6'>kS`u95h "u 9tmہe bo9ݾ *$XYcknr$RNI;Ǘð`1 ð`1 Â0 Â0 Â0 0 0 0,X 0,X 0,X ð`1 ð`1 ð`1 Â0 Â0 S 0Dd'IENDB`bcfg2-1.3.3/misc/logos/bcfg2_logo_800px.jpg000066400000000000000000001644131223671746500202510ustar00rootroot00000000000000JFIFddDuckyZAdobed2    !1AQ aq"2R# Bbr3S4ԕXCs$TuWcDd%5&6VtEeGFfHU7( !1AQaq"2BR#STbr3$s4CD%c5U t&6 ?@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ 8⧴ 83Қʤ-xUo5ih&dX5”uoml$Mkg}<1p{;͇^rJCmY j VW us2Ϋ]jQ\Z_Gn4WY1oBpZpǭCW]c1,JW☭%U[M4UYR*HJ'#5/''01םKO>/c_Q{P{Pu}ů;T[p c;OklM[5iǙ? `ʒY^Û;Ŷu\f[ԬVGT2(*o>:վ"-/ Vfʁ)?OY\'RL{,̈aj+=UMV`\sjly:*HU5u[̨wujk S2+iT]2[3G^e7]wuLVX%<ƦծϜD\_ oNJM_|;1<DŽ'aZd2ejZݙܭZJ>%QJ#Zyﴣ8&!y$ [Ov͛a[cKF%&5,=eV 7][y-i4:y-2zOK]d 27$j N!ip{T#fLey| .EvYHcǮ\-jW#;{5MfS('Vr.KpE>&QoS[S~=!D1[iuUQ7^&P{3od>pZWr\M΅z XXZ/engߒ$F%gGDy VS׻e2/IJyTJGuRͶqc[̚xpeaCiNK,Rkr~.Zl*hA Aw I7S!Jν57+`)L’B m"(--Y=+|i?N>0ho1\8Wl(m0Ϊkk9}=em1 t@j1 qltԗ ^l|*О+^oٱۃi}9^[y*]=hbE)\Jd\m4yv/,aµwx;eey Z%V@ :eV6Wpױ}$(1q>z~n~?e#pA)'ۀ>z?Hmqy@6ZyN8ez=&F}Hݨ 4Rjg(bm> @kQ[1߀)mQhBɨHLt@.u,XĥL ;Oz3p܉J6 uq$] èB#%D@pY]gvO_B)7}fCUj&2`$X#DRfeɿ8n52T ]Pl>]yxAowF 3$YR< ~Պ;z Z$9 omE>xH1>=P~GR?LO?Tb}z~n5?'ۀ"@*mp`ƳkBW?r@5 `̐:L,ܯiQSJs)E4 ru"i8(YMڕW?.\yn&jTɐ0dkNA^rK+ޒݸtew@xQÝgzV9t `!$o%SUkQ ^d2%v>( BIUR <=0Q5~q |%{&df{|(x*|z` ➭<Hڄ&L+ܲ*ZD@t@ԧdN+5ya+wlK]-VHWL֪CV)(Vv}R) L$U fQIV'A'6sHu @A @ bE552ECCh@RH& Ƿ:v8'hm:e^XK K*VՠT Tҭp{9xrýɤkr~o~?u^rlt Zم.6/?NWjjRKoH$m@+\WTmi<}|9L/qwSIuyq- o5-DTװ)E)3;RKYͳo6 o/Y>Eof}Mx0x6%ɭCzrSIWd)+6˻+ۙSv'vjf&ik]koMa, x-ESrcPշs۝HhOnEKNo[\x^o=K5OUҼ=Ӝ6SCp@qZq" L#Bס:sqKZ6*TL"Q0@B.n2,Y7(vN-ZAenEE;H4߳zTs}jjdXPqyԠL$L&b˅N=@muƽ-nJQPSNJJ &@d6I sJx?]\Bx'hl]ZzKEKLВ(6 [3\1M!`udvf<:˭ H'byp,53 qɖttgEZ=ܪʏz0\h;MUJQݿIb26bWB{]ݦ' [&f㏷.1H‡` ?o6%6h'6)>w_gv\%w u+ L"ÓW9:yRRJ,խ=ZC7jGERF>^Cy'xz|Y2p-eLJiR)Rgե j5JR_<`f{݀."U0ZV .jND6ro8Au#9S5 {/Ӽ1.]s) F1L1~/ a|r7ߊ>Ysr7ߊ>Ysr7ߊ>Ysr7ߊ>Ysr7ߊ>Ysr7ߊ>Ysr7ߊ>Yw~1ǩOʩ_k]n;jJusJeg\.MX~3iMkWm! ;o)w(YVX&)-Jx nM>a7'fKMKuVR ro,=5ǴS@q'7 `b Y \ҚWUVRRy & p'v%(5\zNe-?X2ڥ=&N`|.azY@z[vuL5-<:2R{Sgg܀3[DUmuAu)'TDC)_έ)&Gt's)+]Zhw'oLZg@f' @ _̻48DK-^P)BGf+ƶ>)OK,sH vmDns.r:q#A/+Rt![u%`jٰL۸Z˸YUԇuKQVi^ 8EX)\W۝^ꪜoaȑ%Ky#aRiOO-xd̽QnC;iaw)Mߗ(aL]__ 8myE w9Ǚu)gJ8pο\E?,ĝ uNAseAxNR sG}R)BE)RiKlfa_N)k#j%0= wVTz#I_J⫛:(!iAS|oi=g{^<^ꠄVzt)TM̴~t9⚕GNJQ֊!VX^b7L/"ҚGi^[5fdO*TRO:H1;]QRZý]L (˵8Xţ5דZͯ}IQKwmlX=ZR+ À\Q2a$!SqPGSɎx̬/eL^,)i"$-$)$"q)%\Rt,bkZkJzVY28By堽|ՌS Xvoye Hʳ8]ÂkZ/}Zz`p<5Im֕1E!I-D '0z_kg^P>PWq 5n-iu*W6&}zd;qi/7&]$0z׍wV7hTtu[Uz/2kA)RT Ȉ`mG<=]{` 甚J>V+0<|!lt ZzI'@D=zI'@DV ֭i4 KeI~R֡* ,8KJ:xUu Mq%4!RzC( \QN R]v|V7ٟU=cSQ;RHQi lw2W= &InÊ+ =׬P2FqI;uNH y倭#xLQJH[3;K5 N/+pvmĕKkcnJӳaRȒC PN+x/̟\ֺgV3at ;KD ^ts?E+ܟOpJ'}{>Rn8q*C[  ~V +k57]uϩۅ@.YjK[o,U:w iĦYxGh]MJ*FچiS@*V(&lPpBxov@\t+;V19Jj)<=z$ O@=z$ O@=reɐς?Cn(S9(~jʛYХR=.XlO[U꯷MMҲ67qډnõQ lJ Do٬ZvsY\u/*%6gQ73#3W>E+ܟOpJ'}{>裥Q]-O[nPNT>C:Ғ-'hRH"";in9n 0 EͮU%<:URT} LԐ3ɝ'MݡzvӛaƜi9nUnTAPqwVIIid֙B<\J g~9Hu3.n8l;޴eu6z۱R 'uBIuLMJA }؏ ޛᗃz* Tg;c`IpW\R~6o)xv{4cEQ.7j`;~M)exN/)ilqjQZV.,U{fweWdwjjH*+Api*;GNe%NT %glxK\]ԕZzc,^ hZHbGq|'f[du~Wep;gɭu W#yU6HskJ*EJ/ZkS+ҲBrR:% SX4mb&]5WJ>5~)B@i d,%!4⚴$no89~Z]?W_sb;{9J,hXp{E uCí% Һ,7=<-J]/NM[8>;v%D IbWl -nL|"<"+];ĉs @+=<*|Èm:jiOc@n8PE0 >`e^_>cǛ}ZxJ]>}83]+jj[˨\\q]q\tKZԢ)DLtu6ŗ\el6\oWt(S&xE#AUwLH=!ʦ 3A>FA+|ǯ÷vcf_9+w|&"6kM P([* OiF89*5^û l{&Wk&c(6' ڤ$xPb,%x/:^o5tWIf%Ɖ3\_$R=IGo>]F?0kڼiDZP B4zfkUW.):sz{qk_5ם&u}/CMAKu.̳PICnO4l;z񸤦YVts>5j֙g=*KG,峞(n('#͚hvV^ r_:=,v]J6ª,KS~rlLJ@jn粩/&^wyN,/F F{p-K=IƲ3 %w~ZIP*~1/͓9LǸS:( rH~/ո5-KamM2_(Iur2φq]9|ikr/q^=1_Mt.aIٳSJ*n!8ҫ=vMYE+M rrːhǪyj2('dz][lݶ9Or2*)eeuBT@%@v=h?֛ |KiXWJ1xg*A)SIqXRTԠͥ'+]))ۖSe4nѭۄ(@l@cF6YjjcBQWTKN+q)N)D&P#k|Gˆ}za[sax%8Rwʅ++}i$@J K3cϲP܊.:58%66Im2JBCԽnmwE~{(R<)mQ?TsgC>/E6M.'uaT.C[-O{`0vJQcԌ1tY7$7 ciϺx/]]:NvM+RE|C4 +t̴ʭ!_(7ݶץ#4R0m%H)R JA*Iߝ2IٝA.R{: R95-MjNggcSI@_k]uGEV#UPж<T(-Ԣn6o|tYn,{yHbݻT⧃S)ۏZ X}=ǭ }!9Z4Ooz wR-7- mU\MChx3@T7TD /=}?񕧿/{`{x2looVܾ߽ۗ{ǕbʭGKc۫jn)Jnx'liM:ujܷ@U-YzN]oNwJ$V%KQd (io6fѠ!pBZ7]nڞ;_N/)%Dv~Ca$ :ܮUO\W'\[5㮸*Z֥)J$f`?~vNxE~*!TQw pK5 YZۘ7W̤!◈ꄭ 6Kc oh}ns]Ss옴%WUŬ!JC3=^~Le-+J6/7EtlԐ9> .0#x.pZadJ wSmdm.:mN6R` `p &G<6A3@&(O:r7zF]+ Rl SkjoyQ=Scff1SU؍CzJj,nTWVTGͤM`L"[u.ۂ0I[pZj.G .?Pev$YWjM4-Y$iJSsXi7QL4Dm.7)fz4o\_8{v-U+w*6+ӄqIi뮾믾})o┷ZR(TI'i1K5V')<[8t&NIzK3?`Z3c)r/HffrURE Rv>X'gyhЏ8Ǹ:糯tl5ZrM8XSz6 m_RWo{0Õ]5zܑÛ⿰Ms*qq blqHJyE;Y&vϗS}wVf #-} ~%,$١rz- IFT6)? | l#Ҏ)x݋εy7|dVtzSsh&˔Hr? <SQT6JiPKHDfҢg·V>|t ߲mіgwרj_Gwe2r(lLd6ΥJ93/v| ZOw׏뾼IR,"<@Ozԏֻ~Fi'@(#>믫ZN{9}rd'%`%v(Ď~ȯ2m֪\}jBʋ^SCLoM+-Kwo1wq%SlSJغS &+ߊiۗ:,K/gQt̒;`HrĤL$=*.Y-_eWKógMT^tum|K5,A4duNɳIӝO^uN5Hu+Io4W:x<[&KBƳ+V*rX,7w4i wb{Ti-SmMcp8-Fmp{MO[;KRζ>cqEMm˷p%Ŵ;B` |-~NΗ·M=ZZUl|:` 4w'Qj`J{EE26iRA^(TS~fo-JN&6$un\c!uk{:Rum$z~K%r;9 &Ys{}zRQFJC&mo)KdSqSP54zfkMX[=$zZ)])UXI]k/('Dbր*օsFa=eGTCWPҶ:ʊʒ4`gHS)ϥV0L}U2V(ɪ?U=Y[g}AB )"Kp 8ssEZ5jm 0${d7;` G4*i=m6H%AWNO D@dz V-UQָW$?bpf #=Y$1ړj\Y-+\ScnB>s^}.Be7Lu\[RDDvJSoVj,LU&٬Տ[}]cye3QM;)Mh9W}M5BV̸K!CPTP#θ7>m?m`p>zivzm*jpVcWg19\h_vϡ#}%@04߄+XԬUW,I܏tU[Ly?O}ֹ>j{|sb1Rou__=Y5- `s@p/"_.WU%NV8ٔ&r吀#:[r4?HGFs,g~SPe "d2ؽiګq%+v*QTlϑfRޟ)0%Xy<SqzkyaCu!F:;uUU2x @gpZu6m#:5UW/qI{zE "ߥep]sڪ AQ' G0IcBh:Jd&wg~MI-Sȷ$0 5)KXTUaw*z4)No-vV|@Ϭ{ܢZ}N,߱DI`>PO4!Jm#BHTǸ7p?bm-4d5u aGZ@9tEpKYK5W*:T}WtrshNߙ &NM++ыo[ԠE8g)+@(/(s$ikڙUOoN^r1L.xʹZל+D_;lXdZ{>9oSR~:6_c3Ip-=$7Y `IZ]'LQ4IHt[Ѝ > ƋSXͯG\LthՖqy)I*IsZ\kk(#99|Z,:tʔ8ҫ[JnȬTҍE@h'Tu&'9+T3 ]jr]=Z}-(٘NqSlr4VI'} a#\?bq9ZoAd&I]EpmGg+Τ U]VXֻf[l5NmO-x5斞ߨXNKFOJ[wvCx[mIsiPJgА9p#XoMbW|B&{3L@ 53toGX5TYU+5RB6]V"K;&#awkZKD5rx÷ES+~{X%-lt33[.KǪ.Um%ˋ1H*qՄ$xL^A%ӆ/tNk-mMfjB|w%ҵߌZI=WƜii{_-Uu~|q>Ŷ2Hk7ǚ*lY+|dI]-9n['.'BRY>Z8BTZq!Nc|OC{=CKyl>I=Z9o5?CK?{NqA\?-~R]ޞCO;wUWU% 4 $Jf l%B`vQYc]+zѩI8vtR崨Ν7ϯg Ep{j"Gl ׫lM)YC ]*Ee`:tm2f*,g.C5kޭ<~7j3 X?z/0J9Z8;bo`u¿aɝI+bm-A֡ahrN6RGhRH|l9ۗ{ m[seSy>:vtFUN^Қ ԶXWu\>Kã<0l<pnrϚ-4_j6\sٵ䷋k5;َRQfKwZVVP65'F4i9?Zn"Cz="j֗}~Q͘ %w~ZIP;})||/:yk5Y>Qjr{Tee|ޘ -l@Ɲɠ`'bG?rԞ@/W_R ˤ@{Zx`sg@ !(+%vcͯ1Hío*@)3 a}Ϭ}#fnYUϋYUwmd$S^)JDlFh-.5`in$Vj {bT: }CR !o;2|qũ6x^)-OٝkJQ"Sodm@!t[C@&֔%)J$:>7` 4&ǡI;f)ϫ^>>̏}q|!#DŽQbār_ݜ\,Jyz`Q{pz1n&A(+Mn6uaʋm-ր}iIʊRH@񖰝ڝMkN/G8]jY =cly-]#ZV|kEߝ8-IP ;9GOx]E"Si.u.Ӱ;iW&*7n5U5k.ԺTqg}JR2I$/N͍R2&t0 Bj '',kzSWR4 ;k%K՞Bӷ4=CUTo(S8vd;B؛Ǝwӈo~~S(8>? o14;Yw]FES=oU™:S^%!mUl=4KKBR:#T֖p޳p-WUݏUƝ'BߋKdo)rifRx*T?F-IORT:jIr娩kqgyJQVI3$xYZnSm^'! Y˼'F8 O]8hݭ.ʌM*N賰ZV̮\83}3G{֫)ɥȴ.G7me;kmN.Iju\a3ALԌ3.]lɥ#prtm,\1?0P@4hzh>`rP/FMjQQi.n9H\J{ExI{Bs.-qfgC碌* L#aԇҮoݼQ[xxl>-oDuR$b'\nV+yG"++^@9 ǻrn]k/Hܣ>i4]E3r ݮX!##:rocm}]{߽禯'1oN֦ݸ ptYU/L#?uYjjsoZ w/~4MWۡ!ץmL)`Ǘٝ(' tjf;^\XyX)GcXjӡ Kꄿ.Mio+dv_JnZ7%λwu ]L,q2l$"lT_15\pYvX,;$GJ-VO~]VrD]UJ ,֜J%c'[Z>E7 jOx[?sBGOh(U:ߙ2@if:;ru>˦)\O$ذ,.qd.TT-\q (z%s2Y]8iE <@_ߩ*ZO:sSlǢtUZTx݀7.M/PDi/նf)0jJ]%@ּU H$4JF],j+ aD!Go@TG|mmt8o]wR-szBm IJ?4I 2e3Ji,fm[EbuJuRUd( ɵ 9NneW;YiV-~VPV7*EeKLT̂񇊍e&j$蒚<~i)*RV7NIlMc % ߫h!%{nBS f{^|۞uZX߭,˕Ϯ)0H vS`ѿ;zhڻu }ihHKe;J7լvKtoB6ZhVKNSclSy3*cRԼQ!(iJ]hTS2D }q ыݎ]څK]K[N0V홡'AS]Z.W:}\S4A?E)]v9Cٵ@6C^R[q{C)f[k7šHk}c Hۀ6˷Pdu5ciACL[hZ+V*2:0K֤)EF~aIaVkN¯{:m7?@s*&3ɹb֞ |r~3!wk]e/M,*P!&@.~!Ny5@TIN㭯ԨG`4{P8v[6 r7"\7z=@cgѿ;\ ZNέq+{X_mr:zkwx*fT,Өmٶڧl*ŨCI5UmKaKL2\p{enJ@{_[GD}PJ$ 69o/$ ]liiSP#b7u?я 6y[@Z5%'E[i$$r>SG&ROr!#'ɚZe65+m^e}cH*Q*ٱ)Op@uRpp*d[QtMsqJ,!V )@F{*uNOg7o4,7FtLVInvZ:)C[H-_WX.]þ͐P$ 4ўYp:#pR+U+c3 5&y{ S[<'B'z߫֔NUteIZBTVGNݛ"͟ٵe6Լ(6iϨA5o̗7V"BpB(*el:,aG~õަ]M?9Ι@Ҍ̛#%Mf#!3R LV۶/hrKFs.9tgZhx> {*_=c&l XVKddU1ҍk .T<6NQ. O \\BC^Т圄N(+Xl2xh}:\ם˞8˞Rh9mIW?pE<(u133 q]F?j$A^&.^2) wt+B?dw +ՊEs#SdE^XTs^W>'I׵I[Z9E:Q)ސb!(\@_ߩ*.zaRL~{x *iR)X!B\dJ46-KjTzJZe%Fz{Ef>?gSmK25Z%׳`% ]2DŽ@:խՍOL{mmQ`6OA"83Ihi}뻡ޯ5-]EK*=Ry&[}Qi]Pvhm޶׊0|`HB“":Wm[XAT`zV; jNJM6m!;)!5[NM Ѷ(E\0P7T}Qy#bR@> 5ϺWjVWX&]Sck{Wp7fQ{oUUF|G-x=v9xiec4%/D_Dҹm%iIi)7DFAiwm=-N=\0M#NNQV ȳ[6f@uI&Cg,{{Q^o$+޺o3ofQmʯlVBk+Km)UIRKc7߶'&Z|;O9Ϻ]+h0X~o|%w k΍iv?Hd*KKEmC2[O̤Fo+\f4QI:_)y[meWF*Jz56k!Y>*A5ԡ$NRv;7xF|qX| x}BE4;?u9WRl)EEmK{-(i>Tԣ! j0z9W2m%ٕ~x˵VfZ-'JŮ - BߩRHRXl))j@ k'Yз5̹z~貿7Ŧ_eó:|;44d \3<⢦e!W?WKgU8JzֳLsNaYԱYr'F.{.F6u& J')FI!Z-srwތƄY.>3"R6XtT9ERiunʘ)ikyD%/Ke[$7iUFF:>^=[WY1XmXm_+†El׊Uɢm dv:R|2QZj"VPIv,M,x. 6c7[oH# p<Mxi6{?]ݿTC7Xf X]DܹmF;U]|oh[ Pg+RLj-/9[uMsi%=hjÎ+`]wL ~ůԕZl(sD>`i'|6$6i.⊮]ߦB}43JZBgw"n*"n*{)mNܝILm[e-)_Z.Zi}UqҲAuҀ) D<׀'| 6 -5||Z ͶdFrqq?:܀3MiR6q]n_=;v[fVm\XYTnH 6* Z*RM[huڌ&GKş+MRAJvjO[bܥ-Ƥ%41db~3 LJ m$ .` Gt?0`;@)H9@&`s2 Z ##D<)&q.M"h2s -< RE8aIMj`i'=zi'=zi'=zi'u?kOR#3ԫxO.$} ӳvV5{[m}U-6Vm)sTҐ$4@ B>8QQA /]cvN ICɿ EB8Oׁ -3؎G4 䱖VtEi|zYtZkRh+[ #cFqS)<4p}PRXj}!KM;s}VZh|ӎ)) )]pľ{4|wCF-r/+CM2ћuTTy^Bm|UjGZRk]I@4"{M^>ۢ)T_Ud_ PJHKhIJR$H6# 6lcC9g}0yHz"Υq/qU}a^QSPMž]ԤV;yiaoC_M۩_uguL ߋ S^? YjWRQYpo6k eJPIZtWy^YJq˳Tm ޸SO4]o8+l,fakuUAʥO:dːZMO7|gK[jtiF4ZZ;"OY<|"Om7jF.]-8E;-ӈT’5YD@T7N=hG_FVU-)PO){'yhTx;7U2Њ*K+J,F^Kzޘa>EG!;%H&_ k@KS?=&K_\rzkIʻ }#́VeڔpΪw<OwF C#h"seq)ޓGt7J!ץF0]~FZ'\냜lCyuN`aYf{n]KF t&jڑZAP2*҂Dtvk%NS|cz&,ų|&52JT)y)u0ceVqZ1RXJ)68H7M=Em;sXǐOQwW){pdqo'-Y2zz@eϺ.c8\Ý~C{^:\[q?C8 ock@{%@M ]CP@YoOdw@D뮱g=EW[+oSWF isn9gހ) n%-JM|oN6my-a%IWB.I>9MRe]p[* BO\vs@Mysni%u}E0(@RWg`6BcgL6RQ0v܀6oR9W̷Oj뷸*TfARkq[|`Ox gx VP^\򚨨C֢ҷwsP~l;s4߂ [K5SVi*lM.WRHClfBR6F?nS\|B^=Σ_k}GV}=gkv {<H] 鯮9v}q]eH(a?7nj} ,!J,zvu;VrnKJT}H Vb*+b2KC<Kc|wV-p$lUݮ,y Xh8?XZTo$R=@%쮢~&.8$6N磢r|VH@ ݪ6t-gԀ)#[\UW6#v[zՍ͠,N@c x-vO՚E=O~RTcvK~ *V h^e%4q+]cN4U-%KVvI>D+ϒz:,WU]ͪzu3Y3B/;*wԆ*K 6a;4<:*-hT"x77zԕZk[X$/K)! Gu#EwZmBԄU!joy93ݴTr .Toj^4Rц-<8;6i][mMK*kهƒJiKJ6m9†Pp–!Gdͺ  EY@CUUo*D ,+(g9a]֯')[uwK/,keMS[|-ml4OjU\+]uJ[8)K$I$Ũ˱Z[e MД@@Ld'[!g9!Rg`dLD{#FlGoc~ڜӼ:r8#~sLg[LvyٴV}V=iRSOJcO4HRI Tq̩eĶSaoi>pKn2-XkF!l70)S 38-ꪅ 8t*[)@RG)YciF IF RZV"FV#~Coc;{חB?dUŃ2z\ޘG/:fU%>%_q}e 5//{}^6j(LMԐ},kƣrVT^3vBK|'M~r~N><uqQ>0sUTx6*P'GB{uKWUxu)6i&uNϧ;c|};AՑ_{{V~0X]K|h5,JRfd{;"Jto.ԮPvr7j|>̶| ?=8?U0e?+nlEwmN]5mJSrO("=RqxΚq4UƸrd܍*qjq摳xPJLGO$z&BC-׿K'q5N!M wSa ^-b%++@%/Q_FܟZ> 6[N)okjaIOx SC}>U|e5xl% zK҈>TԜ6!ί+Eʍ`+r]G#n;"ǐrc@[8Z^J`:!c!W9xD;X] (PU%֒ %<k ݛt@V?F1hiKr'v5-{ i[0}|0*%c/KAMWD*|S)3ɗYp/VI-n7*riS2 և[3v}o~hȈMV)ϝ}8PC26@>pd.D7RQmvU9;vl,N?;yj [E%Pt$ǣ!h1{Z4#N5 Ϩ::Cqm>G LHSSWctUYQTWD2WqPT^%];@WLrf(i$<n m(Yo[b;=DV);JH}\@}RKoF@OMvh%#V@~q9Z}t'*CV}ԴoTx5*HJA;Lbr>%mͫF!c.R ,y8_-5ihjoTכV6U`H@#q%XM ՞"ӥ-gٽjfH0HJ*tv1JF9 8ml(:nf9mlelK[mIbmKj6DZmMfnխYSO1=bSqeʥoX{[Nru+\iQz1\>Ƚ2̓saqtiӃOiBj)L:jL;jZPBkjyƌ MP-&CM^i]-I#l'i3DR)Kffm @ z*jŪnP )rxOMlIo^-hU Y莀g4o4䱧f}_;WRYmU5E2]BT$G$^(r9 p5ʐUޝM%OV+*d^b) %)t-%.'QwL9*;W4ֶ\sm6,0-dEZfާ4}@o+\mb ٽ*+Cw#,xU{*K +.U\B Hǰ3~(wS;;vw/qeMsU/TU}2'}T{ GL1CT R R̀m$9(u0Y.5Z-]mrnFmh㎧Jc}^t-%wQ`j_C7ٝT3WYei 婘FI%>O8?I+/z~0ΏR7om2ep_E2αJilwc{Z_^Cj]mǵ~ƶ>).qi-ꌊ*.6:› VBYkq2ʪIx &V9U[_]9~9WE~S)~dJUSݸ7Aڔ vUoݩ_Z OSɇ0e],W]W.5J%o+rtN=X4&V9u7)[ekrfR=6MEn 9qcfaubٱAR10jJUz[k[B2Ǚ>}cmPk4%w~ZIP3D4cd=9(>u=mK{@,'E!orр']8ɨAO7G?rHhe`e)JZQ5MOyẙ̯]+Y*=TTʍO!Z\uC ~(h>"uB+קiJMШ 0h>#`R[9ruK`B-S.Hkf[Q] -8Hi~i'G8ZGnay PMEUv06>JW.1UֶeL)y ) IRH ~vɿg[W+n!{:[bmTnϝkRWv ATT] 5A, u^=ݗ2ҕ]gx%}; tXvQVfWc'q 8phmG@MOORD`.-y=Eƌk^Dž;#xL9XL'4ɑ`)]s,!˯u(/U:TpoHwoD`rAzT-ꔚvxOw S*o'hɗJ !CArPqOâHbSѣ $v:x=Fؚ\|H-O@Iu,9{5F;|ѿ}Z(+ܒ,Z9L͂'zKO===:9VTM:+HCJ'a;ѿ;=e'k.hoRq甡=jKvƊ( 3B&Ǯ5xV^ךԞTXeSeJX$Rym;~m]Pi J+tt0~Ci})Rw2H0CZݧq>rEں|RS*yz嶄wf`0-Rܿ?6Q]\*+\`mw /ѿ;_fQ%z)I]*MzJZ` gmk0!9s@jp9?F]acun};P,0 EA -Gt1Eu'om1ދ,wrRo oB[YGT;<'e+Suadu?MxD=MoՏU>F6ai h?":jZ &A=%şmU,)J6O֭aBPS~7%c| ému5ئamCh_5bc<2v,x=8qW|}HV*U) ;ɦAJP6$4xy75uk-Q\ |Sp;;jZC<T ĒjjގKܼ\ސJ#um!2J ؔ'q9.Zm̶R7G@*%N@j# O~r"wߜm6t@ gƠ'S2JJ#YUyf)Uw [O0nS2I>#1YVߓ?4 >hg!k#\2ވЖJtX؃ޔ^٫3Jel*Zvl]Yg6q.KMuK(QhR\#w 옖6c &֊Yd1.͕GNkN|f=ي,{)vR>]*>t)\̻صŢu'ji>;y6.5)Yr|4-ejf˴[c"DM+Z&6nt(!c*9 9C1jqWM;eө%*J qcIO2OO#bu}XU}RU(WW#wf~A.c&?bJ%Қ_9sH6Ow]⏦Ǣpul|C䫙=o8ӝJ ;6tr 2rLUЀkߧBĈ rxޛq#hmڲiwnx cD/p%}:tI:oZe9!ຘ\7k>^QC\H ڶ}==;y k–K+S~~u~wZBKiS -@û믾>R}d)kZRyI&d=!ݝpf9M44/$ۂCM*uΗRTjnϛ}L9HҼޙ[@"l6@^]jFGHCIj̤{ʉ$D)r9x}sԬ~ Sm$7CxXu[\Go=z )j)_bURST!mAIZRH#h0ZtO\Xqz](! S_=vQPn)P T̈L=߸vԌ=[df5ks1KqJTv ; %k5FO/EI,* vl];n$#U>4<m`!#NSzg8ރY~ jAf}6RkE>:MKuʞQH.(xN^/,czݦxm*mەRfT9vI;Lwz_Cj9_s=Tϼ9Ԣ9K× v=&{/3wG+U*J\ !ChA-:Ieh*2P*q×H]lg\1o远> a0h(I{,#kzOn\K>L w}xj>'ӹ=e/}coze1۬6煢ӌ(HLh GAkGRnVZ;]fp3)]*R'q]"J(D0wn+rrϐX[J•$R JvH0apg,qv%o 4ʉS-Ϻq+*as3ҭШu-i*UwάɟUSLtHJSK]6s@^u~uN<PCy1U=4UMz@&S޹*&]܀'[$9YDݞXPQo&cΪ  ! <$,^(F<ݦS;.1{(l[A%$z D}zYp'~<.]0G|sqnʆRȦe@4HRAJj?mwB4 ^۬m2⺋B5}`xKjާ;@@R6欺WzMGZ]oFOkSseE+m&*kD֍O^ZxM N]/_ n9ͥXY0ZQq\ eC*(}^Nʩ*X~ou,֖ Z6e;כ8ƌ|͏Ewp<7lδP{\7MG\-tnژc -ڵ)j]A$5yD.k_\Jiu-oĖŰ[eX k}]pWwӚהWURnZԭ(\I$b_Kov!'LL+7sx\o]`@x=79Rc@!Cӊ'.VexmΩߴ:p6w?YW.e-[9` c(V9$@@{ ⇁+h=s<$c V^ΚPE'T.Qꌓcm8ib52S/2J^e@iRLPVADuUnKm,l^c{z9 --S0vEfͨTR[ 싽3%YUT)䋝9 1* wu N \ F%{otXUu )kKK*K҂:ԡIo̎b# {8!q(jBIeoӑUQU GS,1M4־=XjsZPfs$'o~.{۪q*8:"+ \F_HŻ\GB6t[LI* =Re4&NvBm^EWF'L}h_RHeabN#&68wmTsJb'[+[)yhJٴ"@ě|֎B+zkŅ{\OlI ڕJ(rg'6X)IhOn 'Z/.F/E3чnXѸTA+c ԼVz8ψJd ҉W]e9_TZ,yrvTC)6obKBIiۋ-{T[qr=PԵGLQX-)I'-Jms JVΙIMYK/eeg"ZFwR Tzb^Fʨ/&YܜY+y% E՞+zvѻQu|X=\ݐqv3ۨ=ʴZ+.N#wf2I⢒gIŬ|6 8H%w~ZIP?:;'y@o AN\7\DRSS n4~6Y`ew񈯹`;WGڰYqb+eXIu[r]jUGaD)K@G 5(=ӅG9%mK n~3Ϋ mRBEeH-?TyS6rGī^ȴG(VӦRi2 Odz N`hx% 0`F_lY5r=VEe-[*ԺTӧj V %T5uˇ̣DEP/pq_ruU-A%yI<y>tLQ!V\\uLj+)l}*S[DЖnCǎS2خNNTm5RѮս5J5 P<>Lkٶqj=rnW[>&=~7[G<_ >͵yՖhvNڤ&tr* em7`gW\MBY[M5#j% ;dd) %G"uHp8@)Vƿ~vz7` dhOE2݌x-wIԝ gɀ;LJ]hRСZ&`w;_l1:.UL#w;_l3hKTG*ayMCiKw~wGfݘ.eO :ݽ76zYu<,PjIRm .P>XKЫ{oNjI<dT0\<ˠ]d-k$Tv7nA3jbي^,u (rq49)#72g੊>W \vqí ޗ px+5\il︹Kx \= Z0%;Ml1RZ_dbYME5NEjfUJnպʊ}h'lN̫Ў 'Eu6E;:[!ҝ7,Y=x.Wyt:S~cޔQ:z]}}x9ɶ~OX &ӓʍV}zx>_yt٦LR~`tND~[}}Ei} }8(^ϗ]sl:sI?}z<{wˠvu}{XK]+**(QII -E-qsV2).fYkYUMUhɩZaεymS9ȞxFXx>LL3-iܬb>sZySE#ӝ`)O(w'WWWAf[%IDÈ$*wu;엣 |v)C)%]jH黬*bwz+:%K)]2V_Kps's̺ ]Ah|Σ6Q{G :m* UF_iIerLJUO(S{rƚ(<Ů66WeC!K^6i C^p`k%%aśr_P53>$ŋ4BJK̾H*QM.i#iIZ`adu3Il7oir4Dszҩ7)ko:5HQ0]<TH KXV-tWPOx2S2G/L1G<L{]0Qt1G<L{LR,U%Jgj $Fb-Sx-DC|eRj0S]tMV%,{ՕuJJP>܈2 V1Mz2zn%Ԭ ` HR6$ ٚBB7d m[*IDgZ骪TD+IJS@ 9ejt^m4CP4Σyܤ[ZNd9ոS-Ux([z 뚰:<7R!\!_EYeX랶^.4 V#.H0[0>w<ùF2̵6;?0yNyd60Aφ|DXԷkmuNPTnZ)ߩ|<]$'f'r]jN wze$u( ޺*pd)@2\|1e iK!;dbIBtKIR I>HoAP1(^`z2xcSuYz )A3@$nAcj1J|ٮ9RlsdnS1"DP%*$֌Nuޟk5NEk$ە %BXtCRT{@5O| vሶI*B@&Q')ЈN R;(q IT=|;Fyр;,ULP9[[Rj6[SdP{z:Yح)5M.g[pqGu RHݨzbx1.4ե-tKM:ҒH*l MA$1%c*2<y=[:E 7}M~O :Zj&l"MPaڴmNOHp _xH{Hik-$hrE1uTF B9@=aVÝrz)g[c%Sk:澪b=\S! G$!rs_UO1\k#ˀu}U?{9pBg.kq~CCMԁJד;`@LߍΉhU48T4_­.e4w BghhٽnKJZ׀7.ⷣKFXy 6wϬpllv4oD_(T4qgVZx^h֙Um5֔ 0eBU'ŘLo(2wl˚*Q] 5MO-S OQcl]?2ΣiGqPFWz݊՟HvARQjcJ8kaUݰ RQ[ԄW?AArK*w{p9V#(ݧdeáqSQ`za\7#{w^[vm)9bz1m`ԸVWgm20?x2Ob̻nߥ~2{١ǠOOs9/1+Y_Co~ч+X~q:Uomr2x2_cWoaǠ3Oꬣ+/6KC:]↝.2q=XcŘں F/=hm>n7[R;#)C^Qd_RRNfMiAֱ!&@|-jmĥךxרgYmF&vm=z=߲ͼ%CndVZ)k}(PuUdtR#p%*<Ďe\{G_"~3+*Ƣ;u x>u"m1x+[mi6@˪X{}ɖuz'}TLUڽl(ʻ+U{s2H=I ˳-[@~m@¶.ڀm&]? Lj5WA;j_J5%vS{"Ϗy:p"rGvj{ksMER2MN%J ؿdP[ރCLAFz]Z&huդt;IIo ( a."͏SжJy G$Dh ZYJ3P|LhHvtfr2 3lFimB#uW*^?RTP,}+y%KJAQ򉔠 kdZ5wLa*Rd<yϺVeNUкnJIk]JZfeAMž]jXqEVe%eDȟ',w-_Ć[vf7| 3.ø@4Ifk#x4r\|U0f݆&PBJu`JE*.Mrletܔ$|A(mJP&'3)l[6Bw@KHC% ٰ(yukoyZTL@؆)x%VB9CD%OJu-822PQd.-LЇպpilj֌zÎ٨-9NÍPmNIBMP䥾$eT:4'wt$ LlI0d@N6TXB L utRi2 #YrV Jτ6$e y-Hn\n@$r"kKEsΫ鹷!'r\NIiJd|0Y&_]e?o ybrbrzK\%hci՗_q!MEԩyAHHwy ^e<NMޢ8oe`\>NXpni+M?7Kąr)@(;S^UIRNͼ*ͿPYP].9JFE)%KS#=H;4FSnuWj(m0x%'[iaf@` tԽi KSuZ,N3*RTJ&c[i}MH-ewqD l-:_2=7/0ۡ;^[a]Z9\.d:gCԦ, y#W=wE(qL :S)'9ͳn u[XYuFTtTo(±~PH<r.zs?K~~9>9? {t?uT|O8?g!3zG}ꏃ| '# >OSҗH7=Qo?a\ޜzR:> 'c3ә=OJ]#wCPGs}rzs?K~~c\ F}.ocz?z1gapә=OJ]#wCPJrb.+av$-bdZ)ұD+LJwYjՕI9I޶.FM(+$$"b2i%2K'!$@릋ZO5rqewT+2ʇԨ *-.BjC΋w {h֤Tc?^/xVԎ.5MKnS/PMNWJܐ'uD":#awB5aWqy2Zuִ%|n\2zβ,v+!.*4ʷhAٲ.j0*M<@Uދ*}hOެWBf1uj] W7dU4/.m`KL|E֨eX*m ݊ؒHvA0XBÆlJ6c甐r8cXӹ0%֞85o*x[\bިŎȍ-Yj_,v[;5v.)l6iRT9>+$@+$@S8R7 \;`8F_Β᷁WaTSRQnt+̉ m{>ʘwԩBcr}|ؕo6ƥE]:=ݿW^57V֪%f:W98s RC$̽^O/.1ڞ~؝[Gn`Y m|mNf#mhI7+OU*5,7k[zbZee)-5WbQ.nͻb^2lQFVf8ƞ`G:QYn;7 ʥt'd%)?{sEOqn]w;)98 OU[MY26)7t_*iA4i9x3i^JK-]/蜛.V~3y8jO##e.^I H3)~ETWN '@tWl`>]:| gt?v&kKM@=>gow&T.Km34x@鍹%NM>tr{K H @Ch[x|ri*T>\M.Wo >l>$*m~H ZH)2EhZn%L6D; ͥVMLi)Ԅ %BHH@FQ&m60E8; BH@"O *`Z[mQL+~O0Wl`>]:| gt?v&~ ퟱLΘۛ;)'f!KGRL,'ZTJ @f~ɪ3~C1?g;uK59r.*Hh;U@TȕiKL/S77{pw'UX*+O ~qؽ)-{ݑЖ}rW~.{D^پ[Vk~6͑uT_;4])U0{}5l<_Jؗc';o{_e_*-WwX~"r>W mj?"(՗"\jMqHRV= ԖS5k9&$OLڗÍ&3JFKSPZ]M@I&gbTS<7)w֚jsGϯX_h^\3G*XKI/ yz*X#.ܝ֫Ƅ|r| õm[${SLSػ iy9}[J'exm#dw+Z1Mac3̯*]ʭY96w>N%$f" W9 OT>*niZ_w ݞ~ϷڇZtjRǠ}TlIt'~(*2+.I$Z /)\ESbekQB2{HtE汵2L ~*enS9}XҚq+-%pU%%IpѨːZh[ǣޟK:t2Ue VSqlק}hF@5S0MSBZP (&*J0qLd##._wU~|^~7e7ƾ܇} tŝ}xp=rs%?wW߾oKDu4oWqVo4WBjÅPm;J7=.C(iW*mEy1H=3HVuQᎬzU@o)v԰ Ryd%BFRdx}*)WfBdds%koSǁ1!;7=_B]gu~m~qLې.o?XZ_Z 3zO[o>e;W_nJr2naaʞ:k81Hr-E'g^~9xc[f-@V4m Y2 H$Z?7ds%kŝ}zArs%?wW߾O|^/qٿ;oS:롺遣zjO vmou]wV#(=ݦޜ㫭^d9yӯg8 qxcx2*r_ί0o:ާeꊕ!zto!yJQ$$6Zy)?L>Sl#>fgK˵߾㻁/׺g7܏>gK~-n}O;N.o<\O!m~wJv.Ԟp.ˣ)qk|;.!.lVec8`VCj% FW?{O948b7E?Ed"#wM}0Qu ^FycNBd܏Trh2S{[S^[% +~i,bݒ3R4բ C.PclĨTXJ/>nZ4NSOgp~Y|j_:U@IU=lsBIp䢖-zգJsiE,[K[5*_EQXT-5N:36ޛncGv &]*V)-2o@x1oXˬ2%+_@ 9VeyYE Y6mMseNFT;$sՎ"w(ǸD*wPZ.SI/۞ʴ˭R~.}:5%xw[wRoĸ[-/b gbw&`KV!2z+uSӰ$@JG@yVU$'z[7=1" qTK& W:R3ޫG":~r%_!~Vu[<_}-ȏC2W?.cG3Ȏ${PG4Ie4h #Dh zY>H"D@ 33@> ecXS+b'$(wbl-E @RbU &ɦgOs/^B&J@7J)*kU@6s.T֒eh".rea) M V˥5ѡ&VA3S!|A9RI:kI1ѡ "WSH)rhp+ Nx;O"&*T&^H"F&VSL4Or$QWMj&:Tg<ؐʕR'H+RLtķL$1L 6sDTEh&t`l=JȔNL4-`Jd%Y:$z) 1'"`\#i"f?NGY`xD/YGduٳ!##dY&!=G8 @ s/1m@r<8Ǩ70XƝ SOPHq%BDJ%(ҙ*Tqk~gݹ}gN[~GE~w&KViV\ޢ⩭mJ Wom(]xU_\|+hZxZTeMz◊[u?+-},*T_FˡpjL%kٲ wYZuudL$x_N_e,XŒ\\}s˹E,A7kV'.n, OEU^hޫ+GP/CFCW,"^H!͠f; v{qtmd}u %RVqK_9 xm_v>Uw&XPo͗#j.GGf7w0˓KJABf-%E tTad}v#};NYaZq7`6q3Qg:oE$w\9R\e7~ZJЗ!d7$ŝ#3 ۵o?uV̾_2,]ˤ˕HDUԮWYe%5d"J Xr윤W .nr kpl,&|F{($xc41T M[#ZbZ6b]*o NWoFҰLޥ|^ݝugԵ~|/p|v ÿ[6(blo**Y~ q痵J<@s[GmaF X$\R@J\?r9.캯cF o5ȏOYkx >-_<^}kso;gُ%s@f%@꼣F:0=-?A?/uumqkS7B,fn[_Ȏu|~_fV~}3Ȏ${QKR5Lk\O,I"}\YX"|kGZNT~P!ʚ;)ySs ̦a3J=^(dG>s.&JO))*{]Hi~cbEʞfG|E-BOZdO;I"OaIL.tcWQ_L<>S2OY31sE<$Kx"D)+!ig2Ua1S|eJLt9L7rOLTܣLuPDTQ0Kd^HNL4-v/"}H-dnOT7*{ &l#PDQGXGz!zȖ878#z27Ow#ȳ~LB@ @{Y[5~NZUTu ]e_Jn!E*B(2qx QRX=)|W܏eNcbۆqKR.tP3Y]b6 Ӹ6oxRx=5n)ΚpmÅqkL.+}AQdٟvfejJwӬ+m)%*B m{i?d8bTkNL4-.fJ\.ӒV{KːHvv +"ǝ}k2)!~k ^>Ȟ t3n*uLPU*6RFAѤ?3| CTko+U-Z!!2HEœ`b$KFάTnu%MO[Ǐ]S ? bT ,tf[&~D[s !{p^>zu.9%$wVU̲{WS,X.>2ˮ2d'T2+!~&wdM62ZǕ~)ߒ~@?gGPJ9aK~\{d3~^eT9=gKٮDyh6dyWQ~:?qf<QQCo?{ԕ~[}Gh8@GZ9Ͷ=f?omO}oeW":~r%_!~}Y+1fπV":LGʉ/YK#1Sr$1L yCSDO1-Gy"Y:0ĶML"\G=SDND~ dp#ܩD(SCGYc"Z'4㬎D;?'܏C"DB@ @ ,vSaMp2}z3oRQian5% 6!f ג#'ȷև~.a7~o͛ҶىoݝuW(Ӈ5Bmq5 bnb%D9tJ ] %V|CpxWnJ)#JҰӭбxx&dhn6JKwtJ/pk3]qxj/1aߗ)ݝy\aKF![( lD1{k!55\ʙFP9Ϛ#ȤD}VP==ҧ! =Ow;4qnTd KgH2|%)Ь̎V} +і|씥 ^1S:xc-2υhΤ]V1Ox ƅRT),&JyHddvG50,s[=NY*W_&nmt{rKosS<z_|sK"ʷc}*=|sKV}=ϥOE=͢+Qפ9NOS_GYkEK=GmooOdτ]}OoU5a8?bYVu&:l0?-; $BaTBY IIltx-&=Oѻ?o/\v>ҧ#tx-4?-C6:\UOsSnƞſ~;GK^ʷc}*/=E{H&^&ڛ-UAk5і oyʷxVv1xMyc-#nO؆Uӭ+9՛^NuaanAtUS5YKSFwتmm II3=Oy%wt.]OQ<7XGu;GK^N=ϥKG/Z?KSb?o/\(ݏc:g:GK^KOsR |] ɪ:glsK<~{Tw'b߼?*ۮmtd>/|^ P:+#XK}f"4b|=7E,K8x FԝѰ'Ӗ)zQ%^l%?Et}wUGRE 5QN$V#E4`ŧXP-+M5 Pg3x)b->g({RP؁£r @2Xėڎ`,9W$($pۋ[Z-Gk Uz$9/wIZ<=Cb y:rNiC^Sry8ujE:ޚwfRHs?X~Y>v8zǎ+92ħoÙT{WJO{}:ԥMmL@XI/_*s=V'POV%=11|zOk]d{v?q+c&Gs珪{3z[c'dzk5T5ُKw:Lj{"Uŏ<_|Os]2ћdj*=c'pWƟ{=JYReUh]dw"|isg7əd!{[wIs-Ɨ{75/SkB%ػ$պV*]ڎtn$-h/:jj li~NUzߴ]gݪ2tn UA5[OE|.=(ooϴsDqȩ{#o)ߴDœS}ڎOuڑ #YT=Ɉ7)dwuKt*ݑ+Xn%ڼKfGt{[қ+|JU?chUczmhnRU:Tjt$ǾK~[fԍ9]P qh ժH"s-(x# T6fbWNj}0Kh-|fޘpݧDY6]Zn'W'HKVu%EkQZ"UKx"ja(ztwϴwW\iRw+VޏP!f,{cIc*<{_7Jz1z;xx)|yܳCP8l;nj+1r54)P6AbJG ;HQq6gO ^4jZױ-pq34k:Us6RjP꘺iaMKzsp0;e_2βNkONV.&t{F=k-T;-1kѰ5-3 ԶL(BR6#xfQZ@ @ @ @ @ @ @ @ @ @ @ *b_YkݞO":vj#⼕2kQ ]XzÖyuFjvǪOLޤQW5e4(K\bp­8pͧ)>2<< \#ۭP2* 1f3qƐ7P -BzBU%-oKd@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ bcfg2-1.3.3/misc/logos/bcfg2_logo_800px.png000066400000000000000000003066621223671746500202610ustar00rootroot00000000000000PNG  IHDR 2tEXtSoftwareAdobe ImageReadyqe<TIDATx]E~=asewIKU@$@1 gLwz3^0<3*)Q" A$Mfjzgfav>-fvBOwuY {" ((((((((((((" ((((((((((((" (((((((((((((" ((((((((((((" (((((((((((((" ((((((((((((" ((((((((((((E@QPPPPPPPPPPPPDAAAAAAAAAAAE@{mnu7QZaBV+((((((((V ۖ9g"Ϛ7ie#/ΙtzQ؋ [6| gi #w?L[$+2l~K E@"P[ ޟ9Y4G#4aڵ˷ck BlPPPPPPPPPNرaдieAr) B{l|ϿCOg@V?(m׻;,PTX7rBw)i|쪬 l^1 @^NZkˉzխï(h|U0zw/Ɛc؈ē\gV3ߘ gO=1o9懥𿯿@x{hf8?oZP "A2p[o(k/ "h_V ~6u! QӂsxH>J  pVL>.koPD"QXlds=oE~$HrdX/TIAAAAAAA(W1lxSgx?__@^P'G^.9mκVxއ8hDXspppH.>h1LnUk~W,f 5Hwx6 iA\d؂+3m !r9?iH{s*n}fХdw=5z#7oU~i FMu;@N7PPPPPPPPPPсX?|(m9~sꋀ_OVsvEUx਩TT" tڕ'n}INۭ2 5~i˃'|z=&]rd5 yO>ӗg7*],Ǻ:f:;x{."|ʱܪُ@m> ;_^}' nYP^K{EMJOI\U5!7"ȖQWBho}9<29t<,{ǎAAAAAAAAA `0 n}ɗ`mu% djz9 4wDBr[~Fu3&DN;?f1΀_zNCȇ" (jn HFVoK LtERc]wyKW K'Ͻs>V: ޟt=\8v4Xii 4 0>? Nx*ᢳk6p{ނl(C1ox+ ScO|vUۏrbیm1*}x"}ҕ{עok<_+b-s[ [´3&AyV^)ߞx .~@V/?1:Mo?@\<\[`/%c zx_XWcC?bx*vgǯ?Z1 /!\zuSN%*\AAAAAAAA7]{'+0wrog O H5Їɚ[l}@׻c~tM'jNQ=ԢdͲ *W$ DLF>Wb[oV_eߞ;׳J|(((((((((oc`p9aCFّ]U;y|>$c;@'m&20CGYd$\t!+^Ɖ{Ͳ_k-X#mRbd1> ?5r>{ٷ\ћ.|-,6nUzZ+⡠n }exə#F swՄ97m}#0ːdt:j*-N,{:tVϊ?,,9'f?x 21a_𢭃!bw@F›~z/1ḓ*C֭ix*؉_E@ 56q6zyؙo~ ~Z}7 @ðzF@DDC,#1ɊeJdDNa1wL;V4=S~trBH<Iq&J!Q(cA>6nP^">|6W"C$COt%7X\HsMK~r%wIMD1a1 Y+nya#6d A3Z=3.ߚ_`ױmT/)SPhl8*߶M|^LE@.(zJg,~ ~nrS̀pBx;Ȳd9 3C[K\dkI F&(&YK1#٨MF ^C^d>f$' Qj+kO`[~I27AQPB E@8(m.E5N; *2K[>:#Fɐﲧ+^uє@ߖHйY)nf#!$b&~Lp>b&,&!"oG}W&%\;n[ hoLRp(4E5(Ҹ@U1Okˬ… fEYQ2, 7} b8\drᴀP L,U$A, #]xH,H]r%:r1 " $$jƯg 9?|>B"CJ+ƹ߮Ū <6zKZJK157Ѽ1Pbe]vMn)+++V(Ai(_ģ  2apBBx`¶_#!HBG! [$L]`Ip1Т1ph?R-5ܫ ^D^N@Id|itБdd8 GǢGy 3 I$"㐈O,E:X.ϵ:nEXeczy!- ;7}D} 8>rK904ɢ Og&/Y:D|xs'2йNN#M x :ˆ9P-23_@?G' YaVY vU2njPM65Ǭ!"J${KkA2!Wap2B#"#)1 ;-Tñ"$i L4ZhuVeaTK]m;5v^{R y^?@#| c_&ԹR\A@ q,}F&<L8!%,l uq389tlQ (iv#>9`pr= 爉pm*`ƭ|-DdO  %Bω_Ш?H|Uv_EiuN<aDL$!=_T֬ttpTWyNF:R Lc]1/JKJ( E g q8W4%'ht DDRXuuwE@>z`x n'LCaaw_QJm[B(o ++xyՑa1-ik.3}sm|ގ!$BUlYk7l ^nشvIDǏځ7r"N|6i<(Ő (_Z<$"6cZMMd~'Č-0=id4o J ϩE3ܳ#c noV7~Zf]Uhݾ>$hQ!Y@xYߙ삆j#O;tOC /2t)Z &@Yh.BٞnT5^:5ԟ&wL݃dxtFHw^8?gz׼hJq]G:W_cI֣&GD;3y !Y<5Rzu TacVck vi û.eP2"QfuS'V{+J9\] q 9h,l07 Z䷇CԪjXq+|lxϿ",6D2t sAxf-1d0V8)D$HkIy~o*VuK%,R*aylZ]BtiHHF<ƽUK )FRaކ8"l_Kq! !ٟdZTBPcqgHjFFx=ޓԅ YN*L0bF=gαt\hBj =%ɈE.3thYct&uIeR|F5I&@s1,7lVxh90w)v J spAC(e؊pNh ˅,"Ec,7a"'1bbRND˄p4k*Хtk]el=È7[;jD"AAYn kPdžQNiz JI's1pfp#ΦNxJAzAn pl+xy ӫS A&N25:dDҍxFՋ"s/2"ԠqZCҵ>zqT^*b9kV]h}^pl_]ܐjtH:0:|w,:XSJ 7#&J[wT<Y:WU1K[:D)1]T3<󚃹hI?0T`E!BBlٮ [w@/PY &9Kc\ܱ,NB?L4=uD7s8|q8 jslB@zcr{5Yn@!nSDžI&h% Lu.W.}F~t!Y:n_ݚޮk2q&B<,}{Co|/$ZK2/Yc2]ًGs AH9{ǺƲRhCKhNXE@"Z1-nLfB![=8V!MI硈7ҳ.>' ^ ]#%(6ŠDt4.G̓1&%n-!rAsMr.>oj^a$k!jíY6BkYpppyk5 |bml, 7XY4X|/X3gV!x|7~;pڹmB$4a"*  kollКP%1V uMWDv6Lc!—۱Si}SͱdC7'9Vs .[2K&HuOϓ7%hIA3 !t!V#e]$1\ t'^)/y(;ق" 4{)^%va|ҹ˕ؗ?O+`.]?3p;_.YZ py\6L7Su 6ȇScL᧻4v$ݚYMT} GuTaΝ Zl<###;770G";6VVVV\rٳ^Ҳi@񃝽I6""AK=HKAe8/Mxc_ 17ܭ.XƝs[JfεS| 1cƴ(...@eggdcm۶mCPڵk7Λ7o˗_~ZLE&$B\b6B=Ԁ%!Yl9#1mA#Q~0X^ 47D C[=t%C"#&B.n ;v)5T[a2Q8X6+,Şp@YbuyKr ǩ[(DJV&ox-+f#"i^k-HB؊ sܣ^_·edǂP _-dY$vѢ}AEo3 l񚛠T Xk.V+ņj5HCnԩPxY<ܴNR \NEرc˻tRڬYYYY-rrr2OD:;w\dݪUVk\xofcZ*"U$*mvJ5Ⱦ4>$cqJ SVEД1KA&}sλ#8u{D׮]K žh_:V]F"9+V4wܵ .ƪ9s欗' #yu;zc^5ȿ!G:+/Kkb!˦=WzrSd@H2.~#n08# B/[eñzBAn&Dag8dg²i5<ʂ9tN>l"bICiIP-U)3kTCX0̙6V|+nٰmnݺ]vU f0)&jpZ&A3yhmu:+k@r߯_ғN:;$ q ]'= ʋ;w #Gjm۶I?㷏>K,u՚ "# d2"y{gJKK2c}ͱz %=i c]#˗/Cor7 σϣ>΃Æ k3iҤCz(AQ$#-Z(֮]ÇCH7/ZhҥK|嗿32=7Ry 'IoKH]R<u (ߟA} 80y27BbF<7" Du#H8N/n0$A.XrMQCdkeSzu+튛3ǎm! Ϳg#]ݬ*`LVL֥1TC]Ơ4ubB7(F*JYg<Dā,䄈_+Li\biI <\䤊Ba6{}%EWr; 3WĆ|uUD2o|Y={>.lF.]w݅(}o5Z31Qb&!(PMCްa&To(lcM(nx뭷6!feyh ݴ@+of aڃxiv+16|%Ø1cbU7" Z4ЩS}]:{oO?bi{$h ˲bE?|ߍ7;vTќͱݝnND#kH<,gy/r޿2]ȾIf tns !GuAPXm4@VPܸqR\Ӿ|?{ꩧx2H뛘 ![hm|\7H<*35MnZ!l֣Ge!2~@o8]WKVB]N&'WAWi @~>~f ,q7cҗGXpMMM5 ;pYhѢΊ+Wp5k׮7m9ZnODDsټ !t^B,\ݳgV~ &,//?[f]Om@M6A`-+{z='O|%/?H4@OaIX867&jx-dau[pJd1MNevsE~4F㬳ުU6mNsVβGϡ0 V9AsC)gmʔ)i^k|y_"G]@$k";$7ڵkӧg-:c`gkj֬Y#hUU~5kRSV^zcuJ#[)8HH;pVf(5;N}^ *1J‚#29[y֬Y̆0 b^J,ªrqR%jh<]ɄV,~k6x * K+ QVE>} [CL~eX\`"l)3-H禘HN'Qbj<X?6[Y hTv mڴm 8q"m;~U_~3g~/$E ;4솴ieI伧ܴ6hKc>|x?cǎ/KI/__~-[:رc ۷1iM.W@6#x_mɈa:usW ֽ۳o׿+l&! `8~ I,RX>Rr1NyE?kP?nȐ!! C47o Vúu`۶mln=z}?DBhN@ii)km۶͛36tС_sYo̺ߚ7o7~ioH"Ⲟie#'SYup:cRkGys̘1 G Ox_u8C\~(cϾ}d΋/lr%r;cբܪנH (wdh${J!\ aadn`'.6ϲR"‹DD,eGcÊhk<ߤeZ?ʕѭ":iShTmm!7|7mڌ IcC& MF2i[z9Y"Cp˲ˢUEd1$D"#RcKÝXΔEk^0#1!P,[PHdr;#+3'iy۷?ڤIN뮻+0=9rJ67h^,^qtܠG "[nM4'(..={B.]z9/8}ĉ'"y[ny!p*R$kY} ^2i~i,:uOc Ra5kMxc58!Q$}¡?j%79Cr% BGpЭu1T#1iLSZ,N~F7~%WYQqq_Ӭ9xV,?@R ?\ EyЪ-hLhWv,]9^v8D,+Vm$nh3Rs@D}@vq3sY ZwXalhSn0;~!t0 92I[v.t*M32ĉS#.owsn~!{LTH!''m-Z`paزen>5ɂ4(TkqbfffdVΟ?>sXz5lܸ/)5٠F}ZD~cLEEE*r!@6:Q8eʔ3&Lp?_-\'Y%dnuBiA$y/KP;qYeRN1'Oi},ikD8>3Xp!D:>I;U} 6okd Aaz vڹ HO9#G/P/^Dx+0V]-Fu~\uUc>cq/N_kpY vU` s>[fA^Fr3d%(|8^q}O~K֨.H2"nE@9=#] xOr2dCBB uY8P5& < 6ѐ 4sq 3$C\#bi S]' &Kk!+gG*C>idp a5.1H>Lc[#q,e%1y`{n!(Xj^LE-^lE|pCڇ_۩xv3~D֑;t3qy&>ހ$\D-r6-ҝ駟fBey&$$!BiIg?++٨Q&}&nޛP{hH3t:շ4znhl3F͘1c*E%*"$\}wRAツ)rJ~OB?>,Ț-ݳӦM;cqm=e$nX$Pi$4܁{ecN!caR_<{/*3><<7:ujcT<= ~)kdEH6sIyꫯAĈȐ!C999l^3fM7C=41/q$V7kHh/ɺFMrE8N;(Y?"QX~3|zXoh%šD i[7σz@Ч]+(+̣~_uO> IʫW^y_u yX" @i Yl6ХlW~aؕiQ=dH4 {H>4|p}Eda`+љ-b l!@lMy(C%> ;mەBvN.D"BQ^~-fpAD,dɫD솕09cGm|Ij7%Dy\HԴ- V"hP$!|>Wgh i+He<KcSg2XW]MxpX۟ eoذpҸ&NNB5i\H>ʟ8q~PBܥ)YF9& qzxivbFwqСCGz$͞=iYECYK_ ]tذaBHnM/ov{uc_&"ഀpJGLi]DwN#v.a]wo>,Xf͚6T9Q 7߰yAs#dB'> ͋\pH6K =B%B6`7-ƺP!}Cosz(Y6_[3ܻgغF'g|~n]YxzҲtoC:-onʑS7bĈa_뮻d:%Z 2⶗4(I,#U}R]AIcՊ"4>8~0GGGMFl"1BDo㣄H  nឤY"C{h =K0/\dUB ṋ!L4Ui߶ ,ZrZ@aV2]qk'Qu8q:iJˎ~&t=Mb# .*۴K7` .?g5jk:H\TФY؈WJV@M,f- %r2ok^W3D=)$-ĜSG@uM*ܨx] Ӱ-LQ0L"@\JZq X|XչPB 屧Y .6D!+?Q ʻ]Pr Tߩ7덛6BMN fC\DJ,H,Bslˎ.NQJqˆIu43O4c-hv-]4ˊeŨp.^E(%pݘpG_,[m[B36ca>77 5]d|(9Z?܄$W ! 5Va!![}D K.en : <ƍ]O䝃 /~8s&B`^ٚ 5K"c=sVv]GSO}w_d,[YՊքn }),o _>p>VLJiiiGyN:=|5׼ d AOe p\eǎ{]ϛS/X@U$Gאί+Q>t5k,N>MD|>w~w?e[bT{K' ƕҳ5ǭR]MJ+4$DT4ǿ}:K 0mRV !JWnk縉p|bR)j-Ϋ+,bh챖 R.¥a,FܐwH.bdH6AҖd@a sWi<`W 1-AFbADE}' v \C#ɴ]*LhI~Mn7ũ d#C }}oS\#AQ|xUM{4)Mtxic9c@d$QWc=mX}9s6a„;WZC"!nDJdIUKnm!);1P`Фcr4̰)yz܇}GBf6A+,0jg#m܅"ڼ)@6o >s _~G+ѣG߱e˖JeMA@biZBxI3 ywwy[&rYb=:|\O?ʈ#xq#<IJO<^0,3a9Lg&&PdGV;3U<7g$ fMBxx@Ī *-fM*'Re/Xf(Ai( <()jc8 J@7XML̪mС}9^&ŭG#\ZA,?w`=087>j[¹kv\Hkv?q+#V7lmHYV`(dŒ *oOJ-`!:x>Mm?x w#-" 8ӟ߯՗T"HLLٛv]w!CoF>+>#_cqm~BD &:q衇{Miii $ $UV-yKq 4wsq#,LW"8I>IqDvmCvM!b^z%Yjmyqԫzeyyy׀9煛 嶶|\$FY_ū7hV/k7ˆ^_vP-ǚ^x, nޓ}.gFaJۅ9AB|Af9YmQ!.H!RǢLzԲkdP&ҼW2ޖ#{LH6y*Ņ~DxBF|57,sci صZAn~K+dΝ)6f'dj&?%7L~}~nI'Wߍg 3<¦+o~&4Ӿ+j,0j錮Dlr! 3ax<~If _s5=ZplHpQ(c46m FN?IhB- :/ wF$mCw H]k~doObM"fAB9sƓ@gD'$n.t꫿Aa8܄-Djկ˺ |(8g8kJR nK⡇bNԳ>{6s;{)>'[aÆ[@&5o+d(v %+k}g~w85]L9ei01+ڢ%ZCxݍmK"nC"x'w*v!W5)0ĭܕ VAW%܎X6Q: yan9Ç wmPP "gFٴ(~$r3T[' 3k80o!VJT0Y-b#VV ja-~ݸ߈ J̍ Dٮq5HB~8{ho s/9&HYӴ;\?3.uꗄpQ@7Yd=ztgnBu*<Y .^{zڵkk UH&Ҳ+Q%?±;G AFDɯa%0\b?~1wK.!yAk Z^{Z7)TީV]˗׿ 0Y6.Yxxj^To+u?b$U 0 |DkB3z%CKLKH{Au(EeVBobeQ%Y\;f ՚c,pbB֑n,'B/\xxL/l@֏ d! A  ' :O( =;7Zߎxd+&*ǭ0 Oa_.W1kI s֐%IV `SQъGy:^qcHDYn6L 5.aHcF,h=O?O=sa9$$'@.h@I46jرc;vmWSv'{a͚5|{HIK.inXӦM;gƌ2K= $*f3;i';@|I6DwDFDJR_~.΍>8yq{h=s|۫a unwuL˯Ͽ?P0+{ɰٲõי`ݲ ~xCTTﭟ ,xC,Ff[)]1rPg`g!%Z% β!XM.D[s{TSpG5)sVK" J@U;q 'ci~^uvJl|= P]W~7*]W8($&J1"[89<b΢H&X煫]*- C>dV;0]sc7sˇЋ.P?Ҧw}=T$Ԭ\>|C=aS țQ^^"77T՜\K~Xݽ%82%2 vjrP-;wjT>C{ӅHVv x_p!k{)Ȥ' Q- 1W=XƤ.lshSO4{sn'gʸEƿۼF97 !rNmaxvС9$ L'>*Ur)}x,}p̏ᣥkd j .S^ Lx_~#n!u,NdjTs3SZ7~nTdcAC_QdwAѯ;Qa:jOQ,^8 P tiYs&L5>D3UE[YC;QU,xcV&Xw5t@d#ŅPЬ9[c/S@^maגe`5k8C 2`ksa%b.e ċ$ǹ*q+'>Ruauq8tDB/MczEqd`IY+'G}DbR{;uߢ@Nݾ}(&:/|EK%!dY?))@oPH&F?X*ѽQÀRC ,DZ_5iݺ5U_Off='-[Y\"*$x1BPCYZ)&8޵jժ=sGy$֚CgbyȴtF]Zjjd)"EEEPVV͛7g„yA}@sj| K&5AJ =7yScsYM6M9k:ZkUosZ>D?%,`O|7̅3_kA2ت̕Zm(`z9piWs=駟ơj_aQGPHg(_c[87?GFz\gDMh]Ҁ0oW PCiZZ7&A( ~rY'QQ8ϒ,t, IKc ѭkl\gdYAȊVAnl2-g3#(dpAeĪ[6"vrl%'o5’ܩ@qcdtߢ+y n-6lDŽLjq,bf.sVi Pe?,_kάj  mK Soذa,3ǽaoR, dsهhSmHM)^9hѢlQ]]]3gqjr˸C+-ZĄ>R;w :u;FRB!AkŰd+ [idGmiP2+2FqW_=nfnGO9V_BOLj fB|gˊ|˗AF! 9] >}@h۶-#"u!47(U0 hVXHB.JOqR3fEDZ<ˆ[+$z7ҥK|~XzgɬenZ r-]~͆ٙ1ӮP;+amhfۧ jd_/Iz^F ߥ~y }FG@IJV pfP9, "#!\nY?L(Y݊[0885MMk-n`&O+k-.23]fr/hB|Ͳe`@pJ@vMk]2\ uZ~eea U@ufM>jaq l-#']f,}75.&ތׁ{^C3HY삇6_wԉ:O 0Oxo7ˣ`_\SR4׿}ƽxo^{nbi)=Հ m;iJI޴i-իg݋<~R&.XnUa„ =Q(^BkPH5k~8 4MG5:` IP6zhXnsFwd)"W_O?=aK? V^A@L EiНV /4!Xi^={KsgBtH!?dIm)u WolC|ms-fVGy0ȾeĎYF  xyŠO9e#l >wY.ݠTu&U7brkFGIOY{ri۴a2$ EHpzGxY7t9(|&o076GN:Yy v۴iw,NOTqn/:z˖-;vmN8UVC!}ٌ +ބScgMYO NR@Z|RFhk #GZݪU+J}RYHbnPE=b+1؞ʑC6T;UW]5ڙů W写1G;r!0CYkPH2Wv+ݦwD_xֹelGmIywޗ;ݔHO?$ƻ~3`<\8WkWz< e0GV"s}ͶqX,i/j+a$jW)@>^Q$%]{,S#lfVx!Xrqжf1Ͱhpw+["?'\LldPzXZo|֧ Vcs/))Ol4;0΢$  ;&ҳ0zϴbV)a1vsͩP`B7'OeظZc!CC}di]V;/F$(FX-Fw8XvN؂k1qBv >I"ӃuU7U_ZЬYa' _֋[BAR`ſcpߤ?%mą`0Y )VM4TX8J@hFQ:''p߼ܯ@u2eIN s=Ǭ ![ 'N;ӝ甉׽vҥ߽K~ ;wܼq]?|^-3 ݻjժKK$ڷ]v]Q ݻ:н>o_~:e2Ǐ_o~UW>-DoH Hc% Z"@ xV㫯zlǎ_{IRڭד½޽{AVO7矗~W߿+o߾ipMܵbŊxQ6Q9n/:ꨣ t)qr1!wHdn'=+yA\o[]r%Ǟz꩏:NaIBϥi'|a ڍLȨNY驣`P6׼j$fm %[A[> D#KaKAh6;՞Ov@G^} / G?F;p Bgfl8iܑcOlU_xYH ]"x" ]w%YѾ^>b!jJP .$ ; X@fô_#kGe_Wո!U}C8$')]Y͋  b"t0씕@교WjS`o1(!GT3X,L|z輓)A-mb3;y]ÊB9-NZ@.ma?Q,(݊fdڏ`7Ȉ(X{ /M,\p9 maIS&cӲv[X@*n c6֭[݀:p7<4JD}e!B' ܨ[&PΨiyoIΔT  3ijsCb\KW3A jP`tFJaLt$MݻeCvf*r)cܰ.|II bċ JeͼyƵ쫹s?{&Gq{{fsޕV9@ @$l0`a`s`l`=a #BHP!(*6Lw:kgfwfWtpUuz|#9Cw \v齷@0]ivBt|ub5{ldVKlҤIo0@[lDž2bA1C-5l9 a_~A~\Ƽdr*\aRnps'3|a޼y#"?Ҝ41A=Õ3믿O?}FMMMɆ\q0k,vVrgq`++emq, ikptՓ? > Xg{݇KՕbu#7ةq cF|AST'@eOUh`tJZw6nܸ{6m8_OUTK<8[~Ff`l :Z\@/q_q1ꫯrUW=~2zxFh&sA?J?oض!Y3ֿO~ק~},; |h{Cjay'?<ܾ^Y6@F5%] k! H=v`C}FYݐ"֗jp:ZGLuB``= S)KRTa?TZ(V`OɒJ̉YscRK$*Iw! Z#@e \3\bɦuЯJKdMQ;`l[ eUC\HXB'ە%jU@5%*JŒ_B\II)^qG*_ʊ~οP9y x} )BlǣBbV-3ޟ߄|:=̀a( Yaj(Blɯ>_? ]چ˖-5k,ƌ3µZg@l^veg 01 mda9Dg}vwwѢEor-_O*Rg/(YAg㧗^z_ᄍ\g'maaw^_Ȥ_<L:2Q۩z]n6JfioO:W^SB)8pwHiѐ e0N<ԇ}s9N?0=\k!/hl{ f㣏>jҌNa! \-{  O< oL2Myc=u]- N89AVA=qcKmwvt¯?; ]iSiaW_x]riͻ|G]2s/t 7<3~ȓeg}w? (jɩNȻ! Y/"n uZZZ=дr[_K RV[~Lv>f4laHbv궯.WN ew. *'%~J |*UC@x-mIF4 ׿ ] 0`F.XV6alBW  aro>ۮUv(O܊+610YpGob qx,BQ)xn@<,^{|S K*B(8 =Ⴑѿ;ꨣ~l֐n<[}yEodΝ mis9z4 T.[UU+bExC_-B zeħB `'S!ac"J#;ΗX<5*NQ] . |`׾^tEfa֎y=bދ'!cas=ɿa}pѯꐽD0jc{"x9rWɅ)3=0uPX),xsy Ac̃K󥲹<e]Dןoo?oGObאfռO [O< ]_`H| Օp7r(,0bzfDse>l " Jx8@ NEg)P@ y2DP``4CDW*. v򝛡@ل⢌0ݱcIemԕĠ]SJc8"2?""s2lPAAaExh@,r8&D5SӖdg[> TRϞȁ Hg1^ݪ;ᄯ̀kcVU/nH2"yf 3D3-],xg?zU<_BܻRq/ش0%(iJ~4@쇪^(# î0s-^xW]uCG6Fk.;7V= lڴiix;{; #!==J f1h3Kuwwt0`Rǎsg`ksmVRۅ{8:,hn+w܏)˼ UuNLy&@o.>B0Nacj !|~ '0)`ؐxeOrtuOUI}0˞'I~ۦm"{3, #'V^BքOg+n= pX`B~u{-~^nD ^ə `5%X^td;Rp+r1_BI{u7[iw%CWLW e (EUEPu=4VCڝ{Mȍlb^Ig2 `% K!5Px@EoOUf$ٽAA"iTMT^=vׁ(WT-HrвobXgu.RW\zn򀻓 9@$Kp #ĸ cQc[li~dَq{40 u:"ln/袉l x1l E#-f&.zƍ0ֿ/ vög?v`4+pCfϞ _ׯ_~}L9bb =Է?|?fl ?6,12f$|6N3#| ]p'謘vb8xۓO>̗߬XI.HQC󜇍l;# tM/0#NJi=ca0 [@ԷӧO)++ 2F9䓏8;:` b- )ð>5,XW_Wz&V_O~~"&,d@/(=$ǻ쨳x:t'2f̍򌿰D!ڿʼnk6 lRd&$'g Bx%E~7demAg[B2H=@n$j_T"Q]PMP &8Pgh 5ko]Xx꫰+91*MV7F}a"Ċག$,J$KY>;e?oO"B^ " :OG2*OC `]Qzu#{} 3 &1ɐߒ'xbZѣGx D׉ q/$ybQIIZ1}kiit橧Z*#[=ۤy"Њ%l_777oүUP:W+% Z0樍d 4ƭ)Naɓ'b`45x-ZTаDd ֿ1'6_`}-[k`G4CWF"\}ѿ3x2z!lvYg 5EC pokɯ:̃ 6xKETy'|=iӦs6q@]gf`Jcҏ_|nǟpL<;Ta0WxdY*,8\ZnrG􊋜_iI1 򘧤.[`šcou^xa_!mrqx衇*!alxeH5i!!VHUAA RQZW~,Ȯؾƀ?WzuCVj\j{ւ?r6R29od &ЃK~´M`;l[c24(!%hB!WP]ťe: vjD5B-Pt@ gfpd)vI19(HBP -_wb ҞcU "1,'T٢1:_/ֿB /E٥_K^{ <ؽ|DGTzh=[jTjZo1rCTZÎ !Ta]F;vYE]4]ȘndRvVT6W׿L#+F3zߙ9sMoϟg((`a 4h_\!qa%Yi,Lr{tR Ԏ3&pMMM/䒙XH0ċݫe ]&0ھ}[o*7AJ቟| WҷÇ1 0C9u1ћZp$J詭(f6gL {T_Md3t֬Y w`h1(mLm߿ kpx<&)K9akg}gAl-J±/2o$ `5SM]I2y< KsP# vi`܄tǷF*6@p%x%-[*qwS{mo[_^?"vȀ% iSŎxIu݉ qbWJ/ӫB}~cG!s?A)ۜ9s馛)-0d03wg,YaDB4|xiַ}֟{!]Bm '@bXwSVԌ3& 6,P{CmV0%Cy{/^Q!.7pr Ӈz_|Ec}d1|*qgx'MGsyi(iW)ġ:s}7TϭN0 I<}2PUoi ނ[0cǨ8yd)HF6 - QFx)a>C)b#D;c,b91@ =C7`cwޡf?^` !9ꨣ.p{g?Jg>!c#SQ=PS{y裏>Jg( | A|z(ЀzlssC#ad0R?R, @^<1C ـg>/+yS׭[KWS ck*`'MaWCU8[g#® F%g ͥЊFa:Hh><[OB:wPZ'W {xq5:dnK7{Dy hPǨGߋ…b d/L [ >ðJcTw ge^pT$x2 6qL r*aIYQ^aȻؠbAXH0h߷$uʩ(T[ppq ptYhLLN'ȚrXۂ!VT3WT-Z\p_C'߭' v.@t$C"Y 3WZ0K.q?a_P"@{WЪ߸aY- OPdVX-Bw$dB~O2t]o|iӦy̥Q5{ (ǫ;d$Xau@ \Qo'cƌgtZM *TA~uKlO:[ IHW 16H_Kg͚nxo9r$nݚ&eG<+[!CObm+@ȼϠGl.X@%7"`coE˖- 4N]y) (2 l[o0 $TTTsIc tn:8b]"Z;v5?T%s/ 2F~I)ȐfґI" HSx].w'[;vb[5#ɮCBAgJsd =yh*iY>`g 2JuqFwϨߥl*_{_꘍]N௶ TK 5A\CU{y(q=| Ɉw-\4 =ج_YfS466>oiiYb db@ Ǐxy?شiS,c&,[_>!L]2Lw*zeG5Lja.ȇ~XO/Fa^v Pq$d! 9@~0R!X..Xd dbc)Gz뭷6<* N4],ω sGy9èQx1A;O[o򆌬N3f|taV {fxŖb>z[^Z{l˰gX0=?V rǹw% v#ᚫΆ[>a_ʫsUNƂ`֔C%/0ǃI bug\vJ`{&z-*ጔ%a(kxh0ǩd s4y^WY]Y A&|JYއuddٻq[Z&%Y]\y8lTԯh($?NzaXx-dT6OeK+iA#C< KHvvEOLP}k`'bNRב@wWƟ+K)-G4pK+<$P; a”9ӌ d1:uC h9\ }~,=0}tCCC,Ed8zxMGss#+,~]ff2T%]rw/:K˜w"XcƖ.z# 4A+ij 362䖭_Uq8ƍJpY+U 3^?9s:2x8 YmEByF-I{嗗-++Spqq&$_ a2)ln5z71]HlO*}jؐspP bc=,)-Ęu>m }_AftT8|rhΕWC>  Tys#BUpGqHV$ƋRL`AV~. ăĮD%T3̓S1_HВ)p %c"!,8?;*xyDVX}<戶LHݑ߳d^JO04ĉBqL.X5$5sG1 F  ʺ"*]H"S/cY[|p-kQ?< t1*c=; mP~āSƙ*MSy4dr WV/.[l,=ztG72.Ȁn.:ӟBz~? 6~x%*J[Wb*T JPz;|S^Be`JEQ#[{Ɣ=|0_ _Yu r!${(f7@XM,xGbUX$2g *s=ʕ+Ip!|lmF"@HmxO.\ޗyo Faa1c3`5]J_E+,*[ c:lcVlD;v7+fr`>w< Xrh{!tx J ۋAD/*+ xID{:\"埗1- BЉ P< V@$豓o~6c qcV:@D"m)S 81(G/j^ 3t[n4FV~jO>䀑w|Ld*_=S@ݻݜs֓A 8>L>|Y8ފ Ro:L2#oO',nĘRJ ~e I9M4)Y"ƀ,G@ # ᄏzk-af h:Pַwq1V16=xiQ H.&!Q OOq2;Bokiiy_^ @p`}āp_{}P+!WxDW NK: *ʡ(a@p QQCxw<c(QubV/ ܖ죶-e e8}b$ha1|J$µDrz"{H2U}~7"T-TX)}~rm޳Ʀ" rr͖Y;US UD0cD2‰ByxrXg0,@0Iz:}c+\{sN #uБt'kH3,Q}Ό@N(;`Fis羻bŊ󩡱o߾L# ]RL6OZ2fLBzȊn@b7ׯ_zV[vfJ*pCmӌ<5tÊ 5fgp,aAZ%v' Dh¿7-ZQ٘|AGؿ:9:.Yt),G67ayyyECC͌\󽨨(pMm TIEyauUh}G xZ keY=JĬ Sv暽o~ 6}~sLY6谵JV溢:\Pbt1Clj0Syab<νT"$~g▨CcAzg*I>.U8|e<\ED%Э)+b%K"~}"Ø]*,$z@W ۜcoh?ISx$2ąxK<\G!pq{Q-2tyu\oB}іÏξ M+c=*rZ)a] vi/MWOS ]'J z6w }ʯnjB'v'J=6'mf2fa $ҳ:kyw~]6nN3ϼ a`QB=qƒK/-dF*ШYn#˅¨)1!f(V$&@3{k܌3fw ^ǘf#,Ò{DIrȑ# tqYPa Kdm}|ސ̇b%r t*&$裏~t>WSSE2H9:SIeR̕;l޲Ė'X6SZZ`@vvvqv+_uCra2Q/=|O2(md&?]('^ @p7}`y>G^_/L+gskzѸuPUVH o3Wd#*co ~"g2$A5 @8ڑ T6/M\PBfV2<LVc E(u-TǍ%4Zw}2C^@uH2JL+I# ,/ALwG]~ruN! %a`yq7 =[v߰+'bF$pbT-g %y,G%3PH;ڋ_8f= 2+^0 #JM@n!XfIL!\3﷿7Lcݕ kD7CD`.s=K2E;pf6S(# 4F:o޼U ,潺=Dֆw}Gcϱ?&B0L&Llٲw5fƫlC!W$tH/{ U Bs0L'B@8bر52 />v A}ȶٶklt* ݈J8Ƽg2#z^cǎS8ǁI!eX1~+G9L:Y~wc6]UYٴimӕ aM%%:$*jժeT }538Cb/š8"6WĈ TXPbǸnUy1p @ O@*[82ј{**[r1_6Ⱦ(ҌwZx\R.3~/1av@㯤!#">6?'ֻPEmo*%wEy*Xj\ R0Ζ.,̿(hTcQTk 'K. hiett'ԎWoZyY|ˏ]Yķ^ =!a{1uHarBE PysW*øfx[=rJZ;>π 8 ̈({4(Ҳ'X~rw&Yʞa5cO?[nyL~^$I[3A x̢~z?,cwwa* , &FϙAa„~Ј6!8xv8}m0 ˂1Bk֬Y}ÁٌLhs$L`\Ɖޖ<<( `SŐ@@E ^ruPzmy ytgRg3#Fذm'|;ohC-Temmm[ 5?Ĕ4MV[!^tnyaN"T0?‘L"@ <+&%wyRBJO5W¯Qi@1>^\w0P^/Pņ9jy'aWRzy%\O—F̥"S+Ar"HF`# *Y _Ɇ]I$YY:ڕb+yr4PP<J%Wr!|VŬO Թ0,OD%q%}J]HR$`i1{VYIVM5ʳ'OfHSfHR 4N4/~eDy3%{C 's9' :ty,3pazꩁ-\~MsaЇ-j裏;jԨ2 Dp!+//ӷׯ|w #Sl;- #FW4\od 0Hk HhKhn<7QHsB 6׹AcuL~ǰ)\SO=OAP\BGsY\d槤 ̕ {f8Lg@ک=P[ڽx'KBe6 gOv ' =YMڅā 5PS`͛B؏(5 r"e. j\z#T֒1Y-,,(*Q F\ryh/IknBt8ΫcWʹk ."P)"~X!ݒos _~P^HJk \ɦ:@<-(D]/%bѳ".jhLR|_!TX]YCwD& Le8shʻ~h#AI&SԮ08bpLpRY,c db.ʼ* C(i`"ýCQqh{>;q5ʃeR|y!1D0n(tqa"ءAa)S)gy[l+~H{d|wyg-uT(gq![WĚu YU04H$@փ{HHR`C0 _{E>>/-)HȄu[q%գT7Ae8AcRYvY+Q":IʀAY`)&y/*֩CņNB ~CDgTREWE 140Rڄ*+) ԫ/BF(̒@Cx,gǨ!ȴ3bUtmP@ C}(Ax>TL?~ؼR w [5EVtO7>x[oּi>jԨw}]vgCԸmٲ3XWED~c=WL6{}f3A7VWWѽpz ed!0`@ZK/fb>z[sX'e Jf柏%P[)vz|rW1y9@ӫKϋH(@AXbI*7]HגƩTRaki!W;6{K-J\mݖD v&S^ҺZ`(-K5:;F27=ƃ / ~Sc0I=m g#~@EYyJ%03Ju)CBt5-r@,E9@K1"\U^] wht`5$}`hÎr!NyvB:;W<' u5!m\|E{7', Ø-Z*7^[As>P^~g@0LT K>LO?ninjj !Y͂׋ @be0NSH{d|E1f2w}%U|PzKIbk׾3|R0^ZZZR__?0xηx uq,Y) ۵>D ˜b; ( d`SX+A> Sr!] BXJҋ;'K s@̴SxFEEEbe;ڸpȰ Hfc];0; D%~$Jn2jַ{~;&BE#@M,TPz B WT`C~P- Tp%^9p^ ?n J0^A%(Ӌýf*z7f g "۔$/+A|zI JhנPjؙ7[:{Khc/L?๋h~ YN:xyg);Iſx8*T}υk+..aR8's"X12.B׮JJ-VbE2j.T7A"Xc* )Qu7d!TQ;5o h߹KG⛝oޕkO6l Els˭BBUK*Ha[oe;`N 1| s>Ҷ-_|9d2I!=y}ر0L>Wy&jS3W@(Z@x<{&ҝys mtnx zy!EEEl t\ׅL V $u:w9#/ C| G@m2}Q `40Aϑ>7Ƌ s$Ayqr_mzen){q5_}iaS9*Cg]"q 1{m!B5OE?x#y2-GLRVٍX\%+klsK%Pw`؍%lxUCf tk:Z ń'o7(6_Kh&Adx:dwc2ݑ}sS?|+1/1#: ͰZ+'SA HL %*J8I1Ù8tEhSRqR CSdW .) ^'Hy`1=Xʙk>:rY-gTVM^wȴ׊c[W<.^9჻b VJ}_0g KAsx><7@+ lq<@wOJ3M33 x. Yxh{bVUtYf3kmMC; ߌgoV; (t޼I"zlհ[jB9;Mq\?aҕre=؏\sRȤ jW:`۹>|ի(%4]_*$*-2,r6B0<"rw >Mc`Aa8:A!z衇cscwx:B倰bה?`Ft {!"nE|U@N1@< t88^^hR!(VVDEkkD|@84î+k kG7؏$LSF ,Xc8k.oD yӯx]pd=om{[7-GT4ޙ)?K4ˤNW?)կ@P, Ks"@s%k_jPT2.kyو2'K 8픙ȨxQF!NKDQԒi]THGEoaȘ J:&Ƞt@!¸W*bD$P"ղpct8tR ."C}&!V .%7z>&1pRM+SU:S.ND~J"uB&:亇@umiۊگ Xt.zk+t֭[yvo߃2ST upտzU{ߝ={ _~60e"v]vkɧN~x/{V[kq B 2&֣9  r=޻VÇm72hCCCQtfU[[[3(央JjGwqټc6=Ƭvnr"A/®BK xTRЕBg .ŀaÆoVVVcTg,55~@k CA>,ko l >7qByqp3g?!] 5䧿{n#T6|!2%}hT҂ENHRZ YP83p,F'5B0<&2"HǽS3jr)yh$cb$搰[lDdWxK)!W$( +؇,{pyn !OJ|G oKjxn϶?ؗ1'fIHt% (ie5]*إˌ|nxrĔ mYʚVׁN|aM)o/ °R&ev%h玹1m_A 9IT&>m0$@q|( e-] 39bLPV 'CX|fϞ .RZhmPB؀бHibf夓N4jN3-+ :8c*|+'xGuԴ@L{Ok64xL N ?o~plA y*H/1#،1Zƣj~  X֚Y(c~BEytcxU)(V=,U߭^8^62p-`xxU0윘8^myQWg/It@ylf𺼘W#y N} xc' ޼1!GΪ݇~xEg< *9rdEqqqd@ >P8xk 8 b-Rk֓m)t/G>˽qȅHZN{,c5$#DW{ru?zjz\&0 m c>]zhV!N ?%\˝ 6Gɓ~׿ƒyN忏9* C=ܼMv!,H$ ށ h}=JW4}0X&A@c aXF7{?~w~---I4UlF鋬 )8 }Q;W߾ ` x?Ž>-l^~=ǁjC|&.tuysl0Ơ6s5,fSvfɰz95sT˰⋦cBkՃo;vy|m|;ܙ-2ُewy+:;@Kz o&0#${C=i!W{ B]  % Pn92J M&uױ9\DP U`2s1QAnVCug T}!Lm+u *E)jCTR DVY A=uPVoG/ jao̾A&& (mtalBua;Vfm i.h!8p ȘRȶ}_=o?C,LTX0G70̤q555՜L&!FCB?v`(I7DiEEEoyz>^:@c`m޼)J%!xW@x@{o٪,O3(7, 6LL*?CkjkkӤ+pSs}|6TK*+?<-/||ӦM5q == s_X>p;)S^eEZ![%7]z_e^93<7 :eBykλg}\SPQ.! pwN;{E HXRPƍd>GqghI oWX"i@Cw^'NJ>lOY%w&ܶ-;73 &bTT|֯AXN}d=n TGX khJ>A[Gac~_|p1%P NJRPǮ;"R1{ܼcQKJ6G@ĭ g~hr;EbbY_BC绬{?wͿ՟3 =?XcȑRrX ?ON{梋. Vvi6eY&R a`HY>>dl ?.{O'9.Lͅ",  +;w,H9 ȓky=@¤s|f(z·~/aDZb <+Hjb/_΅? }6닖e3dnk﹫0T2wɘɦȊDN [3r50)AcGiu9` F5lotġ1 k7mVdVQIqd`vfAGy-4V5Rha}30ę WPHF@wWL*ő ʜRod%äⲋ$K+ 3N}V,{UPg3 ʭg8$-jvz*11YsH@]ГCG`?cMjweMw˳/V_x;x;O]Mϸ$ $Y TZp!2p~헦ڰ3<}ر#}i D}d0PSDwL|t|O? yCa!m2+ʓ0JBOl+VO> *x=jj ҥT7*=bG!VF0=("Yj-/E˨k!cF=3XQ},}HˆQ=o RZ XDԍ yВU^ 1*R"/CuE1h,-:fxl}aAЯP&2c^Qe@ZvM[6al!YcQʚ*(o*6 6wCy8[GDYV˃'mU g*xIT*~`Aګw,x}b;v5!T t?cN=&>Ӯ0bdh?A !RXӮCaDLX/?*L>*x{GBS7 8\hWO={-W]u/{%r13kFELRA >nWjdOzW~%Btȧf+JpnAպ=YUPaϸ +>gr-]{5'ji>=wp^#2%mxGY*%!GWNO= =zeITTf:4M@yVZDj愲5l~IxH;܉./~P׾ j>5|*Wxp:("ȍ퐡_\UʕRPNzORXݿƨ.C5C^u -a@q1++PC7?L8xhDnBB2{q)4+c{?FV._ o/[ 4JʡxW;X!Ԙ)13}G^L$߃V3Edgc=++Sf糉* Ok,! (ce#CK??Ү jN8љ -ɈT#ի,3<!}3?N l477?aԩ{iG(u+Bo|ϟ%P9י$B?sa.sL5&,@#IXH0jHhd=&Lpdut+\1x%iD >k8WZ5o@THܣ]~uw2+,m3% jJV^w(FHkJ a܉FzCCO߇v 9qgzаWr6 :RqwW憀z. _Zǚ/3BkR@`LE 4Ȼ$ 7JUpGp0ƒP д:^2ڜU:veKAo=N}^@F!lԾ0\X=(3 jj`î]P lxObקKܳC4]d Dɏ:_0f.$4 zD-aDoBw4e\)С+]/ JKK WϾ5]|0 ^o`m۶᪫kÆ -rHhC$x?[:: k~ |T>p-ac7T]dTΦ& k:AH f[0PV }kmOcGe\}#ԣIqhH^ٷȂyJ frIHD\Ǯ? |TCD TXGM3CZtuS,l`Mz4 :"-P4Vp;y(딤cá}HTQJ /6Q!˛ڂbC*8cO<^}mX;J0(.89n ̌v^\f,Ԣ>%L]XWJf.GOD",(c<_pRH&DG{Q#dD_˸H1 tw~wŊ?\rvȥ MBsvOH6ay&'\7gc[6葺yL䤀,Ӯ$gza C]VW/*h󀋓N:i/~'LpX؁73ï/zqjS޽ |u[֯^|}W؏ۭy xr}<Qѿ %iձT;TJc WZ6 ǡOI4VC^ƕ0Q' Lzx!ո]J2Iq s_)AM3 Z@"UCcN_N>o<X<<Kj=((;7,$ [#98aub.k&{cqv՛7x*wN{_=!yu= a?@H@va_&(+*AG>),=A]+zX L2&5HذFVÔSN~A*Sa} wqfGZx#Y߇}KK;IʅpQG}FB{' O9fi#􇡬/4XQ%uU|1ӆMT b4sK1@u_Zo-[ځPU[UX#[vaMgi~0F!N3] 5pOXD %[Gk@wdb b?^z5D74< 0F0mڴ%};SO=bɒ%%$۶mkgƎ+744k&&ܞb?Lau V0t2&ژC};Ҡғ:@MB*؆c o>@sD ƸCx5s#= WEvqW^y++kG Ks3wJ ^fT%Q!5x/  zd{l--!tz_P8 -9N\钺4x< Ǡ2i Qҩzd}Oim@ekO a86(1a2 Աع31kP)C7`ILc_ S*2v#_?|zu}5vXaYt%nx)S2]A+裏>ƉG3DvQq۪sY$¯8wN],ɶ"۸Nq(v !%'| `>b i'@ )6` p,˒lX睝=diooowU09$̺E̯`݊QI|&à Bvh'u藛`BfAj.huԮBCE(uG@k鳿AL~hFdAرFπ^[L ˊ^E!5pΈ(5 4oetFN롐 .|߶|!5;R[ mм!p#KǜDa$9;#"x68΄wX,|n DB)qWÆ C (,,Iqzq&W$3r饗կ~UVIk (ē b|i/91$Nj)[LT$[\+(%M~M(XEXT>?*|HjӦ&)))H@ xg\ВD\J\k1o-jz$o3|233ESdlv+\^!t-B-STz駟[Ϳv8}?|nWRps9DnS]f>[sa~d-yi00=_k>u24dnim%0ƨDd` #bmK΃bnp.ˡMH%ϖKx{`GoJ/NBBN0v 5/98jgT_$A`"ivZZ %:Lu )(5kBH? $fBz}#$Sd1d~!a"8ME~)B2DGܑ?Xw<|۷_#|QC4^ NYOD XN0!uܹ;b#5O}7"!xݺ:>gQrLB@^e+SDlB6!iMSRXz-EE@ m{Bёx~{-=LhSͅY# Ⱥܰp_=}>=% p,a2h.-q)av:)i) d Ϲr-i ļxNJ#$"0XOFgb-?0HAfr$A;!`LJ)&B;ߨ0(=L膘<ݷ"NyB~C( \plݲ 6l )iy}DG0"7h.o"_)st{?%YNi wU ADɈE@eΝڒ%KO2sq7pÙǏ"o@0b xaSB`xg7nܻ%w}}}D4J @1Jy! &+DžWͮJgC|:xU4&H(!b!Jǜ24ײ,>WAf̙3g(! {=8&"ո/'-օy1OkpST?֦=pGɒ]+B=k*1dF'--~7=!XŒl QOf0*k SZQZB<$B~>m: z8mT=~<|k{nW %D-~aUard O@%f,ۡ`0ۼZ!;!"$"æs %In5L6 ®#!0HOȂFHniZCr kԟb،FI0>#?tJ W^y}dH(XJFFՀ 9qȦ&Ç!s!vrKfn+z!+ްJĥKn,,,Nߓn5:"jB PV:Js Y@νjSH̱eРA1AǑ:"2kԆa:ZcTT&W2ȸJ\hE?OHfjon[_V{@i{2r 9+O#z 7g+N<[ѭ!wO'4ňXB7MxB;SǨ$}3 _fn?Ly>d̈́`0]|Bc9(0M@\1(sr!3d'M_ۉ0|`(<@*1LGG:{4vHvBS Yg eJ00F:`v(ܼ;Y imlTөLL!|s '. 4bteAviq HDČ% TQhT _lٲ]=ܕ]tE'MDKJJ< ӋسgL2 `>:* g1:/}#x%Roĉy4Z!čXyN4TQtB"s.\s;zjB+B@ dvP9ǎo߾ l2&n@Ӱ.}L=:z!{hpJ? xE@P` ARڑ0DntnÙ=u 1txMM1vSW--ȇګgyO=3T?fA_A١ }q*~L蟙}[nKuu5&9䮜t0V/}@HBl!ErD\)P0K~HFG Liɐp[-0sT9z,{vbPРd=R c^ZRkyy}!%93{Y!CAO!%>ݙ:*< "~ % M6Lqfhzr0FQ8\rM2q%BB@o _E@%(KֺCt ɹW H:gΜ?iҤh nGst|p'|G}YܴcXJ*:nywMLF9>;WnMzؾ9ró\d=/]hs=je\@ I@g*mhBND5$ipis3",vxJ0eV^S4}-}q  ʥ'Y223 )~Ds%wfw<3^kȀ g} ޽ >ݼ|} LJLtRoh{P!Li Z"8'٠$Q-V }%d2믿~ɸqOJԴŋ_ue];+'VûKme2ZqǼpǂYпRa~G˿T\\|=fWʅ1J0أD% >>z È~Y'`f>4 Jz72StӖ<9lԚx<݊v <#nL @fF_xp@DIh}Kjk z$ 92QP!?p?!6-Td6H7 wWAZ Huu19.Xn2%c\o:j@MBH Iy1S*)tRG}_I8H1bՂx!F`S[[K5!Yg5qѢE 񕗗444fddF&^ [Ԃ`rF^f͚5|Bn`˄ݥ Q9ʚel{v<[ێ="H_ƨ}@ "Wi@KjΡОO?_Y0I? J;}[YY)N7ow}w ;'.h YOo9SNgN͛Z"\^p:=N+ܳ>K\iPYrHՉN@4OOfW d&HM}3`π~`°piN'7҅091b޿řD#1CF'`4}2 ;ejNKIIMĦ~&Y(b-JK̙w&7"ϖ (ƨcxXPXQqG/ j3bQDI!XI5,a\ML>8pHyWu]&N8_9jȴѿ\$ yyy%ioݺu%%%'Oγ{ሎ$( H~~X#U6q7IW$-nLtl/O6 ?n}23#/{;¶2Ê7ғ6g65@^u%044=jN1?ju /,TuUzWn9HbǬ;zdΑhS jxf[X $ླ-g΀~}RUg˗/1l޼\"\f-'"3u_ щm>;S0 2r2ݷ5 &[4ɛ/7AhۊsMXi.Éʐɝ`v:P!׀h`g@M'D 'Om_ k8qF$B&"4IR41 Marh$]1kG5kcG< rt4^T/3첅</xo@|@ *Rf $ x&(ARPP>F9sfVaaa'"d$"A?>c@Ɲo|c ! E".F&ԙ9oԩL/^ mpZI:teƍM1t$x5BϢ"9@E@Ԙ< JjH 3gJp$A@:SOٴiS8s&DkmCBիȼa~t\A&d~By+H_=պp?!\0;>:_yZuuu?£>ynQDsd v8 ȱ*pL>λɇfn3ahNFnnqӊe'̋oͱp_8 Na[T nس!{v,7bv;E֐ 9c$|!o \cF('`D<$5Y(W90j!pSG~Qse+rLL(B,2#:0*zq/(9>r /͛M;H$$n72 &B0 P?\{0L@HV#9A t(Wۃ~]L~Kk&.))) NKK$}Q;8nܸq2GߜIRjmm$T`kL)j[|-l9->3/ebqyd]dɒO;>AFO^j=v6?~x4LQ 7?~ɸ=Ğ)IB:YX%S$|^3>Z J/G}=8pհkw*Ң9D}5QMH9 ,1`+>6b_4 :~Sn hiihիVG(}(l%zUy\6# Q& *c(N, JkoVzA@P/6Y믗qH@K6;%2mڴgup~n'"`XRU` 9#ZRE4RQQq̕i@ 5CUM9*JW(F>b[gF  w"9G jre}..噑09-sfvb{!:1jԨ>d( "d|s]Z[*E'N~"5pëKWuG(mmS?JwqC#[.u z|p'G0& $+A0nH)<RSYM8ƦnRS!jy1,D OvUh&=?f N4z_Ԏ b2hG6zCk>¤_ªo$< o7h}, ݿo/lܺ d(N΃OC#]XNzw 8(XB.7""l$K䐗w'`Q$"\!ői2R@R/))=bĈD~~>J=%D)8r1&|g;ن߅$ M[C6s38cxQ4yC WfkUb,۶m; jߤn#ncR0s4\@lPAz.YC(Twŋطyyy څ䣬 Ǝ [A"IH-&* jIHZT>&DGmY )J=BAn &o0L5\rH@T ͰЌ˫| P1g̙3矫W.gH!Q H,mɨP :%#G'^ 5<("|1ˀcRZv V ?+Yj*82~#o}Z9Cfo߾ݳ\$ ]w|rU|y"_˺ǟ_-ב_N>}$dES[2{MDa͟?(o#Dʫ, Ȃw/ &TVYYY_wN B$• t#xGOC? H'.rMQh Gz ~ƔISpO`Dw-JY~BsvvGWҮݮ&ٳ&~yRai13<r^l_44hA➀JBvK.[xTUBY!©bTaZ ׊׿>.==11c2pD+F_ׅ p _#G$9_u% WK/9B> 8 }t1ȇE@<OԀP ) #i&t]>bZN+!vp0 )) - 0q$HA n=*i:c[ hqZ| (lVĒkՓ֒|I P$TsW_h 70 gv,lLUN,$+DB IHٹU:w lh:(n!F8Ba)HsGf&8^ɢ|hqHu8l+VwI ,5666iC(E3,URB k.={v8!9w}˗/\}bH.HC#릛nE(bhB/C:}ƍ6"!x]@X0d doYs H_?O322i4^H8KKK gyٷzO=Z MmqSz/uVecKLLL+pl6--TCUNh@T]r1s/?A>+&W^]뙛ku66j?pu]#r*BlsXZ_#tbluy։i DZ8ʡ׌|׊h kPGcaC|^yvZP7vUA]4|,NQ:Sv&vAIK]1O ϪH0UP B}b*4IdQG ZH"vڲQ4O2Hd2UwdB vVY07#|ϧCMM5cXJY>8mI_[Z?,r9úDdό Ћ> ^k@R89Wu;rd&aY*JP3 戱-;?9$x/Ç,V h%kA͛ww9[$M4"iH/co9\&;xwX`$AsN.\ ʤ1 -0$DuV':Cg?̙3hN3|cfjpwa߿Uzi~&>b9rHŋ!:š,tc@MHn+^{m= (N\rrYk{nO{L>(`),,h7Л&MiN޳A}ٲe%^H7mDjb7y?cP;9 2 =C璵͡8tPuh? ~w38xe=>bfWi>0%/==D;^%SN@$ِ׀XAT1֞":67> 5[HY 6 Cs"Q1YtI DY>?tv:$& TYPFgaY|:,?2!hlr׾R}zC1c %]Ȝ9~fEb, ׮`O=[oV?*4z0"bq4aڿoJ΄l(kfmow,*8#[Q41$$|4@;im=e6;d@IC66RnT$a0YԳXN ܔm߷5(N6^ի젛`EUi@TκbsSwY2݀k L!zro#B4 ? j?Ds. FI 6,ʗf0%3 POcӊnqb51h;mP]]?rphYAm۶yoݺ?u]7>8)@ bXw5vW5G('kdo/+q8 pϻ+/;5;| fKLW·FD}XK֙ ."Q JI-`#gX$]&fS˾!'Ro~[ZZ1b)֞={4-Zt5\3ݫ8/yK3T"*M`B9s>MCڏHwsӈWr}/on{o߾=d{ J)-H;&$SmY!m{Tv|62a/jn&K͑,L˛2U--ͰmV6R`M[~";x4ט\%GAnsU_P! [`Öo),mjCnN@y1\'FD5V]M Θ$KrI0Rr#ňUASv"O95_VkqR~Mꫯrҥ7M.E 1o$Vߺ]_|yA/WrVTT$)I@OfiPHE8/[z/#G9B9XCzzz!{w7I"HIb<ƚ'{饗nC"رcGOHxk%>edd8r}/+14{x; k f͚B<sbcnLgtԧI1M}b?H߾p÷V11.Tʫ Kb=b- kv! dPGљ(Cݸa"819s5"[KS#l),C|)7A)#-gƱ."}! mRT Aڵy|cVx iNhreC&zbUfcMsO-is'A8lM H@ڝvD>R:Qi'{뭷~x~M# ۫dff:WV t 7H*Q Wr_Q̘1ce}gۓ>HuG)>jM\s=!YBɮW\$M矯]re _!:旚iAdmZ[VFR MPf4e 7pڵk7o^߄N N;nנ4n 6yA z9GD ,7߄ zWX:8PVIv  L%fW/ ,e뗞ͤv*YVJkf1"hk" (1^uB-&f rCWK']A3Qrc94%v՝k|z mиk/gBDKSvh M k9qk>)MONLp݀A&̅AAS 5 1Oñ&Y,0jP;p T䍁M8$3E {j;43l k`.v+#:zPjy,m7(a"vdn:51e9)Gy-Вyr^ۿ%xGh6joڋN_Kw9ӄ萅9_O6?/Γ/0JWN̴zCr5IΝ',Yd7[yGB@  D&1  +O=ַ>|衇bŊb%A Rn&.4$9^{| *_O?T+@5,Ԯ_~=BN}qCHXvы끤MF4!:;xc=;7--mѣ B4hN9[ ?aiii F ªZD*T5(%{+_ʙd|d !Դ5-- :/H$o:XӎxC\p:\2}\/_ 躰?Zwn9vt愦 hXrB`Sy'USPqtYh0M#aiӦUw$" aiʙw390dLQæAK0jͣJn%d̜#=qPk7#́&&hi(Z؂#?>0|Yжo#~) :ҳs A aY'ӤMWZT5AșP'0EZ&iH:%̤J !H"!b\4"\ iwͨ6!+ֈ*u$#q/䴂zytgB-R]-TrI J+FϘ1c *3VMZ{H /_$ 3zI8k+Ɲ/N;mkر{ Z(hAi逸J&ܹss{ټ}7";XvT(>y 7|㬳:CsoذާώkMwڵ7vAD@BA"_'a@||^{?"8&NrBW\ d>FVgr-~믿;OyK/%{w⢮ pYӣ!>D2ɹ=-cwW)"դuW^ {_ɬ )"b~a?UԄMig0m5Kp$6mmƆ9v:H0SMZ 0@ݐ_u  dvVp.54YאB8hx,B=%pւR}rcfC2hܿrB~y ,9\HG>ZYv,8@HzC@ &s+ Vȥ L7|89\V ťh 8: Z}jXt K,5 Q`mn*-7r!("); Go#[j=# =Qw7kkk3橒w2 C0LAB4C0EFFF:0]EB.ZTPP8zxŊ!:ׁ(e';%k |aɸ$|;iҤɗpNڹ,\pҥK?-,,ܿ}ҭ[VI*W=iӧ:uos1*i;%rˆDR„ Q〴sT̀8CR_ z8}!8&OLC5s;7t \iӦO,Y2B`ba)?8{\L9g},2_N5`rQDݩ@H$twG^\j˫~0m@Q@YYY Y[ndo:ygV'(4\ ś-[ۛB˚c҂7y@KS_BDDM*hs,cM[=^ h$͌|bD3A#5k !edOL1gB!7*rr(hx&t0$k T Ү1Y>#E`?%/ =a rcIXSG*@fY9MO>D5Kg ɂZ ٛke鰿 ` l*L:: Ԥ"͢!N.D!}q,W T(o-T3X2|4,Fôg̫LI+АD,1s||ﮛIч~9HDD;cĈO=5A[_Dpfp/ %0,%]vݻwa(0Q'ï F Zc :# rvK `om݆`MM͑ R:TJq'"3o߾H+\;~pܹэc`\rAB {챏@ A9RWIIl~)jY"jNXO<'|r(E?)}@XS7Ka1hj)~;+Y $õ5%)}ɺէO~'<'555 ףB2ߺ]!qj@@vșg9lU;=XɐIO3S>2dHgʕOC" ]2:. ^Gz~  $ͱԜ`OWi@8GKQPA2J"Ɋ۠KKaGP[#P0|$  M7mRSnnטФ%ZH`hL`F$&ˆ}[ jBr\ڥ/n(џК7Z2rఞEm!QhkA@x#׿K-Z(~&h:Pʣd# >-8''gV~Wf/_(I!̳>Fss3< $I }iuL`. "?駟^{嗿{5vSNիWw qCVUWWS FNs 9Q^7&<gI͇!YB]}qsĺ~qHvQA-EEs`'Aɸoo>WDyADL߹p>st?-O#:F,mCIt)ȇ%iA02CI0 D-\!#F!GЭHQ#gA[U 4lCA~4^%+872iAP*jN7Dz¨fJ䵘LNL%H$͟ @"PGyd:#Ш0`OK*`MEr>ܵ"/5Uy条i>jhף3πdI"L&Hv3;ft+GHn0y8Cv@PMNK}L HS=Zp\o`bA(OG鎂5wIIɞ'|r#$Z!:ɚ,ڂ>c@hڴihךQۀH  m5To`| $=Atd1$'!rjkя~wޙؿ|A@u˖-JČ8?p^ QԽ.aEF5 ׸c]xc.m2dm̺ *~TMԫ@~8k<\'l` >x<6@nNoa>6yrM\lpDDFw1 o Ǩ]B}Pj!~. ԭ:4 b[JHB*mC-;•iD[La $9a3h[ ,SiҢ;&>}Z:)IiD^C1"E^qSģ tM0#ƴ3dnz\38m$f 72o~blDYN" 8k 5[gH= e_MA4c۠fJ<* n%Z7!rJethhlX"`ދ i~DmL"ng;J*(G1K1%!(w4ˊ6O"}3v0?eN6U|Y*b:f8M,4g@3aiMQc?!y=>_t=@(mE`+իW/G@C$* &mrތ;#G|饗^)~!hٷo_:ػI90AU2艾{E!G);zh 7lK i82ӃS+ ֱDFDkBto',Y///o4(d̘1)9fO\q}A;n#H9kĈ#!\⺅͹_HD(K⩹iFe;{nēljV,:&XeB4Ohh<\UTk Rbӊ$;Αs3H.Qߓ)0I$jw gn瑶(6JissE} 3HRH$>)Ia]E%ke0:"9q|3!h ,$6x-K.v$VM7ペ RSaFA1q)S ՗A a߲? _W^PwpzD'RřMMD?D-=r-/`M\8⡝WU&&Fh&{ƒYeQs~2 7j_q7?Iȯ#+)͚5mn[s#ּFw?>:MEQD_|p;l¯yzYT& t' / ُlÙ\pSjmײ"~ap s(@ M!LmXI5愭4pKlX~5xI[>~,Uޒ5=AWtD@+>660%nA3!cBt"@"׈.Z ]n){r\GZF ^p|'%l :A>%!MW_}S%%%51 !<׾+G_qɯX'[vž3 ǕD6-O!jX KaBev42h&D( E]]0/τ}w9!mf)/%gH=?f0OHjmUE^_y:=&;w_~Ĩ(H;T~i~lf =˪U޺^hGf HOfCkaa!BB^dɽ(D~hSn|n$:f͚^AdMUGc/"l8 !4 ps#iѢE Sc4=°Ҝ3>Fs[_EYn2G8N쓛j_cՀM#9n,n8eƧmׯdeBG᳉g:4M&bA[|qI!Bdt@0ΥsIkҹZG?mL|ׁ4dBRlLeKZׅ|cD4tbErfsRU7Dg- * '-)bFJZ pɄV& bw0"t&'`(!L#*$#'M0ӶWV雦<O`=㫏l?8l(Gg̘QqƎTb(H'bc÷_|Sf KfUuSLF&IЫ#Fhv1E4h?9ׯ$}huu5O K$@e ln5wSTڂE)悩A$Y _Ljb(i w{2tNQk[@#п"bki7+l e9Ko8{ D@U¯}  /1BL# Y,VuBHLT!m zLF>[tg#"K8h?~HפKDO* LiJ-Dx|e ;kZx4Z,R$ #S֞]?;VeO>f߽ tK`d!n*Gq]۩\c~.p:vHḎ0+z 싳β3&(bZ$ c~]ј gHsA]ˤ O&ʲxD;T54ˠ966acH. QMI>@H _.Aa3cl['ŋa yh}|vx`R1LZ7tPvwь=9uӀWЕ7w}2Ӈ:,Yr̙3NJJJh,v?>qc|,~SK.䗄|4vC+Q;Gy ~RMAPgH7N??!I8FP$sLkj2 jջ0'5qG "$񋧙t`DbLJrKGx麏&N$fil0>al}N[s CH Cc"A0onRCA69` A7()(i}Sk&OSn{줃: ={Т$~54OҢXv| 9y~~!4qoM7II@N Ab! N:%V0(h "fĔb#`D*af^ Xصl a "F{JRغkUA gd~!QĐ#G}~e̥DsC&&pcU4MS@yMTZ bc}Ndn`xi4O" {2Towj7oE>` ز]g|L$7Ρ);C 'oI ho(&7'!~=_܌iެI+^_d:c*j3Q{ZD>!>Hi5,U*k@0$cM uB^PʭSs .2XJX `9(ò8~AiMήV=~KԦbԔ ؃,Ƀ$ SR"`m d HZ693Yϊ=aI u`Lv -d)_,40|ôQe %TbvD~a:u!Llނ0)IM|Rm!&,0πVN;ef]'Qٌy3.\zEp`3|i&RA4[@ T'|ZCAML(]W人|;ϓ;y7>ڿ1 ى.}ƌ#/u_k_# *G@"פXaWkӅY'VD~sC$A뭷~3<ϿP<iȐ!8_ DUyv/G>ݳgI )7@*2!r,tb~.$?\@ډߩjņ ʂDvDVWIL_B%ɡ'pݏf[>cebڋD2M`PmL)_&Uf!i&x]CЫ-{Ț6a@giBLF`ls 1 5!kZ.p)] MrP_N{No$'C4RJ_='S\M"'FS9yZv>¢Yי(>:M P`Q`K?|1DRьE~38aWP 5dN6Rh 4 2i_˪`lV2.בK@tK/iq7eqLE g#-D#"&ˣf4A¨FhU=O/Zh T*$EQC9zkv ~.? V{ {_!>?dtw#OB}e$ՍX̷V$$tJt0 M!D%F6>D܆eLI$0a6qI/є_Bc\KQr':!QL4ċ WQ͍-̠Dfh02@L$ШHf ) %@J+#DE Y*d@3kv**ȜϑTQyXJ7s{ySk?|iUM-iƍY3 CwKhO4-8訜C H,jB_yުnbΤhnˆQDO\t{o]~NLLtPÃh~r!-E$H<0 >#s_իW6 \/Q+iIWbXXmuW!׶\ՉE:Ĉde-ɠ~綧~ڧ0dP$A"**/Wy1|UJx I`q]v6t{z޾|uW9䐣v%×ZZZ` 9 I.쯲6h"kG? A ͚5YnjuLhQmOz ]C?kwOK:# r5QU.R2M8Nqt$ >W/B{ 5-llˈkeyn|xNQG("xybĢN!Bĥ@Kc=dxړ¶tr8Gm"C4_:< Z3%6:dl2KAM,0&-*}1A>#ku}]HH*@$kr:%jVfzRR !=<ڹ헎<0 q0Gbd h#6eܷ$ِ )wċ{ToV.&>d%Q. `j֯_.sS~ӟ~8ވ5Ґ#2J@q߇37-D/k>=<32n$oUQ[FW?q h C< LL۸M[bI<څqooGiB|۸MF*b)^f02, t?*c]Knql*A'ۅ*LieiTC\av~Y8sT0k YitKsg$ fR2,_|'|kW8-d6*|dYHHȯٟ7{>C?_W^'ҠR֖-[TblgBK$$0Y_|qΕW^y}ݷ4VnS<& [r޴>x<(1sAlҝF8DW c9?,"|vHaڪȈ~bBo`2+ۿPJ"/œ~7kK#k/|(1S-h,xo}H !(XH*X=1j4H>("2l WMD DžF..Ps) @׼BB"Ef:Ck¢ 2RR I6^z )>MlmIVq>{Dqz(|]"T$(pR-&"JZO|$Er=;!D.}@7 IH#syl 0ju Ey)uG)\JX߯~s;WOHcP\ 4cN`{dΊ~7o^z뭷>vwyꩧVƏ|`PZTp! #2PU(0ӟ◿峅3=ȕ _rW&KYb` cx'iFiRG~.gXUAo9sq1ȂEmA1Hs&-<~mć\l'd)c9+fvU/κN{ .8㤓N:E";&/'.Ɉdߐ}D 9r_>lGW>e$\߳ٹU[/Gg_g' 0ї׊76~i^ۣ6 a^Cυdna$CU˗/oKկ~e]v9 &e`05c.+!)`Ddr2!۠4$[%G%ِ$pK,y[nꫯ~FlayA>Ex8}Ll?@EI42n _{~EbZ&]tYl2IN-˶/dVBNK"rzL+̙3}{Κ5kP믿bkO>:PX6E<0  ǰ% v1x',ؓaiTH.0!C sbKj^ynHg2wIe NU:)+SKbbH5}x9 ~'끼,%O;WJ# T͒GË+Z0PU$vhW<ZzWP7UP\=`Iukz r~ \|mIURc֭kްag}{gʕ+W cv3$>2S2"Ρ`C+M =/Wɢ /{gNUc 72 $)1Č6tw3kތm/\ř3g>[sZh\Z0(Xd0 n.Q0&!ij.i|s>ώ !;/}K;OvCzH-[^RRF@/o3r$UP\\/>ܬYiĉ'?O?{;8I2$~L[#<#HYe#"---RBu<=ӣ{@Hj` (עÎ<Ŀalal{? )&C¼ MBM,U܏t ^~yO=oyުU6jYdX2 sG}>pYA풛v)$n8E?9s]Gsw؟ٗlN}_~7˾D=b}/Nov|>/HycCE/F:9SCoHe=7(][EryH /g;X W.^JR7*X^gjT,z~!uws;@sG|r=y|-h Cdu/+{ /,TCu^l ^ތ'UJJqwkEalؿ3pR<AJ lo4OO+^{!/ï叇Aātt_5||I8FXZrh,_e\Ͳr9&i Av_h}jv{pv}YmCy:h)cƌ+ r6^TAh;6nܸ^'k,^xܹs[1Ky/hӞ՗ɾvGչ ~yVg (⁣ezBe>x;7< 3>+[c3"5x@H;G\cx>CRĂさ\<(Mact넳+yd[?{xakua("C&55p<ٰi(ajP׉"`\EU {#pby+¬Į7/д[ u9Fˈ%FƳ C_?)ߝ{r_mx~z>tiӦ՝viM:u¨Q7y7R|s=Gϸ, K͛y'=T*HlFVadg͞=Y&GPѱ:Edx6 cP^ k1PSN`dW|KUP҅b?$ڵk;twqMGqĤ dILxȆj^]=zd un Ekgg'6^zvU!IڃMLYv"KwʳɅd8K&+C@ݖ" Y@ÆH*쯪ѕ,Y\?\@+)z;:C}v//w=Mb% qR:(n;\p򰤌pK =e3|tt21)0ypuC[kVIl1S! I$Pn-IHFyb$q]S% y=x :CE9Z';5 f0wp Wm7Qx͋}rDAz2VbK1^bmQ W'm8Uk7DU}FQǍ7޸6BjaUco4c?;v?{Sr`#6T~5FdygzdsYP{sSHrl [f*T Bd'le?iii)Lny҄2m6^ݔspKxFy6g{=^]Mi}?/aC@dV- uzh#&]9.Ȑڕ Y^C\|I(`}CE 󈛿NLNP>MMDm$}a PT2!]`yH ռ$ >ML: Fk\G7Ue8zfaR<:EUcD&;b5+@Yq%qB昂 fB*NZBW8|럃ws wRF\K"p,3R/C&J֗2ڇM@p8pxY'xpųiӦ<&6lhI!M8xQ`)^4!6Am%˸)#8ƾv*׿ N7/ n>ᔸ?YkJd --m)2nZ>{~V ѐ' ͭmz:\ ]}T } `,"":x гD=ZXB$+e EʻºN H;3 hn,akgAdr7#A+M)G^iw`QK'z<,Z=d.uM8`\C6DmՋ{MˆSEq5Q%N8ܵ*jJF7+7)A+ o)aQDA}ToAzAaBm x_OӡqJJߑ<f=,gua ٴ  5Dt9Ϯr2cŕ!#/!]L@Ҽ$&<𫨎R☈%~[lxY=#rc^IGoX#N aeFG~>b2#lmF`JEevX&(AKJPjCoඎ9ܔry $U^VG$OTxʽy¾J"dpשz~lfa+#\MBs3`>[!684 F0po₢1=* C MҢ ñ~HU9B͓ 8&[`jf{2فa& O=8|#g+#~G1WEaKe#aQK@w[U#LBwq;:;P;CZET zqxꏸ0#X2xONZ½l,Oŗ^_X'W~çU,(J@y0J% Ҽt,cRê7miJ<zx?^c̵"ѷ}S ~Tp<V o_0`25kD>*8i[xmg3x |v 2 <ĕѥT^&Kni x'(ܐ(Y]R(s>דvQ,eHG:x8qs~\V=i/9a|w7.X3(pywc>[w9,n ɇX(J{0ljs*CԀ'2Ѱ`h]złdN͵) )|8LTYIy?sDdp:ŞbBmi-Z`lӨ}`ea9SoNj`{/SNo }ѡqִЬc{Z$}{G%mo Wu֗vpD٢)/!/~5P('#~o_$U3DpB뮓=C0Bƻ2H^u<Vf8بo #˅ך`n{ܥ/`8zvuwO> ['o α o0uYOψ&vʻ0 7XxqGQҹ͈<Aa z%X8,*zM\j_n sZn1Dp2 0x/HʎwĹ>F_\:J G^#ۼϛS{ w(q0Fqb}!2H$G4*d<Ubr"U=;rv4jxR ♢xL" L+d6CP=dBtt/zlo&H:P/ɓ e/=;kOJRQ7OwSX0&ULPu@pess?J,RnY()YQ:cd@y.mV_??6C_'wkI_J%MC  JEZ?6As= C26G9 C!ps5Ha&KN..6{'!U.J\Oڌ9|`dzU^ NJMt7NwtH_O&Y- -0iBcN8UW+WsBMYpK^IH),zƔ'D%4=H ò=Qb:$k}0 ?.LdnGqr<hOB6S~mlիqcS1egBG+qP#>@1jC_w6ʭ Ra$Y:< FMLZnFRʅx3p2RBnj;SZ*hM̥T=7RەύxZ/ pQFC ̏>æ-onhkFqL?zl8;/H%@$-[m5n?bG͍I0\L2gZ^Y ytl_% UG@nZׄ{. kEp!C&AEWgZKbAkm 㛠e,H{9|n$4׮- Z;:t蕯* f{..adZ#@y:E,"%2;$o5ӕpXl/î5G76'V5pDjb=~5B`^u# ;_v'_.jF^-5W8hs 엻{N}ܾ)v`?.!"ڎ&5~'/}ܩaB'a_!G@ιⷢ+x0$k|;D9 XVYH6sqqxn,ԖP!Kł薹ǕMM@+a1]1萢' .yNL^(ۇ瞝 F}u\ &ܪ&nH8_E o>N4O $<( &x`U,n* 怔n8#|0U.IUC[[pe A§$9W[׭9qbumK@5T׭n:=lP}' tw5A8қge?6I$+Y'GFJ_e[r9t>,|*~/r-6p2.؆z)8u{h15Nz:x u4ɍ=܆[|މ{; ,28:Z}pD8 7A8qLY__'ٟwڵCׇVE"Ѻ_}Ϝ9HWw7 8;J܅Oϯu!Xq9 Doц8"!`U9h#E Xu¤?2fx41Ch8@%C*,i`,8#G!Ne.w"!XVWL%ū+("T5K VJ#Q5s\ٜ#vő~CL0 kU% reݠ+vZ] _xb9Z@Uا@5S^̉W%gTvp`CN)9+2Xe0΋K.gx[8fUjb,#f`K51i E!Hw29~\DEnE@x15& F+_ѱcE0R3>7I0XLYGkˆ3$KiRd3| ENjv!ѱ;~ՐS6ȜEdڥ Z!=2!VX1I "UD\tXĆCaU猘"RI"z+ǁ5'=:f@~pˁ9-0nXi^} ;=b>zؼq4oL=XhM@fJ@БĭpuWjd/ plcB ,\jW-aғ0 =c `k0;=̛ȊM= /S9$ GA@ʬy*PţJRcZ'H? 1ڟ*YşYah <:qD@XҘL=}| Ġ/K~3 N6ۉΓ5P:O7AT#RM;Vr?U1 Oe\Ws2ZXu!q=xJ?H/W'\XBXkGNWtCQbp#$[HI `! L}?@#,9/<ry`}vM͏>qP?G@ " ;x|dSݎ:8vr`%2pm+?x2i:3TCg- pԠ`IOz;Kc-)"nKMrue6x7e^Fk'l7<쎎'%,*=\ Ⰽ4.d\$cX㗪&THhu nu )^G[ 6']%IHUUSipA"Qʈ Âv];FM8ut1D.W +-:an{M-JWy?KgEJtX<z's,CIRǬrs-'C: +\d0DGy!h˝' =z@ !k>~pKkCި20 6cL w @B$ 5!?̵GCG̅p#[Q A$IWwχV8~,m.b,y9hkio.7ۺ`SgGtT)סM-@\f׋1I鐔ʵyй n-+k}qVHNdl7Ş.F j1}J Crx#4C~ 24KXbq*\T߹5uEU< b, *X|dȒ#28 )MHoTW{¸1~H]lkY5o Е/R$1^LFP^H-^Җː`@ U,gxL8O`2bpX=XZ@  Ŀ:!- w@ F! UNDŽ"Q~|0kOtCthQ0$9ujĄ oM=*1qψV_3v7ߘ^E~#`ʷ`NX?iwx^^ɂV2Q򹃪%,xV= (,s.QsCĮM: jJ45>t}Wr @ Rz'5ԮKz0(ׅ60 f;@L<]ţ,H&ce Gy(ي\oBn/\o7B lk?8/o`aOa>x8VoqQl,)YR-|xV\T?,~SbX ~zx64L7F٭+3 ì")32kRA̯щ\b׉'j‚>?,DI 1"H` ʿW^B]@ ! JBr6C.w8Lgizqnŗ%B GMP{CZ|pDkjb"s?z &IO<8f]]WnQB Wݦbr6zwX kLUL$C|q¹xhYU"w',U 0R mAq9G$2"BA? e@ l/d3y@Y2QH6娻 ICI7鍒8I]-ƫaPsJ&gq9R&%ꐐ/.SuXQ] cFNWȦ1~JX5^W]U`I2t]_x$_ 7#C!^kC9DC^Cp96)%x̼Uy|飼@ /)~a9YR!r< ׸<.H27" ,& > w)&]RTp1B, wyK˅ך[ rcaޫWʆ۔lJVfF'`a+_t9TIe=U(^YcBLcXkbE@ qɊ M-~' sZ_J%@"GDYBGBbLZVr0?HXu>Sй?Z @yz`9B} @ ^Ksn\*K "χʙ+/I6“= pEdBo Ԩz X/pxԄ+.l(gI/H݄Hn+3` = ᕪ0{:uҹQB!mq fPD(^f%{d(ؗvn>x("eG^ ֤#>ZL?Wl b 91M @  nRba+aGe{Z3rf]A!os2TnN<#ZK ˽ML',6!FXW;2'cJDLg%++`m>1[[W`p4)^LT<*R AgRrĮmtIGîb($ٶ鯗?/*klm|QGի kwx@ 3)W\qeݷ>4jpX@̭) 𼸕'TC_calw#y8J _&"N,Y!'̅eՆ"ʽl!ady191&ky<%[Kezo ӓ 2P‚Z,f k|\Q'@ IXCuIy7\1UB F"ؖCH*"{=:~/@ Ȑ :Q bȱ*F2^SbHp-/ϒqqT-ew#+ckVJ$[pRbyAa)x@X1Q)APJ_U;Ca9U@lbz4@ 610z<` GdŒKo+`I5!Les'rO Nl4c_[*A6 jPGXE^9᥹FT:WB)X~ĵG* oBPaA\{<8*XuABGb׊20cƌz㏇}&L766*nCkk+tvvʥ yxЅ%" , \ K<" ӡWʼn׉(< y|"tb,2So2< J !tfϳ ZV %sRfap : AEQE9 1?xX=1FM$(R&ةB' ]s1pBSS{ѣ6zv۞p6$K9A8D[4?k[_~dy#HWnJxI[c:}=~^{wb" ;a> ?YP-U݋yMrzBdž 3ѥR7hqz%SaI`^U&Ϛa)pDWjp* I— d1BbD%*,UAL!\]ڻ6Rx`sc\ >zPt=і6=chW~YyD{v';7VB4j 6N:?OG>)o`ʔ)0 ;h eDr@ S&/N zbݪkr'{!7sx92^]3שC}a뿾^6 "󪶮QU|\|<,W-^<KyڣkdqvO:URo Ss&3CǃQ[q‰iSiZr&Ox9f\R~/Of;ӣAѶI97>fm(gFô.TY߻8O76x\_}dEO'ߕ B.9qaGM+e ˪ҡZ*'D 0-U=!E2K0W&w#"nIIHx>)e<|^ AaUվ @zzLX+R2DDLއeL0|7R :hVI՞[*(_ Nm.W8rSѵ՛NTrB|vݙ$񹪭US:w}eS>fу>O>`υ|HkK{ՌN庥Υ5)yXom-C-<uJIj~.+=/2׆hs=U1ڡ-GϺ*/ASu,},3׉ *:n2 ::ܵ JQB}{ C =ޮ529'$'r~O/FN(C ـ`s@@b^afRL20< "eC&"a'WࡐE=M2 P$.íL%eBjÌ M]bZSF c`MvM7[L˨n7ޢK@ \aY !HƔcakrD,4CQP#Hs CT(1#$[H&yY%E ӋJPg_bI?7tÜ(PA#C|.؛9j@ D@v58 /y |rB%P,|jM͕T/ ₄ YI?RɈ~eȆIN!Tfy׊U>/ Η*X$k}t\`~0@ .5b//\A sCt.G' T.)SI,4M8qCS;IU+yr &&$^2d%A0,RLnBbH%K'yA0M2:tV %ZK|=,@ " b0'  SJ@0*oD*8\q 7)^h1R8w:Y5$D [iXz9LJ"2;&q]Gmmn)F6֋ _$~(cn2@",( ^.HZ9`+L>*$I Z]= ~>֎ł bWa6@ pݵyo{{NOyߛ+>ւs@L8תS Ex,p-pߛ,4PnC:LM8"9Еmp^SW?}z i}@ bն.ʅBn>?uͺM3~#>M (^mZY6Q5IpAS>H87 .(Sx 꺟-:n*j~k?NqZFB @'Hb*j>s= !HH0}9:&A}!p?VD@XA:ramMPZq+]%5i(q20CUJ|^,̇0cUZlIG8DPeϣ렰_?P/oxEl©9@ B=o<>῿ hh/k1<y&8_PS%# C^9H>F%ƨ~HpNY|lW4ߕWqyk}ұƎiBl&I@$js΄ϝy tCp-ivE @o0 o7?aYK{@"spgN[~[iȿ4;Ĵ\O P'H~+ $#$Г%Ԓ&铤 z?PRrzڦ W]p.L7Ձ ūZφ Aq4c'@ Ȯ^ P`|~6yR5m|%:Ša #ڡE*NL]Hzܑ{oVx)ՋkB @Oӗ.@ 3&y~V-}Jh;D`]M5

    x9ǡ'suk <{up;+h;9𹏜} xCpo l A#FP%@gP@%daow\s%PO;΍C?Ԩ{\ݯ{}G)aN"+>y\q`͜᚛}uttarE / Hr94ݣmB08gL."T1{R̟xc).X0BLJe-. +Ŕ /~:? spCO_]x@U @G͝@ ȎCS,}R{c@W+`ݧ1G^Rȧwx?gRNM I> kUaAYCDN2FN] /cbˏ@ Bop*l'{y_ \T2߱ +֬wx.t-^.n$m9O,]#C_Yp@^0<Vk˛n ~:@ ] b:B&1-d+{*4[и#ñ\A>tvOs>qV uu0qj@ D@vB ڱbz^ {eF1 pҹf^q+dL^)j@ $3!d"T9m2,}p&!Oc+sYosu5 @ d@&iSOoV˾-i rmޓ? O=x U]yf@rFL@ Ғ,绠Auѕ/a gz|_V9 2ÿn9@ @ @ B @ @ " @ @@ @ !@ " @ @ @ B @ D@@ @ @ B @ @ " @ @@ @ !@ " @ @ @ !@ D@@ @ @ B @ @ @ @@ @ !@ " @ @ @ !@ D@@ @ @ B @ @ @ @@ @ !@ " @ @ @ !@ D@@ @ @ B @ @ B) IENDB`bcfg2-1.3.3/misc/logos/bcfg2_type.svg000066400000000000000000002224521223671746500173500ustar00rootroot00000000000000 bcfg2-1.3.3/misc/logos/favicon.ico000066400000000000000000012615361223671746500167330ustar00rootroot00000000000000 ( V (~ 00 %(  NN h^( 9:(::::$:: ::!:: :::::::::: ::::::#::::$(::# % 7# W$ q% # $ # % & % % % % & % # $ # % $ q# W& 6$ ::(-::$ " J$ w$ % % & & ' ''(((())))))((((''' & & % % $ $ w# I& ::-2::# 3# m$ % & & '(()***+++,,,,,,,,,,,,+++***)(('& & % % # m$ 2::27::}+% `$ % & '())*+,,--..////0000000000////..--,,+*))('& % $ $ [^LB::6¼P{mf`NE8 ' ()*++,-.//0011 2 2 3 3 3 3 4 4 4 4 4 4 4 4 3 3 3 3 2 2 1 100//.-,++*)(' ,aOF|ngžO"$ j+WC;XD>&./012 3 4 4 5 6 6 7 7 8 8 9 9 9::::::;;::::::99 9 8 8 7 7 6 6 5 4 4 3 2 10/.3 \H>ZG=ZF<0 ' % # $ 9+# _$ & ')*9!]I?^I?^J?A)1 3 4 5 5 6 7 8 8 9 ::;;; < < =!=!=!=!=!>!>">">">">!=!=!=!=!=!< < ; ;;::9 8 8 7 6 5 5 4 3 1 6^J?^I?]I?8*)'& % $ \3$$ w% ' (*+,.?'_K@aLAaMAE,5 6 7 8 8 9:;; < =!=!>">"?"?#?#@#@$@$A$A$A$A$A%A%A$A$A$A$@$@$@#?#?#?">">"=!=!< ; ;:98 8 7 6 5 :aMAaLA_K@>&.,+*(' % # t' + $ x% ' (*+-.01G/!bMBcNBdOCG/ 8 9 :;< "?"?#@$@$A%A%B%B&C&C&C'D'D'D'D'D'D'D'D'D'D'D'D'C'C&C&B&B%A%A%@$@$?#?">"=!"?#@#@$A%B%B&C&D'D'E(E(E)F)F)F)G*G*G*G*G*G*G*G*G*G*G*G*F)F)F)E)E(E(D'D'C&B&B%A%@$@#?#>"=!< ;?%fQDePDdPCJ1#5 3 2 0/-,*)' % % o+# Q% ' (*,./12 4 5 7 8 S:,gQEhRFhSFM3$>"?#@$A%B%C&C'D'E(F)F)G*G*H+H+I+I,I,J,J-J-J-J-K-K-K-K-J-J-J-J-J,I,I,I+H+H+G*G*F)F)E(D'C'C&B%A%@$?#>"B'hSFhRFgQEP7)8 7 5 4 2 1/.,*(' % # Q% 0$ & (*,-/13 4 6 7 8 9;X@2iSFjTGkUGP6&A%B&C'D'E(F)G)G*H+I+I,J-K-K-K.L.L/M/M/M/M0N0N0N0N0N0N0N0N0M0M/M/M/L/L.K.K-K-J-I,I+H+G*G)F)E(D'C'B&A%E*kUGjTGiSFT<-;98 7 6 4 3 1/-,*(& # $ * # & (*+-/13 4 6 7 8 :;"^F7kVHlWHmWIR9'D(E(F)G*H+I,J,K-K.L.M/M/N0N0O1O1O1P2P2P2Q2Q3Q3Q3Q3Q3Q3Q3Q3Q2P2P2P2O1O1O1N0N0M/M/L.K.K-J,I,H+G*F)E(D(H-mWIlWHkVHZB3>""?#A$dL>nWIoXIoYJU;)G*H+I,J-K.L.M/N0N0O1P2P2Q3Q3R3R4S4S5S5T5T5T5T5T6T6T6T6T5T5T5T5S5S5S4R4R3Q3Q3P2P2O1N0N0M/L.K.J-I,H+G*K/oYJoXInWI`G8A$?#>"=!; :9 7 6 4 2 1/-+)' $ $ ? $ & (*,.02 4 5 7 9 :; =!>"@#A%B&D'iRCpZKqZKr[LX=+K-K.L/M0N0O1P2Q3Q3R4S4S5T5T6U6U7V7V7 W8 W8 W8 W8 W8 W8 X9!X9!W8 W8 W8 W8 W8 W8 V7 V7U7U6T6T5S5S4R4Q3Q3P2O1N0M0L/K.K-N2r[LqZKpZKdL"=!; :9 7 5 4 2 0.,*(& $ $ 2% ' )+-/13 5 7 8 :; =!>"@$A%C&D'E(G)nWGr\Ls]Mt^M[@-N0O1P1Q2Q3R4S5T5U6U7V7 W8 W8 X9!X9!X9!Y:!Y:!Y:"Y:"Z:"Z:"Z;"Z;"Z;"Z;"Z;"Z;"Z:"Z:"Y:"Y:"Y:!Y:!X9!X9!X9!W8 W8 V7 U7U6T5S5R4Q3Q2P1O1N0Q5 t^Ms]Mr\LjRBG)E(D'C&A%@$>"=!; :8 7 5 3 1/-+)' % ! .$ r& (*,.02 4 6 8 9;=!>"@#A%C&D'F)G*H+J,t]Mu^Mu^Nv_O]B.Q2R3S4T5T6U7V7 W8 X9!X9!Y:!Y:"Z:"Z;"[;#[;#[<#[<#\<#\<#\=$\=$\=$\=$\=$\=$\=$\=$\=$\=$\<#\<#[<#[<#[;#[;#Z;"Z:"Y:"Y:!X9!X9!W8 V7 U7T6T5S4R3Q2T7!v_Ou^Nu^MnVFJ,H+G*F)D'C&A%@#>"=!;98 6 4 2 0.,*(& % o' $ & )+-/13 5 7 9 ;< >"?#A%C&D'F)G*H+J,K.P2v_Nw`OxaPyaPaE0T5U6V7W8 X9!X9!Y:!Z:"Z;"[;#[<#\<#\=$]=$]=$]>$^>%^>%^>%^>%_?%_?%`@&`@&aA(aA(`@&`@&_?%_?%^>%^>%^>%^>%]>$]=$]=$\=$\<#[<#[;#Z;"Z:"Y:!X9!X9!W8 V7U6T5X:#yaPxaPw`Os\JM/K.J,H+G*F)D'C&A%?#>"< ;9 7 5 3 1/-+)& % + & (% '),.02 4 6 8 :< =!?#A$B&D'E)G*H+J-K.M/N0U7"yaPybPzcQ{dRdG1W8 X9!X9!Y:"Z;"[;#[<#\<#]=$]=$^>%^>%_?%_?%`?&`@&`@&`@&aA'aA'aA'bB(cD*fG.hI0hI0fG.cD*bB(aA'aA'aA'`@&`@&`@&`?&_?%_?%^>%^>%]=$]=$\<#[<#[;#Z;"Y:"X9!X9!W8 [<%{dRzcQybPyaPO1N0M/K.J-H+G*E)D'B&A$?#=!< :8 6 4 2 0.,)'% "%# H% (*,/13 5 7 9 ;=!>"@$B%C'E(G*H+J,K.M/N0P2Q3[>){cQ|dR|dS}eSfI3Y:"Z;"[;#\<#\=$]=$^>%^>%_?%`@&`@&a@'aA'bA'bB'bB(cB(cB(cC(dC(dC(fF,kK2qS;vZCvZCqS;kK2fF,dC(dC(cC(cB(cB(bB(bB'bA'aA'a@'`@&`@&_?%^>%^>%]=$\=$\<#[;#Z;"Y:"]>'}eS|dS|dR{cQT6 Q3P2N0M/K.J,H+G*E(C'B%@$>"=!;9 7 5 3 1/,*(% % E# f& (+-/2 4 6 8 :< =!?#A%C&D(F)H+I,K-M/N0P2Q3S4T5`D-}eS~fSgSgTiK5\<#\=$]=$^>%_?%`?&`@&aA'aA'bB'cB(cC(dC(dC)eD)eD)eD)eE*fE*fE*gF,kL2tW>fPuleulefPtW>kL2gF,fE*fE*eE*eD)eD)eD)dC)dC(cC(cB(bB'aA'aA'`@&`?&_?%^>%]=$\=$\<#`@)gTgS~fS}eSY<$T5S4Q3P2N0M/K-I,H+F)D(C&A%?#=!< :8 6 4 2 /-+(& $ b$ & )+-02 5 7 8 ;< >"@$B%D'E)G*I,K-L/N0O1Q3S4T5U7W8 dH2gThThUiVjM6^>%_?%`@&`@&aA'bA'cB(cC(dC)eD)eD)fE*fE*gE*gF+gF+hF+hG+hG+iH-mK1tU%aB*iVhUhTgT^@)W8 U7T5S4Q3O1N0L/K-I,G*E)D'B%@$>"< ;8 7 5 2 0-+)& % % & )+.03 5 7 9 ;=!?#A$C&E(F)H+J-L.M0O1Q2R4T5U7W8 X9!Y:"hL5hUiVjVjWlO8`@&aA'bB'cB(dC(dD)eD)fE*fE*gF+gF+hG+iG,iH,iH,jH-jI-kI-kI-mK/sR8bI{gwsqwsq{gbIsR8mK/kI-kI-jI-jH-iH,iH,iG,hG+gF+gF+fE*fE*eD)dD)dC(cB(bB'aA'`@&cD,jWjViVhUbD,Y:"X9!W8 U7T5R4Q2O1M0L.J-H+F)E(C&A$?#=!;9 7 5 3 0.+)& $ +$ ' ),.13 5 8 :< >"@#A%C'E(G*I,K-M/N0P2R3S5U6W8 X9!Y:"Z;"\<#nQ:jWkWkWlXoQ9cB(dC(dD)eD)fE*gF*gF+hG+iG,iH,jH-kI-kI-kJ.lJ.lJ.mK.mK/nL1rQ6|^Cs]}t}ts]|^CrQ6nL1mK/mK.lJ.lJ.kJ.kI-kI-jH-iH,iG,hG+gF+gF*fE*eD)dD)dC(cB(fF-lXkWkWjWgH1\<#Z;"Y:"X9!W8 U6S5R3P2N0M/K-I,G*E(C'A%@#>"< :8 5 3 1.,)' % 3 # ' ),/14 6 8 :< >"@$B&D'F)H+J,L.N0O1Q3S4T6V7 X9!Y:!Z;"[<#]=$^>%qU>kWlXmXmYqR;eD)fE*gF*hF+hG,iH,jH-kI-kI-lJ.lK.mK/mK/nL/nL/oM0oM0qN1sP4{[@mUqzxvzxvqmU{[@sP4qN1oM0oM0nL/nL/mK/mK/lK.lJ.kI-kI-jH-iH,hG,hF+gF*fE*eD)hH/mYmXlXkWjM4^>%]=$[<#Z;"Y:!X9!V7 T6S4Q3O1N0L.J,H+F)D'B&@$>"< :8 6 4 1/,)' $ +# ' *,/14 6 8 :=!?"A$C&E(G*I+K-L/N0P2R4T5V7W8 Y9!Z;"[<#\=$^>%_?%`@&uZCmYnYoZoZsT%\=$[<#Z;"Y9!W8 V7T5R4P2N0L/K-I+G*E(C&A$?"=!:8 6 4 1/,*' $ +$ ' *,/1 4 6 8 ;=!?#A%C&E(G*I,K.M/O1Q3S4U6V8 X9!Y:"[;#\=$]>$_?%`@&aA'bB(y^FoZoZpZp[uV=jH,kI-kJ.lJ.mK/nL/oM0oM0pN1qN1qO1rO2rP2sP2sQ3tQ3uR4yW:cHw`vvw`cHzX;uR5tQ3sQ3sP2rP2rO2qO1qN1pN1oM0oM0nL/mK/lJ.kJ.kI-jH,mL2p[pZoZoZrU$\=$[;#Y:"X9!V8 U6S4Q3O1M/K.I,G*E(C&A%?#=!;8 6 4 1 /,*' % % ' ),/1 4 6 9 ;=!?#A%D'F)H*J,L.N0P2R3T5U7W8 Y:!Z;"\<#]=$^>%`?&aA'bB'cC(eD)~bKp[q[r\r]wX>lJ.mK/nL/oM0pM0pN1qO1rO2rP2sP3tQ3tQ3uR4uR4uR4wT5yV8_CpWrzxwzxwspW_CyV8wT5uR4uR4uR4tQ3tQ3sP3rP2rO2qO1pN1pM0oM0nL/mK/lJ.oN3r]r\q[p[vYAeD)cC(bB'aA'`?&^>%]=$\<#Z;"Y:!W8 U7T5R3P2N0L.J,H*F)D'A%?#=!;9 6 4 1 /,)' $ $ & ),/1 4 7 9 ;=!?#B%D'F)H+J-L.N0P2R4T6V7 X9!Y:"[;#\=$^>%_?%`@&bA'cB(dD)eE*gF*fOr\r]r]s^yZ@nL/oM0pN1qN1rO2sP2sQ3tQ3uR4uR4vS4vS5wS5wT5wT5zV7~\?jOj|xt|xtkkP~\?zV7wT5wT5wS5vS5vS4uR4uR4tQ3sQ3sP2rO2qN1pN1oM0nL/qP5s^r]r]r\z]DgF*eE*dD)cB(bA'`@&_?%^>%\=$[;#Y:"X9!V7 T6R4P2N0L.J-H+F)D'B%?#=!;9 7 4 1 /,)& $ $ i& ),/14 6 9 ;=!@#B%D'F)H+K-M/O1Q3S4U6W8 X9!Z;"[<#]=$^>%`@&aA'bB(dC)eD)fE*hF+iH,jSr]s^t^u_|\AqN1rO2rP2sQ3tQ3uR4vR4vS4wS5wT5xT5xU6xU6yU6zW7~[%]=$[<#Z;"X9!W8 U6S4Q3O1M/K-H+F)D'B%@#=!;9 6 4 1/,)& $ d% L& )+.14 6 9 ;=!@#B%D'F)I+K-M/O1Q3S5U7W8 Y:!Z;"\<#^>$_?%`@&bA'cC(eD)fE*gF+iG,jH-kI-mWt^u_u_v`~]CsP2tQ3uR4uR4vS4wS5wT5xT6xU6yU6yV7zV7zW7|X8}Z:cEsXszxvzxvssXcE}Z:|X8zW7zV7yV7yU6xU6xT6wT5wS5vS4uR4uR4tQ3sP2vT8v`u_u_t^dLkI-jH-iG,gF+fE*eD)cC(bA'`@&_?%^>$\<#Z;"Y:!W8 U7S5Q3O1M/K-I+F)D'B%@#=!;9 6 4 1.+)& # B& (% (+.14 6 8 ;=!@#B%D'F)I+K-M/O1Q3T5V7X9!Y:"[;#\=$^>%`?&aA'bB(dC)eD*gF*hG+iH,kI-lJ.mK/q[u_v`waxa_DuR4vS4vS5wT5xT6yU6yU6zV7zW7{W7{W8|X8|X8[:aBmQkzvzvkmQaB[:|X8|X8{W8{W7zW7zV7yU6yU6xT6wT5vS5vS4uR4xV9xawav`u_gPmK/lJ.kI-iH,hG+gF*eD*dC)bB(aA'`?&^>%\=$[;#Y:"X9!V7T5Q3O1M/K-I+F)D'B%@#=!;8 6 4 1.+(% "%$% (+-03 6 8 ;=!?#B%D'F)I+K-M/O1R3T5V7 X9!Z:"[<#]=$^>%`@&aA'cB(eD)fE*gF+iG,jI-lJ.mK/nL/pM0v_waxayaybaEwS5xT5xU6yU6zV7zW7{W7|X8|X8}Y8}Y9~Y9[:_?iL|byy|biL_?[:~Y9}Y9}Y8|X8|X8{W7zW7zV7yU6xU6xT5wS5zX:ybyaxawakSpM0nL/mK/lJ.jI-iG,gF+fE*eD)cB(aA'`@&^>%]=$[<#Z:"X9!V7 T5R3O1M/K-I+F)D'B%?#=!;8 6 3 0-+(% . $ '*-03 5 8 :=!?#B%D'F)I+K-M/P1R3T5V7 X9!Z;"\<#]=$_?%`@&bA'dC(eD)gE*hG+jH,kI-lJ.nL/oM0pN1sP3xayaybzb{cbFxU6yV6zV7{W7{X8|X8}Y8}Y9~Z9~Z:[:\;^=fGvZt{yw{ywtvZfG^=\;[:~Z:~Z9}Y9}Y8|X8{X8{W7zV7yV6xU6|Y;{czbybyanUrO2pN1oM0nL/lJ.kI-jH,hG+gE*eD)dC(bA'`@&_?%]=$\<#Z;"X9!V7 T5R3P1M/K-I+F)D'B%?#=!:8 5 3 0-*'$ # s& ),/2 5 8 :=!?#A%D'F)I+K-M/P1R3T5V7 X9!Z;"\<#]>$_?%a@'bB(dC)eE*gF+iG,jI-lJ.mK/nL/pM0qO1rP2vS5ybzbzc{c|ddGzV7{W7|X8|X8}Y9~Z9~Z:[:[:\;\;^=dCpSl}x}xlpSdC^=\;\;[:[:~Z:~Z9}Y9|X8|X8{W7zV7}[<|d{czczbrYtQ3rP2qO1pM0nL/mK/lJ.jI-iG,gF+eE*dC)bB(a@'_?%]>$\<#Z;"X9!V7 T5R3P1M/K-I+F)D'A%?#=!:8 5 2 /,)& % n$ 2& ),/2 5 7 :< ?"A%D'F)H+K-M/O1R3T5V8 X9!Z;"\<#^>%_?&aA'cB(dD)fE*gF+iH,kI-lJ.nL/oM0pN1rO2sP3uR4zW:zc{c|d|d}deG|X8}Y8}Y9~Z9[:[:\;\;];]<_=bAmN~czz~cmNbA_=]<];\;\;[:[:~Z9}Y9}Y8|X8\<}d|d|d{ct\vR4uR4sP3rO2pN1oM0nL/lJ.kI-iH,gF+fE*dD)cB(aA'_?&^>%\<#Z;"X9!V8 T5R3O1M/K-H+F)D'A%?"< :7 5 2 /,)& ! . % (+.14 7 9 < >"A$C&F)H+K-M/O1R3T5V7 X9!Z;"\<#^>%_?&aA'cB(eD)fE*hG+iH,kI-mK.nL/pM0qO1sP2tQ3uR4vS5|Z<|d|d|d}d~egI~Y9~Z:[:[:\;];]<^<^<`>b@jIx\v|zx|zxvx\jIb@`>^<^<]<];\;[:[:~Z:~Y9^>~e}d|d|dw^wT5vS5uR4tQ3sP2qO1pM0nL/mK.kI-iH,hG+fE*eD)cB(aA'_?&^>%\<#Z;"X9!V7 T5R3O1M/K-H+F)C&A$>"< 9 7 4 1.+(%  $ ' *-03 6 8 ;>"@$C&E(H*J-M/O1Q3T5V7 X9!Z;"\<#^>%`?&aA'cB(eD)fE*hG+jH,kJ-mK/nL0pN1rO2sP3uR4vS4wS5xT5]?|d}d~eefhJ[:\;\;];^<^<_=_=`>a@gFsUmzzmsUgFa@`>_=_=^<^<];\;\;[:_?fe~e}dyayU6xT5wS5vS4uR4sP3rO2pN1nL0mK/kJ-jH,hG+fE*eD)cB(aA'`?&^>%\<#Z;"X9!V7 T5Q3O1M/J-H*E(C&@$>";8 6 3 0-*' $ # B& ),/2 5 8 ;=!@#B&E(G*J,L.O1Q3T5V7 X9!Z;"\<#^>%`?&aA'cB(eD)gE*hG+jH-lJ.mK/oM0pN1rO2tQ3uR4vS4wT5xU6yV6`B~e~effgiK\;]<^<^=_=`=`>a>b@fDpPe{{epPfDb@a>`>`=_=^=^<]<\;`@gff~e}dzW7yV6xU6wT5vS4uR4tQ3rO2pN1oM0mK/lJ.jH-hG+gE*eD)cB(aA'`?&^>%\<#Z;"X9!V7 T5Q3O1L.J,G*E(B&@#=!;8 5 2 /,)& % > % (+.14 7 :< ?#A%D'G*I,L.N0Q3S5V7X9!Z;"\<#^>%_?&aA'cB(eD)gE*hG,jI-lJ.mK/oM0qN1rP2tQ3uR4vS5xT5yU6zV7{W7cEffgghjL^<_=_=`>a>a>b?b?eClK{\x~{y~{yx{\lKeCb?b?a>a>`>_=_=^%\<#Z;"X9!V7S5Q3N0L.I,G*D'A%?#< :7 4 1.+($ # ' *-03 6 9 < >"A$C'F)I+K.N0P2S4U7X9!Z:"\<#]>$_?&aA'cB(eD)gE*hG,jI-lJ.nL/oM0qN1sP2tQ3vR4wS5xT6yU6zW7{W8|X8fIgghhhkM_=`>a>a?b?c@dAfBkIwWp}}pwWkIfBdAc@b?a?a>`>_=bBhhhgg~Z;|X8{W8zW7yU6xT6wS5vR4tQ3sP2qN1oM0nL/lJ.jI-hG,gE*eD)cB(aA'_?&]>$\<#Z:"X9!U7S4P2N0K.I+F)C'A$>"< 9 6 3 0-*' $ % 0& ),/2 5 8 ;=!@$C&E(H+K-M/P2R4U6W8 Y:"[<#]=$_?%aA'cB(eD)gE*hG,jI-lJ.nL/pM0qO1sP2uR4vS4wT5xU6yV7{W7|X8}Y9~Z9iKhhhhimNa>a?c@dAfBfDhElItTh~~htTlIhEfDfBdAc@a?a>dCihhhh^=~Z9}Y9|X8{W7yV7xU6wT5vS4uR4sP2qO1pM0nL/lJ.jI-hG,gE*eD)cB(aA'_?%]=$[<#Y:"W8 U6R4P2M/K-H+E(C&@$=!;8 5 2 /,)& # ,$ (+.14 7 :=!?#B%E(G*J,L/O1R3T6W8 Y:![;#]=$_?%a@'cB(eD)fE*hG+jI-lJ.nL/pM0qO1sP3uR4vS4wT5xU6zV7{W7|X8}Y9~Z:[:lNhhiijoPcAeCgDgEhEiFkIqP`y~{~{y`qPkIiFhEgEgDeCcAfEjiihh`@[:~Z:}Y9|X8{W7zV7xU6wT5vS4uR4sP3qO1pM0nL/lJ.jI-hG+fE*eD)cB(a@'_?%]=$[;#Y:!W8 T6R3O1L/J,G*E(B%?#=!:7 4 1.+(# $ T& *-03 6 9 < >"A%D'F)I,L.N0Q3T5V7 X9!Z;"\=$^>%`@&bB(dD)fE*hG+jH-lJ.nL/pM0qO1sP3uR4vS4wT5yU6zV7{W8|X8~Y9Z:[:\;oQhijkmsThDhEiEjFjFlGpN{\qq{\pNlGjFjFiEhEhDkImkjihcC\;[:Z:~Y9|X8{W8zV7yU6wT5vS4uR4sP3qO1pM0nL/lJ.jH-hG+fE*dD)bB(`@&^>%\=$Z;"X9!V7 T5Q3N0L.I,F)D'A%>"< 9 6 3 0-*& # P % (+/2 5 8 ;=!@$C&E)H+K-N0P2S4U7X9!Z;"\<#^>%`@&bA'dC)fE*hG+jH,lJ.mK/oM0qO1sP3uR4vS4wT5yU6zV7{W8}Y8~Z9[:\;];^%\<#Z;"X9!U7S4P2N0K-H+E)C&@$=!;8 5 2 /+(% $# t' *-14 7 9< ?#B%D(G*J-M/O1R4U6W8 Y:"[<#^>$`?&aA'dC(eE*gF+iH,kJ-mK/oM0qN1sP2uR4vS4wT5yU6zV7{X8}Y8~Z9[:\;];^<`>wZopppqxXkIlIlJmJoMvTd|}dvToMmJlJlIkIoMqpppokL`>^<];\;[:~Z9}Y8{X8zV7yU6wT5vS4uR4sP2qN1oM0mK/kJ-iH,gF+eE*dC(aA'`?&^>$[<#Y:"W8 U6R4O1M/J-G*D(B%?#< 97 4 1-*' % o' % (,/2 5 8 ;>"A$C'F)I,L.N0Q3T5V8 Y:![;#]=$_?%aA'cB(eD)gF+iH,kI-mK/oM0qN1sP2uR4vS4wT5yU6zV7{X8}Y9~Z9[:\;]<^";8 5 2 /,(%  $ z' *-14 7 :=!?#B&E(H+K-M0P2S4V7X9!Z;"\=$^>%`@&bB(eD)gE*iG,kI-mK.nL0pN1rP2tQ3vS4wT5yU6zV7{X8}Y9~Z9[:\;]%\=$Z;"X9!V7S4P2M0K-H+E(B&?#=!:7 4 1-*' % v"& ),/3 6 9 ; >"A%D'G*I,L/O1R3T6W8 Y:"\<#^>%`@&bA'dC)fE*hG+jI-lJ.nL/pN1rO2tQ3vR4wT5xU6zV7{W8}Y8~Z9[:\;^>dDgFhGiHjIesttuu{ZpMpMrOuS~]rr~]uSrOpMpMsQuuttswVjIiHhGgFdD^>\;[:~Z9}Y8{W8zV7xU6wT5vR4tQ3rO2pN1nL/lJ.jI-hG+fE*dC)bA'`@&^>%\<#Y:"W8 T6R3O1L/I,G*D'A%>"; 9 6 3 /,)% ' $ w' *.14 7 :=!@#C&E)H+K-N0Q2S5V7 Y9![;#]=$_?%aA'cC(eD*gF+jH,lJ.nL/pM0rO2tQ3uR4wS5xU6zV7{W8}Y8~Z9[:\;aAgHiHjIkJlJmJiuuvvw}\rNrOtPxV`uu`xVtPrOrNuSwvvuuz[mJlJkJjIiHgHaA\;[:~Z9}Y8{W8zV7xU6wS5uR4tQ3rO2pM0nL/lJ.jH,gF+eD*cC(aA'_?%]=$[;#Y9!V7 S5Q2N0K-H+E)C&@#=!:7 4 1.*' # t+% (,/3 6 9 ; >"A%D'G*J,M/O1R4U6X9!Z;"\=$^>%`@&bB(eD)gF*iG,kI-mK/oM0qO1sP3uR4vS5xT6yV7{W7|X8~Z9[:]=dDgHiHjIkJlKmLnLlvwwww~^tPtQvRxV^mt{~~{tm^xVvRtQtPwUwwwwv{\nLmLlKkJjIiHgHdD]=[:~Z9|X8{W7yV7xT6vS5uR4sP3qO1oM0mK/kI-iG,gF*eD)bB(`@&^>%\=$Z;"X9!U6R4O1M/J,G*D'A%>"; 9 6 3 /,(% @# ^' *-14 7 :=!@$C&F)H+K.N0Q3T5W8 Y:![<#]>$`?&bA'dC)fE*hG+jI-lJ.nL/pN1sP2uR4vS4xT5yU6{W7|X8~Y9[:`@gHiIkJlKlLmMnNoNpOmxxyyy_uRvRxTyV}Zagknopppppqqqqqqqqqrrqqqqqqqqqppppponkga}ZyVxTvRuRyVyyyxx~_pOoNnNmMlLlKkJiIgH`@[:~Y9|X8{W7yU6xT5vS4uR4sP2pN1nL/lJ.jI-hG+fE*dC)bA'`?&]>$[<#Y:!W8 T5Q3N0K.H+F)C&@$=!:7 4 1-*' # ^% (+/2 6 8 ; >"A%D'G*J-M/P2S4U7X9!Z;"\=$_?%aA'cB(eD)gF+iH,lJ.nL/pM0rO2tQ3vS4wT5yU6zW7|X8}Y9[;dDhIiJjKlLmMnMoNpNqOrPqyyyyyavSwTwTyV{W}Z\]^___`````aaaaaaaaaaaaaaaa`````___^]\}Z{WyVwTwTvSzXyyyyyarPqOpNoNnMmMlLjKiJhIdD[;}Y9|X8zW7yU6wT5vS4tQ3rO2pM0nL/lJ.iH,gF+eD)cB(aA'_?%\=$Z;"X9!U7S4P2M/J-G*D'A%>"; 8 6 2 /+(% " <& *-04 7 :=!@#C&F)H+K.N0Q3T5W8 Y:"[<#^>%`@&bB'dD)fE*iG,kI-mK/oM0qO1sP3uR4wS5xU6zV7{W8}Y9\%[<#Y:"W8 T5Q3N0K.H+F)C&@#=!:7 4 0-*& # ;$ '+.2 5 8 ;>"A%D'G*J,M/P2S4U7X9!Z;"]=$_?%aA'cC(eE*hF+jH-lJ.nL/pN1rP2uR4vS5xT5yV6{W7|X8^>hIiKkLlMmNoOpPqPrQsRtRtRtSu{{||}dzW{W{W|X|X}Y}Y~Y~ZZZ[[[[[[[[\\\\\\\\\\\\[[[[[[[[ZZ~Z~Y}Y}Y|X|X{W{WzW}[}||{{gtStRtRsRrQqPpPoOmNlMkLiKhI^>|X8{W7yV6xT5vS5uR4rP2pN1nL/lJ.jH-hF+eE*cC(aA'_?%]=$Z;"X9!U7S4P2M/J,G*D'A%>";8 5 2 .+'$ % ),03 7 9%`@&bB(eD)gF*iH,kI-mK/pM0rO2tQ3vR4wT5yU6zW7|X8^?iKjLkMmNnOoPpPqPrQsRtRuSuTvTx{||}}g]^^__bbbbceeeeehhhhhjkkkkkkkkkjhhhhgggeeeeeedbbaa`c}}||{jvTuTuStRsRrQqPpPoPnOmNkMjLiK^?|X8zW7yU6wT5vR4tQ3rO2pM0mK/kI-iH,gF*eD)bB(`@&^>%\<#Y:"W8 T5Q3N0K.H+E(B&?#"A$D'G)J,M/O1R4U7X9!Z;"]=$_?%aA'cC(fE*hG+jI-lJ.pN2tR6xV9{Z>~\?`CbEdGnRw\z_{a~cdghkmopstww|}}~~~~}}||yywusromljhfd{bsYjMhLeIcG`E}\@zY>uT:qP5nM2jI-fE*cC(aA'_?%]=$Z;"X9!U7R4O1M/J,G)D'A$>";8 5 1.*' % g$ (,/3 6 9 < ?#C'I,N2S7$X<'^B-cF1gL6kP:oT>tXCx\F{`KdNiSmVpYt^u_wayazb{c|d}dmtuvvwxyyz{{||}}}}}~~~~}}}}}||{{zyyxwvvutl}d|d{czbyawau_t^r]r\p[lWiTeP{aLw]HrXDoU@kP"83 /,(#  xr1>'D.#K5*Q;0WA4\F9aJ=fPCkUGnWIpZKr\Lu^Mw`OybP|dR~fShTiVkWlXnYoZq[r]s^u_v`xaybzc|d|dmuvwwxyzzz{{||}}}~~~~}}}||{{zzzyxwwvum|d|dzcybxav`u_s^r]q[oZnYlXkWiVhT~fS|dRybPw`Ou^Mr\LpZKnWIkVHiSFgQEdPCbMB]H=VA7P;0J3*^J?aMAdOCfQDhSFkUGmWIoYJr[Lt^Mv_OyaP{dR}eSgTiVjWlXmYoZp[r]s^u_v`xayb{c|dkvwxyyyz{{|}}~~}}|{{zyyyxwvk|d{cybxav`u_s^r]p[oZmYlXjWiVgT}eS{dRyaPv_Ot^Mr[LoYJmWIkUGhSFfQDdOCaMA^J?\H>YF<`NF::::::::::::::::::::986420/-+)>F0'G2'J3(L6*P9,R;-U>/X?1ZB3]E4^E4_F4bH6dJ7hM8kO:lQqU?rV@uXAwZCy\D{]F}_GaHcIdKfLhMiNqU|d~efghiijklmmmmnnmmmoxrrrrssssttttttttttttttttuurrrrrrrrrrrqqqqqppppoooor{llkjjifeeedc~b}a|`{`z_x^w^v\iLaD`C~^B}\A{Z?xX>vW=tU+W<)T9'Q7&M3"I/F-D*A'=%:"64un% #&*-0369- % `' *.1 5 8 ;>"A%D(G*K-N0Q2T5W8 Y:"\<#^>%`@&cB(eD)gF+jH,lJ.nL/qN1sP2uR4wS5|Z%\<#Y:"W8 T5Q2N0K-G*D(A%>";8 5 1 .*' # ^$ (+/3 6 9 < ?#B&E(H+K.O1R3U6X9!Z;"\=$_?%aA'dC(fE*hG+kI-mK/oM0rO2tQ3vS4zV7iLlPnQoSpTqTsUtVuWwXwYyZzZ{[{[|[}\~]~]^^cpbbccccddddeeeeeffffffffffffffffffffeeeeeddddccccbbgy_^^~]~]}\|[{[{[zZyZwYwXuWtVsUqTpToSnQlPiLzV7vS4tQ3rO2oM0mK/kI-hG+fE*dC(aA'_?%\=$Z;"X9!U6R3O1K.H+E(B&?#< 9 6 3 /+($ & ),04 7 :=!@$C'F)I,L/P1S4V7X9![;#]=$`@&bB'dD)gF*iH,kJ.nL/pN1rP2uR4vS5fJmRnSoSqTrUsVtWvXwYyYzZz[{\|\~^__````eqdeeefffffffffggggggghhhhhhhhhhgggggggfffffffffeeedhz`````__~^|\{\z[zZyYwYvXtWsVrUqToSnSmRfJvS5uR4rP2pN1nL/kJ.iH,gF*dD)bB'`@&]=$[;#X9!V7S4P1L/I,F)C'@$=!:7 4 0,)% & 6& *-15 8 ;>"A%D'G*J-M0Q2T5W8 Y:"\<#^>%`@&cB(eD)hF+jH-lJ.oM0qN1sQ3uR4aDlRnRoSqTrUsVtXvXwYxZy[{\|\}]~_`deedbagreeeffffgggghhhhhhiiiiiiiiiiiiiiiihhhhhhggggffffeeei~babdeed`~_}]|\{\y[xZwYvXtXsVrUqToSnRlR`CuR4sQ3qN1oM0lJ.jH-hF+eD)cB(`@&^>%\<#Y:"W8 T5Q2M0J-G*D'A%>";8 5 1-*& $ 2$ p'+.2 5 8 < ?#B%E(H+K.N0Q3T6X9!Z;"\=$_?%aA'dC(fE*hG,kI-mK/pM0rO2tQ3{Y;lRnSpTpUrVsWuXvXwYxZz[{\|]}^_djoqpiedisffgggghhhhiiiiijjjjjjjjjkkjjjjjjjjjiiiiihhhhggggffj~cdeipqojd_}^|]{\z[xZwYvXuXsWrVpUpTnSlR{Y;tQ3rO2pM0mK/kI-hG,fE*dC(aA'_?%\=$Z;"X9!T6Q3N0K.H+E(B%?#< 8 5 2 .+'# m$ (+/3 6 9%`@&cB(eD)gF+jH-lJ.oM0qO1sQ3bGnToUqVrVsXuYvZw[x\z\{]}_aflv{lgmuhiiijjjkkkklllllmprux|~~|xurpmlllllkkkkjjjiiihlfgl{vlfa}_{]z\x\w[vZuYsXrVqVoUnTbFsQ3qO1oM0lJ.jH-gF+eD)cB(`@&^>%[<#Y:!V7 S5P2M/J,G)C'@$=!:7 4 0,)% # & )-14 8 ;>"A%D'G*K-N0Q3T5W8 Z:"\<#^>%aA'cC(fE*hG+kI-mK/oM0rO2yX;nToVqVrWsXuYvZw[x[z\{]}_chq}oiovjjjkkkkkkklllmnqtyçƬǮǮƬçytqnmlllkkkkkkkjjjmfio}qhc}_{]z\x[w[vZuYsXrWqVoVnTyW:rO2oM0mK/kI-hG+fE*cC(aA'^>%\<#Z:"W8 T5Q3N0K-G*D'A%>";8 4 1-)& ' % K& *.2 5 8 ; ?"B%E(H+K.N0Q3U6X9!Z;"]=$_?%aA'dC)fE*iG,kI-nL/pN1sQ3iOoUpVrWsXuYuZw[x[z]{_~aemwpjpwjjkkkklllmmmnptzçǮǮçztpnmmmlllkkkkjjohjpwme~a{_z]x[w[uZuYsXrWpVoUiOsQ3pN1nL/kI-iG,fE*dC)aA'_?%]=$Z;"X9!U6Q3N0K.H+E(B%?"; 8 5 2 .*& # I$ y'+/2 6 9 < ?#B&F)I+L.O1R4U7X9![;#]=$`@&bB'eD)gF+iH,lJ.nL/qN1~^CoVqWrXsYuZv[w\x]{_|`dir~skrxllmmmnnnnnnosw§ƮƮ§wsonnnnnnmmmllpiks~rid|`{_x]w\v[uZsYrXqWoV}]BqN1nL/lJ.iH,gF+eD)bB'`@&]=$[;#X9!U7R4O1L.I+F)B&?#< 9 6 2 /+'$ x$ (,/3 6 :=!@$C&F)I,M/P2S4V7 Y:![<#^>%`@&cB(eD)gF+jH-lK.oM0sQ3mTqWrXsZtZv[w\x]{^}afmxtmt§ymmmmnnnoooqt{ǬǬ{tqooonnnmmmmq§jmtxmf}a{^x]w\v[tZsZrXqWmTsQ3oM0lK.jH-gF+eD)cB(`@&^>%[<#Y:!V7 S4P2M/I,F)C&@$=!:6 3 /,($ % (,04 7 :=!@$D'G*J-M/P2S5W8 Y:"\<#^>%a@'cC(fE*hG+kI-mK/oM0`FpXrYtZt[v\w]y^{`~cjsunv¨¨©zoooppppppqu|ǯǯ|uqppppppooor©¨¨lnusj~c{`y^w]v\t[tZrYpX`FoM0mK/kI-hG+fE*cC(a@'^>%\<#Y:"W8 S5P2M/J-G*D'@$=!:7 4 0,($ % )-04 7 ;>"A%D'G*K-N0Q3T5W8 Z:"\=$_?%aA'dC(fE*iG,kI-mK/rQ4oVrYsZt[v\w]y^|afozwow¨©éé{ooopppqqrv|ǯǯ|vrqqpppooosé驨mowzof|ay^w]v\t[sZrYoVrQ4mK/kI-iG,fE*dC(aA'_?%\=$Z:"W8 T5Q3N0K-G*D'A%>";7 4 0-)%  & *-15 8 ;>"A%E(H+K-N0Q3T6X9!Z;"]=$_?%bA'dC)gE*iH,kJ.nL/`FqYsZt[u\w]z_}cjswpw©©êê{ppqqqrrsu{ȯȯ{usrrqqqpptêê©©npwsj}cz_w]u\t[sZqY~_EnL/kJ.iH,gE*dC)bA'_?%]=$Z;"X9!T6Q3N0K-H+E(A%>";8 5 1-*& $ $ 8& *.1 5 8 ; ?"B%E(H+K.O1R3U6X9![;#]=$`?&bB'eD)gF+iH,lJ.pO2oWrZs[u\w]z`fnyxqx©©©éêĪ|qqrrrrrtyȮȮytrrrrrqquĪêé©©nqxynfz`w]u\s[rZoWpN1lJ.iH,gF+eD)bB'`?&]=$[;#X9!U6R3O1K.H+E(B%?"; 8 5 1 .*& & 6# X' *.2 5 9 < ?#B&E)I+L.O1R4U7X9![;#]>$`@&bB(eD)gF+jH-lJ.|]CrYs[v]w^{bisyry©éêĪīī}rrrrsstwĪĪwtssrrrrvīīĪêéorysi{bw^v]s[rY|]ClJ.jH-gF+eD)bB(`@&]>$[;#X9!U7R4O1L.I+E)B&?#< 9 5 2 .*' # W$ r'+/2 6 9 < ?#C&F)I,L/O1S4V7Y:![<#^>%`@&cB(eD)hF+jI-mK.lUs\u]x`}fozzs{ªëëīūŬ~stttttv|ɲʳ|vtttttswŬūīëëqszzo}fx`u]s\lUmK.jI-hF+eD)cB(`@&^>%[<#Y:!V7S4O1L/I,F)C&?#< 9 6 2 /+'$ q$ '+/3 6 9=!@#C&F)I,M/P2S5V7 Y:![<#^>%`@&cB(eE*hG+kI-vV%[<#Y:!V7 S5P2M/I,F)C&@#=!96 3 /+'# $ (+/3 6 :=!@$C'F)J,M/P2S5W8 Y:"\<#^>%aA'cC(fE*hG+kI-eNu_{eny|t~īīūŬŬŭuuuvvw|ʳ˳|wvvuuuxŭŬŬūī¨rt|zn|eu_eNkI-hG+fE*cC(aA'^>%\<#Y:"W8 S5P2M/J,F)C'@$=!:6 3 /+(% # (,/3 7 :=!@$D'G*J-M/P2T5W8 Y:"\<#^>%aA'dC(fE*iH-oN2va~jt}vīĬŬŭƭƮvvvwwzĪĪzwwvvvzƮƭŭŬĬétv}tjvaoM1iH-fE*dC(aA'^>%\<#Y:"W8 T5P2M/J-G*D'@$=!:7 3 /,($ $ (,03 7 :=!A$D'G*J-M0Q2T5W8 Z:"\=$_?%aA'dC(gF,lK0aImz~vŬŭƭƭƮƮwwxxx{ȮȮ{xxxwwzƮƮƭƭŭīuv~{maIlK0gF,dC(aA'_?%\=$Z:"W8 T5Q2M0J-G*D'A$=!:7 3 0,($ % (,04 7 :=!A$D'G*J-N0Q3T5W8 Z:"\=$_?%bB(fE,kK1tU<{fwŭŭƮǮǯǯxxyyy}ʳʳ}yyyxx{ǯǯǮƮŭĬuw{ftU!A$D'G*J-N0Q3T5W8 Z;"\=$_?%cC*jJ1sU!:7 4 0,)% $ ),04 7 :>"A$D'G*K-N0Q3T6W8 Z;"\=$`@&eF-pR:dNryƮƮǯǯǰǰzz{{{ĨŲƴĩ{{{zz}ǰǰǯǯƮŮxyrdNpR:eF-`@&\=$Z;"W8 T6Q3N0K-G*D'A$>":7 4 0,)$ % ),04 7 ;>"A%D'G*K-N0Q3T6X9!Z;"\=$`A'gH/uYAxmeªzƯƯǰȰȰȰ{{{{}ŪòòŪ}{{{{~ȰȰȰǰƯŮxzªxmeuYAgH/`A'\=$Z;"X9!T6Q3N0K-G*D'A%>";7 4 0,)% % ),04 7 ;>"A%D'G*K-N0Q3T6X9!Z;"\=$`A'gH/uYAxmeêzǯǯȯȯȰȰ{{||}ŪIJIJŪ}||{{ȰȰȯȯǯƮyzêxmeuYAgH/`A'\=$Z;"X9!T6Q3N0K-G*D'A%>";7 4 0,)% $ ),04 7 :>"A$D'G*K-N0Q3T6W8 Z;"\=$`@&eF-pR:fPê{ǯǯȰɰɱɱ||||}ũƳǴŪ}||||ɱɱɰȰǯƮy{êfPpR:eF-`@&\=$Z;"W8 T6Q3N0K-G*D'A$>":7 4 0,)$ % ),04 7 :>!A$D'G*J-N0Q3T5W8 Z;"\=$_?%cC*jJ1|`H|ê|ǯǯȰɰɱɱ||}}~ĩͶͷĩ~}}||ɱɱɰȰǯƭz|ê|{_GjJ1cC*_?%\=$Z;"W8 T5Q3N0J-G*D'A$>!:7 4 0,)% % (,04 7 :=!A$D'G*J-N0Q3T5W8 Z:"\=$_?%bB(fE,|^Gnxë|ȰȰȱɱɱɱ}}~~~ç͵͵ç~~~}}ɱɱɱȱȰŭ{|ëxn{_HfE,bB(_?%\=$Z:"W8 T5Q3N0J-G*D'A$=!:7 4 0,(% $ (,03 7 :=!A$D'G*J-M0Q2T5W8 Z:"\=$_?%aA'dC(dO{gmuĬ~Ȱȱɱɱʲʲ~~¦˲˲¦~~ʲʲɱɱȱƮ|~Ĭum{gcNdC(aA'_?%\=$Z:"W8 T5Q2M0J-G*D'A$=!:7 3 0,($ # (,/3 7 :=!@$D'G*J-M/P2T5W8 Y:"\<#^>%aA'dC(kVyf{gls}Ĭ~ɰɱɱɲʲʳɯɯʳʲɲɱɱƮ}~Ĭ}sl{gyfkUdC(aA'^>%\<#Y:"W8 T5P2M/J-G*D'@$=!:7 3 /,($ $ (+/3 6 :=!@$C'F)J,M/P2S5W8 Y:"\<#^>%aA'cC(s^ye{f}ilqyŬɱɲʲʲʲʳƫϹϸƫʳʲʲʲɲǯ}Ŭyql}i{fyer^cC(aA'^>%\<#Y:"W8 S5P2M/J,F)C'@$=!:6 3 /+(% $ '+/3 6 9=!@#C&F)I,M/P2S5V7 Y:![<#^>%`@&dC*yf{g|h~jkmqwŭɲɲʲʳ˳˴¥Ĩ̳̳Ĩ˴˳ʳʲɲǯ~ŭwqmk~j|h{gyedC*`@&^>%[<#Y:!V7 S5P2M/I,F)C&@#=!96 3 /+'# # s'+/2 6 9 < ?#C&F)I,L/O1S4V7Y:![<#^>%`@&iI0yf{h}i~jklnpv~ŭɲʳʳʳ˳˴¦¥ȭϹϹȭ¥˴˳ʳʳʳȰŭ~vpnlk~j}i{hyfiI0`@&^>%[<#Y:!V7S4O1L/I,F)C&?#< 9 6 2 /+'$ q% Z' *.2 5 9 < ?#B&E)I+L.O1R4U7X9![;#]>$`@&lN5zg{h}j~jlmnoqtzŮɲʲʳ˳˴˴¦ĩ˳´õ˳ĩ˴˴˳ʳʲȰŮztqonml~j}j{hzgkM5`@&]>$[;#X9!U7R4O1L.I+E)B&?#< 9 5 2 .*' # X$ 9& *.1 5 8 ; ?"B%E(H+K.O1R3U6X9![;#]=$`?&pR9{i}j~klmnppqrtzƮʳʳ˴˴˵̵§æǭииǭæ̵˵˴˴ʳɱƮztrqppnml~k}j{ioR9`?&]=$[;#X9!U6R3O1K.H+E(B%?"; 8 5 1 .*& & 6 & *-15 8 ;>"A%E(H+K-N0Q3T6X9!Z;"]=$_?%sU>|i}jlmnopqrrtuy~Ưʴʴ˴˴̵̵èĩʰккʰĩ̵̵˴˴ʴɲƯ~yutrrqponml}j|isU>_?%]=$Z;"X9!T6Q3N0K-H+E(A%>";8 5 1-*& $ % )-04 7 ;>"A%D'G*K-N0Q3T5W8 Z:"\=$_?%sV?|j~klmnpqrsstuvx|Ư˴˴˴˵̵̶è¦ƪ˲ϺϺ˲ƪ¦̶̵˵˴˴ȰƯ|xvutssrqpnml~k|jrV?_?%\=$Z:"W8 T5Q3N0K-G*D'A%>";7 4 0-)% % (,04 7 :=!@$D'G*J-M/P2S5W8 Y:"\<#^>%uYC}j~lmnopqrstuuvwy|İǯ˵˵˵̵̵̶èçƫ˳лл˳ƫç̶̵̵˵˵Ȱǯı|ywvuutsrqponm~l}juYC^>%\<#Y:"W8 S5P2M/J-G*D'@$=!:7 4 0,($ $ (,/3 6 :=!@$C&F)I,M/P2S4V7 Y:![<#^>%uXB~lmnpprrttuvwxxyz|îǯ˵˵˵̵̶ͶéæèǬ˲ѺöµѺ˲ǬèæͶ̶̵˵˵ɱǯî|zyxxwvuttrrppnm~luXB^>%[<#Y:!V7 S4P2M/I,F)C&@$=!:6 3 /,($ # {'+/2 6 9 < ?#B&F)I+L.O1R4U7X9![;#]=$tXAlnopqrstuvvwxyzz{}Ƴǰ̶̶̶̶̷̷Ī¥¥¦ĨƫɰηммηɰƫĨ¦¥¥̷̷̶̶̶ȱǰƳ}|zzyxwvvutsrqponltW@]=$[;#X9!U7R4O1L.I+F)B&?#< 9 6 2 /+'$ x% L& *.2 5 8 ; ?"B%E(H+K.N0Q3U6X9!Z;"]=$rU>mnoqqstuvwwxyyz{{}~Űôǰ˶̶̶̶ͶͷĪ¥¥¦çŪǮ˲ϸѽĶŷѽϸ˲ǮŪ禥¥ͷͶ̶̶̶ǰǰôŰ~}{{zyyxwwvutsqqonmrU>]=$Z;"X9!U6Q3N0K.H+E(B%?"; 8 5 2 .*& % K# & )-14 8 ;>"A%D'G*K-N0Q3T5W8 Z:"\<#pS=opqqstuvwwyyz{{|}}~¬ɶƯ̶ͷͷͷͷ͸Φ¦çççĨũƫȮ˲ͶйҼӾͼɺɺͼӾҼйͶ˲ȮƫũĨçç禦͸ͷͷͷͷȱƯɶ¬~}}|{{zyywwvutsqqpopR<\<#Z:"W8 T5Q3N0K-G*D'A%>";8 4 1-)& # & ),04 7 :=!@$C'G)J,M/P2S5V7 Y:![<#lO8opqrttvvxxyzz{|}~~Ʊŵĭ̶̷͸͸͸͸¦¦¦§ççèĨŪƫǭȯʰ˲̴̴̳̳˲ʰȯǭƫŪĨèç秦¦¦¦¦͸͸͸͸̷ȱĭŵƱ~~}|{zzyxxvvttrqpokN7[<#Y:!V7 S5P2M/J,G)C'@$=!:7 4 0,)% $ (+/3 6 9"A%D'G*J-M0Q2T5W8 Y:"\<#orttvwxyz{{|}}~̷̸͸͹ιιŬ§§ççèèèèèĨĨĨĨĩĩĩĩĩĩĩĩĩĩĩĩĨĨĨĨèèèèèçç§§¨ιι͹͸̸ȱ~}}|{{zyxwvttrn\<#Y:"W8 T5Q2M0J-G*D'A%>";8 5 1-*& # 3@% ),04 7 :=!@$C'F)I,L/P1S4V7X9![;#westuvwxyz{|}~~̸͸͸ιικŬ¦§§§¨¨èèèĨĨĨĩĩĩĩĩĩĩĩĩĩĩĩĩĩĩĩĩĩĨĨĨèè訨§§§¦¨κιι͸͸DZ~~}|{zyxwvutsvd[;#X9!V7S4P1L/I,F)C'@$=!:7 4 0,)% $ (+/3 6 9 < ?#B&E(H+K.O1R3U6X9!Z;"m[tuuwwyzz||}~~̷̸͸͹ιϺŭ§¨èèèèèèéĩĩĩĩĪĪŪŪŪŪŪŪŪŪĪĪĩĩĩĩéèèèèè訧¨Ϻι͹͸̸ư~~}||zzywwuutlYZ;"X9!U6R3O1K.H+E(B&?#< 9 6 3 /+(#  $ c' *.1 5 8 ;>"A%D(G*K-N0Q2T5W8 Y:"|aMuvwxyz{{}}̸͹͹͹κϺƮ§§§¨¨èéééééĩĩĩĪĪŪŪŪŪŪŪŪŪŪŪŪŪŪŪĪĪĩĩĩééééé訨§§§éϺκ͹͹͹DZ}}{{zyxwvu{aLY:"W8 T5Q2N0K-G*D(A%>";8 5 1 .*' # _ 8::::::852/,*(%9A*!B,!F/#I2&M5(O8*R:+U<-W?/ZA1\C2_F4bH6dJ7hM8kO:lQaJ<^H:\F9YC7VA6T>4P<1N90B/01456678999::) :::::::::::::::_MEYF<\H>^J?aMAdOCfQDhSFkUGmWIoYJr[Lt^Mv_OyaP{dR}eSo]¯ðııŲųdzǴȵȶɶɶʶʷ˷̷̸̸͹͹ͺͺκϺϻммммммѼѼѼѼѽѽѽҽҽҾҾҾҾҾҾҾҾҾҾҾҾҾҾҾҾҾҾҽҽѽѽѽѼѼѼѼммммммϻϺκͺͺ͹͹̸̸̷˷ʷʶɶɶȶȵǴdzųŲııð¯o]}eS{dRyaPv_Ot^Mr[LoYJmWIkUGhSFfQDdOCaMA^J?\H>YF<`NE::::::::::::::9 #(-28::::ykdXDgQDkVHnWIpZKr\Lu^Mw`OybP|dR~fS°°ñıIJųƳƴǴȵɶʶʷʷʷ˸˹̹͹͹ͺκλλλϻмммѽѽѽѽѽѽѽѾѾѾҾҾҾҾҾҾҾӾӾӾӾӾӾӾӾҾҾҾҾҾҾҾѾѾѾѽѽѽѽѽѽѽмммϻλλλκͺ͹͹̹˹˸ʷʷʷʶɶȵǴƴƳųIJıñ°°~fS|dRybPw`Ou^Mr\LpZKnWIkVHiSFgQEdPCaLA[F;U@6O:/I3*=  $ (,/3 6 9 < @$F)L0Q5"V:'\@,aF1gK7kP;q`°±òIJųŴƴǵǵȵɷʷʸ˸˸˸˹̺ͺͺͺλλλϼϼмннѽѾѾѾѾѾѾѾҾҾҾҾҾӾӾӾӿӿӿӿӿӿӿӿӿӿӾӾӾҾҾҾҾҾѾѾѾѾѾѾѾѽннмϼϼλλλͺͺͺ̺˹˸˸˸ʸʷɷȵǵǵƴŴųIJò±°yitZFoUBkQ>fL9`F4[A/U;)P5%J/D)>"83 /,($ # m' *.15 8 ;>"A$D'G)J,M/O1R4U7bE/z{|}~®ïıƳǴɶɷ˸˸˸̹̹̺ͺκλλϻϼϼϼмнѽѾѾҾҾҾҾҾҾҾҾҾҿҿҿҿҿҿҿҿҿҿҿҿҿҿҿҿҿҿҾҾҾҾҾҾҾҾҾѾѾѽнмϼϼϼϻλλκͺ̺̹̹˸˸˸ʸɷɶȶƴƳıñ~|{zaD-U7R4O1M/J,G)D'A$>";8 5 1.*' $ i% ),03 7 9"A%D'G*J,M/P2S4U7~eR|}~ɵͺͻλλμů©ªªêêīīĬĬŬŬŬŬŬŭŭŭŭŭŭŭŭŭŭŭŭŭŭŭŭŭŭŬŬŬŬŬĬĬīīêꪪ©ªμλλͻͺĮ~}|}cQU7S4P2M/J,G*D'A%>";8 5 2 .+'% & =& *-04 7 :=!@#C&F)H+K.N0Q3T5fI3~~ɵͻͻμμϼưªªììĭŭŭƭƭƮƮǮǮǮǮǮǮǮǮǮǯǯǯǯǯǯǮǮǮǮǮǮǮǮǮƮƮƭƭŭŭĭì쪪ìϼμμͻͻĮ~~eH2T5Q3N0K.H+F)C&@#=!:7 4 0-*& # ;% (+/2 6 8 ; >"A%D'G*J-M/P2S4U7pȵͻͻͼμϼưª«ĬŮưDZȱȲȲɲɲɳɳɳɳʴʴʴʴʴʴʴʴʴʴʴʴʴʴʴʴɳɳɳɳɲɲȲȲȱDZưŮĬ«ª¬ϼμͼͻͻî~nU7S4P2M/J-G*D'A%>"; 8 6 2 /+(% # _' *-14 7 :=!@$C&F)H+K.N0Q3T5zaNȵͻͻͼμμư«ìůȳ˷ιϼϼϼммннннѽѽѽѽѽѽѽѾѾѽѽѽѽѽѽѽннннммϼϼϼι˷ȳů쫬μμͼͻͻîy`LT5Q3N0K.H+F)C&@$=!:7 4 1-*' # ^+% (,/3 6 9 ; >"A%D'G*J,M/O1R4]?*~Ǵͻλλμμư«íDz¹ºººº»ûûûûûûûûûûûûûûûûûûûûûû»ºººº¹Dzí«¬μμλλͻ­}\?)R4O1M/J,G*D'A%>"; 9 6 3 /,(% +% |' *.14 7 :=!@#C&E)H+K-N0Q2S5qaǴͼμμμμƱ¬ĮɴɴĮ¬¬μμμμͼ­p`S5Q2N0K-H+E)C&@#=!:7 4 1.*' # t% ),/3 6 9 ; >"A%D'G*I,L/O1R3gL8dzͼͼμνϽƱ¬íȳϼϼȳí¬¬ϽνμͼͼeJ6R3O1L/I,G*D'A%>"; 9 6 3 /,)% $% }' *-14 7 :=!?#B&E(H+K-M0P2S4}mųͼνννϾƲ«¬íƱ̹µµ̹Ʊí¬«íϾνννͼ{kS4P2M0K-H+E(B&?#=!:7 4 1-*' $ y$% (,/2 5 8 ;>"A$C'F)I,L.N0Q3kP<űͼͼννξƲ­įȴоŽŽоȴį­­ξννͼͼjO;Q3N0L.I,F)C'A$>";8 5 2 /,(% . $ w' *-14 7 9< ?#B%D(G*J-M/O1R4qñͼͽͽνξƲ¬­Ʊ˸´´˸Ʊ­¬­ξνͽͽͼ~pR4O1M/J-G*D(B%?#< 97 4 1-*' $ r % (+/2 5 8 ;=!@$C&E)H+K-N0P2iM:ð̼ͼͽννƲ¬­îdzͻøøͻdzî­¬­ννͽͼ̼gL8P2N0K-H+E)C&@$=!;8 5 2 /+(% # W& *-03 6 9 < >"A%D'F)I,L.N0Q3yj¯̼ͼͽνξŲ¬­­îŰɵŽŽɵŰî­­¬®ξνͽͼ̼whQ3N0L.I,F)D'A%>"< 9 6 3 0-*& % R# (+.14 7 :=!?#B%E(G*J,L/O1aE0¯̼ͽͽνξŲ­­îïƲ˹ĶĶ˹Ʋïî­­­ξνͽͽ̼_B.O1L/J,G*E(B%?#=!:7 4 1.+($ # 3& ),/2 5 8 ;=!@$C&E(H+K-M/P2iX̼̽̽ͽͽŲ­®ŰȴνĹĹνȴŰ®­ͽͽ̽̽˼~hWP2M/K-H+E(C&@$=!;8 5 2 /,)& & /$ ' *-03 6 9 < >"A$C'F)I+K.N0T7!~̼̼ͽͽ;ı­îűɷſſɷűî­;ͽͽ̼˻|S6 N0K.I+F)C'A$>"< 9 6 3 0-*' $ . % (+.14 7 :< ?#A%D'G*I,L.N0iO;̽̽ͽͽξIJ­®®įƳ̺ĸĸ̺Ƴį®®­ξͽͽ̽ʺhM9N0L.I,G*D'A%?#< :7 4 1.+(%  $ F& ),/2 5 8 ;=!@#B&E(G*J,L.O1o_˽̽̽̾;IJ®¯ıǵͽĻĻξǵı¯®;̾̽̽ɺm\O1L.J,G*E(B&@#=!;8 5 2 /,)& # B$ ' *-03 6 8 ;>"@$C&E(H*J-M/S6 |˼̽̽;;IJ®ïŲɷĿſɷŲï®;;̽̽ɹ{R5M/J-H*E(C&@$>";8 6 3 0-*' $ . $ (+.14 7 9 < >"A$C&F)H+K-M/_D0˽˽˽̾̾ó°Ŵ˻ŹŹ˻Ŵ°̾̾˽˽Ǹ]B.M/K-H+F)C&A$>"< 9 7 4 1.+(%  " 5& ),/2 5 7 :< ?"A%D'F)H+K-M/rYHʼ˽˾˾̾óñǶ;żżͿǶñ̾˾˾˽ǸpXFM/K-H+F)D'A%?"< :7 5 2 /,)& % 0$ y& ),/2 5 8 :=!?#A%D'F)I+K-M/o`ɼ˾˾̾̿ó°ijȹ÷÷ȹij°̿̾˾˾Ƹm^M/K-I+F)D'A%?#=!:8 5 2 /,)& # s$ '*-03 5 8 :=!?#B%D'F)I+K-N1~qȻ˾˾˾̿ó±ŵ˼ƻƻ˼ŵ±̿˾˾˾Ŷ|oN1K-I+F)D'B%?#=!:8 5 3 0-*'$ "$ (+-03 6 8 ;=!?#B%D'F)I+K-S6!{Ǻʽ˾˾˾³òƶͿžžͿƶò˾˾˾ʽĵ{S6!K-I+F)D'B%?#=!;8 6 3 0-+(% ' $ +% (+.14 6 8 ;=!@#B%D'F)I+K-U8$}Ƹʽʾ˾˾³óȹ÷÷ȹó˾˾ʾʽ´U8$K-I+F)D'B%@#=!;8 6 4 1.+(% & ($ N& )+.14 6 9 ;=!@#B%D'F)I+K-U8$ŷɽʾ˾˾²ĵ˼ƼƼ˼ĵ˾˾ʾɽW;'K-I+F)D'B%@#=!;9 6 4 1.+)& # I$ i& ),/14 6 9 ;=!@#B%D'F)H+K-W:'Ķʽʾʾ˿³óƷƷó˿ʾʾʽZ>*K-H+F)D'B%@#=!;9 6 4 1/,)& $ i# & ),/1 4 7 9 ;=!?#B%D'F)H+J-W:'¶ɾʾʾʾóȺĹĹȺóʾʾʾɾX<(J-H+F)D'B%?#=!;9 7 4 1 /,)& $ $ ' ),/1 4 6 9 ;=!?#A%D'F)H*J,T8${ɾɾʾʾĵ˽ǾǾ˽ĵʾʾɾɾ~U9%J,H*F)D'A%?#=!;9 6 4 1 /,)' % # ' *,/1 4 6 8 ;=!?#A%C&E(G*I,N1zmɾʾʿʿôǸǸôʿʿʾɾvP4I,G*E(C&A%?#=!;8 6 4 1 /,*' $ +$ ' *,/14 6 8 :=!?"A$C&E(G*I+L.~j[ɾɾɿʿõȻŻŻȻõʿɿɾɾqcL/I+G*E(C&A$?"=!:8 6 4 1/,*' # + $ ' ),/14 6 8 :< >"@$B&D'F)H+J,qYHɾɿʿʿŷ˾˾ŷʿʿɿɾs[KJ,H+F)D'B&@$>"< :8 6 4 1/,)' #  +% ' ),.13 5 8 :< >"@#A%C'E(G*I,]B0ɾɿɿʿƹ¸¸ƹʿɿɿɾ]B0I,G*E(C'A%@#>"< :8 5 3 1.,)' $ +$ & )+.03 5 7 9 ;=!?#A$C&E(F)H+N1pbȾɾɿɿõǻżżǻõɿɿɾȾo`M0H+F)E(C&A$?#=!;9 7 5 3 0.+)& $ $ & )+-02 5 7 8 ;< >"@$B%D'E)G*I,fL:ȾɿɿɿĶʾžžʾĶɿɿɿȾeK9I,G*E)D'B%@$>"< ;8 7 5 2 0-+)& # $ k& (+-/2 4 6 8 :< =!?#A%C&D(F)H+P4!xkȿɿɿʿĸɾÿÿɾĸʿɿɿȾxkO3 H+F)D(C&A%?#=!< :8 6 4 2 /-+(& % g% L% (*,/13 5 7 9 ;=!>"@$B%C'E(G*H+_D2~ȾɿɿɿķŹŹķɿɿɿǼ~_D2H+G*E(C'B%@$>"=!;9 7 5 3 1/,*(% # I$ +$ '),.02 4 6 8 :< =!?#A$B&D'E)G*H+lTCȾȿȿƼlTCH+G*E)D'B&A$?#=!< :8 6 4 2 0.,)'% & ($$ & )+-/13 5 7 9 ;< >"?#A%C&D'F)G*L/~hZƼȿɿɿɿɿȿĺ{eWK.G*F)D'C&A%?#>"< ;9 7 5 3 1/-+)& $ ' $ x& (*,.02 4 6 8 9;=!>"@#A%C&D'F)G*N2|gXȿȿȿȿȿȿ{eVM1G*F)D'C&A%@#>"=!;98 6 4 2 0.,*(& # s" 5$ ' )+-/13 5 7 8 :; =!>"@$A%C&D'E(G)iQAuueM"=!; :8 7 5 3 1/-+)' % $ 2. $ & (*,.02 4 5 7 9 :; =!>"@#A%B&D'aH8pZKqZKqdy}qpcqZKpZK]D3D'B&A%@#>"=!; :9 7 5 4 2 0.,*(& $  $ F% ' )+-/12 4 6 7 9 :; =!>"?#A$[B2nWIoXIoYJU;)K/nWGywnWGJ-K/oYJoXInWIX?/A$?#>"=!; :9 7 6 4 2 1/-+)' % # B. $ & (*+-/13 4 6 7 8 :;"V=-kVHlWHmWIR9'D(E(F)K/kTD{mzljSCK.F)E(D(H-mWIlWHkVHS9)>""?#@$A%B%C&C'D'E(F)F)J-]C1pZJqcvvqcoYI\B0J-F)F)E(D'C'C&B%A%@$?#>"B'hSFhRFgQEH.8 7 5 4 2 1/.,*(' & # Q $ w% ' )*,-/02 3 5 C*dPCePDfQDJ1";< =!>"?#@#@$A%B%B&C&D'D'E(E(E)F)F)F)M1U;(^E3_F4eL;gO>gO>eL;_F4^E3U;(M1F)F)F)E)E(E(D'D'C&B&B%A%@$@#?#>"=!< ;?%fQDePDdPCB(5 3 2 0/-,*)' % # t $$ ~% ' (*+-.01=$bMBcNBdOCG/ 8 9 :;< "?"?#@$@$A%A%B%B&C&C&C'D'D'D'D'D'D'D'D'D'D'D'D'C'C&C&B&B%A%A%@$@$?#?">"=!">"?"?#?#@#@$@$A$A$A$A$A%A%A$A$A$A$@$@$@#?#?#?">">"=!=!< ; ;:98 8 7 6 5 :aMAaLA_K@4 .,+*(' % $ w"$% `$ & ')*.]I?^I?^J?A)1 3 4 5 5 6 7 8 8 9 ::;;; < < =!=!=!=!=!>!>">">">">!=!=!=!=!=!< < ; ;;::9 8 8 7 6 5 5 4 3 1 6^J?^I?]I?.*)'& $ # _+% >$ % ' (YE;ZG=\H>>&./012 3 4 4 5 6 6 7 7 8 8 9 9 9::::::;;::::::99 9 8 8 7 7 6 6 5 4 4 3 2 10/.3 \H>ZG=XD:(' % $ " <# l$ Q=4XD_ME0 *,./12 3 4 5 5 5 6 6 5 5 5 4 3 2 1/.,*+_MEXE=}$ # I% (H3']I?713 5 6 8 9 :;;< < < < < < ;;:9 8 6 5 3 11 ]I?G2&(& # H% R' ),/R<1bMB>$8 :;=!>"?#@$A$A%B%B&B&B&B&B%A%A$@$?#>"=!;:8 8bMBQ://,)& # P% K& )-03 6 \F9gQED)>"@$A%C&D(E)F)G*H+H+I+I,I,I+H+H+G*F)E)D(C&A%@$>">#gQEZC66 3 0-)% $ G" % ),03 6 9 < dM?kVHI/D(F)H+I,K-L.M/N0N0O1O1O1O1O1O1N0N0M/L.K-I,H+F)D(D)kVHbK<< 9 6 3 0,)% # % u'+/3 6 9< ?#A%kTEpZKO3 K-L/N0P1Q3R4S5T5U6U7V7V7V7V7U7U6T5S5R4Q3P1N0L/K-J-pZKiQBA%?#< 96 3 /+'$ q$ % *.2 6 9 < ?#B%E(G*r\Lu^MU9$Q2R4T6V7W8 X9!Y:"Z:"Z;"[;#[<#[<#[<#[<#[;#Z;"Z:"Y:"X9!W8 V7T6R4Q2P3u^MpYHG*E(B%?#< 9 6 2 .*% $ $ ?' +04 8 ;>"B%E(H+K-N1xaPybP\>(W8 X9!Z:"[<#\=$]=$^>%_?%_?&`@&aA'cD*cD*aA'`@&_?&_?%^>%]=$\=$[<#Z:"X9!W8 W8"ybPv_NM0K-H+E(B%>";8 4 0+' & =# f(-16 9=!A$D'G*K-N0P2V9"|dR~fS`C+\<#]=$_?%`@&aA'bB'cB(dC)dD)eD)kK1zbQzbQkK1eD)dD)dC)cB(bB'aA'`@&_?%]=$\<#\=%~fS|dRS5P2N0K-G*D'A$=!96 1-($ c% }).3 7 ;?"B&F)I,M/P2S5V7 _@)hUiVeG.`@&bB'dC(eD)fE*gF+hG+iG,iH,lK0fNfNlK0iH,iG,hG+gF+fE*eD)dC(bB'`@&aA(iVhU[;$V7 S5P2M/I,F)B&?";7 3 .)# {% ).3 8 < @$D'H*K.O1R4V7Y9![;#eG/kWlXiJ0eD)gF+hG+jH,kI-lJ.mK/nL/oM0{\Bww{\BoM0nL/mK/lJ.kI-jH,hG+gF+eD)eE+lXkWaB)[;#Y9!V7R4O1K.H*D'@$< 8 3 .)% $ */4 8 =!A$E(I,M/Q2T6X9!Z;"]=$_?&lM5nZoZnN3jH-lJ.mK/nL0pM0qN1rO2sP2xX;yeyexX;sP2rO2qN1pM0nL0mK/lJ.jH-jI-oZnZhH/_?&]=$Z;"X9!T6Q2M/I,E(A$=!8 4 /*$ $ y)/4 9 =!A%F)J-N0R4V7Y:"\<#_?%aA'dC)rT;p[r]sR6nL0pN1rO2sQ3uR4uR4vS5yW8mRmSyW8vS5uR4uR4sQ3rO2pN1nL0oM0r]q\nN5dC)aA'_?%\<#Y:"V7R4N0J-F)A%=!9 4 /)$ x$ i).4 9 =!B%F)K-O1S5W8 Z;"]=$`@&cB(fE*hG+yZAs]u_wV9sP3uR4vS4wT5xU6yV6{W8dG{{dG{W8yV6xU6wT5vS4uR4sP3sQ3u_s]tT:hG+fE*cB(`@&]=$Z;"W8 S5O1K-F)B%=!9 4 .)$ d$ ?(.3 8 =!B%F)K-O1T5X9![<#^>%aA'dC)gF+jH-lK.aGv^yazY%[<#X9!T5O1K-F)B%=!8 3 .(" <# ' -3 8 =!A%F)K-P1T5X9!\<#_?%bB'eD*hG+kI-nL/qN1fLya{c~\>{W7|X8}Y9~Z:[:_>sVsV_>[:~Z:}Y9|X8{W7{W8{cya`EqN1nL/kI-hG+eD*bB'_?%\<#X9!T5P1K-F)A%=!8 3 -' & % +17 < A$F)K-O1T5X9!\<#_?&cB(fE*iH,lJ.oM0rP2uR4jO{c}d_@~Z9[:\;]<_=kK}}kK_=]<\;[:~Z9~Z:}dzbdHuR4rP2oM0lJ.iH,fE*cB(_?&\<#X9!T5O1K-F)A$< 7 1+$ # t*06 ;@$E(J-O1T5X9!\<#`?&cB(fE*jH-mK/pN1sQ3vS4xU6mR~efaB];^<_=`>gFiigF`>_=^<];]b?eBy[y[eBb?a>`=`>hfkO{W8yU6wS5tQ3qN1nL/jI-gE*cB(_?&\<#X9!S5N0I,D'?"94 .'$ $ +2 8 =!B&H*M/R4W8 [<#_?%cB(fE*jI-nL/qO1uR4wT5zV7|X8~Z9v[hihGdAfBhErRrRhEfBdAdAihpS~Z9|X8zV7wT5uR4qO1nL/jI-fE*cB(_?%[<#W8 R4M/H*B&=!8 2 +% % K)/6 ;A$F)K.Q2V7Z;"^>%bB'fE*jH-nL/qO1uR4wT5zV7|X8Z:];y^knnLiFjGoMmmoMjGiFjGnjrU];Z:|X8zV7wT5uR4qO1nL/jH-fE*bB'^>%Z;"V7Q2K.F)A$;6 /)$ G& ,3 9 >"D'I,O1T6Y:"]=$aA'eD*iH,mK/qN1uR4wT5zV7}Y8[:]"9 3 ,% % S)06 < B%G*M/R4X9!\<#`@&dC)hG+lJ.pN1tQ3wT5zV7}Y8[:_>eDiHkttuSpMwUwUpMqNttciHeD_>[:}Y8zV7wT5tQ3pN1lJ.hG+dC)`@&\<#X9!R4M/G*B%< 6 0)# Q' -3 9?#E(K-P2V7Z;"_?%cB(gF+kI-oM0sQ3wS5zV7|X8[:bBiHkJmLpwwxWtP{Y{YtPuRwwgmLkJiHbB[:|X8zV7wS5sQ3oM0kI-gF+cB(_?%Z;"V7P2K-E(?#93 -& " J)06 < B%H+N0S5Y9!]=$aA'fE*jH-nL/rP2vS4yU6|X8\iJlLnNpPrQtRxz{~\yV{W|X~YZ[[[[[[[[[[[[[[Z~Y|X{WyVzW{zptRrQpPnNlLiJ^>{W8xU6uR4qN1lK.hG+dC)_?&[;#V7 P2K-E(?#9 3 ,% # (/6 < A%G*M0S5Y9!]=$bA'fE*kI-pN2uT6{Y;~[=gIrVvYxZ}^bce|}~pmqrsssvvwwwxwwwwwvuusrqpp~}xgfcaz]w[tXiL_A}\?xW:rQ5mK/fE*bA']=$Y9!S5M0G*A%< 6 /(& <'w8!B*K2$R9)[A1bI7jQ>qWCx^J~dPiUoZs]w`yb|dlvwyzzz||~~}{zzzxwvl|dybw`t^q\lXhT|bOv]JnUCgO=_F6X?0P8*H0$>'G3)w (-269::`NE\H>aLAeOCjTFoYJt]LybP~fSiVlXoZr]u_ya{clwyy{|}}|{yywl{cyau_r]oZlXiV~fSybPu^MpZKkUGfPDaL@[G=_ME::::952 ,*'$wr/6<$C)H.N4"S8&Y>)`C-dG0iK3mO5qS8vV;zZ>~]AgKtZw\z^|_acdefhokllljjkkkllllllkkkjjjiihj~edca~`|^z]x[uYsWcG{Z>xW;rQ5mM2hI/dE,_A)Z<&T7"N2H-B'<"6/ `H@ $ ]*18 >"D(K-Q2W8 \<#`@&eD)jH-nL0sP3zX:lPoSrTuVwXyZ{[|\^_aiddeeffggggggggggggffeedde`_^|\{[yZwXuVrToSlPzX:sP3nL0jH-eD)`@&\<#W8 Q2K-D(>"8 1*% Z% ,3 :@$F)L/R4X9!]=$bB'gF+lJ.pN1vS6jNoSrUtWwXyZ{\~^fjddkeffgghhiiiiiiiiiihhggffegbdjf~^{\yZwXtWrUoSiNvS6pN1lJ.gF+bB']=$X9!R4L/F)@$:3 ,& ' .5 ;A%H+N0T6Z:"_?%dC(hG+mK/rO2cFoUrWtXwZy\}^crohmhhijjjkkmquyyuqmkkjjjihhiforc}^y\wZtXrWoUcFrO2mK/hG+dC(_?%Z:"T6N0H+A%;5 .& ' (/6 =!C&I,P1V7[<#`@&eD)jH,nL0yY"D(K-Q3W8 \=$aA'fE*kI-qN1jQrXtZw[z^du{nrmmnnnssnnnmmnj{udz^w[tZrXjQpM0kI-fE*aA'\=$W8 Q3K-D(>"8 1)& =% h+2 9 ?#E)L.R4X9!]=$bB'gF+lJ.zZ?qYtZw]{`k}q§¨topppuupppop¨§m}k{`w]tZqYzY>lJ.gF+bB']=$X9!R4L.E)?#9 2 +# f$ ,3 :@$F)M/S5Y:"^>%cB(hG+nL0kSs[w]}dus©êvqqruurqqrê©ou}dw]s[kSnL0hG+cB(^>%Y:"S5M/F)@$:3 ,% & ,4 ;A$G*N0T5Z:"_?%dC)iG,wW%eO~jmoqsyÿʳ˴˵ɮȿȿɮ˵˴ɲÿysqom~jeP^>%Y:"S5M/F)@$:3 ,$ % h+2 9 ?#E)L.R4X9!]=$iTmoqstvy˵̵̵çʲ»ºʲç̵̵ɳyvtsqomiT]=$X9!R4L.E)?#9 2 +# f$ @)18 >"D(K-Q3W8 \=$jVnpruvxy{ª̶̶̷¥ĨʱʼɼʱĨ¥̷̶ʴª{yxvurpnjV\=$W8 Q3K-D(>"8 1)% >' (/6 =!C&I,P1V7[<#gSoqtvwy{|}­ü©̷̷ͷ¦¥¦ĨǬ̵ʹǽüüƽʹ̵ǬĨ¦¥ͷ̷˵©ü­}|{ywvtqogS[<#V7P1I,C&=!6 /(! ' .5 ;A%H+N0T6Z:"dPpruwxz|}~ô̷͸͸§¦¦¦ççĩƫȭȯȯȭƫĩç禦¦͸͸ʴô~}|zxwurp~ePZ:"T6N0H+A%;5 .& % ,3 :@$F)L/R4X9!z^Jruwy{|}̷͸騦§§çèèĩĩĩĩĩĩĩĩèèç§§¦ι͸ʴ}|{ywury^JX9!R4L/F)@$:3 ,% # ^*18 >"D(K-Q2W8 pT?tvyz|~̸͹Ϻé¨èèèĩĩĪŪŪŪŪŪŪĪĩĩèèè¨Ϻ͹ʵ~|zyvtpS>W8 Q2K-D(>"8 1*$ \,,)%"zu28 <$C)H.N4"S8&Y>)`C-oT?̹͹κȰƮƮǯȯȯȯȰɰɱɱɱɱɱɱɱɱɰȰȯȯȯǯƮƮǯκ͹˷sYCdH3^D0Y>,S:(N4%H0"B*;$0 $&)&"(-27::`ME]I?aLAeOCjTFoYJu^MybPhUñŲƴȵɶɷ˷̸͹κκϻмммѼѽѽѽҽҾҾҾҾҾҾҾҾҽѽѽѽѼмммϻκκ͹̸˷ɷɶȵƴŲñhUybPu^MpZKkUGfPDaL@\H>_ME:::962,4v6@(K2$S:*[B1cJ8kR@sZF°ñijƴǵɶʶʷ̹͹ͺϻϼмнѽѽѽҽҾҾҿҿҿҿҿҿҿҿҾҾҽѽѽѽнмϼϻͺ͹̹ʸʷɶǴƴIJñw^KpXFhP>`G7X?0O8)G0#=&F3(x ! (/6 < A%G*M0S5x^K{}¬îů̹κλ˶̶͸͹κκϺϺϻлѼѼѼѼѼѼѼмϻϻκκι͸̷˶λκ˸ƱŰî~{x]JS5M0G*A%< 6 /(# % ,3 9 ?#E(K-P2cF0}̸ͻϻ쩪ìĬŭŭŭŭƭƮƮƮƮƮƮƭŭŭŭŭĬ쪩ϻͻɴ}bE0P2K-E(?#9 3 ,% % K)06 < B%H+N0S5|m˹ͼμíªíDzʵ˶˷̷͸͸͸͸͸͸͸͸͸͸̷˷˶ʵDzíªμͼȵ|lS5N0H+B%< 6 0)" J&-3 9?#E(K-P2tZFʷμμíư¼þþþþþþþþþþþþþþþþ¼ưμμdztZGP2K-E(?#93 -& $ T)06 < B%G*M/W:#}ɷννî«ůɽɽů«νμǴ|V9"M/G*B%< 6 0)% R& ,3 9 >"D'I,O1zcPȶͼνî¬ʶʶ¬νͼŲzaNO1I,D'>"9 3 ,& $ M)/6 ;A$F)K.V7!ǵͼνï¬įμμį¬νͼŲ~U7!K.F)A$;6 /)" J% +2 8 =!B&H*M/u\Iƶ̽ͽ¯­®dz¹¹dz®­ͽ̼IJs[HM/H*B&=!8 2 +% ( '.4 9?"D'I,O1qŴ̽;®­ïʸʸï­;̽ñpN0I,D'?"94 .'" $ w*06 ;@$E(J-\@,Ĵ̽;¯®ıξξı®;̽ñ[@+J-E(@$;6 0*# t% +17 < A$F)K-oVDó˽̾ŴŽžƴ̾˽mUBK-F)A$< 7 1+% ! ' -3 8 =!A%F)K-l]ʽ˾°ɺɺ°˾ʽj[K-F)A%=!8 3 -' # # B(.3 8 =!B%F)L/zlʾ̿ijij̿ʾzkL/F)B%=!8 3 .($ ?$ i).4 9 =!B%F)M/rʾ˿ƷƷ˿ʾ~pM/F)B%=!9 4 .)$ i$ ~)/4 9 =!A%F)L/}pɾ˾²ɼɼ²˾ɾ}pL/F)A%=!9 4 /)$ y$ */4 8 =!A$E(J-n_ɾʾ´ùù´ʾɾm^J-E(A$=!8 4 /*$ $ ).3 8 < @$D'H*s\LɾɿŷŷɿɾqZJH*D'@$< 8 3 .)% $ ~).3 7 ;?"B&F)Y>,ɿʿɽɽʿɾX=+F)B&?";7 3 .)$ ~$ j(-16 9=!A$D'H,{eVɿȾȾȾzdUH,D'A$=!96 1-(# f# A' +04 8 ;>"B%E(Q5"viǾȿȿƽviQ5"E(B%>";8 4 0+' # A % *.2 6 9 < ?#B%E(T9&ĺùT9&E(B%?#< 9 6 2 .*% $ % v'+/3 6 9< ?#A%hQAu`QwbS~t]Ot_QfN>A%?#< 96 3 /+'% v( % ),03 6 9 < _H9kVHI/D(O4"qZK{nzmpYJO4"D(D)kVH^F7< 9 6 3 0,)$ " $ M& )-03 6 W@3gQED)>"@$A%C&D(K0\C2nWG}iZrexl}p}pxlre}iZnWG\C2I.D(C&A%@$>">#gQEV?16 3 0-)& % K$ U&),/L6*aLA>$8 :;=!>"?#@$A$A%B%B&B&B&B&B%A%A$@$?#>"=!;:8 8aLAL5)/,)&% R% K% (B,!\H>713 5 6 8 9 :;;< < < < < < ;;:9 8 6 5 3 11 [G=B,!(% # I# M;1~_LD0 *,./12 3 4 5 5 5 6 6 5 5 5 4 3 2 1/.,*+_MEM;1~# 9^JC&# ^% ' ()+,,-......-,,+)(' % # ^:!9 : ' # A% g$ % & & ' ' & ' & & % $ % g# A ::: : :8:48??????????(0` $# " C$ c# s# s$ b# B# &"N:28/3 5 7 7 5 3 /8P<3&"( ' /R:-D*A$D'G)H+H+G)D'A$D*R9,/' " % o.8 @#bH8T7#Q3U6X9!Y9!Y9!X9!U6Q3T7#bH8@#8 .$ k ' 3 =!G)O1qWCbD,`@&cB(eD)sZEsZEeD)cB(`@&bD,pVBO1G)=!3 '$+(5 A$K.U6]=$}aKnN3mK.pN1sQ3sQ3pN1mK.nN3|`K]=$U6K.A$5 (+' 5 B%N0Y9!a@'hG+lTzY'J0 W:'~cO~̷ƮƭǮȮȯȯȮǮƭƮ˶eRX<(K1!@( A+!J0#]B1{cQıdz̹͹ιϺлммлϺκ͹̹dzı|dQ`F6M4'E1( $#/@#P2w˸íðųƴǴǴƴųðí˷vP2@#/$#'8 G)|eS˹î˿îʸ|cQG)8 & &".=!O3ʹï®®ïɸ~O2=!.( $ r3 A$hO<ȹŶƶǸhN;A$3 % o( 5 B%u]LƹƸu]LB%5 '  (5 A$pYIĸĸpYHA$5 (+ ( 3 =!Z?/øƼƼøZ?.=!3 '$$ q.8 @#|fY~zdW@#8 .$ p$#'/P8*E*D'_F6qZK|gY|gYqZK_F6D'E*O7*/('!"%I5-9!/3 5 7 7 5 3 /9 K7.&" & D$ c# t# t$ c& D!   ( @ $^KBm)/2 2 /)ZG=l$ # t2 T<-D'I,L.L.I,D'R9*2 # s+) ,< I,kQ=\<#`@&eH/eH/`@&\<#iN9I,< , ) .A%Q3^>%~bKoM0tQ3tQ3oM0{_G^>%Q3A%. $-A%T5bB(oM0pV~Z:ii~Z:mRoM0bB(T5A%,+# t< Q3bB(qO1|X8{_kIkIy\|X8qO1bB(Q3< $ r"2 I,^>%oM0~Z:jIms̼ͽͽ̼sjjI~Z:oM0^>%I,1 $WD:mQ8(hM8z]FpVko}xxzzyx|okpV{_GiN9Q8)ZG=l)D'\<#qP3qU~`xhiooih~x~`pTqO2\<#D').I,`@&eKpùźpeJ`@&I,/2 L.iK3Ⱥé}|¨ǹiK3L.2 2 L.sWAµƭŪsWAL.2 .I,sV?qɼȰǮɼqsV?I,/(D'pT>w~įʴçīīçɲį~wpT>D')VC:nQ8(t[HŰ͸̶͸ϸϹϹ̷͸űv]KT;,]JAn"2 I,wʷŴ¹¹ŴȵvI,2 "$ w< qYGɸ®®ȷpWE< # t -B&|mǸƷ|mB&- & .D)u÷ötD).) ' ,=!wbRv`P=!,)  % v2 W>/dLo)/2 2 /(WD:m$????(  ]LABpp^LAA$%8^>+X5X5^>*8%"$%B%a@$aF`Ba@$B%%"0a@#~W4|W}Z~W4a@#5F2(AV6 bChtbbsibCW7!H4)>qR0ûvuûR0pq[:![;!pC/$@\?)īɳƫʫɲĪ]?)K6,?+ Ųʳ+ 'U6$÷öT4!#):{x:&O4+CqpU=.Bbcfg2-1.3.3/misc/python-ssl-1.15/000077500000000000000000000000001223671746500161535ustar00rootroot00000000000000bcfg2-1.3.3/misc/python-ssl-1.15/debian/000077500000000000000000000000001223671746500173755ustar00rootroot00000000000000bcfg2-1.3.3/misc/python-ssl-1.15/debian/changelog000066400000000000000000000002441223671746500212470ustar00rootroot00000000000000python-ssl (1.15-1) unstable; urgency=low * first debian packaged release -- Daniel Joseph Barnhart Clark Wed, 25 Nov 2009 20:40:46 -0500 bcfg2-1.3.3/misc/python-ssl-1.15/debian/compat000066400000000000000000000000021223671746500205730ustar00rootroot000000000000007 bcfg2-1.3.3/misc/python-ssl-1.15/debian/control000066400000000000000000000071501223671746500210030ustar00rootroot00000000000000Source: python-ssl Section: python Priority: optional Maintainer: Daniel Joseph Barnhart Clark Build-Depends: debhelper (>= 7.0.50~), python-setuptools, python-all-dev, libssl-dev, libbluetooth-dev Build-Depends-Indep: python-support (>= 0.5.3) Standards-Version: 3.7.2 Package: python-ssl Architecture: any Depends: ${python:Depends}, ${misc:Depends}, python-pkg-resources, ${shlibs:Depends}, openssl Provides: ${python:Provides} Homepage: http://pypi.python.org/pypi/ssl/ Description: SSL wrapper for socket objects (2.3, 2.4, 2.5 compatible) . The old socket.ssl() support for TLS over sockets is being superseded in Python 2.6 by a new 'ssl' module. This package brings that module to older Python releases, 2.3.5 and up (it may also work on older versions of 2.3, but we haven't tried it). . It's quite similar to the 2.6 ssl module. There's no stand-alone documentation for this package; instead, just use the development branch documentation for the SSL module at http://docs.python.org/dev/library/ssl.html. . Version 1.0 had a problem with Python 2.5.1 -- the structure of the socket object changed from earlier versions. . Version 1.1 was missing various package metadata information. . Version 1.2 added more package metadata, and support for ssl.get_server_certificate(), and the PEM-to-DER encode/decode routines. Plus integrated Paul Moore's patch to setup.py for Windows. Plus added support for asyncore, and asyncore HTTPS server test. . Version 1.3 fixed a bug in the test suite. . Version 1.4 incorporated use of -static switch. . Version 1.5 fixed bug in Python version check affecting build on Python 2.5.0. . Version 1.7 (and 1.6) fixed some bugs with asyncore support (recv and send not being called on the SSLSocket class, wrong semantics for sendall). . Version 1.8 incorporated some code from Chris Stawarz to handle sockets which are set to non-blocking before negotiating the SSL session. . Version 1.9 makes ssl.SSLError a subtype of socket.error. . Version 1.10 fixes a bug in sendall(). . Version 1.11 includes the MANIFEST file, and by default will turne unexpected EOFs occurring during a read into a regular EOF. It also removes the code for SSLFileStream, to use the regular socket module's _fileobject instead. . Version 1.12 fixes the bug in SSLSocket.accept() reported by Georg Brandl, and adds a test case for that fix. . Version 1.13 fixes a bug in calling do_handshake() automatically on non-blocking sockets. Thanks to Giampaolo Rodola. Now includes real asyncore test case. . Version 1.14 incorporates some fixes to naming (rename "recv_from" to "recvfrom" and "send_to" to "sendto"), and a fix to the asyncore test case to unregister the connection handler when the connection is closed. It also exposes the SSL shutdown via the "unwrap" method on an SSLSocket. It exposes "subjectPublicKey" in the data received from a peer cert. . Version 1.15 fixes a bug in write retries, where the output buffer has changed location because of garbage collection during the interim. It also provides the new flag, PROTOCOL_NOSSLv2, which selects SSL23, but disallows actual use of SSL2. . Authorship: A cast of dozens over the years have written the Python SSL support, including Marc-Alan Lemburg, Robin Dunn, GvR, Kalle Svensson, Skip Montanaro, Mark Hammond, Martin von Loewis, Jeremy Hylton, Andrew Kuchling, Georg Brandl, Bill Janssen, Chris Stawarz, Neal Norwitz, and many others. Thanks to Paul Moore, David Bolen and Mark Hammond for help with the Windows side of the house. And it's all based on OpenSSL, which has its own cast of dozens! . . bcfg2-1.3.3/misc/python-ssl-1.15/debian/copyright000066400000000000000000000057001223671746500213320ustar00rootroot00000000000000This package was debianized by Daniel Joseph Barnhart Clark Upstream Authorship: A cast of dozens over the years have written the Python SSL support, including Marc-Alan Lemburg, Robin Dunn, GvR, Kalle Svensson, Skip Montanaro, Mark Hammond, Martin von Loewis, Jeremy Hylton, Andrew Kuchling, Georg Brandl, Bill Janssen, Chris Stawarz, Neal Norwitz, and many others. Thanks to Paul Moore, David Bolen and Mark Hammond for help with the Windows side of the house. And it's all based on OpenSSL, which has its own cast of dozens! License: Python (MIT-like) http://www.python.org/psf/license/ PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 -------------------------------------------- 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python. 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this License Agreement. bcfg2-1.3.3/misc/python-ssl-1.15/debian/python-ssl.docs000066400000000000000000000000111223671746500223570ustar00rootroot00000000000000PKG-INFO bcfg2-1.3.3/misc/python-ssl-1.15/debian/python-ssl.preinst000066400000000000000000000006551223671746500231310ustar00rootroot00000000000000#! /bin/sh set -e # This was added by stdeb to workaround Debian #479852. In a nutshell, # pycentral does not remove normally remove its symlinks on an # upgrade. Since we're using python-support, however, those symlinks # will be broken. This tells python-central to clean up any symlinks. if [ -e /var/lib/dpkg/info/python-ssl.list ] && which pycentral >/dev/null 2>&1 then pycentral pkgremove python-ssl fi #DEBHELPER# bcfg2-1.3.3/misc/python-ssl-1.15/debian/pyversions000066400000000000000000000000141223671746500215340ustar00rootroot000000000000002.3,2.4,2.5 bcfg2-1.3.3/misc/python-ssl-1.15/debian/rules000077500000000000000000000016121223671746500204550ustar00rootroot00000000000000#!/usr/bin/make -f # Unset the environment variables set by dpkg-buildpackage. (This is # necessary because distutils is brittle with compiler/linker flags # set. Specifically, packages using f2py will break without this.) unexport CPPFLAGS unexport CFLAGS unexport CXXFLAGS unexport FFLAGS unexport LDFLAGS PYVERS=$(shell pyversions -vr) %: dh --with python-support $@ # The tests include network-based tests that fail under # Launchpad PPA and probably sbuild, so for now disable # all tests. Should figure out how to run only non-network # tests later. override_dh_auto_test: # The default auto_install target gives this error: # copying build/lib.linux-i686-2.5/ssl/_ssl2.so -> /usr/lib/python2.5/site-packages/ssl # error: could not delete '/usr/lib/python2.5/site-packages/ssl/_ssl2.so': Permission denied override_dh_auto_install: python$* setup.py install --root $(CURDIR)/debian/python-ssl bcfg2-1.3.3/misc/python-ssl-1.15/debian/watch000066400000000000000000000002111223671746500204200ustar00rootroot00000000000000# format version number, currently 3; this line is compulsory! version=3 http://pypi.python.org/packages/source/s/ssl/ssl-(.*)\.tar\.gz bcfg2-1.3.3/osx/000077500000000000000000000000001223671746500133275ustar00rootroot00000000000000bcfg2-1.3.3/osx/Introduction.txt000066400000000000000000000036011223671746500165510ustar00rootroot00000000000000Bcfg2 Bcfg2 helps system administrators produce a consistent, reproducible, and verifiable description of their environment, and offers visualization and reporting tools to aid in day-to-day administrative tasks. It is the fifth generation of configuration management tools developed in the Mathematics and Computer Science Division of Argonne National Laboratory. It is based on an operational model in which the specification can be used to validate and optionally change the state of clients, but in a feature unique to Bcfg2 the client's response to the specification can also be used to assess the completeness of the specification. Using this feature, Bcfg2 provides an objective measure of how good a job an administrator has done in specifying the configuration of client systems. Bcfg2 is therefore built to help administrators construct an accurate, comprehensive specification. Bcfg2 has been designed from the ground up to support gentle reconciliation between the specification and current client states. It is designed to gracefully cope with manual system modifications. Finally, due to the rapid pace of updates on modern networks, client systems are constantly changing; if required in your environment, Bcfg2 can enable the construction of complex change management and deployment strategies. Bcfg2 is fairly portable. It has been successfully run on: AIX, FreeBSD, OpenBSD, Mac OS X, OpenSolaris, Solaris Many GNU/Linux distributions, including ArchLinux Blag, CentOS, Debian, Fedora, Gentoo, gNewSense, Mandriva, openSUSE, Red Hat/RHEL, SuSE/SLES, Trisquel, and Ubuntu. Bcfg2 should run on any POSIX compatible operating system, however direct support for an operating system's package and service formats are limited by the currently available client tools (new client tools are pretty easy to add). There is also an incomplete but more exact list of platforms on which Bcfg2 works. bcfg2-1.3.3/osx/M2Crypto-0.20.2-py2.5-macosx-10.5-ppc.egg000066400000000000000000014564701223671746500215340ustar00rootroot00000000000000PK!  k0Ht-3pMPr;MZyG9 ȷ\qw#$, W@,ihnln?Mٮe[yjr&;?@Tbeim.Y^4Y7U+Kڞd͊cP$xDe]֞Er@02q73 n 5vԒ<f#S3#]1kC,싛:r& r8Y.e9 T]m^Y0,iEr*$Gg0Ujk]=- j @v. prfMCI+ߖ}-2+䏂)YM #ͣH*ͩHذ kͽo\/ )hEZRzCGRDdBlc%l*7c1/DccY?'Ux?`òa/X~kB/u0c{D9T:3pJ%hPҚWDG& }(au-G07vaT"}!oPɕW;#'5[s5S:TD*buEGR8 ^a2qz+OP9 v}\_xeD娎nL\vw F K}+}lqBB$RVIN¢߻MXZ)Tk4o.QY@[x-9d?ְWu×6өV(ϓ\R"LٗNՏ筷7hwz}OX >D.tdN[yO݆+* 'M19kH ?ꬖ/W1tzHuFZJ 02涉;c0fMQJMb+&" DRG_N6g‘ܠ]3-':Zn!sVDCPԧK%;';z ?7N2B &^uOgcn pzPt 0)"n4tjZe!Yz;I,'N"Jy[ʒ~gJEk(F3a?='P]TdZZ :i/tZgbj⯴ 9u">XRx եk9B`ǘs"ܞ C>'gݐIBbax8LnoϿ>|{~PLJ`V<3ʪCz?CP*YG&G r_z9(%G6Gl8kP}Mr+=L*\\;ehg0qw9DgY+܃iow=}K6Cx=%ip{-"ø}ǣy@pia:dglaٿ%`#|foR!y-~wУ)ku6aU?PK |<Ǚ{ssH[#k0B#\6m!9J PYۢTjQwcf:E 43h E14uV"DmȠMFrV4뮰i8f@پRd57G,19=4d@9f0lӠFHvb~2n ӆ~6j)nJv5:޷KgHw٦?pGM&)f/{zuŊx]3xKz`ǍM҆78(˺L@Vk ;Nca*y[l>6X62y k[LdWVn(@ g9blQt:u,!4҇iP+(:n")R٩!li)yvy(* _<^<-zʮOeחrf0Ga#A r"[NGcC_b6j=xn #`m&[[oi&= ݀=Z\z<P2*$x埋x ?B9 ܕr#x$9>yT@8辇B L{\s:xY{;yLhwOGS{}X>޴ڦ=+ GoÄzCgx ;*qBc9r3PK*pn VhNtx ؔug8< l9`s_vy`@,@4 ' *f} TA.@{ 5*הugآ7px ؒ7t[v;8|]β l2U~VeGG`C`~Ï=v 'Q0`_>mR1kyz .(T*Lk ?Ae9< @bRȔ)/䙈8!Bi5KBA)Te˂u=(dwXdEyqQ-a!Fk[ϰco@УbMl% Z‚"(‚sГerlt(a0⼒eYj_^AhѶ^k=.NOIq&3bab U4})_W+qXcި:dy 1IG2}m.iVqN${m3m+]Xs]ҭОe[ֳu7닎^>]K;=n^Ro;냮qeP]z=;mKgg3=AgZ(mk}S6ѽ֨C/,Xʰv1ciumK-3[C#>&ѹm.~)hejA(ǵ͍<y;̇&QoH{4ԪFu+ Sw%[VPKM2Crypto/__m2crypto.so xL?>I&1" זvJA)jbRJ)URT "VU5io֍I5}{Ν{g?1c&y{{76 A 2"װ:; RqHB3 >w &HE 2 )ҠA}{yd|c~+42t}gC`#4>FLw!,GqP(4}1Կ0o6x=-7*zkekH<-}:݂[,ѐ7: 3_ 6Yގݻpk`6\$GLn;gXgo5!m?2C}āҷ:pke7eytܶϭ. k/nUQW" STw-Q*= !p6{”^Nob0atkRm,OG2~rM6{|Ç 4^W'Xdzyyyyyyyyyyy__׎iVWfZ8W{Zo[nij<i|z}Zu"r~dkVR;;cɖ {0).O KkV8|L;&gai)1ߺ%*ߖRٞ7pPq1ܶ=.e71#!:&7Z,YoHqr8{1sd HPLsrkm\/EΨ‰.gGN]7nLL›fKѦfgS\.WT-R>{-7w{J g+Bn͔̙R1͜%5_L)T!I9G$4~圅ptf¥iH.Y/KWeeZ$7__<3M(wTX57_JwѫY}X q6dkݭu1  0o'WRž&Ȍ09ϹY6 {YlVxi053቗ќA[nSLT{}̉#S PÜm7g9ŸlYG/ Zh߷~o0--#<'`[Z8St b{^Ս8 ʷInHR}^4P `rʫ&oO@s.hVߪlm.Mv B:$&CvF^Ɩ9e& n6|W"ĬH#ڂɱ=CdK3Va4| i< %KrlV fA@ 1#4n>hd$+c5{$+PtYP 0 FJV&hjj '{Y8T/jY;;72!-}mo`ޗaҜ= i B0_M5gu8,x|!;. -Y`"O|rH-#ؽ wFk2g1,[Q>[XÖ½^lIW2RWIZ=Z9"gMcBVBid.8GYB47D[ՄRJ0pSvCֲo@3 tYLT a;fO7p_00О уy@MI({#pi$ƫXCs{2{qv(Ü.1ӌvԔ QLQ#| 8eQ5ެ*·U[v.kUQ֪e5)e=A)m}ճ=b!w0!xcKYHL /^ܐ4rZBؕZ]r s3Lڞ_!YPGS̟b["8wQhtw(]F{V=J_xe6 Y#4MOϰp=]} ^*#GzMB p`ٸ <r4:C~-ږ|d:5eG T^Uv ƆTg)h]ѿ˗y⺲k疥"K6\tdF%KY,.KWH^Lo"dLT$Y*ū)Qaa<m-3S٪:^hN&üZꌗE*QK 1VFpqX^sS(.il֩m/KA*M`"mc[r]HKQܣP#sv6xa"4BPk-Us^_z '@L$;'^R0jц(u2kPZˣ[8FYhq)<OǑDRkGTNT9[ĸ/K͔,mIy՝/sM- H߾Ԇ#͇#:kzKƂN彖leKb-:ƴr UwGG#YU{9YpL q-<v-9 q d( 8U D);q+H_l.h +EhVM=ZPc Q)ҼC#(5DCI3QYXr3UϜmE'VءF/1 ,0ǽi]PkVh>z͘0l xeDŽa^Ⱦ55㢒ٗVOq}D<_lG`8V%#ف>^9'*9U=B^u!Y/ VS=fޗ75ZzaKQt ϨFϦ4]gFkᠮ&RItG7tr &aMpuWJ:+EH&n !<yG3=>KKBrʢ]l),K_Z m`VAwH(Ƃ-.L=xTXغUT`Ow?èM7a>뉭vcogIdjNb53sPd ;:}V5 Mm[hZP\5\5/InK5}9b35յgr |9R ~p@~[n VP 9a' ʀGRdE:,>m(160_9zs!͔:2{ ;(l Xe -zpvf`w}A[5%rnh VvhICk,.vcR#8mxpsjё p9PV%>{wQk0Y:J@)dzM+}](ou7Ѯ,l;*!LdžSR7b<)ڄ^ &6Ut*76q;DDtUŔ^"$ y9ᜇ5p-bo}:V77&ɧg[+X5-/ @@Fl O x(6pIcYL:?*E/6.nWwgua UcBU`MGF鲭BH*guiS_"QQ>s}2ۭ#zsخ-rU5gMՃr5w@|y"wW`Z6@EVwKgU|[F[[2B疢/ɺqa&2L=;䱱1D;.oy&M'C+h'cRW8Ϡ4~̴c A<b)gm S*]CEWUa7>-G_Uڰ⯈G)`HzdJێ/.UA (3KRWѹjff˻$ )UA a0屛3t>!Kz{FL@?R-GJYݪ-U ,9QE}w7fPx1hoN3݀pTtgq$!E;<9-}]$_a!bihǴ3_:r(lw-Q4pnW'.:Ք-O(yIm]p{/CDa"&cQ= )ܑXf"Kt66YN{DRg[&R3b"sD ۵k^mrضiOi XaPXw-ERY@$Ctц}0kn]J&V#-+ bǢφ5S.o tOݜfA\i2g+#X/=PXj 9ߠhHVdgYZlX9rɐAe 62gK+=@1ndKQs~o{&HTZHlmD+*/0 *q!P`UDň`ekO8+~1h0"(cp`ⲹIf)_% m 5Ϋwk9H~GG-Kmf35З}Q.x=1hz&P5kzMQgHXaX XJb*{UWus*Mܷ/$Y#c#=p_{PhkK1L%ܓw!VX]Wy-B EyMlr6g)#.O򜙈_C<-HxLaA|&]yf(`f5MG(E +Ѡw"ij,B!0hTY= ʑGbyAߤ:YEf/VJ<>ԘfΣ\boA 6oȯqIsSfM Ky\>ak8=cKo?{qT^Q'Vm觗ûly+o =VtK#HIf~% }K U>-QY~%1vDQ0.V[^AJb](`/_{4o"?9, 2H+G3>Fw|~k=]`iJJgb7O8h1;haI-^O~JMt]|\ucll%̺NcT[ H#.KL +{2yC<[\XsBtd</K,bYaTgcK|{>j)܈J֏Ṱ#ac$a{(v9 ۞ ;<iYtZ䖵I#kƟB֏TCղCeM9RԻ^I}x"Aʒ~" l\`P6Lkg}FWc!G8ccs9cӐ9Hl17sFce$;9!?BC.dwei=i(RH8 y+ϧ}з~g3 n:sHo~5iYӷ dX?t\CZo[az`ߺ`7UfKL PQI&=|\`=? =HRҌ0}@|$ g[~lV;$KBxPBa>PW3(TQ}Z 5[1(5\@ JHT&oܦ2Q[>7_t9xC~q[ũ ȯET$M2b[m `mGZ^!oB9c#"UW"J 9+s%X ?ܠ:A uĹGu9ɟk/Cteߨs(pAPN,{jI'XyXY+@o X~^!s7P1rAWAΓ`rOIssSxh$*Bhk. .gXdBa[W`'n6 r/Ep!5+Tb&jf&jiHAky> Y,+Vm PN[W; g)a6i֬T9\?$#nfT^ךYy}o0#(XQH!6;: ɰ ֜g/ / W== Dc xkTzEZ$ X aPl:ZH*Q\l8o []{%;ґ:Pd'-TeDc=c^Jmgb}Bg`eߓ&4ОqdQ Ig؎e{\J.5w_o1#'>9[qa {l-V]|d f#.<Ϊk+ NV0z9݁>ָkblcjz 'J~GmC,PG&TT ; Q\yr |SX)2:l|d܆N{eKs+U8l^&+oc9f4wWa`ae9F&Pn'OכP n ̾qՎN4qҹ\'iGsn5zm8-#Ё C|QP:g(O ('岶swI~!bi9iOe{FeO.8 n-u|2?kؔK>/pG[JK\v3DUZ/0Q;sǛI7UNM Hb/M;@ tda75$6)H̹&ѳX^$&]n+%Cԇ.]̐<rݷ:dAUoݯ SuFn`!>^OW Ӽ_c AFeC00jDMF%sS sKԔAzZޤp% ,/;ew\}j:[[-7YUFn;Stoh Z3ܧ=~J[L uT{ U zY1#۹׽_GBVHpNorʰ|&iw c&<څxٜ*l(o=ᰕU\c f hi2sv54eg*eP|پ%nQxM9[bLos_N\iYZ*$xΠ~K}Xr˻0&q" |:BDLUvYMFSTyʻ߷z}O/B2&c}ga y ])w}W[neh("a˳ʅ٧Z=Ϛ+ \rPql&)Q1۱WVL|ZC:rkR!{,_sfIIg/íC}Ψ4K ӯ~"(َ59m|8`;M@kᵓq3=oA׳mGٱ+PBH:bIj\Rҽ:apR]!\%6^ofٝɲ?4m(k/xg Tik-f:72 }cwa4_W=WtDV[d=,XkGNkAe*?i hrK=OGOԂ}Jg$2QwK(ZJ2K~XN;:-ٯjv*G5gOf?~umoԬFZ4 ۤYNqz=::LpDYZ]ǫe;N laX;fAqMa\\f>cvWׄbm lv˚f-l.zjrK9Rp?+Bf>qKV_ =IWY2_NNE$F2NA~ݵ]]tljlx1n]oSDU J!fwp$1\|vh"H8a_K9cB-W^k'ԣ N:T}ď4L?p]c{~Is~Nswkq EN{ݖ;{+A7pmfOtI!9C\>DZ[XTF7^j [xB|!'> {ZH/ ړ@B. d=+Y yLx/f{@.err'dy'|N 2Ay?h |Qԓ@B  {k!{B1Hq0{+8ΈoŸ!ҤDWx\5]5͘G$ O\tcEp.{d%S}2kfk۱k/*׆kTײZ_2vu\Q^vm.:_g}]G G]#.h./!()|w E=L>śy|q"\52Hy0].J. XcrG<aBЇ䵲Zvg^h{T5?hs$!|!=4gKL0f@9#-FCMkR\Vk~@üa:1FKm2N1l@8 x{5e`4@vedO@E @Llyr-+^/NaW`08>YNJFA{-:N%i#Q.-ZxѤ=hOÐi3u"LG1 t$`& _|δQpy.ȑĐ2),򅛕h\9;L;#} 7fE3Q'hN>J4)*ɜT񧍅^$hq RlDZQ'`봈YbF.*++SΒ S=cdr[e2 `JvVWrtpygZxc1:[:i)O{j,O*=ϫyX`ׯF6X.|,2߰Tz7=Nj EBkWf!Lw|O!_5q8=v"zb{l= 31^ P^ޥA/܇Sn=] OjAzHMuffE}ZL(LqtW 1 2}g  ǃ$H7}?wj@H{ ٭2; '$hA> sY=B2SMd >H-H$@od 17N} I2\ UH s HdQ7tAnl׀A& @Vׂ P%UWi.@̤* 7p"d7|] -2Tb9M2J] U mAׂ #nM]Z}AX7i >H5-*T8j@=$&H}}Zd(9~ ⼡olZNstF}W SL2X2 R^/qvȅ iA@ d>-ElAj@bA^Ђ(@1Cn>-˧8HI 5]  Hot} Қ >H'-H9b%9U]P-oEa T A|Y"@J1 + ])}p RCu-H!WdTՂF2A>_5 II T|@rA郌҂\O #ȧ*Sӂl o3A*y 6i@F YH>-Hur# Aw<_ +b~}.Z Y2A$R '@"H >4 9$]O ߦq d S}ZYD2HrAs<Qr?UUr.WKҏP,/@3T A:hA r n}Z:*N] _ ) d>R-@2Aׂ uAȵ˺ U د'o@* ?Վ R_2Bd@U_: ^c={ܘ݆wr{{m-OܼɈێpz n{Os.[qjq=恻]]A'd nT,%Mڅ}AP{5vN:ݜ.Nz.NnuN<}NW:+;Nx>Nϥ\<B{NGONf+D1]0 t%eNMUlp!3i`V؄KތеGRc^|L,/x=0PMq70.>9"c=w+wH̻5hH|$.=B%[C8,*%}._cDx>VZPGbI vs"ɥޣ찝]v"pSQA=WlOlwt-<,%|G>~gXJb U(&rLɞߔG[3@Ӵ` t8;_2&[>+һP8>j B9ʻII1G{-Dž1lZ|Q^z omg짂~WfŅ@HE| wn>}u` gh4SuK&u̇%+NW}78'<Ƶ U oC :Gaht{NeZUߏ4|O'd$k XGMNLs>bq|YI\\;-Ou$=.^Uk;_Ψ>IĎ:rkܓ-О)dHx6'SssuC(D2N q<שQHcgh̀<Ħ1m)̮:rZb5޶C^c3 E~]< P,Z;FE˸b3NoƵn}v:۷p^Ot./;CjW1gbuK%pwXdKEB\ϥ<#<I$f nb4r%oBJ\gQp/ʅד\2n\f:N20/Hvk6.G{qvxb-^akm?oSm'+Nq: we믠?2x]NIHH݇{g( =ĥyVk76q@)L'eKgF!Jtjf^*=P Jѻr!̆5X< fQNƅcgYH2#E8曱W8+J"pN  'zvlmbl\ܟ֢|?E"Egl"Bz$@||.r/ &pƳ&2nr} K+\usz{ƞ:QϬӆ@5rZY) k{1^avzIN g_};~+YVo7Ɓ,M veyݖf>oD)> -HB7=8PnVއUvЏV {ƒm8~דldێ%[k.%F3mN 9o̹8YސA &UL̗rCz>;n9udeK@B-㐾H! 飀8Ȇ:GsJm)q)[wJ%c@EngϱFe^rcڐM%6@Bui|h$n\v;{\^r)xGe ,ᆘg(۰.bQ`Ds>bv2z=IWҞ`go@\N$ q#.˞'B_{6c^HKjUBc({4' ^bC^[[2M|ll=:TEHU 7m%y+畭d}C@3Qqvݑ24[6y{b~,Z|r4ߠ>wq6&\Yy걙I̧z3>13_0?p22.Bpm= ˰ }{/e_Aox _/_﯃\sݑty׶{y߼WʽKuyou xBYٟu>'ωɟu@.y=1#i7ǷM3=ޫ_Ym)Wm&n&Gl&?LLLLLo&l&;l&M@|}82c6XVG_bOUac+__;vAr[gkDDm"~n"~{m"~?#~#67gįFߞζ]cvGv)i*|q>`;#'Ӯ٢F|F|FFyF}q_x!Ԡ8gТ22,7*-5}1! )QH 턴铀 TU)<4}֗s '6f% mE* kTXim  e,ߡS J]UzOzK:$p @NfOkksZZĹc-qZ|Z.)ge{lĊic2]OaPkDvw>x|߇ƛO%|g7pj wr9Dm+p_5Wr k\}+p_3NjƋ^E& kw-P CφptWejҴ쵚쾚ZE\&.+&.W_7rEO\瑷?lArԄ<ˑ†ϱ畵5N`Sn Ƃ}6PSGE Z'tOW| MOT]*siA_|Ϛ0bk@3]EohߤdE0hAk~JΕ?J_Jߒ_W~ZI|%oJߘs@cϋd2s'̧}f}&dGs'n!=@Y /Hp\@AWv.OIt!OJLO۪W& ~/Hb!}!tk0 ψJwĥxd7"7W}%VH tp]LʵI.p&L0#,L):-|ك]Fu1(n&p%גI埵ZFk6\}Lҷ]-_/-#(2t2n2jr eY+H +HEW. ]Ʈ ][A $vx\2-%يo),-; Ʒ\l[~|KA5[0#Geз{|4e*ߢ^FG5b 1op4qӴzF:إEqq^f+tpo A_[J:t{ %KH_.!f_[v=oCY۟K擳5Y.8c۟I}t@q2Xfy>uH\G !}kh~ ;ooio-&ޮ/&]LXLmYLYLoARW46Ӷ} 8hy|T9lT혏ZFy9idiBC78n|a{t"Ա88ݰ88=8ݻ85/"N-"N@xB4yVB&O]oR2Y+5S~?n17Q<"l: 7 ۫)ߝ>ť@KoiϖlBU?`+)-!U\=ƈEm=f-$UkC/t_-vjr]0K.T@v \Hza!魨vn!魊VAzۺB? Io;rvL.kg]|7j2P˙YW]X\k3Y: U(]( -eFq'k]GN7y|#[@z6 H/Kk H/xZjƑ1#x8P7W7kY"_bj:.$UE"AWW+W8[L {yv;N|o>]N|5uj-GeY$W $fld;S-?YdN͑4fNC ^R{LY^ -[wLvG%z3 zM|D'[aS*OgI)umbo.<6a\foNf%>t\sI'_%lK:i:t\ɤsI'@LRS R1#h|[ҞvFmWU]룫n;Q,&Ua?s.uT =qZ5HC  !+[bkHWM$!Λ'灉yDS"q<$868m_hS&g:~gHg!Jq6X .CbOlA6`Op{`gѤ0#i(uW,jsrQ :%7 lӇ6GVTa 8 7,O?EZl=+"OG֚I:);trs$j& ҉y&L3H'fN$A:t]t#i0l\VM[sFt0I,y W4=6C5w 0ף7D:|R \;o}]lӧﳧ裏L'M'N'WN'N''3ޭ`rGRP{^}{Db4Y LͶ'nG}GYdqzAox{8 =Laox{8 =L4?? J aǿebBԈX5aߙ)39V>M?Y ue*TT5*zx*:x FJJJJs~dƲɔﮰz wL#v^V t0}AoH\KA.O]fQӑM|)OznK*{ݏs=.m S dR{^y` ǓS }铉o&ޓٓ~ӏGk ^H {ε%{ܼQV]F1duh=^W;.Pxg_Ry=x}scZo$$u$u$5txi$+H+o AS3|_K\.*fs$%8K<?ޡ0a JO|Nz[cjۍڣms`lDD6u"q.qDԻmwu?&'>I}0mZn‘u1Ǡ먶9<3яM=vJma+7J@+J}l%޴G6AOq$ENb ?bV~ZIDF#UŜŸF\S#U)ⸯ8nh!-qS qLk-i,]f\8n45y;r^]yߤSW(䓍A'#/##ߙ{Y4/w$ݗQݥ}۲yϏ=syG|ZZf#>ACF|=~mozkкU;h=u醴xo{s`ֽG%=Kz Kz01XS߱'y )r,1oOeͺ($5G~]@ Jq)8[Jryz4C1*<]Y#C.<3BO6yL@mhScmJzKNNo:tob(QH"]6t1vb(ŶQH]AeY)9뻺i79)RA\Gɦ"(ԥ̭eq7f8V#YF`{5%TYEH#gowI.IInIƏ$N$N$N;$N-##.#C |f=q$=^`I3u oOAJ-Z#~iittpp M;temgvVcrK8nQd?8c`Vsb8;>/@KawÈÉeכ9ÈÈzqqX]$n7l XAV H_ʩ,G\&[vF\,`Td?@ i3O }i ͆4 QH R@zqd}t`^GҠX$m'w2طD$<"ц#G8^w/(Pziz>z]CH7놐n!B=tsz!~CH7snV! BGG/_l@6tIRlN꾐By. hFk56,F8矆 gm8 [E[[ `O;o~ &vMI_O.x>YgA6:a0.:@|H|3n6n4>1:H| UƖ>f@Ukfk\"bkh_|1 &O@~ZQچ8c3>rHHscs_VrKq&9q(C_%R? C}=>HTX5M4`,R*b-'!?#''#/###)Z7?`F`RpÕZˠ!?<\ zC=x'G=P=`,| FHCZ=Hs!Mtcbo`›X:uc^'73u dھEoO b"/qC҇8-ݗ8qB_}?_!Nؼy1= Wla.(hP1#iL+H sU8ֵºA[jO;x^ Oeq886;͊#nUq8G܆gx#fKFςzڸ'ب%kH TIԵwz{zz".E\>׋ًދ|qYU x#C!'5^Z,>àz^S@fIГ8,ԓ8|'qX'qا'qس'qg0'qX'qxq$3ZZ#ǶR6}UeTBd>婄Wtv=7)-#ܑtP#`@GAqIc:t H:x# #QGC+Qd%YV>fZd/oHC@wM'ZA*)͂uȚLR<{|n{w+hXiɲ/Z# %ׯeʛW%ֽdyH;!m !-diVPA.,C[xu}Z b!ah ϶cPm}Fl&v{M.5xUGሯ30#]ٯ:b&gϘmf⿢֖---͌n1?,6A?IՌ|ow@>!m%!e! )IB{k|3{f!;Jnڢ<;OԽu=s '^.O?<}}>|>#> ֓Ç\~\Nk \vmτT|>EK?4}f=",-miii/M+RiN*ȷ{WHo􆿻7'zÿ7y?GoxÿkKeh[i\'2Cb=ya:P_2~QˑsveCrr#_N4ҽNc=ޢ%/h7<0Dly M`kn^8Oؚ{ 'l/yrp]WLͻMy)+=y P7i.h4kIiY"ڋF|g3XCUez~H5TW) ݆^o\&j7_<0YCU>0dDPוr[Cq])HnjRuu<ו5^!>twxvwwx]W|ו{!HF`:he vCU|¸N] ualU¸w]'ׁq0^u`\a,1y*Wo\ܡ19a ~TT% =i.P_>u:b'2W>5h7xwwOۨ6j{ϹWy}Q]%3l3[J?2/Hz":]+KAϖi}Z\^ #krw-X˳`y,jrb-X.KZ'$3lvIj*.'w}Ǹn f&Q\WQcgiWxtg%WxpgUWxNvWx³+<=薜#ϳra_{F雰k*kUJ~9:ڟ8sU17\T5CfhV?RK,QZG}Kb.nkXtu_Xր ]\`Q֏^9hYLHT&ɽ&9 u:+W19\iO *AHUONϊE"Ms?n'rͫ wRu*ﺇbd Y /iy#_(BrrQV]9S__SQYXYWSɬK?FʵLeU0G}rtc*?*eGa ?")jh 0}P\)4\gnALʔALFLALK#&- &e'2UyiDI6Đڂ*yu+B42lrq-Lw0$x/.iw;ot;l;߱ ;w|;;YISw7Su&(^VA选bRoL²-,]la9nc ,b Kw[XW+BgzrKDһ{\:(Q䮃+>9"e(|RO%$Op?SƥnP + %$הƁ"wr86m@?Vw/PA~4F -ݤ9gH} ml6pof6p/k.ΰB=.{ kk*WkC˫T`-jYXn0@gӭ _jVnV—wN +2Tt g/+HTpZ!)oĂO}#j Y `|AEEB;b4-+'rFS,%`V Y0+Vf=JK Y0Tfh9k*-tkm=鍲"5VJnk[zfss.~Qf.-KtU/XQ7:weD|]-]I/Yڻf;b? ~ ~ ~ ~{7 ~V抨APp#޵ je܀0@>~R=/T پTuMKhɮ#d/]EJeלVr[zbQ1C ! JbPN1pC ~ -[_^y\rqrð{in?-(sk^ApC[E )֛kɭquTn9#Rn"[YA"ǠH!AAl*`}6bp<7z~֒[[ȓ[r+˜sH[mGs2T-ȭ44YhxNi/X'wރ~OxOx ?' 3w:SdP/5yrhCȋ|(\-LBNh&Ӛ+օX?`úXWOa2'Ej W5Wr*G9j%11q҅ɢ1d{}ҌCz7 %ƐyV"_R}$*mTV#YXB,Vf!Y},bob1: ^DTig-zS>'\C9OEE*$M[&2 ˡɄLXe’̈́LX˄&,f!Ŗp} {c9 a}czO6n(z}hoy֒T O53 <|Wx_Y+<}g"Or8.xI=VzF"=ֲޯ*_HKϰ7CVrR=B>ɀ ò ̀qz3`<+{3`|>2`](oX7tgL+WVvsM k(Y±7P~ [PT3+dh/U3743a-a+{a#a/a[/aEΰQֈkF-rkFdq 3N rL 4X, iHe4XLeb*,m`TX^4YvSj9w,Og]w;&UU5[ ?0TyoX)X׆ R)pMKU/+Rz.R* 5G]mD_Mfˠg#gq_{)We+NiP2L'tc2L'H2L$ôQ2L'tV2LazHpxfe󘡈2& ى)HkZ&A! IpIpDzIpD81$M$0G9!uq-i`F#VsS]|=};u '8_gD8gD8'y'8G|s'8k}Aom:޶ ۬җuG#{mЗ\rq79 !~^?P{}FrY O3\kmzr!YՃ` ;xuo-ū{{c1q7 7n'Z2%}! yMS}2ܒB^ɼ?? qǽXڎ \!!b9C,,H E=feyi O{U x;&b!އc=)F择S2`+ݤ&rn1$-}B} :]KUk^>Jp:;-[{ oa-W[~ oa\po{n]uRWTgnQrs]!}v&ʺe]muu{ZWש&G߉굇Ruu`9j6} o_;뽆w im޷^7Q]%3re]ըZW+gimbTj$O]_8%Ɨ0Nx /a[_KO{ FYu-uJey\ƗsgDuðWh1ʺe]UNuWQ]5~#%KS娫ûsx{DNcؘ3KgjK/Ka~i3Z+=_$ O; g?U!eFd?D"N"N!N"N!NV۩j9Tv)#\e TAr,cjȒΰSIsQ~-%,=d/KY*%eb,_FT,3"`y6,X^XD%9a[_s@?bI̜q'\N,gKRmdW*.n@}I2B&|܄o[}~o|܄M 7k}en}$0Ѓv׫.T lA登sdaNJTaxYvd 9[!7?߀?unހ ݀GuufqDnψ\e˗3;4M4FqSmMҤՒR6s2.; ~o Rn>6O|Z]1s8Qє6  %vlh.ϗ[UUUUfz qpquq{uqrq3`&9a;=6]8Ґ"d.|Ec| \UW=7 # !㬕/{F2zڄl;)Մ,-:)ZyWWJWWWW W/+W+W+FZyR 9ZDr-'\'ҤkEFD=^^s5ϋ!t> _AݿFu2ܩuÝo❨/xq.k3/;"\wEx߸]7]~s._Hњbrvڥo ڲhzNS1"ІWsǤH 2LW_ 0=p`zL_i 0vӄΰ;qC# cH{.coE#R g|ióyx?}UóyxꟇg2_NnXQsvez/-{Y}Fd8GRMdQs0M? {gaZLis0-u{Y&遳B(9m9x2G#20Y_<."GKPMCR?e#g G(9aͶ掫h %Ru >lo:b:گWzpd9)M\>νqp_gp89/+y$\wjr݌9NF@$ס)s|&s`6¤w#ۇ`EpBO= r':OO)8Sp> ΅Nùi8>˧CO l$_F;18>Kթ]BT>k(zOJR y>}IN> 'a$l-XM'a[$l{혓u^^r8h{m=)" B[=rr=zn'`hv'`hq}O & :aq.2$3xuVK/ #G}(uG}YZ*JFZ,/{ t~RɰT"s:Fd>cOo~$pTqORӜ}M7iʟBO%G-"iy)CH"N{/p_hCu!O咕>FaaltXFaaltHFaxWJ{_7+LtU"),1?:)ԑO/~Ь9ZDMS|IrF* %_K#{{A8ەߊT*Oc g@g*-d"R൚;%>hJvi6+ˏ悹>vާ {x;ꀰ7Jt]%3lUݏV몚 8F6_haBM[ǚn;%J].~~P\yw]&Ä` aB_0L }0/&Ä`;M'6q&}BWh m>MșvCL{e̒~C0B;/g>eTr߶М3兜)u9C'qΈx's7s Ixۡʇ@V?~A`.iSo^k aUdoµU~bqIڍ8A\v#.{z{[/=Kn^>.^=r~ϛ pB2MD0_D M\r?xq$}<l}i($/ɬK[ ,Cz%s?{wp 9F1h/"g0e_TlNw0 h C!ՖZ7Ȇ?Ltmo 9#ҌkYQ""߭Wzwm{ծ{mu}V7 \:{w N;ӑӴSӬSB[}&^R LQx_K!=%)FO0l2TtyP2zC)-v+fZhvrd2&vĥv%vRn;qqqَ\܆$nC\~oC\nC\E䎿72}X3dEU|9u[4&g2l@pTn \[FCQ{:~䎦:zw_-Usd׷{[`z m-0[`~` o;Q\'P PǗPaXE:>w5C_t[K?]\oRu|pS*[\v89aWiBu6:x/+W1_])}?mUd+dJur 9Z=6Vw6}}7}+pڢ G l7(-wR&= [/<=T"ĠyХ`B!M&gەd[h1VKcohjoƐ{Z.>}VwXYGR!nuzĭz:-kVd=qcr#[ 1y{dcT^Mm17p=iMsӭIMin:<{C ˡpP? {P3p p۹wLsr/49am17})^yn:fhnVtޫl]MN[YoCjX}Q+vk!Okp-m»Zxۭеk]n-;kl_Jn{ :0r\g *t?ݜ|sEKi:սҖ.}$ |j8_[ g5pΖkl[V9b5cVyja+AK.GOe*WS?ۜ~*@K"$U(?e-U?CwJ,Jȷw P1"#Yc1"Ma˹XKn}=t}\Z~9aOT[?PV V[Scż{᫑bQ4z^#$گ몞JyFqKsk%b([XD,zD,D,>@,,V"W"+ ߑ oL(1iZGߡywcrree˚ee˛e˘e˺ee˄e7ͻn2=]썶s>}9˗r^= G}K^InJ鼋]#ʻ5:ɳyݭy~T͈#,E\<".K.KKK!KĥRĥRGoYݴ2\+|N= 6ᏼ XNrJwVI]p-ːp7bT'S1yɍ`d[0b&1|FLRɾ`B0b2?1yۏ=[K?~܈~OoSsO@-D >O@-?b]k,=ʰ/_A)ruK NO9sS?&K=_kp6|Οzg>\{̇p]7} W{N ~OE* >gljmwkսi k>TsAj[}_bCҿDct2/[hiRhkiTk:B}_]k3Ws~\Sg¶aѳp:(H6>3bW~9$Ҵ+~OsqRo3-gs xu3f<+̀g2Ug!'9a_+=5G=|3 "yO힭z/>|Q2݋>e4Xיin1־adj:Ouoj;GL*_A9"rÆc\aY}(Yr=hFIqU<{o9Sv ?r N)_4a=m.xZӪ(0Q3 m~et#,^rdXv Kɰ;a8s&l2,Neɰ,<۾_dIg뽨'e\؟#hns~3@æ)@ .<O & }O4I}O4IMD>$xn'$ݡԳSeen@n֛c+7Uu.$?m;xf"L㣉p\7~"'L㦉p$c8y"%+ߐs֩r ;u͔sxɽ,/PMumw[!8R 'd[e4lKQu `;ޣaplGh>%[mcC[+P;bWa<~ NRLIzqoK8E￟S=6R#H?R#H?R#av;yȜ:P2X9|5]K͇_Q=#I9`FX6#`m=FX Xa=a 0vʶ8cPc* vL-uOv$a ۯ`{~l m]= 0؆Hrr>v<&0[ǕhǏR8E_Q(<.K*o#gn9 ºPX 5Ca]i(; :uJðZ+THEWGߩCtQYՇ~ :C`=-wL Z »X7ip iESwTI8\KZ1$r$6\<5d#2A<4`yl,A S`yw,֩oY۟ZQZ#oE[Daz::X2 &lEB5pCwhuUy29k]p?}@h ܇ps˽JJC7S/(q=cy樳yj5o#y%I+}e^> WG|<9~bg;Q1;$L:g2lF :=>gBx8rɥ7p@EiE˗5FIa򌿻m~}R?N1`Uֳl :Q[Dgh[rxr/{BE*\`R>kJWp}j鞧}ຯo(u/u(N]]i{?>3mG'3@|íT7|c{lo _z׿7|'[r8fPWfʼ]@swZ~j@;?{6 ٥<^ =Y<珞VIgذ&si1 IPD{y;(c׈_}-S!B!B!B)B!BekDp{U(|_]o5 (doQfmW%cMpLwz;\{tp]} V&Lkb*Ïd"4 OHbX؟PM^a)Թҫ,+ 3hTl%%׷kߟGwM y]aܻ+C¸W ]a\+w0nWҲ/y{z-Wf~s6m\ޚ s㟞)?-t;2T&*3~ݶ3m:{`gx N o$`ΗK&hs;;9o:F89kejRۢjc%/)mke'v-aWvaWv;}U? Η/'w(;7ns^=#G}RmiVytSR^RVjOnOǖ,[U x𬷄Y'דðCj31hͥsZ7V`[(e-Hr*HsnmY^snڻ9\~\9 k"+l%d}+:nE?\mPJV'? O~B꿟P'?bR^Guu\ɠI!K#-9Vy8ܝհ5PeǷ5o ?te//oޢ͚~//¾"o8/O$~hDɰ r$6.6҂Bma\ ҴaSzRWhsƌ~MMὶ)٦vh M=)+73%s *vͤ=bjIclo1 T\U%Gt2]JrneBrNy,w^>0Omsgg4ng5yG6_@q_BB:soEq2ΫB9nk^{Z^ȏfToT)K6/Hf{Y臐~;e4GȭK}7zý7{?oy}7y}7ܧzs)ianw;ײS wjm{×j[1|7//k _m7 F[1|_y M tWo} WlC~봥kn^lTvY%Zpkeo:+"q͂d\N~=WK~6Q ZR ["˾H='b7D{"v!'bDy |iΡ"v*b7S;&cG~>tbWZ:+bWZB<~4'_HV/qz ~=Ec/x ~=ߜ9j)KگWO{B]ƕKss/4r/X:=.) ^҈{X^qg崏 jK*je%iD&T&_5m}Co\6lx97@ 6@| ^E ^] ^ ^/Cc}k,\ !/\\\$*TNf y)_>e ;ic} 7TfT4ס4mCBbD9V0ҘF -OxX{\E%zpqt_#אatPXo2i6@6'yl^R7M貋 ǡoJ* +8oG|~_s&z~ Ԟߺp;r;r909999P999P90uFn~G>!i횯=W(;+lg߫\8$BS >O{(bSyO) 90vY}Hҿ!U7dj㇩UwD\H@DIB:_ z { y"cos?؇+]cSE=6!ֲs{EuN\>Ηi755Qz}RW9!CnH7b~ 1qCǺ!n&t(T(sL}#vj*͢}#'1R}#qhhy]:XTו~2/GV&kYm#~WR#dzW_)WĨ+b&bd%D"F嚈QrMH1^1 L1Y[1C߅Vemc+VjVV&c[Mc,*i(M)V{9Q"-UT)%Eqko WG=u_{JW":#>;藍(g٪D9 ;rhdE,|U:@gHh'յ%DɳL8tL/9tL``[`:Aə=sֺt#LT+"ҟB #`ڹL]+tt֩y`:LVi 0m]+tQ='3U9@9pj Np\a npa -pi ^pg p/ۓ6EuK9_DKA" I./< ݭ`haöV0@+a% `b K6'2$󃦟pMaFzEy=.YRu\h"5.r'Ǹl-`{k[KZXvl#-`k³}E>&R.}eU}u4C_e\=u:.JVշV"#.ǚ1YνEZQ]r;z218b28b[1W1iQ1Y]1YY1/./T,xunklu?}&\jJʫ _iуor,hjEniܝn=z HK4h0HA(K~[-YgI֚hw$'ɚ4ofg]3C[f9bdnm1C"3hbTZHj iT {?'Du&}sX_βuEi 5+_%!j\CJ~#vWYC=%tK kH|M+VeO ڪ*kH#%UאחZCڨ붂 "Fc "F "F "F "Fg "F- "F "FK "Fm "F}:E9 {O) )BØ"Ms7$/z5hhy#3icc6i7c7(c30u4i=c* ̿vs`jN.LMm t^G)[H<] i{YF0M6m#({F0-UV``{e: 36Y&YiHda& ~rMj+zcpn9p=k@CF!\{u!\µ0?A}?{B.vuy)omE]lR嘃5҇=8>Ѓ>X^h=8&Mډ=9a|M)/6"F9X/R[4{5tg =a3Lki0>t&_L.vȔð3`z4vd4hR$FOitHw:L ôs:Lۧ]L>`:G'3goW ,w8ilDD <'!ǜ}J*ߤL*3Sx!h5p ǧp\ >|!9modqöz*~yy!#6jiTU%@b2ޚߕ)߽)) Oo78S[}\E`Q4Um^qڰ&N v^}yD'õ^2\-.%:(ᚖp /IpmXݕS׀bWJ~Ryj4Rd'ǜhZ"cx.x)Ip4IpD8nHc=/Q;Jg6mmJ[:"ITv7 GgK+G;|Op< ?'8^68}cOpAr=Rh;:9Ec#ɢ .p{':\߻'E<+&<$3 ੟ Ngx6%9aOEҼo2/ÒI^&yJIL2͕x%UԖiԌ|6,~3,O}eGXf}ُtYv}jjrtLx~* Eņ/ Kdx7ga6a>aÉ0c0C"Cr8VG MBNi?Z^W͂tZsqվJW6))m -v4$pN\|p5>Z ~^H]McOo}ջOs?O<٤/gf7 ]sGaظ4i5^x 0&E/a(G/8mhy.k^O-3{&5WE}K"&G{ģ|,Gi 铒se'"?TTAntt8xs^+s2Dm3z55\ZFv~hf@~=iq],1F D#}}ۙt?DڷV)Zj o[K[J-VkKbO-#1A,Aľ!IAJJTr=9dw}${;s4ha4 FAfiРV:?8+pr:Q Q q"96/wҿT^mш|'yjϗA}f:x{%l1D;U]#1.Q9$5l(Cl{VUxC_/ Tp} R;.Héz.\kkz^0[:% WY\Uk$hq\IygqYpp#΂gqYp|[ǟςijW`nڍfˣ=bZml^D>'(,/SO:3-]g8πg3 x:gs> O΀Nθ;̖9)uUqw5ȾG@St9 wR19=OcJ 8V< >qm 81)mU\f˷8ĖFۼBڒ&cZJyܫՊo3ձ PIa1f 0jRVpaTyզS`;NS`~'x N7M0tRd'g LO~I`z/LJӆI`(Q*re&AsޮjVS])河vXO˂`y*,+$eDJ˳ `y#,\v5Kl??f;vVԫi}~L>-K3x?Sc9x;ggc9x<}ƪtטc:tA=gƘ lUJI|wG`Qzl۟xylۗ`[*Vres4XVex#vkRLi89EpoŃ#x8{Ǫi{j :c Xo[~(Ky&? 8d`~1Wܥ5(uޮ Cc]3Ԗvy9908O< αyapN9 ɇap~sSIodxQex#dwE{LK K/nӯ/+,~(Y: K Nx?W7WZ<+ZecqР}4 AZq? ;q8h'o*oQyl Rb~HgiKXU,]eiAO&I WcXc+j- =xV#UTPs˵jG?lժ c ,k?xމx'o]xG}m?x*Tl:aN-K:/mKҘ]ثZrKNzS57ĂX0 ;b8(bh,wI`i|>뷲O|Jr<_d˧[>J,ϧG%ߵU}!^MGN ;{N;Ng/v{J1[ ,EԲ1Õ ؛?Jh,[tAjnxVϚ;s<_nϷwg )Be;*0c;(E#gmn_gMvGDZEc}T4XGuz փ{`]7c׃փuzgnJ{3*eC>q^J*_n8UeZHGzu`qXցu`za[߯ӹt:0ihG^21[*b^ݠYFRo\Zi.C /CRic_L׊l=4hxBBkA(hPt-4\CkUB- gi!QBB%otʐ&oHmw-S5`րu5`=z XGkzܔj}7{̖zfۼ5`h=gx}~bZb=EZLՇ`j0LΫtj0LӮtj0L27=TL`rgn]+bz`=a ڑ` V{`dӖ` LViH0M]%ognJ=f6F5{Q#g+ bb?LmetANr/>noJ_ +7}%F;+J|׮ؕ;q%vn9mfKٴ&_3 "}zZdwz!+`l؎\-V`;uؖYWm`[~X͖i9m>3YIGpw2,R|{poFq0.`|k97"r0 Dqr24d=8e~IǃC{pt2n=tZ8où2Еr`>\LYzRpMZ 땥n)>\ w뤥a)Z KWWU͖Sxj˓z xvKȵ2>}V2oZ$I uD8~3pp]pp?kp] &tْ1ss"E]5>wney¼e98 B K0-`x ,ۋm ',coJ1[L{Tf+N-Xu;eV_%EK=kerH{rZ%ţzM{"w- "NX=qx-oe]YA-E&r7;ʊt屙u䷬Bf ޖ לm-+/v Bpv!\#됅2 \^-Bp=;rY$KݸD+\:.^qY*+(op]LZ1Nw ,e0`yXZRO9",T]_%*^yUū_:8uu>xE|2M|62OTg*^yݞ^Nגy5jxm^W<:2^恗ex^Rv;ŝ5y|=2.nF^V#ٿ"|$XZ’.,q?[;Y[,w_/;„x1YSw| *gp?Fbh 4V@,4%ڛ pυ?υ)s\?o.>w ̅mMϟ%}###########mN˟St} CP?T* C:k)S翑#?KS$}7 ,7 ,7 3D1O$WB??&Io&Io&IoNʟG7W{b"?D"C!?'%?R}BOtttttttttttuo*l.m1oMQ{Jeoɷ^PTKA5zT|T;d1zCMH]VIg۷۵G!ݮ0yt |ԎdzgO ҧ٧iC/%8)[ɕl=a+T^37whsi?-e[s3? fqחj0Vߘ370%s鯣h,U&3psd.ryTGc4Jc_7##W##w####A)p%h4X@/~jlkLkq!n5募Z1 )ҝ^d'p#_b4(plzYFO宧,dq¿-R')զO#6O0{dPT=Y22?8a?hO*'ni:7"7?Q+',"',"',2׺I)H%ߐH%Gi1&r8 [e9@~'t0II"oLycEޘ$$7&1QI"oLgg$re >'}.Y=PWf(%P/x~ijـ@%hWzMeb~cUe뱿^[ Ы?z}4zMW Ыʪҋ.7[r6z^5CdzUZ"#Ɵ"uLȏ^%^֫-\I}A+:zM ^G)z^gW 5;z ^^J/l)7Mgp4YMe^zeKzzE'_E;k4zYj ӜJIq8Ur<8z^Aqk8q.u^tc?Wz6VLX˒=$ #7zW"#z>z>N:z]&j2hNz}3zu j^BwBQcX6z ڌ^B%͖=}q]]^&UfԅN"J~Gz6EFiE*zk|M%T yދ M&FCmɸ$a4492|1L M,IФ sJ/ &RL_? j$皉WqXc&}֊G^WFQЧ(sg$; < }Z>GAH5 cW >q273V)~#Qɘ5NAj92I;>eI{}咾[$%#$-5$G3dq B<uxxEco U$>m@h[*v >v@ m@ۊ@h @̯]QtٲYV>H^) r~l,K/o; Hѿ2Uw c_2Tb5믃:_KE_E_E_E_E_;*rWMg ]ݦ?#Z3N@YU=IZ^Ū^d^^W%zu Ϗ^}/: )׫@-&aaЫ0f*< z^àWaP5y*ͮʅJoi+kRɨ\I2Bw&E &%G*M>55ﯥɿ{u$j4 M@MCɨ!hrp4f3h4=|1DHչ(7"QQ(aDyf/JM2aHnQ&^ƛR^n-k]Ft"~&t! SK/}78 BnCJUN~gP1PF*w ŗ{El"d*;R.h* "5Skn6aߓuWzݍC'kߟSeͧƦ]MJ׌ hG智e\iY7}v{Q?JhgXZz6lzU7um*d'31L~!M#ߍ%ev6b݈[$^D}T`ƘOgO 0|ǯl| 0 1'5+z uo2%ƿw9LHwBj,ٝFxlpxPCMI #◬!}xP+`'2 ߛ&zOw^L-=k]8|[s2ziأ;]+OxkYlc(G]' A{JiBlo),w_*iK ̋Y/"#i[?M+hgC 5?lMH]꧆H~;Q<Ğ 5ei6\^^ vU%UUU_&)W*OWjbPuRW-TJWm* *¯ڮ\]%W-6REjm+loqe5+FZN# 4lLmOq?Fn.\nk1iفkX0ҟ&o?;OnP0"ݳYly-fS͌sٿG&~SiMWU77}C7Uk*f^-o^? {Kc)cz0Tl}lrvᶅWs&gYn;؜{#3/s5V6XWlo*,h J =# ]R⿬VZ}fVMX[P֒?*v]l)Ұi'9r&r|97JuӍ_n) de [Tz6bM;4aRl؈r[bs+c v^(md 6b;^VLlŶͿVOf)mTlJmM7m>㶯',[5aL&(e{3[,[f)Ep"vJXISmbVʦܶMNm۸R6.q[bSƥ+6l\P")ZlC:=Pl|6ev$IHn+IVY@\֕) >ү}m(%mEq^+w`L n[ؼKXsFvZnSlKz&SlJ؞9۬m3lf,a&V:C *aϊVGuRArFbsQVTlR"}͓n䶁bq6]p:Ŷ->{r 6\?ƮSUxv@aIQɤiR VMS}E,faPl9m)cl8n}bUVxъmD1-Tu/f0RlM,[v嶓]bm{n{~=sy%6z۞ٟj)6bm}ؾ@~_[Uo[!x .>nۡ)j˹b;)X<-Cmm. n{67*Vm-lEVۚ*ְy1u-*EmEnZ8ron+AU6MG嶕m'lx۶)Ű_n;~=_ebkWϿ%]v[ ^2הo[ =/[ Fqw휗-MfVl{l18+e^ۮ(ɰAlBdm[Gh3?)C-[=hHIm_)^8K@-΢mbPgb;PgT,lVlZgq[Sl%V۾SlVlnVl7hѲG َsߊY!n+KM~C ,VQFqm l4!}m *n؆Vlոmbd+mۇly%%)򲍕Y-M|Vfmۊ/?n+ )l+?n˶ʬ㶷,6DmaS+U*m[3*MfMYZ[o|*)n .n[خ{ʐ%ܶF)Fs(av K ްU+i+JpYV=<[ O[s^*7mQAq[b *h!MPlW̊ͷͿg4iY'UlE zmbm|la Sl6l3wŶlؖz Ϲba+x>,6V6dۧe^*w=Tu]p0zNuV UtA6w.nzԖn-GPͷ{6{=k:l+̾rm)d64̠h}ї5?(9(=tՋP. 5zh|L[>*Ϻ`\W ]0_]+櫫v|. u|.9o&tyV"|`E,j_5(9gX,ei+KYZb.4נ/sOR=hW {`d  ` ս[~~>~W3+~ܱ@2Y|[QZ !,%t=,maiK+XZ,1 䟝 r-WaѪ: Vu&S\'!Tt«d6zGntL_oϞaos!Lwir8ݔ;--[r9.˖t[BueK-Yj{,f2K>,dm| HA|'h@_4H Ju:CN 4 ";A&ti\$-E_<UcЖ?ZJaZJLx~^m;Ďա#x^rGv wR !8?*yyY4pqn|ouY@uf˹³$:kձ\+6'6zuvv/w:_A+6zkzĆ^MLDlufz<6v=wlX9Ďx*ʹ"c\{:oOv`:bW;$_+jbk3ẔF_Pkݑtz<96]"Nrƒ+I?S!v:eY];g9;sY$;J&Zq>{[{RڀW[Km~ۂ_Ŷڀ6෹ *WX3MrvL6Xej=[~˷bGtm},WV2ue>fI)o*Q웆IBZGb$ᓁr4h'G~~x])Y;g46;/h"IKz霛,yD>9o txoZVཫxV=xkާZx¸Xyha25>!q&4Ytζl?A90s}2 8^>+ήϷQ?t7ޓ J5ƛ|SYYa7uxOIAA[P+drB62Q![ *뼈P0y\~~Z{yXf-/ m~Z߮׾i-S ;:z2[eyݺuh>(>K7XyN@e=ώCug^9h͞|&]a%oFU|!oK tňM`Z=Sν&+B-2b3.bІmm"BiL2Zc7o0~c2~l?m5wNs lÛ9ؾl[5j2`)(Mjݡ`U l~4&sEhX~|@_F"  Diڛ,gR0KA,Q MGN;eۏ7bUMoc4#%XH<}l,h \{,yNj .Û_Sp i .k .KˢR)tk .?6wEt5uHnS!) I*$ÁD,!b53z;_y i6fMcdhї691EоM@T|M 4H3~E ֨Klu)O\'èw;fǬliC!NkƆ`[!~lhemC!]#iw3*$^&%Ӥ&gM1xUg}Idx:Tԃ~և~uC=zo=qfeN< ۍT1[lgدE*^/K4D'KX*l' z)gL|M0e baU4Y䰍#e:KtbKvgG槵$_tْK6_|6e|u/RL˻=: eq+q-=}\u.]gvˡjCNT./V!v:eK%I)vsXʫcgE!iO)W\(*eZ\?( ʀz4ʀb^c)pit}KJ]g'?NLjR.y\qp]( ˔qk|ܾ0su=oip%Ur/v7rz07P _!-My'8䛜DZA3I,Yt},mc)U,]ei}|VctR e惧W aWJ(j}}2E|: {}aEEߢТyQh1(hYZ, ->EEۢТgQhQ(ExJu- ܏d) g,p}Ur߷{}Ts\xKDadL3w>ߩR7+tR*omt fU)ȝ-yG oxġp;T>h)R~.j j3 =^j3|VUg` <SF o|O~dzN gm)q zi}Vv7R'#8fпIٟZѣ@Y54Xww4x>O@'РShT *?@'h 44mණAMY:fljk:ҀMҀU4 _@'* d9* 4 14| | "CРch1414x14HeJ4l5)kO n(Ҁ 4 lA,hki`3X4䅮_Chx>)AG4X>=1ATfMtҠy kH2B;VҀU4ϠAWhcSYۗU>]~~]:<.S@eRt x]j?.ӕg0z3@q"#0np@9=}:f8:|(F21—n^׊9-􉑞=~/ ~.=w=s~z_#_n1򎈑DEwY" |ͩV2@J+'61=< uf4'Q'o;eI58>q|n_;G_;?;so_\eIͲ F;%z8>ufˬsq|$}\>>ݖRۇhd]2U>ط}EڭY81: ˄N3L5:gBЩv&t2: fB_cU,5"EyE˻ .%Tn|E%oz"d|Nי-VN~-9>S LkLC6IW+|^ eZ,L U/NOyYl[B٥cd!03J8%/'JӬE +5.SʩIJeAv.˵rkRnQl- gy]M_&Qס$:4y|ܿMסɦuh2:4!iw&XNCkFdVrmMSv\f^[=g~IfeQ D ¶.%k-We]j4=a[k'6 6wLJ :\GsT !c𥓦iZTyy"Ǵ3H**ܖqU0^z\W8*']WU0t_㯮qBbk΅~0z.d1 ߑ#= =5ufKxx^Ϡ;aﺺuNN+MtE6&"DWDh]m+MtEjW-o]ѧmNJvK1rvn=G;<#8D:#2վ ~._72\?epj{[:@yW.Ҟ7"W=}<1 uf<ܞB#䎩՚<摿~s})}L+,~SX3bvTnjc,t?aG7ߎo7x*-o"}krO'QNz<4QvNpӇ?0p,Bh}k =v el.* 8GG4ae yLHB8[n2<):,biKQ,-g2Kanzri>̖x0htro? P\ h5Jw^,:پ; ƃ`7LŃx0-tL=x0=vtq<c5{|f6`,Xcp;N4`=}|ڏʶ֕ eɃ GaB!6x4f[1rX:^,-f몧H|]jt>GR^6xeBc(%Ba1r^h}AV ;dwj5q^^Mz#e:KDGA-;C`!_s~!owG!\ $j} f:mYi+F~3[03"c:쵟yoVl)Ak{&oA =~_ U?(抓qq,v'%q9:j' Ü۝] Cv{I G |+~|DZ[m?86cJ]ͷ 4긮Nfy:n˷J}X]d31 &F8{2qy௬\.9~|ԇR:[9*4S3TgY造XũM*_jg8Y`7=}XE>tҥfˆUmTa:M V8LI7^C:GwƮЈcOq'}QAʕ T:h)༿h%_z:5繰_l: @ǧjt<: -kby{2%ݝVuX˹b5pŻVm(N8YK8wye:Sj]B7C}(X&pN48qUqڔ,a KX*YjSW=yn5E7KX,ei?K;X-V]wcT\8~j95+_u,`iKs){LbKc|_WC`X dԛn,dfCOt2m9o^Ϯah_%JKAtvUZxNeEy]V48/glu۱`h҇,){2,e3&cФVhRu+4v+4 Ml&/@}u+40YuY90Hj "4/J4/9IMjx=V(d-W]'g'^:(X45~k L f\f1/R 3=am_zk~91,aiK X:,7=J/diשꮯٯ+YZ\f4,`'ֳ]O:TD-?zf&˜CXoYg,cK4zeVOICg4ꮵ+Ʈu_[x5u뙨Ck"wK߳-KYv,`CFTCOtٲ}!*Ѩ8|αznFud.ךc^jVE%Fqt4Gh0 ƝxT4XK4<ǹDhͱ(K|9++HW&&ZÿUm]>.f.3:Dez0+.x;oc2m|~NzvTԒ/xL1n󍛞ߵ~/'fa.i,ң773D1!zË%1Z6y<+.ϊr~Hԍhyn2aSW4 \=TuI%1~A% p}ގo:dz?8~wcT bU.?Z KY*~e8. 9}YҼS4(h^- wy(h> ˓,΢-_nP1,}NiYw0}=kEQЙ ]Xj',5f+]fsZR#Nf̨,QdܨgK|)m)]ja˗곤ؠ hT`Ü :x-r?td &wm"rn֌R>f, WU_2/Z_-,*}ruWOѯzAO?;#Eamr^uE檧9]g8Yս4ck^Wh3%m\Mm}"Q3ڲTpэ#r`~0 +<,̏3!GqRڰɔhCKSg|qЪ^9Sh]ܙU" L3}h*-<gNqyS3Qy-*>IQim2Hy -ڬڵng k28.?۲<}mw]I+d^!ܸ\-քմl"z>_66/G.sXMHva4i,M`iKCY>,-g>f>J˻`I.eO_}ND*&+TBr^F-eegkix7˨`W&-z`h ص`wr =[vE+AeOEEg"pY̼ㅕ^SaW*B-R6:.P/?:{?2mEœ%\jԊuWE~ a< FW0^' چW0^u/4sP\3[N"x5-,+AZMH9%'^3dq"}(y_˶|5,a`5,ke8ˀnu38Fq#U=ou9 X:⹽lů\rEcO2םÖѩ}_{3j_7zÿ>ezygY q$'ǩNO<\f_g3{7dX=Fs{r&sHݬ.yr¦^|]!|q?wcT'Jg.'GA$t>ߤk3d{h4.XfG-^7?>"V-Ki׊ڵ"mN׊"mqH_+vǵxh}ĉ#^ g04T~SM?ҽnp>qn>t`qm3ϲ~u}X0 .-tXOGGVq5̙ɰ{eì~5}m&6?%n,VT8/\QG~:9t85tFB>ޭ"n*oVnwVOn"Vm"n*oV|ic=~_AC߾$7lbF}6[8sqd[[&VK۶Tп>')_662Ot(q/3YEK:24"}9La 3ԼKޣd\LǍ-o&ssغ+Gyf>nqp;8E>s}= :&~ ,ckMqƛkMj&ɳ=^I{}Ͱ{P69?$EYgnyֱE[Dm"lglyYE[_=^k>]dlOxkݕ9ngc͚ɸPֿ, V,˸vkWwv{UY __bޘWlN>N^d8Xk~3-,^Z\ kD;m>&oy}QCFmvȇE> dȇE>AîazƆۆ6Lxmyp2ָ^E>-ˢy :cO]tDǮ3jWwh5Q:ctV:vұo&Ww9X9*Eb>nq%rq&>8 .?h9*61#1x6F%ܜ'_۶'s*6-0_/ٻ 3?ٜNnm8J_gs"?oD~_^玜r"?sNy17ۚe[ޙ{;{l^4ֽ~=g`сִњvdkVޚsWk'њ}xe~:oKoyzct;Evwu%>^ef*~[w{]^{5~[2yccKRY21O}zػ\NǼؾګX5?j|IH8,).)$)%)bmRKm"/&E^,Hl÷+hFV{}]~>;Ӟڇzߩ"g>}ޙ3_}T }ys_Ǎ;>ʤfWQ y)OW>,b+~L诟`۶-{}}Ԡ_Kq;W G G$Dy4ytHȣ[Dݒy4ytBѭ 1t=e ~}~~{:K?sq<}pj?*f3vvV/;gdk}޸~!N}%`l ó_ʾ]z8n=q{x}b7R^ym_& Y.|rbd[7eX" C8F+|WbpYq.}4f{ ؾ|/]#kDFKnȗ/׮5"_X#}kD4r/?Tڻ`9&=7x7tw9t\2]Zg e[Y̟ΚɸqY樋Zi녓d_#:{ߤtD>{&f>wf}zGWGY0qh?[/&-:֬}ZAHՋyct ?tLYV0< yn?|5v1f9pCC؆;}1?Eڼ,;"mfE<,樸H"mhif6/4YͣսoaivamWe9wWJo8wŜwd7\۶_;#5WƟ6_m|ණB|`7왐>̖ _lfB̄Ke6ym}&e`%Xpv<(4w ^fCXPԘRSk NKW4 O-'fgU+ a+^gVū+ 8K7CsiErC9Wur\Q-H8/]) X3/0ɲЙK&әD:.(֕jf:.($iv.ui:KglvMKp\:SHYO.hϺEG (S @qJ 䱃[fܶ|On.ݓS{p֝Ym}=wl]o{{*hKg;FT2lOӬR)dLcQY8H?fCfWJT2ݓX Mb,nJ \%G|Jm(\'a *NVafB^ĎU2-tZ!kB?ӕ,{V򩼕~.!Ӛ]NlskcI/RVny+4&+pyk[EUOLnН[HlXbv @UHYvҦf ox,yFJe6KZ'SMp]>w^0o]r9ZM;pY9jZZa^{š*KÖlg5mKtjaj{H՚5QiHN\[']iV0xPt{(mUq21dՠdδxw6Xy,""]=m a'+[W!ȋ" Ck4 Q'⦛FZ40d!֤uȘ͕ΤR֥ yG$^P2 ]2,y7s#]L(V׬[R6\[+u䐮PmՀ[Ay^ٍL:ߙJ"#ޒ\,iѺX ҭ],iQ̭zk X Tf|ek:-!%n/AvmۨBk`P.ۓYryNfE@VP?@۲qތJsSEBf'E\[KI;%m2Օ*\<^vu=.rʤΠQ4VlNRv;QHg$R)dOaHD* ӫ-xtw{V:K]&@m 9+i.E-\)۰F,Da#GR2Y&^Ɵ]yapv^=6+K*c jڨvt]n|ȃ@,OyRJ2F.W}j3!-[v6v\k{AmQV{."}RV4vgIe^rmH:֡mmKlŚ]^H[1lV4o) RWǀI>O}Yo*Ӗ2P8ؤTuJdR]L`hby룲Hw0FuU夃9 M^ǃET $JejlX-{ mYM ٴfe]M0`I+ jZeH9I³;5{t;5VWv Yβ&+ӔXU/ĹTh}K7٥2܂v4=“?KbE'"k;gpM ک%n+l!-qͭP8;頊#uik&!6fh6]O7 |dSj(.8烲J2'mI+ibYؓKPIaSQ.&ZD,ն4(ߘȥ:A yӥFTVɓ˜Fgc/qWy"jE}^N,2RyW:͋S:Yb]4/OC3} +d:藣Tk1t6)t"VsFkeo'>Np=EOoGZiBwݑyt~^ vA,2 \"a M lagumtd;8qѸ9ޛJ%SioMwɮYTe=W,?#]*Ρ5{z\^ԚĚLL7RNmRȦ5"jyyt>BhP Js)srI)l>F0U(M34DICT 6\a9| YP\fq FvFv3L$I}xw'9B^TT2=֑L'P*+"U_,6eUzF<*jQ1F#nu J@; Mv/uIFSQ۔JT7XJ4'1g0j=]0k<4ojԳs9,¿3'!R#:P ^yo7r٦衏s6/r?ۚcww9)Մӿ&~ {3a ζ:};Uz zޢ)5@,C&M\"A_%˸2ēI.*oc6{~9mL4<gzC.=D+u; c`V78u;J;>|X=ܺn݋:uE0_9Q~_J#ək5tkp,q7]CIoJ2/EKr.0Ru#t|thur8Ӽ}`{ѯ*Xfӭ6Xg>=X"󸈘1vm\~^ԫUV')/VcnʣOW YF4k=]-W6І>g saC;sï#==F } _+ +~:2A)-Hv[/Vx)hZjnE"TСgkW~kuv.fjsoX8׹;$TM,6.TE@x߫ϳx5HC2]^\o݇BN5^_5T!VmN69f1e@?UNs!-[YUMUU5@g'٣*,z7Nrac5OrFԻ "\nN]c$Ճt~߯{Ou z$bHfA1a@bh?Bsc_d{o}REܡ;t!5Էg=LG>o뽹+,*Zo9{]7 C;SPqukLbzJpxP0V,+Z7a3Ǎ=˛Vp|ۿGS!bNs=΂q_O?:>b¦Ft:|/x|is(r%eߩ.tku%o"\^Ӹ^?Z>bmL-"ڷO[!2ĩ+h?-9ٝ{Nw{oR*nTf@S-ksg-´aV5/?DmA9|(}x>\ŎO&%l}wkMYgC Ovn?oexA ~ rm!!9Wyڮ#rITMt_sC_IiGWunw[ =s^%5ٿ &>8fQeOtkB};P q%o׵E]Kwokk8b߯1[ l_Q1,93R#o|tP.fy J~dTp+m ٿt MdZڱ[JCT;^~Nn=Zx.}pI֞C)}ϩKu 5-ȿߙ}ַ5Bo*] ^OԶQDjԷH\T'KoE~ :2L j{~^zv cC:I\yyc3sP⬐rW,M1KO0M(bo|beO^T40-ëpm`Jv4CZƯg7ZS E*h = {w⸸6!ҭ;k}îCq n2:ط bCC @߷;< ϾVMd̤r:'z3s5'ko.^EuEB.b^zI3Zk'=BJ2X^v7mLȦa4jXkguy賿t=è94:ZS}]=Ky٥+e(5Zj8 pPG +Rv ln{:eשge9E>̖̭˷)=a%RX֡bk؇EVM.>DT-RVDPgU]}ʔm{!Zw9A]YUbJ460Z'6l.abM;R\.a,hˤ-d]TCj⭫в [Yi|r(-)hᰬC6~BanL0k2Wʯf$BgZbš27w;{zgnAw<[Q ޺1@cw|]"q++h$ۚv;گnn϶zҥ/I:vԞt5tv:K5]=-0suP mX)HGok5SHˡ``r\s S'Vƥ't B]oJY-,w=2B]W]]=SX5:a:c۫m-V*3~6ut O&Qf(əV:ֹƴ; @߆\6t)I |1Q{ .?iLhxyJ1.! ީ a~`U[|~C:}2RWzv6rON Wޛi )4dp׈~mr ށ*ks} p&<= 5zMo)2vt[+ x\WiO&Em,=96MT>O9x蒟V6?7WLu'($ 8z)nowsZw[Rk;[ijoLw$\4b$mBs"þv" BMygX{ AX@CZVN?"K"ͩlLxlu9mX(6֭XjY֮`1B'uS\Aײ͉XU7X: 56!Eq ^[hi[cQe}auv+܋UuD]0{L=i[ZP}Ayb<ѳ^gkh(HEaI]hiۑhoMwf]+<6V_ݴq:5v JF\EI ċ6CWV&jAuF8ԬZhٳ6'SφQ{& ٺiG \φt-e:Z3mZ֮UtA1׋ 5ަcGzCJjanwSOXLRHNjXI?%ŌW5*cT1We\ghU|US,@y/D=eYFW- pAl=.ɨWDJ5ՋVԸ\kh4h8U{{$G՟& zym%U&^.^W*c uzY,xReɆU\-cC>նbM4ވ$U/Y[>l^:5pŪy+_HarsS좆 *ukSQɈjьhm@n^Uc#8|S1yVCW𧄠]X]kf!gA x\#U>?x֤}nz|*Y{=5kB<#׮eps_A)c)|W;S݉TO9ag)>>5˞05Ef_ _ \O\H땴 gdŭGuUәE#9cs$7Wb$ 0[_^b[]YT~ͼ|zq._3h/Nd׭O'W>ԩ5TYf5N{B{Q}]3]J3jYzFGA <[DQ>U`GWxxňԭJ kBHeܷ;zs7!)ГN՗շ6Zq k͝˙/.%<oǯk\`w]W-a-icNx1G/T҄O%W!ǐ `w|.T@rN=Gbw<AQ,E@_ia3>)2ϰ d#ۏGa=w_w ϣxt@HJDzNƳ?(?gMQs7(wlSBw=M>omu^n @kc99ʻ[~-sVc2o\>"-Z} \ TH?Zگߗß֗>E]^~="?Ii~lA᾵,W); Q֣}} L(Һ9P2wOqe:Оԕ_'f-;^Z/_G1>a<{0ܫOj򧻸;Zz{5ݽBb~iڰHPкi?Lӥk[ֱ%e.V=;\};߯\_<⣐uwN׀Xϟg 7SƟ{il^^//Fzcաkf5Sg|S/%w[7HwrB$D8΄0 {]bĥyg., b&M.}$S])>m˂թJ Zqzk+Nxա+X}&@sΊa66'z]bo&/t)W(Pp:*4mw:|B:guL::ϗ /]!H:U&:tpVpVppMGp(r\׻K #l~֥ٚ _{-yn;c$Io[{3mmMpRlr^G#us4Y't<#T NG3gW{],lVx#o}܂2rBͱw_7Yӛ[E=ga<"O^&_}GݽV?6>*x??9tT11CǴD^d>EbZzOi,7e=)v>H ]Bv >t,"fv_^ޯn^_י:{`6VijO߰{HչBZ'(?C]doB3+eڴ.!y8_wWJz^Op6L\^\з*¥B_X>Pwm8]4ô: Jcwj} A.U˫W ^)*3-ޮsMωB/פfamcB=8sy0گ.5=<Hx |Tue˿_"oݐۺfjRRAWFk{ޱQ 趇G  WQUC/~E(F}Bр_et̥àco1HtdXMG%8WO :踅>::hc :cO1B=t б:j(c.{t\OGtT1x5Bקգs\:j9:kNUcWcU{Bc+~ٽm0ٽ[c_FviW5]dIc5ݤKvkKv?"s1R+ؿiȮYc ]>$}Nc7=&}WcC~^ؿAvmٕj]ƾ>o#K5Y۠Bv7h?Ivi@vjJv~+ٽ?*?h[_NvwF 1 <@Ÿ'~M/l$~qI;<)ݟ'^%?vIG^J|%/"~)_K9bK@<\x%>IO9wߪ}+ }!yJ$%^? #/w-㨜?. G$oJ>xV%q/CrGBӇ%爯<$7|U~n0qA)q/?^_$䷾{oW"ݒk_8_" Y[ROz/_/+⋕.qk_$ I~xǕR~͗Gwv_Iĵw)'Vܣ{e_WPOJ.ߩ- _O|7Ⳓo#I/)U~H%R/QSi'KonğQ1V)|a OOI>Z&R8F'%_H5/!)&>M^7K0C *o)cĿ=M/H_7^9#m_'U9]% )ˉ_>❟\Gx5W)^ NWwH(័|3-?C| _'tws?z'v!nݦ' ys$A\wK^%y9WJ^EJ{KܿYeĕ$x%%~etA$*9'JX|vH}#ĉ2!> /]qKVoQB|tҿ?--cdҒO$~]N#tt?[*e|/!ZrxuRv6Jx{/_PƓD7o]+{g*cJwwVҗCJ}x" s%IQ?|.q]~^ y5VH^GS+˕'c15?A՘̏_^.ħ/)K$U/__(RWIK|ŒO ɋVr ;exZOKHOJğ`ɝď(y+oQ%x%_yxht-YL7fYw^q׿%̧S^|ut_C|+ۈwNI.0O$oyS[׉?$IoW()JO|$K4'H\=!3IKB տ|+$&5.Q⛟c럕Gg"^IOc|i^J{$/#'/!>ǒ/#~)7_}%?{RC7W?*{_$r*Oߑw[roI^M|cJ!>Q woAɿ$mHI⎯H6%kw++ !_kt}~[Qҟ_wImwJ~~?|^ħ\#~wHW*剸:)_Mۤ{3q;i}ėߠď8vtOJ;ߢSJ|~BSH>Y^?&O#6= Q%~ħ]/R\+rK(%}*ħ\)yO[$Bm2:c$CܾY?6J>^+Gٞ%&xNAo%~};OI,ە"9'>+ ]s,I1ķy">nKzf/ Kn'C~2&~r@aogCIoRt^ OEx4<2Vw, >_/9O\Tm +ĭK~wV?kT~, K + įQJJHܪ軋x$!P"^ve{6ݿKK%'>YW/ ~˟p_*O F%}%.) &W ެ7qo[s#R?s|?B⶿J"[d|V]o#GNJۈƔUwMOQEě~ğxv|U߉k~$s~I%~ۿ<#ˈiDeg,%C$_O$ 1s習D!گH~Hᒟ!}R>VVL8:?i _J/RN!7~\zFYȰ:裣J:"ttב;tBG%: :/ w:裣J:"ttOt GG tD0_At GG tD0%w:裣J:"tbw7?2}u{65~e- p p:}2/Eܗ7w>Dxlo~{el pgk~skܛ7;"Zstf}Dj rT>>S#LКiKE=LA|l-]raĮ:a?U:IT13yøC(57ݗ'B~dœs20<f%0`6Œ\ s-I0`fa`ny>0̝0oy+A`s7!001{`> 9ll2'` 5l%3K`·f%aYy=0aG`s/̗`u̜s.`Y f $M09s'{`9s09 s &[C`F`\\00;av9 O9UdxÜsJ50W\ 3 za!^/dxÜ 8e0+`¬3yhw(x <OII#Rp\ 7p}~x'x <Gc=qx<6!p 8.+p#8n'YpwCax<O'S`c>?#Rp\ 7p}~x'x <Gc=qx<6NFOF%\Wk8gp< #Qxx<OSSp 8.+p#8n'YpwCax<O'S`TT\Ke p%[Ip\A.x<IX/@%\Wk8gp< #Qxx<Op 8.+p#8n'YpwCax<O'S`tt\Ke p%[Ip\A.x<I8?#Rp\ 7p}~x'x <Gc=qx<62\Ke p%[Ip\A.x<IX/D%\Wk8gp< #Qxx<OrĿ#Rp\ 7p}~x'x <Gc=qx<6!p 8.+p#8n'YpwCax<O'S`@%\Wk8gp< #Qxx<Oňb\Ke p%[Ip\A.x<I8?#Rp\ 7p}~x'x <Gc=qx<6BB%\Wk8gp< #Qxx<O%\Ke p%[Ip\A.x<I8?#Rp\ 7p}~x'x <Gc=qx<6#Rp\ 7p}~x'x <Gc=qx<6AA%\Wk8gp< #Qxx<Ossp 8.+p#8n'YpwCax<O'S`<<\Ke p%[Ip\A.x<Ix>\Ke p%[Ip\A.x<Ix?~\Ke p%[Ip\A.x<IبD+p 8.+p#8n'YpwCax<O'S` B%\Wk8gp< #Qxx<OR\Ke p%[Ip\A.x<IبFp 8.+p#8n'YpwCax<O'S`A%\Wk8gp< #Qxx<OC%\Wk8gp< #Qxx<Oe2\Ke p%[Ip\ww`Q?ρaN%$)ؿ>l[XcZu0aaf $N]00 07f~sN ]0a|QO| =0q0| $)| &Ï0[`9s&#Y fA#0a=0[`9s&Y fA#0a=0[`9s&ۣY fA#0aSsC0D}Y x-I]0a (msQi?s/̗`NB~΅^( #O+vqͣOo0è0JqK Q~^?K37FȳsS~PT'n?/%s&# jȳsS*&!"""7S* y M##y/3PkEq7)E>ȳ}Qgo(|"; (Պ|"ɜ 75E~Qܔ?H?Ho%AU/(E$eGƇ!M~E_>lnE[?IȳsSxExE~5a"""|ׅ*_U?J_9ٹ)>Lt~?\?I2C>B"  W3<;7+o`S$s*U:/ȳsS~"[?8Jwȳ _jB[[] yvn߮߮nøܔ?Z?Z_H2_??ȿN2!|a<;7?L8bC^O | -!$9y9ETETE6y򷅐dR?ȟȟȿL2G@eEyxyvnL2e?XJJd*!dVC$C^yvE$sd% N$ׇL2B:SPP'o MgKywHLȳsSޫ=d y$3*!"5>9$?ٹ)MߦE2@.E5E5E 4ȳsS~E~EnBnEEE>}sd."""j> M*EJ$\O*^H:N_/SO2yn_@M|"k ȿG":| @)ȳsS"E"ENT?DG!M+WY`<; HߐЂdي"""?4_g/(w<߭??t8H_^YGCoYqq^A_^epަ,'Ol?\xJ8{W*z}a?8^w(߫=GU9]U?_9+UӇ{:s!}a#N96pҟ :^9G_"e_Ř#}aPu~8_ߑFw~1uɞƜJ_CUCUu:S{'v}/ [߫~ne>}a{KT_|}pfпO+xg?~ g%uvw)~^8/߫|~/ mep_|ܾXO.sY&8o E_<8}B_g 9p/l}j8W+߫< /(.˘ [+8?}4}~d8ҟo9V>>sPRr񅭿Hy?}a{W4?Z_OpѯPWs9/WW};N61R{0?@?5* eOpS_c̫og/m9۳ Vș&S9~?{](3~ _wLU?l}aR 叅s/0g+G)K82v}a t*zB+ϭp:ѿUYR8 #F3Ώ΃SD<{<8se_AW)߫= *kl|d.v*8p_| pFߐSDsk"}a?8?A^o3-y,?| _ =p6ߓA{GGޤ/pWo 94[9~KQ~wӁ~ ~9pQ#DQ [(0K(8oଢ]/4KFi}p>_8;QW{]8]{}2wiJU=ʯ?+}aSN% ̡01+xc(̭߫p^} 3_g;*\_pN/Ӆ~i~ k2?-g :)+g(N?/lʟLq埢S8G߿sIȘ?`Y7Kpܢ/l}v8Yg gKZ)/lځo9V_M@;IݔZCds'}ag5iL* G.+k RQA_o ^N[mu6NOg{ևpӟ|&85oRWqQ٘R?j}xyP$_1Z ߲;7c= =0_m79+Wc<P}/l?}Ϙ~3Q_C?\?\K_3?C'l3+֟gZ@g+ʘz;+}?}awUUw/l~*~*];E_%/Qo/ MR~Θ [c1~5?}a_BJ_)"b~7Q/l9jN_}W4f,}Q>q?>gUϫj}V+̓}aj̇P~f7}aV߷ p|a9{(0~OTf}a{0@Tuf}xySۧ>:~g~J_͕\ =u|㨀?X-ߧaF_S<.`zX}ƪ /oz2?N [(S)A ~~V';A}Z~xFB_o0 gL?_WO]> [BU_]<4/}n0wH_د0~??o߆;_TTw&տ.ә=wh܌/lY*Y*;Q8 [߫5*+ [*L7@_^1hLK7++νcћP /U~[P [-g/S0 [N*:__~q/l}KYV0w/u,`Y2߃#1ހD_E/R~ފɡ/lb+}/l}!:*`/l}UE.<g/W~֠}K_yj}M/l*z} [[+uӍ{2` [7o!`/j} G YԷ/0_*Ug0s|aWxbA_د<60Vg0Qlϼ'}a{3`TOUu)7w)[&u=aӐi9v}aQ~8`j}Uss }a*Tmho/~ʯyju|B_^#񋾰/VsZwa;3; 6՞]/>f :o>`f?R:^O0O񈾰'0/l}7 gǘW~ c mQ/>[K_/B}K_^ҿQ+ yUO]~>ycA,}a{/ZA 4?u~AsKTH㿗7u}a{^>gM }aW+Zg_tNA_UUysf5}aSS?~Ml/l}kQ_W}5p^/W#/l}HG4}aiM_AӓW Z4 /74ϿJ~v?WaYN_^ ~_4O컂U;hWA_4 [ZNj>4 [Le&hj}Uem4ќǂf+}aU\_4; ϴA}a{O~"h{&OM~Ef}a{]K_د4epsAs)}aMh[^{ [~1z)h^/l}=6T4?4g_oYw^񑾰ߨy#h[ 74%ɵ[o<[ wb뿓.{7=^֠YfZ뿟[wcYMͯ?]t>>Ay^~Ol¾4mW+mA/eL/w:h4+ w mS R~^@(.?zq>H_^_9}a߬/A/l ߲ + [߫Gߺ!s+}aC?~O_zdl/lTǩn2;6d>/lDž !|5F!s4}awVwVpJȴ/lmߦ7LO֯X{촐N_u_G?2ZKg^]{ k?^j.~ r}@\H_O))|$d./_>y{gx={*d/l>2[ [Zj}N{>d/l}S/FQ?J2 2 [{(!SE_-ݐpVv/0dۿ8d>/l**BgRW~:dOT߆q?TC}4/_YjW2 [*q~D_9EρL)}a(?GcL-3Ƨ 2 [L埩e/>e>/l ߲Ce~/lʟ/&|kzukL慨pzjf'T 'oO'iTF>y r%l^ >܎<\D6'6Ln$yx y |Þwx#p;nNmOۃ/!_K.A^C^~2xyx//p7m!BHCGɳWo""o?K lNm~$ En6qn6uz6ar$]\E> :'lm`Gp]6YdC> <<<|y:x)y)??#_l?ؼ g7#mjW"^eo߲ofk'ۑǁsK7mlm^a&mƓ_Faa<]6O͠EFam$l^ux#>yڦ1y)<ڦu.9M2roɃ+9G#?eDnOLE6jB5J|3 בo/$/E^^E^~ &Gu`9YI^ZC#qG>cɟ<7TzB~Cv\ߘɭߓC"?>n$˳<9HkOgg$s1~'3/?ayE9ׄ,ϡhMgLt##eζ1dy<"2dӸY+'\S2-dCQY,VpJAxe|̣[C9red1[:Y,AdwX=,s&2wz=Y\,>OR%<;2d#7`edS,@@אe2dࣸ? S2f;̙ٟ,aɡq3,X.!Oe-d[r;Y捬a4؇<`"db,+>H_&˼e{!G8`e 2`,^Fo#˼k2f׷,s>ag!Y;,u#wd.L9.#|t2&#,sCք,$˜lc2Z YROy%he~W2w'dl/Ykڐ-Y ey4`n"]27קdw̩8e6d kY湺,sX T/edWܗ 2di(Yq*#M2Cd7iYD,u29d'Y Gfe;2/Гd}3Ys"2yd?',sL ˼7eNdfYy,|A9d~%0_e^2gK&\+Weed#Y6,aYADFeR2:G"0Yx,sn|@4ve s2FAS,s\ 9d,N!˜K2_d 9YpA" ̫pY.MeAd`Y(Jn +2o uzWd+Yn̲/h"fd;ߋ, 2bgG21,%2w>T9,ceugH%d<,o"˘2 5LqğeOd[2>YA12vYF2N,[eKd1YƜ&xiX#2)Yxv"͡dGqQ,%e,*sH1e|N=, 9a3x(J?WX~7.[PY~ ly;({kV'gǶ%xfdߦԦ$d[ YMl&˘dP,e,dВ,cze|XJq7 ,}_#KOg~Yc{\q6B4QI9d[^H~㗑O~,}'K?/7>qd}6Y=s YvN!KEdY[DZZL>,WL]lI~簌, g\K>~/ާd+Yޒeu)~kMpa,6v+;g @^㺜,}&Kҷj9o5m!sY;ILOi0Y d;,֒dY%K?:mZ˜L3m70d }jYL!Kds'Y%d}'oirI^k;ـ'N$7K>dc&@z;rF~ܟ|0Ms~%ȭ zOɛOQ8!kvYx䎧`'7<+-c/9@#$_4r-O%//"rF|qh;xy }8H^:O8pg~Cމp|&~Ӆu#7C.yZCOO;۟YSk _C&ѹPeNdt!C7|V%y(M's%!הH?H)spPw;ɍO\E{/'w?$1r 7w!s=ڽ >ܩ4%7lbLrNu^g:Mڬ,p&#R86"/'/&oEx=>}?#K{[T;sXǾrL~up#g*yJsYñrx5 !AyL^Ob:"W -$C\Jn '7þSCn>ryi4{.r)V?g'/ _>՗|҇#l8ՖQgc?nc&E< &s#俑/y[ñ5lS+M%6̘_$E>ߐ߁?<܎%?3\N#/"F b~#rW# m>\le|9ݣfQ~|֑IgJ>|@LށМ|t|V.Y>Byn _ry=Ƣ"w$<_}G r&ó'Xُ1#Wɯs1o[  ~{˱S`;ǑsCnGG~(!^Bހ$E>/wC>~#HoCnuC><N%/&G}.yȍ_"2T쟝*8[qDW+]񃊟SUN*>8')na+T{b3,)>RIQIq9sW\x9oRX ūoP-?Wb3<+>MqY*.R^qwT|k_x?}ŻF&X-wRV;*8Wq♊oUJ3(R:THoPHo۩~*}JP|߯iϛ*S+O~P$(nъ+#O(~Fk?V]MI*n⑊OV|k߭/*yCPqsT\2ŷ)^_)NHOSEh+[&(ޣ^,]TRq(W\2ŷ(SjO+~MG:ſ+>pbORL(W|EWUW%T]S\w*ޠC{MpcŭQQ<]|KQIPuR|(xyP$XSQ銛OM[{]V`c*.WB|)3m/_=_׈o;;" !sN?!~Fw?=?{I:{D!G8q`!Ce :#u9Q#Dh8q XDCq'=2IF #NE4A8qs&8q9%5 ; :J8q>s=Yy;)R/ F!.DFA"!a-@DB!JrDU(Bq1ĥ|4 1q9J LUY_jqbb>z 7!nF܂XXqvQrb b)N]Ľ+#V!@B|)Bd @| 5b;Nķ!{v?"vs/ WoO_ EE'_DP8GFq@A \# @2G<q XDC~q{<ĉ2)ƈSM=oS3eEas9%#8AECGt@tCt߮nҶ@B\9}}}B "1YE11F\̷Gňg!*RYۘJ:) WZZNJ-`MS+(rB}]]\_AV:55\+R,(-w*gͅ*OJz{4Z~{yQIyX^rE)SJUTFʑ{ҢǻJhAIbryRb^xbMIޗzUeE $(L.7+DW1Ҕy 9::N(Hʠ2VR:)ZVRNsLiӼ\iIZ.p~Anu$WEJՑJ&+29˕ZXQU\E6 ';cR_G{ER,"Ux⛴ģ7P%E[JEp鬋KUت;\7!"UbxBnaUE6z5pNp~_T^%N-ITxRhqҹĊ*VE&&` VWMM(91\U*&^WF'-׍&l&1]Zˤ4TUpkOblpZ\NN++HNu6ms+ iIjѡer^NKj>%/$%%IIy!IUnۺ}3/ZFNIHIP_3UxWuDZw3-9)h?ž+))h&.wR*Z$ ^R)+hѶy|(k(mAٝkTE) hH;'2,ADʒxkgi>6vLLtNݝxT =,CU 9+Q㉗ 2tcVZ$2>8*i"bD p0)RUS Hhw/ΫrDikV-ON)uאܢEnZw8I’Rw?bԩ!xWJ] 4Q jJ6d@%%Hŕ5%\TiVL,_D1/ U8tWV '/ȋF'WTESqn[Q_#W-:k# G'-r'˩F7%ՉtYQ6+()6b:윩y[2mzLDL3yquKRցAM\nA3U2[ex>eAd>ՙv*pEL5XMӘȨb;O0^kʼVVTcaMj6Tv"Y9fD /&%Kadw[;z}#m4e,LYV,ZÓk.e/qWWn4?D͟cRZPT; ƻץcU RauRQ%=FҾ)"M3ɹS+&Cȩu-I~쁲PVNNK,g ykj9w(y%B޹c ~>kpJ;&"zv3ƶ:zUZbL.mߺpj hf6kX#;7*5rwmlD}Rɩw+jdQDjd?vE6{{,'_Rvd_p$ẠHxa#A1?MT*Ԣ"^oPt8$ZW`b{q0 v9]x4V)9VYE˲KS~S0]'tVlG]Uu.y$^,KK]/Q6ՕCEy%)/FUMW"wtЮbcvsw--V;W޵WNR/_Qn1pQ=ie΅:JUk2'~.(Nbo֏ 6WlSvaM4\9rNz4>iitr[Y\'oKb$UpEMu}/v\zDٽ$钣< &+fc;[(9;AU$]$-t5%xF^JM'uZ+I"RTQ]箛 *p//MpH볢5Y|CIRsW;eS_T&˄FB] r!*o\x(UItKJ;c>J];8%7m)*%PRQFu 믚|}, )+%t4Sݍ??cIilH8t|!d0iJQdWWM^`l{)JNPb0򔴚䤢䄉)or''o+d׺T:* UiKSe\ONKLtBQQlJ^fJp-N+)-eOJUImw)N*ũ88'߾8$յBVm^,ISGsGpc]K,WI+vpt5x )uEΪk4YIb -tkJ=byQUXRRmXrII,14ƝZXQ*qi[KqK<4r[;X;XK*%Eh;3_KnW~1O"WKL}{5s9.!mruTW:,+qݜM*t)Ņ))yEQXXJڣ*\Q nDRIM4)K]ILZ+4ܧ)uoWZ_IZyNP'ĸnEGݷII[Q׭Vtu+:oEG][Q׭VtͺdɲX'X(Qo-V$iϩ[I:Y]8W˒ܮWc+#{u~i4E;+|׶(sȽ=n;))!AƮ}8LpN'&SpTJTS˼TXꈻWne4R(uV-QL#&X3=cVO((l9tG>^rxOjHRjjZ%iJj^orWvENגtavlةbkUX.4/vNm!-1R7~C{kyr.;wmXV$1ꈓgGUNIyze;m#>R4MX^YǮy,sm)i~WRj WԽQIT:RI6OMjNrvشޖObBv[7}; iH _1*6<9%Jmnh Ӯm)}x*gKVcmIHZ5*b :>:*`FϞu@ܞY9CFvj;[ߞs\)\s=gD%)CzNIKfE)=G30+eI!ӆJv>zC'ftϕ?7gvJrw#5ScI}9$)U{~=][Ŏ1sN'5N=kOצ9lrr N'59rԯ3W,קpg`T^YCX=&f +%9u%b Mb*JL9"|!pRCx_Cثz'U!Y=$-IIMTk\=$ĒDOsUT:z'WC<9ͻ]C"I'6ULT!z'W!z'U4Cx5e9m^#SHUi.\zDF:#_[tJ`Xܲ[b}wݝC(V%nz$]Jڑ!y^~`+` %]KR20DW~}2X$WpERqDDz+$>cJroݏ%Mϩԛ0K~d7bJ{+ t=RPZI]Tg`Vcٟ+M0ݥՙ&ͰUFkQF>z^5wK5F5=/fQʸd=M(4=/iWLxtp32a3iGLxn>FSdgaҎi:`!e{\瀠i별GӍJܐM-$a:U2?GHvI*Rn6'_pmrb/;y&Gn4i T&ɳ%^˥Xq7݄qjZtƹ{_&L37mmfӕ( 洃S %8_d_7^So܄u߃.WUT}Oך<"5B=ҵȽMè]pSEGY&ɣK!̉YE58_%1 ׳5yo^\kD^ ;3jRxiM'.RR38e}}:~S_cJ"sRRu$%)=C&֓JShiSgڴD#rnc26k_9 ;+_ؖL%i]tke s"[ͳ3XU9b_CNJB|e|a{q g_ d:5}kBvl2$㷤..ɪLpKUpN-Q?9%V9Hbڮr\ɩt;{iRrl=U 6 6AI@K5JBDH HސriAὋ@_Є$6(ЂM4`P *9{$_~sffq/ 9`4e"BR\']R+ʥO|06lمGˠ%[5': 6@ )d](HEGb"@*)L!5EQLR~tC73=%> +W˒_ x͓^orAzK4#|fB"M@Jhס|:G+py4C#l!>&31}_XcP!!|)2B&/QQ nf`1i LLiT׳vtrεHК#K<&92|icGk smaG9p;aFKxiQMHR= x2PsFcRC#4pޕ>"5;NNpHl髱tŀ>ҵ>R3ӳge,DBƒ1mZA3MMc7d?3nGS f 0д%]XHr|Kɇ.F^~äe1c_1#s$ gA_!!5rw+O`r dx| v>Ll m#``X+8<,`:& T\ '>J&ДyTUk)#Zf0Ah Aϛ:m, q%0.-s 0T`cf=^]b$Y$ȏfLK"0F½}L.7>{Ak.R͜T @th_p$Qgot83dy0lKE1D#P#prGN>$erXk}8]ω0 ecU26@yK`z`@z|q9='5Q\>ԀA]js}Iҫ@T>ARwΈP_2fQ1]b27)#=S)y^~l\hfzթD~_Q&b#qc~n%͠S>رjcfN*+ƙ#`s#zt H$:@.}8j@y6DQ&&(Y5bB{V!\aCE1cX@",Jj5 1~E@(D. ksP3v9GFIDQ oQ\:ӌHQ"PKIGvzqLbA-!6v= ;~=|4'b>20s㡱(M;1Iݦ\1Oe°!a' KW6䙻S5[~FM,`C<Ř}!4 ^ {EXƼd`Lt>.TŎ )0uuڲ#)ȟ4#{3@Y1`# j#|$D5%B%'dCP?˟ ~,ozp_"D\ kȫ2  /bn`eb7Ou 2+!&}O8c&Q(mJ*~ǽCFK&9sPQJf.YRj ic`cQEE l6͔C4S.`sG dTOȠ5IoԑȉFt~HZ̅w Z{'E)`JdUCdS8YoM2ɷ%}!K/G9Maf!"0;B, Hm8hޤI@݀J 3XEX` -1[$"[(!ɀIU,8Qa|JcSC!KPVs%he?6#L]>cZ,#Reh#55jӝ@^%OgPuDf@ۣ\F3>~†ڣda&+C4ߑ* ೴Y6 Ab~ƫ$B CPdj܋M.z[KmnnX3%ফxAG? q=#^:6ps$q?No d`2mAAyS NΙVT2 !~] s9Av%N`5c ]:-s-S@_] *x Z'WhmppGbuWpxe`ΩWt6z%kkv일ܹo[':c%YE3ˢ٭el,{1N^6wvu3yY$o@p/X> <'9y&g={8S_osݹo-9ysm,;~z^r&8?I'BM><}}!.[2ͮ6&gĶ= jtt{zэ{;o-n;.;'Xvl1xHNӫMO@,98@Iyٯ-' NmxuJ~R)>vy}$7XEѵVvl糣1~q l4j8sq$9]lNfVw7[4[A;6;C_8h%tv@esx;'b]+tc`.4)Cl tHwq=)b ۘ+,SLQp)ΑQƥx+ACX:1{O9WAڈ:F")Nxw'+Ư f%]vmRMW8"z; Ž$Xn')A-PQ̈K":n䙡>&pZM*Br9n,Ǜ5{ce%ehn~9cPREAm@%reJ_ nBkPYyL%UQ{Y{ Uq.(2{)ҫ0.T\רI8H7<>YwQnʁd.}2&td :P;YO4<:FaڹUv(3\_pƕ`띒8jK| p9G./z&WIf JٖH1hÁv;M0mB3)Rd$UլPŸkhíQ؀&A퐃 OKc2|6)e鎥2H(Kt94Le)r,bT|ꮡ蒣39XLd1X)|e1ئܕ& ʗ'7B%D(b,Awl=epR(^6Ym颷ɦ&M.z,]6Ymxd颷EoțM5UpL\ IOp͗B<1GƔ"9a1$? i$=f#\_Z"\4 3C $ӶCJeDY,#06p ~<+)E "dl MOԁɑ;,iv f ,;EXvX*"ZuTr̩ΉզN5Tڎ>d"QՎ&۬4%KPS6dYNnB16XWBue jźw.)Ŭ)fMG6IN]D&D#ˍ6|҃> 3f[I԰`!<%+โfؖllڒ$VOFeц@rIi::=nYl3c71"~-;pN9GQ9[xJî/1%Z 2 )6΍DQN4)Z,M:9 -> :"3%+YhbЖ]fXG3aNy.cikߔH+NՍ/̇w1.?OLPt;rmgl!kYl@$Vl=$|c4ml6K?Li>uOc eٻ٘6˘-ɂ]wXZ$_!`YE9IμR"Ir@0s1 -MY9[Sukl 6TTdwϗΗ%w73\tz -%6uGq&-0o&%<̠dx-YɃ!Y.L)uJ4m;dPfH1n`dy=Tc5ʚO@ʁ=4Bt݈ 1!h8$2('JѢxjπRn)(ada-)6vǎμT8&#yd#b`&m$uLhFƬ4waNVx @Yf>EDHxQ*Exѡn 0Z!߉g+-$I2}~b0cc/`b9&rCN0rL!rك)kffhf<&*C=سl8H':T|!rlC=C {1`$ @ {0HP=!Gb9Aʯ{q`n &5[g {p {ȑ؃j; ;>a0e̓sZ@mpf"f4P3{g!__O-$%G(f({3!^g608ȠCJ!e 6D|S8K;IG|wk {LOE)EP]S%ZSxuL.6)kuW^ʝCRʜA/ byvPz@w%HjEc$_DD3sfT e*SuU UbP[U"dUDMr]w]w߿˧||~7/UW߹s L>\(~S%Z_ 1U0꥟o sfM4xKuEJeDǸLס?P\]?'2̂| 42:O@$3̔d[$#љ"gB}?pXYX֟u( ;"ܭ`) џʀ}AA[诰$ש+L˘TT$i)c2\g-33mRF:"~vkϰ'n|g%]p6QQp1}3ͥ!~t]w]w]w{<a/^:/ODJ9%(7/X'w._0q^5?}lAdi~ײua#G}lY@o}$_RY%ѣU nho^-J@a[E&U+f+N$%UTX({vyE\}g%JsF}Sh?ʎ|QR1\Q%A,NT_E xERI=߉:U} FH{JüZ6.] /uSetH )OI'lFz~^%:(߃0O-ݣD'=Du֨y~hGJtY%m_ż}Xq {RQ˞mLJ|hPyhpyG0J %Ha/ -yh[tаuϬ섴QλQQPqepOrOiߨߕNQ~lZV 4lP°_rz7D4:^V$%WC۞kIY.{CS0Wy3qIhm<)e@>/)c[g1׹o]s[̃St˫7 hڅ,K<Q"">mP\_(PDhي](٨@9c QV=DPH#*|QRz_(ф)4);ƛmU\~ xbET2fLp=D(- p!$5VOȨHtxF~Ġ׊9> 9x_7dTG}_*.mk@h?Pnoig{ ǚ[*5q@ǯn  OrX??Y螆lj7iɉaL(r~<얁>PT{ˠ?sg9"q臲Xvx3FV.0lêي5VX/x yB(+Z׶38./P''WC8Na=S(}q~`_|w-{p^/4a6?/x,o G>^ᅾ.GXLk_o{e[mGy hΓ>7okϪo}|![f_E} c2\)8EFވ;T\c M$J@{z"c33'o&(d}6c}x(1֥g9MjY{C{==ClO:+М)eqK,dtIH{׫+g rkrY^R_&[BG{IWb3'NQCXSu.=bۍ1e0|yM &>^mn`w OWym-kؠ (e̥rEhO@yΖ+8^,*YuEʋOi²Z{7B{`{\3#|)l];`Vc[-ƛ%=,kNKѴ.uZh7n~ݍv]8}ՈĺÇq-C9fS}]y~-dV` uF}}"^,7A1mOh{"N %߄]DnA>Xc2տ}k^Zja??&hGoKj+eT.p.brj:nE%{eF![h(':q{,1<#<[l#oCo/m칔+/io/vomWqڮKmQǵx?OI“^~I^{cLҹ?I%gq mE/\dzy*7gE2EX?kT4<s!|#Mc[-P-7C1~Iq hs17ٷݷW%w!nyrۿژ`m|/͐6=z5ooMp$maۇmȧG!J x8K/r[ԏ3/!}`*2޶o(in(wO= [#ԋk{gm% p/Wz;ozxx~ kHJ~/gܠϗz_}C$yAmNdG/a]YR &п yE;hW.d3/cI6C>,`y)1yNX se.q޽u/+QJpA|(/=Tu޻eoH=)W#ڢ(,ʲ^x_,pV?"/1uL% .(;E !ֲ.~}ijkoD}'^}kF=@~A`չ_i#_q2c-G=οpox1{m{Q>3߇*S蜻)<3.f9ߋ}Ӈ=4ke|Ci]#~Av1'X|VOC~&:o'JKc̮݉֗%_MME~^?\8/;&)pdj ٚ8\[q<|4Pl=}VIJЖyƺG E9#jG=s -p2FFXN_LtqW~Cwh8<2LuY`+ $q"m~0Zغg["+Zvt]k_x?;S]q̊h~Niɒ,ioS6!8%Ǡl-i KS!k}'=(OxeHݮ~tv! uqLև סэVGxa.|7d+#W\d\E~d\8a|_^ 5p% e/70X9b;O^{L3ЏIdEkZZ|Shgl]xMa~uwI4l@ƽ?.ڏQF}\f}Fַd?`컺tgDƫ5$4ҽ0I(c476{/[{lFg)1]_k,7! ߫hUV5[EwgŕGpr=95(CY0TONX| X):T1f4H{߉^Ҋ:h|N '1~ - vgv 'ۯOE + j>sXT4v]ק`EIsO31Y]D[T_?~7qZ3L:#L=UNqvg6"p~<qS{wudjC{ +[Dgŝzި^Jηs^T[uC\x7^@fHy\ƱeӿSV"ԝwѶoj֖h-gR!PisO:Ou3m:TtȂ"& ZvֽcG5߀tJ۝mAƹ.N50VXԮS]V?XOvdX)] n8361AFݰ̓E.ڴQ_!;r>ZwNht Sr5 mF=d2]GqGV`{L\fh`zj0+/Mg1_Nt%MDq~%;Q~e{YP7c3ߺO|n*ө+ި#3@Fxat\5W0DIM3/[g7]ۤO_}i0_F[&&бsauDgΕl㙀>ԦMڄ2d]Gd8O<բ-7mb}!,i|xM͍[_;h˂1Zdeb;Aח~tׇ4őQ2v {3Ϻk&w_U3])`kdF0y>Vs0i{r~F}Ṅ{Y3ApsS`r>;ApD 8| {K!qqp#p˂0@ە>r&{n_ '纱.;۠ugGoQ"(9׀v-U+}d_`{/& };;;#LNo@_muuuW^z}j7K=}04~(y4_R!/pG,ȿr~;|aECڼé7w{S{o'm}{V]ѐUV?m^םr{v[koS{M{#5_ {n}{Ohly=s;oYWoMۻq,76x{~޾*~ a<.x{Oށc^Awaq6.emg߼3}/{|3;n XEDbsgqd{;g^0ڳ6h1șK 6M{aQLg9OB5OKsv4}W?[Am}\0c98=S?]0ar\x:ݰl?tfކm4>sC vEOۇmM 43j<>sb{i LOɟ@}R md?ݙhtLCNETc:]WcݜtLmЙn4CZK[Ԥ-J;ۈn9*% ؅Q4Od $W;| )m櫺 296Bp拢>lؔ<컒D[B]hKOr(g~&yn^ߦfc_+c_YiIz;pOKRoO|\fF@5ѕ(&6 |ɅZ}C} M'Cζ-۴S :o}Blg.X7G[ț,wɭ~p̲f羨P|x7% ^aP^ϕ׽^qFuW@P3[9zr&7S<&4;ϵcsxwGF.=sd~4[ݖV_Ѿ#8O<ŇkNy_9 ѥlB\y q.Q=7q X%¯Ii'q >]Ovyh ˅x,m͆|Ct[[|qQ"ž$=lޥB}w"wh_:퇔qnJ~ӥ=]+5_A{`o6c[@ma>7އ4it(zp ḿ13uV_o$_ =F@2X/Ix3JXl rmK >aweE]8Der `펥o=4w4cߵ٩?]s8ڭJ< {S< ğ)Q/k$_Ca`ħ&>() CaF~X[۬j/K>^otN w]%ئL(@due;MYԓ7ncx. 09m﮳q >"iǜt]KϦ~|*|Ot/JߢYƯVɾh 0k\6:K<5DhaW| .Jx:?χ0Ik:&?2@Z,[6]J|,HӦޚxy&kFvoL}5y١?ֻՍߎ)/W?UG秅O?3Md箝\Ni峤DKJgKoY2jpmu~{ݯ>5-^1Y;.#[߁_B=r6\a%~^wR?v|"LF|}'kx_/tu|Ơkw4v?u};ei|R/QX#X-aK8bMqKS|KVO=i>d65:bsq$kބm]1sfMt=NwEe[y|Bj%Dغnuߚ'OTLxp`?)?9KqE-Pj^Q`jM{zjhG\|)ovx5μ#nel̚wh.t,m9c@%Bu[goK_m>*d!tB#뾁Aow2B ܾшS6/>sp;J(Ϭ_ag~{d`]$}٦`;av._Cn(p'oV]m-g[5R߉6w(s.zV߸Szf7gUH1{|pJׯku=^Lկnij,{ȶI*qպnsJ Vfomb/WwVJ}a_[reY=~aMeMrDC?=?[Hmլ;5ſx cO}]z`,jx?'g 5L6$|՝MG0,;\9=_5 K!|Gh%W]':J|5&B^)#6\,45`X>_~5lIlםq9cG618ژmڶ8džKV_οr[lpo 8g|M0ӂشԠI^ƻ0..&WVx{*=w/_9-cg'rDVv ^ؔ*5߬ߋz(j)/܀Ez>tDwu3ہbL9[p"  y=6b{YKlthDq6vFm K^$^lmM䅒~bv^nz_^ϭ@coh>5Yy-Dc/e)Q{C?4ϡ耋݃D_s^:a,֣07z)߇."p cl;w?Ru1$XB* bƙU{J~MƸVHbycn3dUwqrk_{}_$/wn ;Q MV(i/*HFLnZ"qGSx {ra658/P>enG6g>K +u~- Ygyw eeV"yzn<;oS\r9As^uZ-FM6w*}xF:@$r&r.eyn.l?Q^n봏Yx[Po : =-RhgF$eZv*{3 NF5*ʝ@N+嘟;};ӞVw]ĺ:shv_`OAguؖ~wfQky~?QQq|7=!=X<"CX]Y]ڇ<Ie(8mbS׽t'umeB^0x}b<~h|)2ƎJW=o}K?x]xc7WǶ۾t/x%%L>|Sq :{ޯ_y;Ƶx''G_ 7cyGFF﷍|HPx|ޱR97ҽ?%U{c|r<Ƚ1JĠJ?/k\3g#gI \ZJ׫6qDuFbalݎy1uV~~[L:Q9J;ZOXx6:`wq@_gaC`Wmށmv]DE8|w SaUkXU;)*П$2MY[}n6軁EYȷDؿoI,;[ă32w,AQ{p{+(_¥(aIs\IecлQ0GpF{[QD{A>E99>t^AYc/ uw'[_eOo;gtzod=Gcfc*󼏼,Nt-ѱ; ?V`-6G3 FtNA=h,x9'{6pEu~Z7QP\{.5/4\\99mp2\ WlU>˟gť^`2(u!"s\37ZpE0\):߭E6뫃!BYqupE1\!zs\Emp.RBul[Wj+f+ru4J濆[ϥߚu_~e6Ba!F>Kh/8?\'-ؐA:Ù",omp7D\рk.ǵbrC&՝B\I W% !תl+~k õ D\z:hk gBղƭ>uZϊr+5=vW06mp./ 3q,Շ*7k ZqfץM&\!8v2\i&\jh>lv6 nj=*:W"._qW1՟".jḞWÕEq}54\\\oU՗_uqˆpM~"4\GZ/ur^&񆈧T^jmӗX4+:D\ߟ݊+:[k"jp%1\!ܪOXg͵)י澀KX ? q p /Jq}~[Y-V\o>P?f CV\hq-09_2߄+06ȸNZ6&Cn%Oϧp QnElp-0 Q_ oq2!߁ka:+D;~6n:"w:,:m_%v]Uh7D\ ḦN2CõpMpZq҄+=D\Y+\ekGB<"\Xj9m>m2+p W WLh@Gۊs28jIS1c']9'g,~+jxHEy xx-xW2x!U9V$x(mWql7P8(gAyw ˛F ți#o_;VȫҼ7w7~w]yWAU,%F4țo#ofyy?:9v=wず|J.rĭxah濫`UqGlEs&Ś%*wHyȭf-l'R'\,> oK34rcUz+C%Rva%h/נjTS㮴)F|a?}>XC:r$G%FL/&|3 4Ics|bk^/`[Pl=b|cd7'gtgZ֞bO{kx?g%ޙ-GNEh}zgE {f`ИGriްzX(/,yy*} A { :Syxy:y,i=/{XQ[-Vvz[jཱ+ 0 `$vEb#g{r(6ES&*q`}ÒG5Ħt]ϭaԂf(m`G~:h?Y폅o̾1|2x ށn}3Nff|i!qd|:WMeN0{6R[XJò=]0FA?D7 :o9RO.a|Eݎ cU-g{7;][Ms|~%2_9ny&:EO/| mwAS{c"9#\n⻟K|a4{U-DL\{S[H9Jdėe] P~ H'S?C+<) q?(s`ԟư8͆I?*[X~(shh]kzoPn8$[i}l]5&H |Ƣ$.VM/5-!LGQu}d}B?m3brDž m4/mVuM}vi#T"a{3G̣(wT(7/é?@jQ ~?t(N3mĻN+Ob8Y,;G9=c}M81>9wez5W?懆w&CsjN=q5 s:5PtE׆Yw6(58|#sc:o0/;B&w4x"){rwEp.a!xyɌd`Z\\ñ6swDYsa,>Bx23߫?H/n|/&CyYlo)ﴲ5%_~ ׶UZb\Rdy\w'4@ ,W~bc鬭^ok+̀9u\=F%'iY,wng6-;e3q6%xES\j<{sU|μϙy{3o9>g|μs|μ̦U@H/ 2Y.6Blַ:0#n5NK6`y)ꁆUMmǾB"4xg~hڴӴi+iS iS3i*NӦ3M8MibhpC[R KiEB;i7iv7pm?i}niin{%|N~oj`Rv4CncնOguQ'$_VǰcrS1<&m1e;74HK0neXvcLJc4i-lNm9Ͷ4ۖi-lk f"8Ͷ4[ ^]&.X\i/i8Y09^!-|-ݎvqMki,Ƣ^=Ʊ RX2qKN-*$N-[28s:6t%ӱNG{+Xo@z 7!l8"o#s.~_LeW d! +k@Ch'gIëK8 Q/ӰiX4O44_iX߇Ӱ>Ӱ>ӰL0%gzxA/C찛)R?.'6xXOw }4 it_l{\cJdlߏwϵߤ|8//2`|x?X@}}vߤ?R~ʤ?oRG6?:S[h$ x3klnSƑ;h1$g>aw9ф?LsW;M6\sC?|,N9p>>Ő@蟒k?%ckpypֵ 2"8\? mω=϶}Ǫ#e wUh>Akj/%kA[#^zkA{Iz/g/}S>e>uӗZL5SG[txfxW*Rnj']\9]^sty1*Ns9]^\N9]^b ˋI.ϝJ <\[M./t.vzgߛLOxkiTwӨhiTi:hu4Q*NFuG98?|.5 h+ O=SnNO/?'j1P:sݳnK86Gyd# |sdfCfsDsv]sdLxG |H]'>j$mP7y1p0O[T ,k=O On4n4^i^}Ӽzyu+yNy8ͫwpW/iț2ӽyW(}L${rwnk)Ͻz|[a0o,v|*Ӳ*SU}9-;iYiYqZ>i9N*&~k74{\Y׎ εg3:>:;A>(Frv2:'Mo7OksN,8ω|N'cewω|rA7ik6J&nN諠\*wKn Z ݯׂ|T RA[Yqtkei2\L y"CȠk >Vx.5w3o8=鵧OPznYq蹤hN8=sz.$sI⽜[9=Drz.93=|=/2zL"3όt}&Ngp.LNgp.jtU *m=Xu[õõ` &D0\ k}pmB0ִrBcvbin4~JFw$7߾qyLIt`2L&с$:0D&dLIt`2c{LZo֜d29k> bԏ /Woӱۚ"1~Yo;^kjRp|_+_*jp|-Wnq|F250PWYWx'b89?$ZZ .}yJ~Nuڵ f^tKQJعυ;W^mW p} \_=' jZ ւZe?W^ㄅGouNfqݫUv_-2<,糅6)8VFJ[~+coE#෢I/+g|isUY$^|H1-Zǚelysmޮo[MB~s&˙|YuvxfgZz[,׫hb؎Wsӌ{Ő 6Bů_6Br P\!*!*}mȅJ?cȅJ߯_K+u?5=ADӽ"TE'݉97ŸQH!3{eڵ! 5w:ߢkw˟wCf|q[G%Ԯ GUQq8Mȟc><%ฤǗqI%8/y|p\RKqIr.Nu N.RXI^W}eM<.zWZ1_"AGLOŶKh-xݷ E!cpGj䳗slVG6DVM냤RqRd)̯1Vs Cǘpj. y6ڿ=b5`/.`0_E` JD[?D1x1ccGs ]i]x]Tl"0C510yl_ Ts N!C=js~Oڇڜc PqM ɛj؆]UF1Og <|E`h/EU`(Ճ:_ r~Q!/JQ}=GzI?#^iV.,[[~࿢[~/4~LT 4̶LΗkT9arl6[cU.#`ɜ'Mim j磙qqrl%9[I΅$'V`+Ʌd[IN$!>^l>>N8aZcSX|dq[RXus2XbM+jʝcldY=`qM%>Y%>Y%>Y%>Y%>Y2)K_%⿿Dqna6*QNdvp+6tu 6ꪸ>b߮kՈ񥾯W^s`9zZ޿klnX= FO73`t= F`t3=FOoB.fV]9{߮Otܿ}5ozS>-޽x1#)c,ӱ%ʺ<o1;?y"]NG~B3.l_d}zd;O^847FT~Sc/zT_m?zܼ?mG<CG춑S;OS+}דp 4mZ-o_u\\6EnSRmJ4M)6%ܦ۔@pnSmJi_d&Lx&kuS5AiAG`1`SOAL5<OxAL!ԀAL#H^w`̀i{퍘Z;cϚ*rXF8y:MǙ>%@<ϢXohzD`Nhր_'E_tE{zXJTI޿`TϷow>jyAb i}U"oȳZD9m{;Y#|{[搓{T{Ʒ?{9<}m|^I_Og_Fٗj!X<^<3u><%οƓ'O?o,s ØΧnCW3NuPOwK]U}7E6yx q5 ƅ8W ^+ƹ8o/uy^ux*+Iuʖ$Io_ZN-W6/;gWx2xGWx&xGWxxWxxǂWx"x*_2y]PuG  G3v;2 )f7Sd" ߾ qƒ,Kjǒ,KjǒQD?^B&sB\oA؝؝Xhvr=hdE}+rhFT,g8Nb+XeX X˰Zc|2lXՁeX3XoFu4J}n6`CtϡⳋժS̆5 *44 T[Ԁe(}Sem&֛(gq&MP0%MG(%MP1N~ {dmgoO ȳG|a^I596V;L8Rȑ:@r#uH G9Rȑ:@OyDJA?9gbF7x)L̺Dd&r&C 1/}HOÎ^slOn goo:O8!$!1pCH Bb8!Sc1$ǽ[Pz,{ w1^.lԌQGmT Q6*F5ۨzpnmTJ1湍Jᄁ{oս:XvXՁ]g:YA_] oF:PL*~p`5FՈVV#jDXV#jD5Xns:P]a ‚y]ޭ`guN^,U')Bݪq`}yGUj.6Hۧء'o8 Nj&h8 Nj&h8 Nj):^{?޼8[;ʺ<6s&a<Oٶ..տuQ ⟧H RϞbD1>erwdR>|B8 oozW/CF1b9w?F]#> SO;.x P`@"?`+?&+Xq-Ll0Cym~#zʷm6tD/Coh6 ME74Vr~Coh& V1!ם%~=]f]Dߕ~ }S}8zz3Gv,R}[?%o7{ ^p/yK%o7{SF1MO9$ ~I;_aNCK{XKU1~;jwj ?ϯϯJoD"a/=]i] x _ 'c~R97/6C^&9/ !C`y$݇vC}H~o!>$݇D$5k2\+U*;Ւ7,Pޛh?&OSoԛh?&OSoԛh?&OӔ1&^l|TJ.L8{S ߰yiq6c ^~\zK/wp .g=^>ҳ=uOƥO=rGs5J[.xhxxGxxH#QY sy]AL'簺EmX'>FC6Y]=[&=GLzԃ`ңLz4IV0Q]>ɼ207Y+ݙ`?uQ-ie&aɨcҝz\owNrI;v'$۝z\ow\j^LnR^˜N?:)yң'#mfuAw{;Aw{Gx݋I1q7k&!6ʽv w#:݈w7ݍ|w#:݈w7ݍ|w#:݈w7#b̼X?O22܁]f]"y]-M1?"kHwݫD|gnHqz-a8"_E*rWU"|s*cd1*Nd.}\}>p|s-8Ws!87s8Vqy8WsrKu -<`7{..E.Yf~^PwEc(Y?/TSZ0sg=n=IP ґ>(ґ>(ґ>(ґ>(ґ>(]M0sxǞ*ח-lzO4Mc{bAO'+?aOd]b{Uy+ 0MxFϳr?.i;q< ߈oD7рh@F4~#P ߈oFPq ye%==r?=6٣ˬK5Om`HL\&/Ĺz;*2ܾka=v8[} ۷mVρ[G[ܾzWpo=}[ ǝ7~g7sƵk@:[%1f6Q{Hf4{TX~^*8`5nBIW.l?_yV ܷ!suWW~uWwj..꒰7suic{4u9#7Um:{q{'0̾o<0̾Z0 ̾ޣsuwYs_q‘o 2q?f{n.Vy9jMΝixlG}6Y/p< gl"8Mp<gU`Me..n 7O>6|:V%cvC4ms;Ҍ7 v  SO|`y]V,pr}U_ժK8N( cVOOC;K5g=${Iw;) N mKM3Q3a3>3q33ɰ3I7`3g`3ga3۔6^Ij95opBXYu7Y17aĜ6ץZ~ WyY/Ni޼MNy)wnS~] ާ8> GO9wuOq.!FW2E6W]r}oOd*s2|N|9>'d2 VF[hp2i:~+3>8m} kKh<'_\AUVekU%:@DS):ڨ܏y݈a78OuuS{M=c~>jOMf??.3~S'fW> OpD x'InO?p= ?}y?uvO=S TtOu3G8G{C$>DC$>DC$>DC$>DC$>DCa fMmSh?U0^(?9$v-w1c?zѷF:|CC)̮/ u?:X~`,;X~+``?¼?: g;G]|h_n9_ /aQy7[ypjp;nkʋ|" ,3M珺̺VM˷WM˷W>}|9?S/OX|9?ˣҙ{4~&1' ,"'g:nO5}Y-ؗ}Y+ؗ}YؗU}Y5ؗ*SX~C]e]yb{bk=ylO`{B=I'bO{=1i>c>s}5^e2y]C5`/`0߯E` 8rNȗܐjo=WJnwXQA&qn<[I }T[I+s*p)3ݝ 0ݝw]sgc;Y#dlb|vr¦ 6Ƿ8/j#:N,-f8?v"]EkEjvڮ]v"]HmWQmש$^}ֲ*儷>o;P60sDxSs'lCsڛ 59jǓ"hžqI-%-V^P4ZdT^w;I;I;I;I;I;LŻ]n?*W\XqmnA8y]!A̺D~R7[FaS6ʱRC&vVW]Nma8q2^y4}ޡ?X Ĩ;çvͭ="ӜHV?g٦cl ?+$ B2?+$ B2dY!VH?;)6=_͜yg62CL=̷|{oO`= ̷vW0|;.ޑ={\I Q-Rzp~9k޻V5{` VUU7X{{`^5XWV*ڧ, 8aMtb6@zЎOGmZ%OwIӻ]R.z?m&OwIfRtB&1럺1ZIs^{1[OHmO#=y'ڞ',sӶ/`0d!?Xq\.+4Sc+|N6{=u:y@^'a D 9Wl:W+Qkrz!r* '9gX%.򂯻[ n2SS]쯼eT=*?]zWլ'z5^zWլ'z5'z5^zW6,絿^| NHg>rfߏ~;|i=5$m|ޗ{o9:&F=?B Fѯ|'\zmպXnj֒9GkɜdZ2h-s9ZK%s֒9Gkɜ`/cm}%v]b x5CTmJ;9!5V Fm}Dpu]>8]] v+nu>ح`V7 []vKnu_8kkYl~Wgpb_ߍj]گŒfhѲQ'\t]nKJ3GcaӫkMش,~Xz5}EܿJ?׹9ZMz'vXvN?4`s04W8 5Y`)sM%k^Y bSzo4?f{633h8c,Ϡ)SAf_f>֓wI5>BBM\ 0\0 ՃaF fR0\ZpY9~k(Vħ2 ;O&3_V >GEb&|>}Os2jO+X8WO׃_}? rOoNR٧i[mOq6nͧq޼FTiwPu^e{iv&>--Ӗr`4 H0\Kpi,K=p/3xwn-Osѧ-䄂RӺ I1ḩR/5rO{D{2zD~ Ԍֽ}J'*Қfxu,ә\eȫnoGOȞ餟 c vZBMfC-p%6ZJ Rx†xR`CZPьۂ|^[Qm oy0˳KK͕j-̇h*'좳*.mq:Y\E(p\ā"iq'8. EQȨB^e=߷ܕ֥Jz*N>&o`uZߏ6]jogM1ve_~/a%s\>υsY\&> Esx^UOr>swuLpv?5yO)ڻd1fuU1s;_ธ5)8)4`0N$_0Nx0N"b,uMƬonp\%'|̚ O1]3eqycS'U%w۶\eO cl~Oj/( svΏ `;?lsEvjF~XF?ߏR'Dl?ζ~me۟l?lnM۹Q`;7lF< svn,M۹`f@m?F-O{~X ?7D0f-nپXrOΤOD?7k9.֟):;\ggXpuv ΎI:;HӾQk?,ς03yq~WYO^{,gr¤Xրӂ8!<>hgZ, _vұti 嚠}L j-|nk.6iu;0N_gc h8|x>Fj<'5I1R|x>Fj<_k2Ztjo-x ` `W_v|<`vB(\v 岻$e"\ڥy+lMlŖrc%J_UUѦcZ^*u x S-<ՙ੮Ou5xScs;ư: " k=`Va7*,J*Xe)}c29=[Lχ-*и|f0nL#c7߁OIh0M&OIh0z?&OWb/[kKOdF>\ ~rv S?;ҿ*I}-2SMZe(V9v20Yu 1 00 80 d0 MSih0rmJKxmMLp5bL+/W}xwXL[L?uccLmzH=C!Rg~3?D"zH=CJ*o> YmǞ.Nf>SI#a8]H9؅]H؅T]H6؅ԁ]H-؅$]H؅]Hr?k,0C8aP6%ǣӱka?̾i:ۏGbS/\ĘβULG5Z0U ZtT9i+Q`:LG*b{-Q90q}oۏ5EfLe Jve~`_p q7FHF:(7Fz߈:/3Q >L^y|-`#-"<翜s1nO]u DzڂHO[i "=mA- DzڂmB<H 9$gl|0VIdnYejtf,xf8GLpIGpIGRp)GHpIG 8gEP=F{wY,^zynlN}=.E}0Nu}hQb'C1,Lp,dZPr@6XI8w=x}>D ,e]j }#¶ce0mznmf|OrI?'$w۟nmOrIoM/+O o8I~_9F)*n[`s9NduevwaY8vΫ켪yՀ]?_v^E`U v^M`G..jvYfU N۝.2;mewiF-tG?ױo$ ^7z_D~w__~w>)$lsamWƀO9SFSx]Xw!}HsR܇?!}HsR܇?!}Hs0YAZ\^g= ^A`zE]//vg3^`׳EF^.Gk"Dv+M 7Cz9Cz>_%S{/4ޑCΎA=v ڎDуh;zmGA=G_L2M&}y~_Y1MH33:{]6^jJ 4Þ0ā     V^'gC[%)JV{ݥ8"%X,T){6rCNÝ;u'NɼSw2ԝ;Nv'NɼSw2ԝ;uQr~,צ9l&4=~a3O*޲Y!ʴƍ30#V9}tFt :@݈Dԍu#:@݈P ~tn - ^q۸+yE>Wa}6?q<;7 F4܈&r#@nDȍhM 7 F4<{O)85Ԧ',k`tM9y8_5SDݬnr,4B* H,"B* H,"B* 'Zޏ*1"@؏7;hv؏sڛ-G9.S =&s]s4Kгv/ r/:r./rn/ r./Rc[)vBzюܜ8ax4ӳ6'ZLzqs'\.0Y]6 zvU e_ռ(s_ &yߖ=L3m`\f8+`{`ٰ*C{졽^{hO= a)EJ{X=Wq°pfg='s¥J{( {{nljV{#k̿sg߹ `픪9(ǽskhihihihihigtd ]vrΕkMsg=޹ʺLIkniߚ ~ goH\{or}:yl:#5OV_F齡M`{w^mlGa:ɏ_'$?~ǯuN%$?~kD'N.m>F}xNww.mv>4kܡoԇ7hmRYp&lJ\hg+(N`K&wz-U+!|o2}7x~gH>0╣~9o/苶 /z E&0_@H=n1^Kc}!}>_H/ B!}>_yc_[vuQڙcLǾuF]ވײwE?e웱 {Bs}ot< ZgCcہp?\ p?4 Dh$A56rIY!yFã^l8ƨW1>D0>OK=`}8} Fg`4<<9Dc@0Zߡ kR2JYnQc cq|z0j2̷̷̷߮k |2߮!gԐ s&Ѻx4w\d?6S'ÁDz\Oq=?zm2kyBҷ78ḬK׾jUjŃY>dMՔ>dpev 򝷻/{;Oɝ<'w{yϓ;yr=Oɝ|޼1ٝユ>݌Ij3v دvthss}8XW~uGZ4WW~uW'774i!c6EnqWYYݞ71_r^\Ss฼7d7dd7dd7d7ddd7dUk'wq=]f]Rxi3M叴>Kʼ+ȿ.`{veIm[=s鍖{V\l|9T_j Jz*U/-|U gŋxvYI:ĸ>~Q"qu*v3$wΐ;Cr~gH &93$wN+ڈsj8BEor>%_|1$tvMۯ8=Gr5D^Ctkx !:5D!~儃r#Wq[0ӼLt& u/%Ex}Q ^_E=x}^_EE*x N(eF g;kٛvu NWu#x&: ^ +o#MWJYy^J6k _`n:N&X: ^'S$^':u2 NFI/Se^'=9cnEv4O??A{N9A{N9A{ND|NH?FvmUf]wU*9{"}*ZE^?'}IB>/}rCh|9+' mkǓxu|{Q,FiY*F==j=={: {:{:T {:{:{:T^;boBFuu)嵁,FiQQ]f]Zx鍚|ʴ9 ?0T`Co㶝7 3OKx?ӽQ3Eb(ZֿX|o>s9QT@M=r :~ :~ :ԈzڡRu(~ KV@Dr?uM2}"L[ՉNճN?;(hkީ>&>&>&>&>&>&>&>&>&>&>放g3/Dm`N&]xn&ˏèf]=2lƮqؕU]Y9ؕ]Y-ؕ]Y3ؕ5]Yؕ]Y ؕe(y{?24NؚOMwj;0̞HfjO}_)G$WJ_)G$}D5{ ?|Ï;{32_?ḷ$A<~O Id?Ƀ'y$Anay}x};qJ $JJ2JWI,xh$J2XNx7vW7ΈUE3N8/~x>l+i+8}XN{i8}XNVӇa!8u+0?cѼ*O+hr3r V2z:oScT Fő`TF `TFũ`TFN`TFa`T FnNcsg2ң;:S9no&fno&fnoN#UyF9a FjxZgbmhO5cTkkvѮ:07?hW)j]`\3U o3F6Dm9b=~X9( T* L* (rP o1^w]Ȑd+TQiF2ӡ@}@}@}@}@}@}@}@}@}F$)EThNA"'nvlZd.1SCmsW2}h7睅9DhB_H4wDhB_~s.e}xsV~s9 Gn]tf?rj|Z總rm'gvr}r}ٷ}ٷ\Wrx흒9-wSߪɐ2Bl!~s?[ȹ-rg 9Bl!~s?W{ȥޒ%3??{)*^ʖ ?`a;oYğ]m&_9I$L$+'_9IWä/;_9/*Ba?}Z"#O_9:u7Hs q$y9o8 N7Hs}"ˢaU wq\ -:֕8ۼFUy:^HM&RuDn"_7HM&RJrϼ6ʼnkG_R/]^>paHHHHHFR5R5R5R5R_mݏi"S<Fnckǜ;}e-Pe{^wwwwwwwHݻWIݻWIݻ 7CslCh+54{q<{H˪5gmvEK55'"e$/#H_F2e$/#yٷ>K4Bk*ח%;7碪vʰ$gXp:'9pIz'9pIz'9pms[+yޛ dž8O%wg%O?vuAh2*օ!R!uȨ:dTz ZDs|Kw"avZ{/crFj-wNsukk jm6Z֖"jmZ[ֆ"jmZf9]dѳsor<댵\K$?\=z<3zUZO^q`/Dp_fu?!۞ -?q<:\w—\ _BrKH.| Ʌ/!%$—\e|X%e^<g%Cs;e9*xeso7h܋\9gZ=?hCuFoM~/󋵎w$>\M$>\M$>\M$>\M$>\MժS's0^4uFoߜqFm'fLg*Yqo}%N9#=_匞*Cy/ODwtǫؙVǻ*+?7s{"%;m;+>c9'Ԅ\EjB"5!_$5!W//HMU&*Rr&0IR }`ߝsnc|Inc/Wm}E{żO$sLKjE"^gSn1o3?G7x?8t}eg{qsJ?*ݏV@CZc{ъ r r r rYz{$ iF= um%822};dx?~3LM忐mgVkle'V=w=ܞ"皐s2r{=׌NuWWa\hH}ו|ޯUUKZ)սكS^*rʋFNyz䔧CNye)9 8䔗H뿚V+D^Zn@F{d>u }2MjyZ^FV˫6dZ ujY,O- 8&#/'P>]aSn Tu}jYrZ惜rڐӲ`ӎiӲHs 9\GN˼SNrjufVasL4<jc~&)#T>GJ'MYa09pdq/C+>:8Z<Ӑgv"gv6̎D3ۀ<}gyf#lrӝzv&ighx\ X>I6ZUω}O3x⤎\T!%ejI6ZY-EVK"% jIS68zͯGn!%m~#r_FnO!im~r_g 4ѐPʹ5Ǎm}ّld}9W ~) 9"@v 9)= 9DNim)]@NiG%?YN;(]ܚvy~Gs͉-\=cG 9:qB_{kϒqY2=KƵgɸ,מ%<2=KƵgɸ,}k`1'@i]}=pز}9sgYZ2-3w`bd67 ͭBfs3:d6͍Ffs\=2w+$D+ssEe i}=y{B?W,%.gϫAqps9cq689فqu89ΩEs"9%Ko+}5u;%M?21 d7="=yq4]S<߼_s{_'N)-7"ߔ6R|g{"M)E)7M)A$[ճWGNncYfuudq Aݟ-i <7h-Ky$J5^G|.ƚu :\g#Yב7r} ڈ\gDr⻏fm 8#9J|ݣsgk?짞8 ~*9%#do䖬Cn>-9%'"_"d_~M|FSݜYu~]Çl>Dz? :œO]`eS;a.+O%E#פ0\krMJEI)5 &#פ`؎~VSO% ?q &8w.~?rYf#oF73܆f "򛙊ff#O^aHq%Z?h_EWQBDH%#jp^\UWb鯾DURG@׭$!2MHE 4! &"ӄ4AL2iB>2MdZW! Q:>k5h5 4_ si<~[;7g~36r~ ~ӚߴoZvMA~ڑߴ67MmC~4%C%y_QECd o=p8K~_Vo) 7MD~-ojoE~SÐP߀/!x䷻^/A4<{?7s~_uZΙϕAo4?1p$ )qJ r!)qJr" )qJ$riɱ6Axb4%6 Qz`zٍg4gpgQNltٯLwAڟm y}l>.UrF\m,aKm,aKm,`(ɻ,Rs?gG;qU};S6B8PBWz݃?Y>p3&$2D?gL"Id3&$$v< ?c{~nWO~NseAMsR{R1U=c!ǘzScN!ǘ s 9ƴ"ǘ\ScjcmzI6 :h;M8?5il=؉͜BG.E nYd& Pd&# nud7Ǿ%N !P ~]>v#||O69nM۴gf8O"'}">|GO!ߨ /:k7I3=بӢ!ؓ3޾S~7x?m:<;{4]N;;ђ3>ZrGKh-9%g|䌏ђ3>Z?Kkmu=Ki}E(;kN8:zKv{f٥;Z oCvˑ|d7ٍ#ǐFd7>ٍ7 en|%a~I=(pv#9Ֆqhgݳ].$#Hr?G##Hr?dW$5{fY.gŜ.??i}?%}ؾzLW 6 #a$5䷆0:䷎%a$5䷆0rPXh+2_GF[]9pʶK|b2:^Q2?JG(%cd %cdI 2 g~ce߲W|p´ak[56G}z{vשHd<ͷ1L҅1d;ǐ2Ccw !1d;YS!ɛMOשw2-.􍾸6;ߠw&9}ӟ$? :~1us4MF=d`4MF=d`4MFGct{:Z+)o AAMoTz:%5&v:WBBƿ22=Bƿ2/!eblI޴Na!,b[j;UW9УT-ЯyZ!*6Gd`/umP.ZChۇѶD>}H}mP"$Rd7N|]s&BGHK;>}0m?Dh3жmG\G>}0 m;BFۮ#lZKNq3lHɺ~ Y$d]YAɺ~ Y$hVI.=b۶#ζ&Z\g$Nb$Nb$Nb$Nb$Nbh۵6ӶmR{g@Ҷq>@bHL 1${tn 1$FbXmgl;Ҷ*6l 5H͆R!l 5H͆R!l 5JжkR%dmێm#]ghжǣm/Aޟm{9`hжmK$Im;ZV'v(e>2Bj%̇ZCI-󡤖R|e>2Ҍ$a۶ζXvN#ֿmmߌ/Gm[жh[ .fI.maoZvN`;&{ɞ`;&{ɞ`;GWIK1mֿҶ{\gH^}$s뼏u"yH^ 9u"yж/eK m;\KNp/g֏5?GtȚY#k:~dMǏ5?N klvHa-zش\Ӛb_Vg45'B*[{Rǀ˅-4D }Xnq*LM=C|7قy (5BTE={2P#ġF)F5rF=A{P#F F%qYƶF lk z@kUyR#$Bx΢FbGc'h'5CF|RQ#>\ԈO6j5ѢF|0Ȓ?R#5GHGX}(k$o1jz=׭5/ 5R5R#>aV#_td?/*u̱pB$nu7Ɂ@Mr&9Pw$nu7Ɂbʡ0Xch@QU 1ej`[[>eԋsm-i.ף|uGjM1zR[϶˹~:uںjF]5ZP[wmCmuuW;j.jP[w^[wDm\[gmu&;Z[7vlܝ[ Gmݝk-OVU{ke}k[˵Ui_0j+Ͽ{m  Ѡ?-oqw7S%P[7yFk~8nZ3`VK:8zugjNr;xw;Ԣ$wA܁w'&ru1Ц܁Ug[[{lkAkmg\[YVRx=j_ /[j뽶rT~ug1qӖAԖg-j˳ ل܈lAmyEmyfx|'oڼ,n}Ң`K̲? j4u]Av:jAv:jAvp[xס O}d$)ƶ/1ֶ/zWڼݨ5!^Z}wїe;nxZ_X#A[RG?t-cj˨Cm -cj˸e,CmQ[xԖ1e Bme`  㸶u`,7n[[[kXk+5 _)ڣP[kc,eLZmkxkki?j7j%֍ 6֍H uuuujkA@ں(ƵZZvҶ)-cN/rm@mK-cGm\V[9*zgmm0a m}׀njRwP[ߕ;4j wQ[ՠ==@G ᑡ`ԍr8ʶvnZk[~43P;7RJ)VjYUk皎kghn@gvP;DN{jiOFgvP;|7%DO]_ڹdsMb YP;Kkڹ)^lF;{u3J>>?"tW}ï΍~|d]|4~+?GnҐgOs su΋Z!E ?^{+7Mrw~?<>ծ0͹"=0vdp~Wdž{Ax+#lTGA~4o Wۉֿ ΍-x⟾,hYxLJ3u<36ƽ {{es ?l✹?:akxޗrw ^^v_u#-u >l޷rU=Ӗ,dpw W}t#E{mz_>]p3^ZNS%hX;1+ `פOOF@Ha5cT?*y  e@kzZq}{,V}bnA#A*6_|~WE`>_2?]#>Rn|,}>gA& g>ZޥL~t"ɉ蟆}^ySzü}w^^Њ}җ~D+od0ͳݢa~3lve:Я`v` ]9Ne[ IVL~|`S_\q}[>|fNSjOоTOlg@+3~gD>Ggll\˙w&|<7 ek+/> ^m}',|Y!7 FL>pFϦP83&_|a^'}63mPI ,V ^s@y Ιlykk5#U W z]N|N3.rgGg;-Vߣ^,ɍIVxxعqGv$ϼ'm8Sς>g=߫jx&ϡW\̙RfUmzf8|͂!gQA?6PV*OG k{̆V=Y0>l |&oWA?{שBlښB[ <5Ry8Up~?@0U,3X>dzsf?I>cmI=ޓ Wt\N m˪0hK6z.S1DhXUbF_AIN`g(i:Tmvs:hɟ/ 6۪j; myT/ |l, y*,{*mX<#;Mu<_({'@?-_z rUO[O3vzKJvU?b]rUO6]s*C_yӡMm3ڠM; m/ڠMgX~\}jfhgXӲm>VT|B{_ Mw,U[@[EڠMg5U>pzzy?Up9˪6Խ3POZ?snUs*eTm-sYeNLѰ2[gH,Fm#S@j[~)v+ UbqP5-m}Z?݅HS_TK/GgƿZhi2URnM5-OcLlcLl\2Юz0W{Ta7Sh dl;'*/]M:l P290zn mW2T2Tmk O}P6hxR_3TD*v,P.6-go6o60gQlOG[aL؏b.u'|s @UO/11r`)>h*3'⟘1#-o:FNXsV<TO2x}x8̱jϩ^nbQoN>]sEX~sk |jρ"z Qjρ?߄\GaΗA4U_s \Gi /seЖu/!e  M6B@n4/OPާ~Zh;#Jٞsl?SJ=$xI!N({s-M:?cVB;ޜW!{KW~dL}Z'?'qd),}O~R'qiߏ$z\~L\{iOSav+{՗Sba_@<3~zmW3u9{v֥T'L'L'L'L'L'L'L'L'L'L'LS-B],<6lظ7%(Qca˧"}0W4y•z(ϙ1)N42էiRW${p x\f`d:22ԓ3_m6"&dl|mS mJF_BM~ȶ)~܆yJ3 ^X{c> tSD|ssWpGh?B$?2?ң?A(m/OE厩h=JEʝB/LwgצK}ryG4ԉ,~nZC]PbzvnZ~k4%U8 u{P}r3 xa_ӽ ׇZa0y}>B^f daPD^ IQaR;Hg |'}{t$9YR9ArY>:;Pxg;y8a6:h|p]}&U&9> ]N.>۟FV {VWݷ&v:365&Ϫ:;6{hd ~_b9^呝B/}aF5ζ,aۛjH.TsEm=jGySQmŤ4STRJ>[iOcZH2ǨB|@;!wC}-[r;nwݢE!wC}'?vUЃ W~결_cw<);RV:$hTgk7[A~S='E£qdDXl{OTI4! א{kȽ5^rp W+\C! א{kJXx=jRHO8:#s {\ML1li |(Aqy0v?Kul.Ұ}(o@6뷇~ cxk5Ţa>}_g^d1|Ռm+toj' 7(c ~AW>v4 UǐcU=r*GUcUrZcU.rBUȱ*9$M{Еl#u[أFĮh s$^MK.A_oFP+~S.0QvcdmFBtQϧE5]X}[|t s ¬0 * ¬0 * ¬0 F|5>R`yL+O%ٯ:;wt~[+yI%ij~6C׽W@__v|aٱ~p~:I,vSނ|")oD>}fS^|ʏ!ȧ {w߯}׾7-_N-u_4Fޯg_]sjRw5hM?[{BtUrltUNvo"9HF㻉n$9H&㻑n$9HFrF{XGuӚ>W_)2zϓhUlwr{l$Nkn0gXeokvF4w7ew8yqwPYC55>/n9@m 6sȹ ro9@`5Ll\tTr)̏&+ I8ucwу8X!K4|Cc/X4h#7Ip0,=dk=ZO֓}dk=ZO֓}dk=ZOb2|97Ii֙=`) yɃH+hGi^!4t5}L;9sz亼Dr]^"./\HK$%uy亼Dr]^".Ur)t=T+J:!l2sWOMgzLH BMMl+u~Kβzдv֣MycV){ݯ͝qN mϓx~w'$dVyJ2[Iy+~ 4'4JgyF6JX>_bpWzCEP64ws,ƶ=ǶۏmWvIŶ' g:3gL|l>~6`?tg:3g?6c[=Ƕj]d^cc_hH]>'HO ұ5Hq;Z0wzOvʯB>e'?@>' o@>'? Kt([A-q&= ZӞ\Y$]XL_>ꮐ8۳.d ?>>?^4śoaMϾkdolUb옷 #|W/kD~y/πʐ_^2U 3hd^ flVﷻɳKƯnz={Gu1O yr$vEOn33yfF"\䙩E%3yf!h䙙<3-w}$jϛoIazi^,]XčvΟ}gZ>/l&jkͺ6i l2&sֱc++*3Ա yEOVUpԢFa[#fXjΝFri/Fri/Fr?jDsL26@ k[# |52NR5rbjdw)5Z#3Q#bKnF:[P#'mk1<jdێ{5)˨s5vZ#\#FQ#iy&5ɖxk5/j۝5rFh$55ӎI E̹IFjQ#sP#sdH7jdN3j{>>wmk,zk8ȼzȼ55r}FRJ#vlYg;n{َS!ΐ< ~E氳v6ag9Wd;ag9l2MrKDbϏ@I 8ٶ6:!v mY!S<>6LMOOm_)t^uD&#g3:d2y&m'rFNI$ $IR2I*F&I$ $ dTLR:_O׬}r~A Z-v;%.o@ ' ʺY>WuLS$v>ohru>In"ݓJ''to]Tb؝P.eZI=7?V|lG>cv!Q;OiQ;OvJF<y* Tjgy*yOm]~ƏY䍑uqs9nb+}m,^/cCu/ ew4t\?Nn@6ӳd3LOD6ӫ d3=LOF630dv:1d3#s/}_}Kxv-PƍTE˦+<~$6vXL az ދ|oDr6V0rCr?h-L=9zC|6rOE9^u9 9Ǘ"㓑s|^7:X_'x6jw30N?sK?^>Ar#%Z4Ns'퐌{N?D:> A;铪/ķ?,saC#{qd/ő8G_#{q}^u6v N3J.p] '~?nͷSs%rmgs% Ql*2@Fi(@FZdbQl4/e\fuui2Mb7\na&yKV4<Ƶe:Zk[''ql)(x1d2L,G&O!d%d2LE&Zd2Ռ-uoyߙX318!Ns -5;1W4*v^_ɶ5fxkvi6D{! $:LLEdL&D&ڐIL&4"k\{uu ,5;h5UXR4{Ѷf?`_7:HW& >wfRIT2 C&QY$JD&QdG&Q>$*D!(RY}=!{~ױtݾ; \Mׯܶ~ߨ4AxrVy<9|Z^mAzιNnaVK AYv?3I߿+ێ5y\т<"GD-hCu#yD #yD#c#r>}F/2h0Rmo4i8@M3q-ܩmYuN86;&fR0k:EE5@?)kur <'Ao=p'|>|>|p'|>$OEd ]qV*׍;ڼӸw^جX27e]gZ1߿*NhTaseYwB}AT~/ (o٪Orlzsg \z#09FNcSY469 BNau)9#0$cV~!~Mw%Kt~C'k\6eqKu C~%95J/SjS}uc|Odڂ Vd5& Y EV * UAKVͭ|;|A_n]r|Znw> cа}{]{ҝl"_CD}gOٞiѧ{YcB(0 ! (0 & `( 1^:z̩B _ypcV]._  ߫h1CN8 թj'ӺqЦq={6(zf9@88UѨd4* DFRѨ d4*BFd4*DF<-}ǩqyЧlPbU/}6szzj]%cqK:ڕQl-13mKZXM /ǐ1H3ySڏۤu6c7jr#d؆F#vd5BFD"c*:ᅬ,Yt AU??wp1%K2~[W2_Ɓ7LuxNfWԺvH0^Df/ڕrE;w5wZʼm -?/$K]7/o%p BMw?|jo@3 hA0iyC ¼ sμ`FUA@hAP7y0LչPr(9?J%g3Cə!LPr(9??TgW{ph .:,XwHhRۯ`{miarɳ9gjc=y`r09?L&gl`r09?8ro/ym9ؠLǮ;Ir矜0u\nUv.,1lg~ٳ){Vqq ՞ۏ)bAj2f}o#Jƕ{=2~$*u3Vn`ܟůXu_N9Էv{.L}OZd sE^/_82>%¿dR&L~:~Ml# ?GN8/|/YOK'%{Ľd}^>q/YOܫZ~N؅-XwQ%?spOs6Kdo }"*v4q0LGuЖ]6bl!olkKu O=3|س!L23_m̠? 4P0~Y5œISֳ]]%cT?CN3u}n[m{ܡ}q[2G&o_4LȀX`a#Gs c>-EnK_ X94wfFY mĿ]1HEW b l91H* q,b?!H#d?&#U?~SKu qgٿy?Vc]nNX#vEKƅvlpW<3ę[=Dÿuӄ{;~03}n):L֫.:Ji^|(7+9Ql-z0!it,' hNYYyZ.x `bW71_4CS>`Ğg+{-weo=dQ{-eh,jKGmuPԖGmu::,qdAي_}ly&A7E9]>}rB66;-gAN3'}1}zp:/|My_z'ܧpx_w{>O:y0u)Ҹ YKYK44B4di,C*dḭdNd|6yʥ9S.9O66yJo=q9O6sYɌi|) t;'9Esw‡IEVX_/8D~jߵ2w ]ۈ kgߵ,w]ۆecU cZl.L+?0fZna+=ĔJEsO3)m RC&Vnw]=uhON/L<̭6}/Wr7*sYM(hr4y[;h1b=*-r$?uhBWdp\ن \GWڑ2\i@W-巚$Šc]w6u30?fe{RWp /Uqб#8خ*@2&R~`e uª!,;ěΎJ_ ExX8VYѕ/_VQ8:vb _l/یs&*}lWP>t%P$Ҹ7ŨorQ$)C|:uM07)oP߈: :orW|9p=; "}^2&vR,){'E.uw]$"]H|E.uYkuNe糧~['~"ٓ}*5xRR+Vmpݺԅ@r# B4rE.V\ w`6[^1Rӆ1b>n{@Բwjwfꄼ .?p5,o#I髬&usmjw5z@y\G(2o$C_H ÄpNVi~rr_\Ggnx'=~E_"ۯ}-ck_du :ٞlC_!m^oSƍ{ wlSrul~ ds%7ıNㄵGwR-wR9KqN_hY~Ss%9yiJ>csY)AgN!3L*>ST 3iȺL'cq#9/|mQSHoS$=N6J̑l:#Qd7OGb؉N#1Go$s8av 8s8a/uC)8'$:q9uѧXc sl^rF6ʂ8:R7q#0Qm>5kr^_k\uݺgzdY2,}V>K@FU ʑgDdY2,h]fJlk^jl5na+UMςEÁu5YgMr9C'^ǔyO# ݼy7 ,lGhD;ZwB[<{<bfR{!"&bC(ky5>d}>^dzFاγ:<[02yZSg2@OȄ8ϛGvZy2:{^uՋMp4E-iWZWV$9R$K$'NX9-s|zY-۲-ۉ%94)$\pi` {S{ȅp  3Y3g_}ўko~̞=#_=#=#Sn/;9=>ϰix"{mw}yГ_/g/g/g/g/g/g/g/g/g/g/=M}}8g\sv]՚_ͅ]})n?}3^A.{ߴ^s [?{J?w~^sxnk0n.dSK}Knڭxu'/n5dTY?{T=|lX5j1p?t/*ψ4;q|}pE={/_d1c|"{E=ou=sDŽ<|ٚkw2?k~O,i :׃lnnb_\=`}qo2?|7`}l^`r[m^唭]=[W:eh7VxǍ yL\bX75B./ͻqo~oǍ 3ĸwAcMh69ǎCʱ#;7WF[~r׻5ڭg7 diji Ckl[m?soٺ~޶u_ol?m?_]?k=d\s͐/ߍ.QQs Yb=??._V;(g3 K/'k3 y\$EBvv՟4$߉OT]-w"jgŸ.+Ͻ~򉷍~役*/8Mށ3u3at+5+Vp{މD?د,~czbgՋՋՋՋՋՋՋՋՋՋYzX7uԯ_{s՟Cؠկ|S;7\r]ȥVWݯ~W yxOSyJ}Mנ_.dsY+v?r1~r$ϔHWUZ9Lj@i"35W}M^ 5KX>e|~k<̠/qum}n.[l̝ޟ[zf3z3Ͷޟ lϻ!|-x yUs5m$?ZxouGZw@ ;Er]&*xl-'n-lȿ(׹ϖ"=]ŧoSbں{N[wOMۺ{jS7ۺ{jl=u;} 5WoG;^7߫+!5z坚.'1W⻽׋\kosk??grsV~S?CWp 5.䱵9gA#+aGgߙ}4Z? wYs'Oḏ>3ݣg5<8wy[ǘUT+7Gv-vˣڪk_[l={Wl=/xU[%oxI[^xM[瞶vcKp7 Ҽk;/$B6b kސEĘ/}/z5\sy|z[֙?C}-ƙߛ#zݞJͿ7osO*vCso]*.O1^rУL>ȇ1A?ܮD*;Ûr2Aow=2::-z&̝f՟f՟f՟f՟f՟f՟~iv^iv^iv^.+͚7<~wB[uQzx褭l%[?qEC?g(.ӆ#TpTkOF[*lZn[m|Öl*M[=QsƸL;+`'ʎh}$rV_2?ugG5UwP{@\ u"{ɚV0=,'~^3 ^$D2-G c/o >yjM*#9V;mcE+6Jo#_h'6紑9'8`slz]Oq6w=eF붍T?%S62m/Ԯ2lDm#.R~FN]Gm䴑o/F %\a^yU`'˭y1~!{\ƿ`&ޱmb &.6qLmb&lılmb%&61?Ļ? 8+>+t<+Wb~N\4wR\Y\2}p%ĸFOߜ.>^Y/d^/ <7Tszз[-7I k`-ȍkkl[WGVں:rl]~m]hɚ{_Dhn{ s<>biQe~͞+ϽDseo|Ke~GٹT7)G%ׅ%?z .Ms kyf-4^*9fs7|!|` -hn7kO91E-O6~ NC_PO߳rh!+zq0} W%>V;`Ķ`X~~RAAA*tFUo[)EZLTǤ+7* _2~ä+s|6ȫN6wC)7>S~~դ_B.}(?J* UW7ާT_J~ɴ??d/(}Hqaa-?jP׿>_zEy`z_doV4Əf4*<LUoHѿ)o+3) *GlP)߀bo/9_=Հ>Kۊgt{U(Oުz+h أ?3~ )+?f(tIW/iEV7_~C7}}+*o~n%R Tϒ% '?'SI_?%E_n&[a =?^zR)EB~ PN&P ҭ$Tt%ߩVS~צGBB;㫔WY^R 'A.t).~vSO?vO蟺Ia_NU/S1?T S¾vfءv(G*nWb~WȧC>+O+Ɨ*F'_Uզ/mKTסqETOSVZb|ءXPoӢvEVU*U!VEB1I}]1~)G*#DWy5})צ6EѪ_+> -{RR(Ӣ=x>ѢXئ_<_lSo}lS6EM1>?lˈb}'- ~YVVVE|HE[ |HZ!ˊU)V]1~(UQ/+/+Ê }XVU{X烊Ê-EEEP)o)(-}mQ tbUa?[w+*|~~YQ^~{Ps)_?T7+͊ff|P ?ߚ|_(ýsW~TިkTG7*gWܿY1W?߫5+߽{^E\EnPO&^AoRMhPWB>W_i?6*N?N?){uWUSNaAAߤhߛ{b{?ܣ?V+T+ 毰{F^X_QEVXQk5[(s=gbJ?ݣ(:NjV/3u'TK1R? |m>7*{b|ZbB*b|^XQobFѿU(~Vj+K_B~ժ)UWOb}JѾQ_*jEV*oT*~˭'$QM+Q<;h%џP"(r(rǪ/D6UF*$=nu{*˛E>57C5D<1-+#[{Z |숅HhtC cf\XB~yZEE֣t=,Wu7yŅOo˛yѓtٶA4iM͍uwfD{:gf~nԮ7ԎXd4\a-15H:b:dWO,o7bF9ihKR;2wDR;k"-mrj}Fmp6KE[x{<Ѣ2UŁE58"EY:7=^:w\[Dܪ.'PkP 3V9]I$Y!.<zԵx$JM=FXO؋.{hdqݖOeOQ쌵_/+B6ˊke)4tiXD,MtN )ܱlL%Qڂ{Zy> ldw*e&;u*j_=:?c']В]z%`h"nq av^+*GE>fb ~,=NX΢s,aXk튷) XQ̨Lt_OVS~x2dۑytEm;ҍSf̣zBQ(qԸaL#3U3XOʓdZu-Ӭ+a3ݘ{fh {YIcrde}6e4%V;hFJծ]4ꢡ~uO#w]~3O&90b(s#=V+5IZD׍3F/m4+of;Q3_]Nצ͚[2Ke;9#zȮx&k6e,t;I_3l;e ft̞;cʁˀKz(),ռnik#Ȗʭd"kBg.M&x^O+.}("-M#c=3Is޴գ-bٶCW~e4]uqސ[(T2;ن׻#4Iu`6W-argy̺qin8Dy'#ÕnuR4Ku[21Û%J_Au4sӅo;]>"8lW\MyWZs ]2ǣdc}2D{NE4MK-,> d[K*q!2Rӗd!v#S<\akW<~hWZAF-u ]ƳW˖ȉ3+g)h&zI(i5kɦFt6d[ hMֿtcħ.ԗa41wկƺ~ g_x&Xcdss`_F전31XK#-eBqYc8!~hXNO1sȖt.{ "QLbNYz4˂ʻxXiHu4%QO"bQޛ/?`OD)q?!w{S# `xu(hіl7-h{"ۦsqwiveڄq 4Ŭȃ4Qsk!Kчn6_9tu@3 m0Zuk7-aX@tMoBf\VΗ#T7iIzXXifg`[xRRvfV3bi GپVLDxdYckgNV<6xK }%'KG_`X&/4 2԰" Un[ۅ9fl{i$ɔ&Kktc&i->gi4!ZF;sDn"ΒXWDƖ{CE[Ȗ/Ӎ$g+Mt$jF1ߓo2zL2v͐Y*=R_e !tQ3AGE~M(d67"gSdI(%ݱdöOda&%\" Mr>f^J(dU.}uNulQod^[iɁ&'\g?RdT2.u-JentCR@]zp\z\z\z0]jnm]cPH!6#kƬRצRymj+x7Mufpm8+onm8/M B6,Ml3b)ۤX6/bCc)68bc)6@6FMf_|oMV^v6MVܔNKT6Q6Y إ٥lv)M~y^_jH>9a];iV/( b%Kr[W&)OhܫxҶ>.~֔룷l1 Eiқv\oe9Fo66S~~jKmInɧD bEX[,ޣukkQg g Qʮ˜xxpˏWW~fL3i/_9fwk1E [?0e=<-ZH3ssC"D5r3li72Yoc/FՊ|3;v<G"4~k#3 % p2_)6x՗ʖH9O= ߰5ilyWƓյ _^V+W_]|9q9/͇ꊐxʙkGD*cӎd7<t5%=u ~NΧC~ѕG|3M0Yϐ==/7-hgmOXvWDߛ)6R~'Z&&7 <]5U+ZQnLz[jl)D>7WOϣ"͓(֌Ӝk2**at=nuт4ks:oad>PC7~{I ~)xpҊ<ֺɽ/jxCԽr`Z|0z2]2bnBy&<(G@F ]x[eTPd|@AW̛^<\z~1-OEJlEk'LA6⹏w!<Շ7[Kn+–_+?+NZkG3J"l˹GazdR g s3aʹ ڑM[ҺAzXΜ۞(Ye2q!ʭuT7ȀgPbceت5 9` uwv~4Ia{aʆWzǖ|o s;rAczB0ޒNb~ut%ZB91._q餇sJh(ϩi+C="?Σ/y6&z7?NVgF_ҚOy?=|1eBcɖ0~ H]GmY:o]ܞ]Utu4#"rKI˹-Xf]:,{s_i)à.%'~Sb>9$Zc]^C^V=C뾏'O{r+}[[a`~{тم+WJN :;LYNK)utn쬧bdiATMr:835|tn#(h7_@*'8ڋ ybAxno ce |ӥZ#u wUy}y<]F{gk/g(>!y:}H{tu|߆~ i]x5Z ?nE%<-;_Qʩ~Z6mDRI[nݟ~(ؠ/}<-ϠWT{>ۑ/1/~̭\"~_Wˑ!Ox>"γT]qՕ,ϒH@_?}+Nwy97|?I% ~l.`3-0W}Џ#$_>ʱF2Xp/d|^yF QG;jQ$lY W}$ɜ3hNe }#>Rx9׶yIwIz&òX/ >!]|D;?_^a|q#s+:++^b:dnGVUƾ 8-̾ <`qsَ<*IqT"9Ցd eˁGGaX0U>3xc5|)IYn c'ʫ+UfɆ*2C*PER^ߺ!dזN嬏8y0Z sD5{[]|\Dɺe'/RIM $8i:*کD,+N awa_ֹMٽs QuD1=E Y\*yf#Ռ 6^52*l򗑓QdyA Tc",U|RUO`c4mO63%ze!i6.n^"9 'U35ߓvԃMX&+SNe3:h!jm2B6_}#M}a^`ˮ j23Wk.T{ZH`Îp.9c2<)-YJ 6U؊d+jx!|Xm~xM"bzˀvR'Y]D"fݺ[vOLcM]8lYC=nbHv,mrґ<8}y+1\t 4+1\lOjIG%ݒƼrRz^swoNbYM2~!ٙ6/;{JW'Ϸ|AkFʥg;!mzHKW.&!x[Đ=Ɛ_ܹg,N';k0uJDb}: ۼţե#31ݩ%AzZq>xɊ>cLrXaoY3!?wv7>%T$k1'T}g^?ltyIB4)+Ojf[ꊷLqDe|LQ]G{.漁'CilI&\d wǻF].! 7H~ S?vܐh Sɻ6 [Qs+w<8C̶c\XZON:/}=K^'oLFmfܜڂikFսtTpdoD}bdw*sjwDRtiE"kl\Bڑ8izjJ%yY}Ce*ūlt3_J֬/iJ֬Z9o-͂WI%u (.`,JU͚jm+ƕڲ"e<~cus%ESCeyuY,w#TU5*7E+jEu6DhvuMҵh wDj#uDCw+'w֗D4~$þN q+Xx8NkmU+*H֑5T7cؠ%t;j̕D_mG5r=kֺɖXN [/wa:uGWn.#c8+j_cDc<ӕKJcƕf\/J&wL>aigs鄗uhlzJo$seeյꍔO'E+99R_W^imjU7[~TTc^WU7wUno׍뛫˚q" uk!hDUV"˘f9qKW~k3 u3x*j* \^!4BMU+CZceye}tX2Rq'З7oSe}rU\7Dj5쁮zָkYg'4BtæfƲ7Q] +ġewVFH",YtH|S}uwCOwm޸6k\gNiBsfʍekiʚtr[b镛eO~:gzySR鍕nljvTk4}6Y9ascC}S%GIrGz]C(27UP%u44ʔ7֚q=੫.֊`:ja#p!si75X$W%:}gszY#>O7=D[ʛLw$Az7QmUYu]1;^:|cPS[Yt6M)=1KoƩ:]ԫ:MN`MA y9x;クNI+khP,&(:bD-uT xSeHTiJI41)לVVc3NƎor {Y~\ jeI0?F#&f\VƍkܱVkl*TS7\*7M͍kiTTKH˛uIt&Kws877o\/As.2Z9M}3_㺮^Ff̛47ohF1bXa类+ӧVF;n^;^Iki]ᠳd>ep%p\zy? @+Me|XBӐiδ YZIka;\GgO4,tCqW>b }eEƌzh$vׇ4] [nCde<4ϪM{xXo=x=sW?S_&.+򽟻̍2!)@q.g$%p7{swڥ~y;K>e'[GZwDR;k"b}l˛#W~Tz3-X9J-Z!xx7Y:ߥ+9o_Tow/9#7TuyygmA"Xoy ~W֥.5(uYu6nzTe5VwVT#~,_NBc@{(if E]7؋C^O Zms W'|YvP _6>) PR}BP@?U3/VY.}oC N *_U=Jy08(ϯyMzP.p<PNґ:v-x>ICz?$U*l9dÖI:+ z{y)-0e=.VZʂS;r-Q ez3YOQ$Fz:v~)T(?>~k}:qUQ8yN 0{,d`} f=><|ѩ󅰢o¬qbqTd߶Y1Euj /v(ȟۊd]Ö8fZ?s3`0 UNM枖< /ݯ&7×|qi)c8>}%5:BtJ<~_Axa%Pe}Xl:q?pHB zTN6w40P/򮻤{bae;r~%_w@zuEX)J/$&A3Tyu%'lRqD@9 ̷ ; =Tu3vh+/UyU/?>rk7[TQ>'!He]sYO3!EY!EZ#*~ċMg%%x(,9o~='y 8 c&%%ل %لǒl&jy&·GX}l}wM*}Cd x,5NDC/pg{? WX_C>r6J[:& >*hM(x<' #(xåsCFĄ_wg#~5*sKXoKdz69lO1g ˺/־K9(y<-no)/ۿܲ}n댦0|Uvӿ.Y*tr=D9?az.nߓ^^a!>օNEȻ-Hʭ ;$\xOƒ^i+'[9ŵʗ_KR˯%׊+E鸍wdw+w?? (D5i[@?xEb/tYD–uQleW^ĽKVˌ>>TmQ9>p[Oj |\~]o|dυKiD)fkG\#m]6A1^ydx4#Z~^}.+˔ї<8N [~ağ"JqXQ9nN D ;KIuѯ~w6-nȼC+L'7GAEWKu~ *c_~[WA?ڲoMo÷vIkG7(Ic {V.$ Gw$6S#G$}~s'wA~~G^ߛe<ɔ~HK'm$m_$~ӒmOK/$]Iw$iZnD[%+'CzUHMn[wV4.I oo mm˻ڲVO2Q{l轕JV VۺMNx[G%3m}~v$x[Җy)fHL@Wߩi{W8ޓ,U+❚xV+~ \`hʼn~2XV1M7Y_jEsoMZщY|Vt?Ek5[,Nˣ,N/~ 8ӊaZѫZ<?bq͛,VWkE=,VeiZQv|V wYD+Xv軯z(ЊĶhEۯxVg=t0yNjE>L+z}ş֊/iEmϲ+Z_gq_1ZۋmLhE8}p-Y'|1nԊ9Ao8N}۟3{/i8w;ro;]ύ8,NuA6⤫ï8Y|&/(S'uѽ8G[%,NpɷK=tj S,$ŋYx=쳃K,NM&%2A|˃=Io'7}vk78̞:קYu~;I6褺_`qvC'5'{9'yJ'yN}_c}N־; >:n:ohE\dKco'/jEjY,ߩU1ī'YY+*g/N;+N?ai|V+;N~/OKLq3[Y%ůhEߛZzfw1yĴ&T_ax[+b߾^+.fNo5'I>XGϰ=ko~ vώ8?4M+ ־˙A؟fwǴVl<#_f;Њ9ձYߏK7⛘?V|3kgE+^ 2]akV.k]Z?um yţZ?hY{ IofazR+k oKZL]ohVI>e&1wߦg*56S+nwXު8o)?1ݓZ,~R+i&'ax_Њ?-vOՊ?čZZ>jkfI_f%ƎGbVZgKdx:A1<s,>e3qY+mֿ&^ /&7ogxZ7?yV V$$_c a$_f$%&VU?gǙN>~7I뿓/h^i$ JGV)6K g;Y{IQo<ߩ'5RjN+.gM=WncKd_Rk/3M/e&VxWw=nfo;ks٬a٥bs'v Nf_4w+bN$Óִ{X3o GY}%Z!3&'iϹyӊ7\3}Za/M`4b&~K+3j?ɐ= (gV_emy zN׊Y=vũ[=eȟ<[m~揨+(3Kjb,駁siY>ZoZ=}e» %?V%~$gYF L9mc*OrQ>/G^䑣oVY&O3_9W+?!{q/{jwG?C f=dcQ_zh1P1{ s/7kŭ-ъcl>K}k߽4ar~zi? ?O/':~{Iu't?ogK_Kd}G+>+߷\+f]+xɳV+6jcS oߴVOVk?}Z߳F֧] v_ b[x%or/|o '?ax+]V 5ǴE8G?;LTmfiE3{ cՁ۴Y7@ S{aOO2 ^b LkLgcsVɗ̟ x({Zwݢ?sZWExkWVv'euV+6Vp_ ~E-֟ ޤ:zS+x g$ SZ33H g4Z+x~q^?/ͳ8  k>n kj e;D3= =◴o0GɿFGiſ(/^bq?f~tP+xTq(9??Q}Ǩu̟Q}?F']~7cd0W78m_/ků2yfnd{O+qϾq?N?;Y5NyίO+ 4yǩ}f^V8odb7N{LԞ??AgYs ?d?=AX0A}>&h~dOdO'Xw&^ъXcq^g36A_Z۬=ORϬ\9A[V?I/P_]Q&Iߏ1MRiũ!^_~__b'-[46zM0Eua)ꋾO_cZ/36Eç?-?MmwS?1EgvDWl6k-݄MyT׷8{mq7{D:=WN [Ls礯Skݓ{d:O*Qg?hYE*VUg?hYE).7;JzZ%]Ď-S+݈X?s%D&ےhDٵ:'O|z&ڏ~߇>:Ų1fwJ&i >EH$&|| Xg¸p X,t kt7~߇}߿R`ԧ.# oAx+!\vC7 @Xaf[nE aa'.)Y}"D8p,9'E81O |/ Y^ִ" ]#f&!\N6#܂ a $ig#"— o"|Ųތ#\a­!"C8pc@,K_E·mYވV%!\a(NG!Q4w$ⰏO"a/ܛCwxFāġÞz#============ }}}}}}}}}}}}}}}}???????????????? w.] w.] w.] w.]?????????????????C?C?C?C?C?C?C?C?C?C?C?C?C?C?C?C? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?#?#?#?#?#?#?#?#?#?#?#?#?#?#?#?#? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c?c?c?c?c?c?c?c?c?c?c?c?c?c?c?c????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?S?S?S?S?S?S?S?S?S?S?S?S?S?S?S?Swn 7wn 7wn 7wn 7{={={={=? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ^ {/^ {/^ {/^ {/>}>}>}>}~?~?~?~??3?3?3?3?3?3?3?3?3?3?3?3?3?3?3?3????A? A? A? A? !?C!?C!?C!?C? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? a? 0a? 0a? 0a? 0?G#?G#?G#?G#Q? G(Q? G(Q? G(Q? G(?s?s?s?s?s?s?s?s?s?s?s?s?s?s?s?s 1?ǀc 1?ǀc 1?ǀc 1?ǀcq?ǁ8q?ǁ8Ǿ}c?q?DZ8pr8'!q9\cq=c a1b #;q3[b #;q•Xp=B#;}d'*V!>GvGvNDv">Gv"!>b #;1$B#;}d'CB#;}d'. N\A}\OB>'${='Q$ʟBS( O)?PʟF(Oi?Q4ʟA3(Ϡ?gP ʟE(ϢY?gQ,?#(?hnE#|!ɣo!|;_}ab7# ݊mݎpu݉p W sùf96QfߝD8p!޹;pc=i{%/{ +_EAs9?yyyyyyyyyyyyyyyy________________E_E_E_E_E_E_E_E_E_E_E_E_E_E_E_E_Cy<?Cy<?Cy<?Cy<?Cʾ? O>%}w¾`#$&KTT@TPT4 **"**mmk[[mKն_g^s3o̙͝׈kD5"ȿF_#׈kD5"ȿF_#׈kD5"ȿF_#׈kD5"ȿF_+׊kE"ZȿV_+׊kE"ZȿV_+׊kE"ZȿV_+׊kE"ZȿV_+׊kE"ZȿV_+׊kE"ZȿV_+׊kE"ZȿV_+׊kE"ZȿV_'׉Du":ȿN_'׉Du":ȿN_'׉Du":ȿN_'׉Du":ȿN_'׉Du":ȿN_'׉Du":ȿN_'׉Du":ȿN_'׉Du":ȿN_/׋E"zȿ^_/׋E"zȿ^_/׋E"zȿ^_/׋E"zȿ^_/׋E"zȿ^_/׋E"zȿ^_/׋E"zȿ^_/׋E"zȿ^[[-r-r-r-r--r-eokkcc#{d}-oR|+}-?Gs:{}os_[l[lJ2;|-R+}}oRA<?[lCvзX]7bW mᣮ}#Odd<ݷضQ1_[XyooW튿 .}>tewbWp/+oZ9ַإo}-W-v1ǭ,-Oҕ7[~>gݾ.^L-oc_o |ʧ}-Vk~{W|]=t~}y[c][l#[uo^ƷɇLW][clW}U4]{&E6X%cJü&W}jo-v[_?ao5b'6=b's?⫎u[<3feXSηO.Øn[le_;^V}-T|yŖW[l܀Ϙ][]V-vWy-vgŖGXìTbv[lsc.[-UO[V|My?/wGV-nMIdjůV|Y?-e, |-oV|̹[l'DƧ'5ƧoR]5^zu\ވ-O]3Ʒإ?}6\3ӷ5bW0C~,-vؚŖ_EO#_Oxxdַ)6\Fk/Ҭ/a|4e_L"&_;bU|uk[ZAY#!ƿ >ɷw*N[lXZ_CK?|11OuboU} 4;Y=ܷ߆'a}];K-v͏ۂ-v?o-v{aղ=/oU|7BUejZ}̷KϘ#-vQȷɇ̷ U[oX̷*?:4b'cbXZY*~c[{oK3k;Oi{L#qEko-v[x>[O@ V+&tnyb"悵bu<&~Dkb2ʵ-v`mu˔27*b-DC҇i~obOo0NG pŮtlfx)I+{o}B+>lݾ.{b"/;솷||/ii 2_\4? >Yi:_r]ȷODNMo|b6u]}|u_={:O ?W{ꠛ5<ﻺͿ^'5zNc Y'kty}]kQ}[lڟT3şk=?!F]/itƾaLyz|^Ů,@L^4!>/3|| }-OU|h Ҭ z=7>gok`F;?Ů 1~!lmN>t7./BL~[Ӭw %~(JE}]o03y<_hyb>g}]r۾Ů^n /_C/ϿIbs?G7-v:y'kL_]{bgǛJ}/-vg1|MS7$|Yv[ t_>&yTUʟB'o·_~|]A[Tu{l粃n~9||-_L*0Ve7sem:{ge||Ɔ5Nbf|ybHſ-y}ol\[#xoW+~uom7 ܸ׷إoQ}a#[liD3lo}ͭ|Gs1,9эMhq7&w-v7$n}i&aN6bWb-4͉;|} ,ϣ ;7Kj< |͢qw-2Ǜ?o4 ē]/E-޹ |[yS\tYR[_a'}n"o%_[d9?O>}G%پ.O"U⽪&+fɭ|MK~Zզ&𱦸UӪM#&UⱪmMg*?3o}·e/[[~|%DW`C.@\r3}]olKt/6J^Y'>cI{W7M|E߄toO+d4oIlZ[l/1 6k¦|]o4lzطGoU&ɇ\O7I$z5ah®YICVi?Xڷ>|m*i>]-|Yv[UMW!MW': b7"nUz$o9&j_ݸMֳk~u4m[~jm-v/}඿[~w32n-C(X.w])k?w%膙M#Ƹ]gp_|?Y׶ j||e}N϶6wC–&ߘ}׭4ct?pHo}EnnL]omw-46L3;z՛ܑ[|ܱط?TMŮ;[u<&V}]Yw~a'>7mj1fdok> 1fyx&#mr=|7~:N3Ů }3~ޡq'HY_ɗؒ <$j G}]V,nmb:toup'b&gsyv{&/#aRd'o/Tv'lvgw*Tj~WL5Yy[d~|P?_;Dٶ~-Wsmngwl>dŖtVWU|X?IҙK[eJ5ΰ*Z>T*S+:c<W+>WL~U?uw#*wl0_A]*/>b;=GSGrly>4;cx罾.}x s֝2?>cR.^ǽ).k16Oǧ[#&}S-v?۷e_|m&~ojkS.![cno+~km-vs6ɿ=0 |w|wl.j=> | ? kokoȷ6x<| l.'| ,l}|]~| ]~a';d\ l.ُ={<[9{|6x>{|6xwgb'ke>}K7yKa'8.-vc-Xj j>be~?Ԅb/!Ƹ[Ԅxȷ1ݲx ˈ~ +|W!}]yQ|Y_~5|Y_v7/u|}~)_ֿGGu_k3ah|_޿&]ױ.S> [Z_1.Ơ&%&&l|󛝛./wV|v|&_?aN>|vo?6 jJN^e=NG̰]kw߂.~7E>br~[sG̰]ykct߆! cCG>'Ϙ?œC/HϿiyv!\??dcRϿw)F#&o,KT+y_Wj}3?b7&7dt %̃0WgHo;c^ɿXOӍ3|+uTр){7y_[[lWﰑ{E%;#3fė/}m3fW{E1O|'Sއ1}_IE&Ku$b{_?gC| vO?;kx?`۾3OtcŶêT*~j?[W5?lϻ?3vy;1ΰcc't~>~Nng ݸ~U@V?9cu?dC??-Q㙭sD|2=[첯qx}d㏃첗?2)?1dK/ 3Qx}ak|f¦|*]39^+S=3ǫpA~^cU,3E;|<`A>/`WK_"{|9]*AO^fi+.6৾.~d jK*cw*|~w-v4إUuwz`.E5gϮ: Yscn[Ĵw]dS__ >jüx?`|oF v˭8`W[IU}S!>`WI9Nq>FwJǿ:=:$Mwb|ttq~.1`?`%?f?ɇykO{XӇ=0kO>c7n>[ݰ}m](|üf8d0'?O6o}}bp '05ck|^vb?|sVï6az U vB5.K{~}OS_M}|]qYŮ|ޟ7I]Sk}-?Wsh|]>bZg3~' vCd}^Oџ@3u<`WyDvG .3u|^?ÚN3H'߿ߏ3wK_ i˲zY~Y#]}{f>|c?𿁍KwQe;4Y-4g#fmT2D%'M vV }m]kjld7 vf[9`ZcM vϠb;,1n~`Lݼh?gC'w}$\?lSgYcx{x5a_Y~. |~`u q_g ?:A{?d]~G vߕ`W=[~^ |]g~.}_>?1equŎ4kH[94>`O5 kG ]eX=ljr.أ&z&3x&k*G9_`^ kY3=xw(';փYW?7 vհG9`|1kG}]d3<~==>|c}]) >`W/c8= u)8`WX=!L棲<ϯ Yrp;Y+bOQ5e/o5b`? JzTw.{9`}m8/ Ag[3/&Ą-vx|~.c8ҷm;ѷſ1.}}{ vOfvd?!C4ص^c 4?17ҷر 1>g07إϵ |?J}x~_2]!}?oշ1Alϧ5 c?Tpş@ mSgg!'${=wG?3u/"v} vKEO>~~ |n*t W7ص>=9 u‡? >?/01|.[+]moZx?gn1~'q5^&??G3~7~9|~'7?>O ?[/:>IŚtG~֧Q7[''9?`c]/1'9`Ig~I.{'I~mK<>?67b'< ]U/-v񿆬|ӷ/֌O*/)®1;lLj}By;!;nþOƚjl?ⷄO;[ vgv+_~c/eC^٧!8y⟎9꿇9cgxr>p};9ͼD]ssgxn:&Soc>c_\ >GmgfGghc?6RS`~oeЁ xV9)>o`um>WwS v߳|~`~xc v|>?fF~߇2y`;0>wd4J}m]3o?#com>'ަCnm|sh1sb3ovvDLoCv b郢4ww9`>A~kg?s~.È>]s.n_]09`˯Tʀ|j?Z/}XS _om_y1? |OϏ.]?`zlUo[f>>O`W |0ض?[^:~(S_h >`+GcZ?'Ůke^y O{`m_'+OEǾŎsr098/#QoN+0/[ⷆ,^ljs¸"'lGūYAsEޟ7*р/3m va"`"v|>WyV锛qϫ4՟Έ1;9]e]wD9,uY~ V?jCoX œ~!xn#9 ⏤|a?zb]×x`W߬`3k8_`?$j l%0ʷHɷ(rfٽV0`k_rBg^ vO>t%~ok|G;˜ǝ_/s~.^u0+1O(_ |[ R'[8y` k#Ǖ] |ч5jYϘgl7GFĄ/`Wn;ϛ vo;"~_Ľw0|}g_Y[lySySNm7y`Y)+Q93x W1إ?Ob|K Qk5 yk|Ȩ[V0|a·1?QOkÜ`o.|~ | |aΏ2إ7y- -~ϓ1o[~.{3bWV?* Q l^,>UO :ll'? %;f-Kr?TFlU~?~fug'[}K/5;sη7#=3[_u^ѷ#|>1Rlߌ^ 9) 8T0##8G `ęG!~-[0ÇS0X-M>-cELڋ;#2}91@0b#.:*"<:K0hq cut`MaG }8O0|#1G"2=XF0`c_˜ηy}`1/}`[c_`?S'ٌ0oc`ߍ1c`FL0s{0o-67(f`ւ1o^$ hMvg;s-hMj`j`jW? e|vcҟhXcc"{1ϽhX`l`ԾԾ>j}}[ߺ\0K>\;[[DwR L&@09' Fomޏ#=M0H0{`w0~v^_mOI`?gv- ?B_>?wZ g^aLd9߉3ދx57޹C0wqxN0þy]0w"s; Ƹo\ W&s<ѷ _x~x` Fy.9#s@b'SQ0Lkƛ/fLߐj~`ȷS݂qEM \􊾵\􊾣$0E6j11}}ED02- =1} O )}ی<-ė7wZ|ƘC[wpZ7-}зӷiZN hy5cLKkӷ`ZT }kc>KK JKWs7SZ~#~SӷNNq"}o"}"@9E|j`&)70}r`SS~"}k S>LL߼hu2cE+9V⫏?2oCwZ- }n%j#cA+A+E͂V2ZGh?SeNoz)c:AlSesOǘ?u5c:oԭ,SEo:et?aL۟{tv:>tc:s>$}CbtV|HƁ΁O6gw졟3sCoL80祟ƿkB?7c:) ~䧭bLgv/c:W47̙eL灟!c:419ޭ=tFwk!ڭwt&vki{Y֭3s[83[}O3O1 }:9t%yΧO1sO?X>es~gdL933XΘ>Isl|3~̘=CdJy^1f85gz[tWŘnfL۔9{1[aΤ~ØΛbLgɆ٦9^olG0\tj1tjXtJ |jA /2gt~?٤gvcL玞19SLyg@9g1y&|γNaLgou1c:W󬾌̳'0*dLg]sY3*؜?y-lɳDȳ93gB݁1x6 Ygߜvc:JtU[_Tm_gLgOe4JØΌ:c:|]YO_q:c:|7:|^;絡97|s&cZ'^~ޜqQc:"If2).s'.9Μ)q.t^E: #震~ʘpHtg셋y.0*\{̄_.YYsk1Xt(Xl#sŢtnżcIż_aXٗw}zuyw8ļ/`L;ݗ`Lk_k.%bSZҼC} ǢKx˼| Kw/| ϭ]Kx1_[%`Lg?fm:fm1y'=m:׼K۞yO=ۻy=67﮶1ڞ_iӻx_μځʼaczXnfeczdͻ879v} c1;=w;|͘+qyg# >`]o=b~Ҽqcz# \G;q9F2u}.ۥ=K7w.ۼ_vi3_u潰Kyo>ץw.嵆yRK97O]:oލ\S'ދ04u5y_ۣy Q';D8n7u9һ?x]o~üa>N'o]NgzN{K'/҉@eO;) I]85q\&2w4.~ѻq,jޛLlމyߡ#YS,zE t :slf~ռYƇr;_5?P}g^?μ39V4ޝccr~9&rLncMeL(rr_~i_&rɯ¾Nw9Ew900|>Y!'0}l>LQ!gvl>Ly8da<0}l>LC6?B6a0}l>L!WC0}C6͇{s?da |l>Lߟl>LߏC6B6ߩ!ӯ]0l>L͇W0Vl>Lm!ӯ&da=0~0> |~_l>L!ӿc0gl>L͇4da|l>L!p da0N|l>̀!3`R P> 03Ր͇͇͇ߐ͇(C0?3_ 0Ev3PC0o |wl>=!3Pla(foB6f# |Agl>̠!3hH |A!3:da0D)fS!3H z/da}03C6fp͇,I0džl>!3*da0wl>B6fOB6fC6fC6f?B6fHv i0C. |!C6f͇27da, |!7l>̐!3dW y,da<0C |!?|!߄l>B6fy!3k 0C |ѐ͇z} 0Cl>Н!3>da>0C|l>l>C6f'!3͇ߐ͇40f`a |ub<mx0d4l$%LJ]x)Ê0p5vЇa2n>{D0k؋q_~ȸ9S?"z5Ob#ޚŘ +j˥?-jovJ;uK7K=w xG0׌/0S^S0b1>v=B3te|oGzX߈{AGLaa<)ZFzc;^Ƌq_#j#01 Λa#~!wƣp#f<x4lgnwd[0GpfGF1rpF캢#q<`yYÑO3~o+6 o{@WG~ џQsVf| w\QWQWX}]5[ڇ%uè%womԃ6quH#'|gRΒzsWCF=GK?`FuG`|5Z80z pmtom BF˸^<2폗/m|*񷈍@ `̧&x3U 9'?1b 0wO8۟ cK"N'(vDoa6A||e&|ԟoB&o_yab/E 0Ew4Iw'!ߊME6$H;Ib['Z$1̂]2a'^>L9}>~k^פr/$'%>)nLn%1| lsrW?~wrb6ɥ1G73^ |'2wE*џ'\y\$)ݤOYyyJ>y:h2tnǡWSv3."1Ot~ާ>ߛ"`6W΂VCqN? =z~O0SA* *t2U1ڟ_*y182ľ0$BďŇK_&~x lg1[&r×Nxz4Ŵç}% S6BM9" }.~) ۙ~`t(l|zX%ƈ^?%~r|>]mNX0|efd!.9Y—*S\(gY3WXkV qMޣ~_RM՘ jh£ +l oWX'~E RaeSW~9 p{)<@1 +9.RXnm ߫# /,We*|=VvtU߮^a寮ڥP}\lVpH_]*|O)ªoWq_Jv 1f_3_ ߠ Vo)+<*[asTPa9j.ews~QƜXV?Wesғʾ*1W7*|(d1W +=tiVpwV׵g+|ʟ\;Ha5/\k][JTXk_SXk嫹,_3S.QxjW}WzUqwʏS68/a)]y?SX KJ>On OP*^} Wm?,OS (+y *߲% oQQg Eg+I OQx7(Cg>R 0[N^ VxjoT*(W wQXI]bW(Aa%ӊVx]_)ưg*^ Tx‹VUy)le+_PX-V< pj`s pTQ'G+QnRXDUU{&QSV{z^ 9(Ecj-S{;1fPXYbjS노ڳ}e WX-V눸Z3ƟPXXתԞL/RrZZU*VRUjߠJ͹Uj_J.QcDU.QKT,djD KX-١p/*|ZG/UХjciOzp ⢥QK(|La_ Ӏ/Rt eNlGsĠes.R8 oU(_1UlL6~lgW^tc9 *6[/a )(\^-ߠZ_TU߶=2Tqg@r,+Qf\r55(sP\2G)D)B,@)F)A)Ee!J"r :;`1J%GBYez(PVAFYr:(7܄e#(܊B܆r;|[P܉ .:(;PEe'(< !QjPjQPQv<^Q<I(OF(@#ʧ(B3_P>C+P9?P@'ʗ(B7PBP ʷ(pXg/#Ã"#Dxlf('Bg8@ir p*!rJkQ@Acq&Y(gA9\PڢrJ;\ Q.BG=J(9(tF JW:W;J(PzEe@A(Q EFg^@2 6~=خ`փz[6ͣ1P`{`ՃzUV=ة;`ԃz1:S6F=ب`lԃzQ6F=ب`Ӄ}zOm39P`lӃmzMIgyM66=ئ`lӃmzM6=ئ`lӃmzM6=ئ`lӃmzM6=ئ`lӃmzM6=ئ`lӃmzM6=ئ`lӃmzM6=ئGg6=ئ`lӃmzM6=ئ`lӃmzM6=ئ`lӃmzM6=ئ`lӃmzM6=ئ`lӃmzM:mzM6=ئ`lӃmzM6=ئ`00l3 6ð0l3 6ð0l3 6ð0l3 6tnt l3ð0l3 y(0l3 6ð0l3 6ð0l3 ۤwð0l3 6ð0l3 w\P`afmaafm>ð03 >ð03 sð03 >ãQƠbPơG2ed)(Øð00l: {Þð09y8Og͠`îð0: æØð0: îØð0l; öð0l;98 þKw}ɾ|)_CϐߺӌaWS/jm7󆹗RK%;%ffb\3/̯˜K6Ozac c(Xfw"ѽDǠ'&6b#hL.iR@r1|#Y4G\Aw̏_̛O26̽<[f#'sodwh~hϷ~8)e[_ PBq xQW-4Z)# 4\o();ZoP|%gߠibI-)<|o/j Zk})|@y3q /0} ?"D~|H~#7Ghn0h"/>2wM|?dn14Ds192|dϡXM&nbh%)bQRab9(~8N:RJq*Ʌb6((6x)y(b4(^8bx}i@q  ;Z,?(N>/zb5M(Ρb*7].gk7(&(곬Q(("GkZКaE-5F(V/3k"ZѺOvIZ7O$ŏRlJ$tb&%/YOڑ5 ͯ 1N9b,(69^jjh~b(1P֢({H2ѼHE~W?(nyDZuҸA)C)XbC / q!ňhY:a!֤5'5i]IKj;0ϘB ZSa1>!_BfCq?inN)bAS)ΤuŦRHq WKPl@4khD_<6,2: 14y1,KUɹ='eHY.OS EWMʂ҄2'-^PԨ8+Z@<`0;W\ƒ'҈4)R7,l+vw&,N[?Lʏr*+*K3EEǒEǒEEEEEX£XX£Xh'Yt$ !MuYIRv[DW[t)v s(ƊH$X}^MPͦ]N%?-n@'=n6at5I0dl8,Lw CVɵ0D*Gj')HbVne,R= I0Ej.b_9aтFԩ><'f3{V Ҙl)z(] K4;q9aG{IAYU{w,TWѪ`L<o˓PƱ6*$9i.9.QZS ]rSZ^'mw< R=$ =6Mkĥ0,e)ĕ{Ej:>qO\_dU`SVٖ&E!,d oOP ]()8 | Ue5JS6hK{SX}4OůLsmQ#_Ra۾=/|$'=y/1{G/$bliu)G珜9;FGOkTgRѳg1nVTb\3ҕ3f9!bg$,Uk&75oZ3'sd F #gyLИqvh)רnҨTƷ=zq~}T❏JqSR 2j9vt+f9.QL}3m|lMbl'ub%.3z(ՕRoy#{ӏ`Be?SZ0)3\CHƯd( ]+h΂xMB@$kCS7 8S.M$G\k X=xh).pR`bchM^Qs9aRC4-ttXc՚3φ 1ۧEh,KQwHpDo"l`&oj˘dxDHk.b=ódZZA'>. sy-]B|&>zL(1*S,(ɞDLAC2vUŒ"el8j˗\KqAX+DGK|TEZY/`Ʊtjo+GbvIʖlx 7j ljMd*,(AZ5#L$ Өk ,k,K,4+\]ޱ8frBMO"I[%@Qk16jUUJ[Yٛ5)5Ҍ[hʄytfG ~ Ryi617R:b䞁S" ~B߬$A6M+cʍ~2 Q()_ؤMؐ ۘP9Ww9})?"11$;۪喒~-_-mOS"mMc*vcDr2EI 51_^KOnlɐmzɤ3 0jr-R8D(_ q]6!SHHeeITeȆi¶v{ܗ%ܾ+1S 3!_n/>߬=~I5 Wג;͘KT[AY- Z{V%׶#5_r Цos;KzC6 G_Ea` f "D*xꗒ/osv#⸋G|Ռ2{jK\\L442f;uLwԆ9eua [%c͌Eb Lj!k&it3ea3q^vE}EGOo>׹hZ5}}}jS^ӚeO_^'o -p++8j?[g_NKONnVν"W)R;H] ?t ;mnn;ߎ1~p'N om_99͆ANwKN ҧj}|I" zXM߮ G'btO H@ѵeZa]xͥ1w|:{WW+ެVӣ"g>)U͇+bCaM v毋i| H8]JYRd!}> 75lDg,+bSj˂$%-sN>ҕ'h@ȇba\\-&,{*v⻂ߣǷF-L m})[۰\/]aC%y.y:ayǚr8NfuHb삡'uΧ%#F7XZdZc2- s7a a?blV{VUUmXڥ%%e/(,X#HkKy;:||\H,mJ$;~^}ßQ8P $G0~<P\T>:2Ụ4xR ),Ņ۫Y>VyjR'QcmdDjt!qg? @JhA$y$(; d9&e˾TsmRjN"q,kd^#&*蘫8 !*n"Ls$ FU6`9rOQ :2-=q'eP}vAV;b ˊaV<LWW:uկp=ɲ`ĐKϑ2m#tBҁm!!w " 1O^ P*^G7u>kA؀>DX~ru1sBi-}Ֆf=SN06_EfSJN __I:w3W9Q bW I<(e)'/'ӏ|\,]m%*2eZ6=–:Ic;PZF{x8 h I pV5UA`tnd_WWNlT;K&:*XWGQ'@[,4a A5PZs؊KFB -[!8 8hV;K'/OGyI}M [SD+C2b'8o4%M41F{Cs ~Z" lzf}-%cktxf'$^QGqj(Os/nbT/PV/J$h(.\ͮUi#iX#ÏkiWg˭a1~eg{krewkWqgiˀR6Cs:  HBB(pw 3j«pt$Vzb1Py,̬^ kRz$_$[5Hּ_C aj"|&ѻأ ,KLZȍQdʥ6\ef53 xQ'$<|G@"6?xBk^ s{{ȦV!Wk6::8һ]Dw53Iy{]֜Y~"J?b&\>7  kgijCT ƳLz#^\kh?jGzҏ ~d+!oUq2k#3gxn>_C|:iG6א XäP&8Q[ƫ>"֡u^jɧ2Jo\.2!~7:Jtj4EJ#i*+3Ksj.y\D aHsבOHg:Гgu]f8.q=r#ůL |:"b tBtYr&\En&n@*"é)-|J?.㯬 bx{ob@U4nY¶ cxixϢO9 G ߵ-pp#rW@/(w˪ᰯ^o^~a@}_-hGai 6WHqm Jnn8ڠgnHXTgjMoFY\ڕF@Uh t9h(jKJeHU A]HAl:SX Yk C1'VhyD$LC 3%5DL=jgptSbIH V#M  eR8i(7`+SFl{R෹:m~os:mN96|ۜ~K9'ʄgM8NyZF#% =onG-GÇ{1\rs8bD2u#AbDF2[P v;K5Vq)N[fAަv=쮞;tĴ |z"R]I989Ԣ |pO\a~" .nV% eϸ22O42woqc>ծ;[J2P{piO֓b>W$E"ZZĤU]N5OLk_ut[8ScѮDkQ{ml+fTeo w q&-7j#nL} o&3Pa4[ΌɌQdvF_Ϋ.1n,YJS3QBSr.{D4Nj"wEx {ΐ L/vq1C"y&R hcYH6a<}XJ9fi+M2{=zBfƇ&wB PS{=ZGEqj_1¢:tDΩԞO|[`}ov ʻ)ɛɹ/rƽq8n:pJj+׋iB}(Qx{\4|biXq]!mLO SKĵXa.54?E <-[pqE-(38@[ .1%auwĦ.R}R},/!oÍ7j4ܨgHҤwM7~p@SA+D9A%^4 `Q ;jӁGJ \)I;"s$5 aQĬĉEWGt0@>wȹ&GcN[S }8YrHPޮ Ʌ ,E QB}ϔTeV[IthSV NR<&]+&\UW4bl{TF쑍#G2bdш={d#HF씫{eбS9`ϋ̉`/B7m6zHNBt-`S"OC;cf7o<Ouwnm8M̎F=D!4"Ӹ伳V Qnn&+À0wS #G2.#2/U <1a*^T[' "4۔- 7,]=&4c+(q^=D%jl>nS4L̚{3+'ӂ^vP"У5QF _d$%-Z5Jjy8@B(zF-.wC Q+eo s+֔5F(g_&Ƒ[;%4ĩ,FzR *8Jvj% gse)]9^)Ko];΃;^<Æz¯Ikp|ަ l[E  ֊Ƈ,aYHQDki^{ InĠ -k"0FN`=ur2><Űsu7ZM'"A4EF3",ǰwm{{򞺶Ĥ|.E5"ZBI`Sy_O|@>4 S lgp9Dac7+<{XֱJ&R0a-Cy-Coy0a.JbPY[ YFCC1A/<4عpT< vyaàY< 0ZՈYP\^Zwy(gay:˃.HZ`;;Ap< bu9gYgEz ]{x:}kސ| ,$%o$Br\gRpN*U!EYI(y#9j8w!D'p~ bQi4~i4hd%dd#ffǖ<]|^|~Mvz:6aE.cҝEɚйe>ٱ|'j֏)!Gv@Dbq>0HN/wx2u+*n\JQ X J=(+[^7 BC}N$_MM\@:>{€* 8MƈD$+tDŽ:nPb}R%Gv*xh#?HS砐 8n2O@wH/kӚVV)33!`>'yeukfd'ߟ Who#pu7 e_G f'Q>~Wfss ;?nΜx1㢒%J:SIPv̱z(J$ò9Z iiuOF Ξ),J]лΒ؆"ۑ5/+r#LI{eL-'r2k@WFfm1d%d_5Ŵ>U=) =L2GbA3>qnOAnO>' x-J~6WTmvJ+R U&^о3f[g[<pKb  Dar໒ᆄ_}#h1$J :A4pi4>Jl~z*dnzk(<) 2wLgxt:MnGoZ?g8&6#Q@ $(r>Z/uo·m%y_ ?kegd;dcvxM&ij93LPslbXF^}/UJ~WAğnbKdjJ?%A+m"c|PK§,Yև#N4(ap zЃG )8XGp%098w`MU7W:gJMд:Y(=E$hN\DVN8J* ȂU ꘆCURX YYBlhoeH+Uv+̧h)veԳD(kn"UES)hYQcI}jKoTM"Y7s"3M@j1UVyf0GOSV푭w*iKi|;'q8='q m@{ G DْR6&)+s&Uٜy~l+kɖvYk!cL4Jf3w$DA 5簹 2U!I f3Ow-`{}DT}\aGc.;U~e\{}p>< '}F*"GX>:(KT57Q)NIwYwEwQC&IdF~ {8A+#xV -v0 wk؁(PtG0WO" \\Q󡰃^/b3R4d:#, < 2=c>~ gIԆ #BpV yR34i^y8q}4ooW$R # H$(JAv=8cU 0-7N1B2ؗm]ġ1Τ@s;Uω0IXA,dv Gb{[hhTf>:nWmG_7se7.b%ubT7W.G%1ˍlRM2gi`6ǡ'AHH4⭴K05?-) nT/A{Br{Ppb/ Fjo K ?n?,11<;]FGLv²#՘.A7O}Cu*(A~ˎV|r1{ck{P\TFO~ce|2#QF=*j jF T~ǒ iWT㮾\I~d(@bsߏ$L)*MLBa$. )mB:]Ԩ@ؤ.I "HW.|L$0b׷>a`-/ 1 fibG R:G?lTEQ茜z@XyHp IAXTם`04(gm1bdʺX JxqP\pc'DTawݛ߸71柹YT _RdJI\zd;CTسh{9KW$`@mu)Wjbͣ(Rڎ5=p_LY,VOWlLŽ ͹0seMs X|e1Rb~X^L Q%=$z7sz!93ʔ4r{,m0Ir(ܾ1aN7WQ69s?CC|H('&\XƂ9fl d]vՈ%UyG~X1X,cHM$@wvTZKެGϔALu\Tj_dD2 $8E *C k L)~#iՎށ_WLcs1ԇlXk=/ Y}Mx]3~5&R7ke"qs5rG_v1vOD*xEXڂm/ڐ ^gXy\oFT&P24tx~?4K$n<Gg&'i4N 8|,YUk.X)|tVJ&;̐sIA5s!s9JWNѶ5&N{{z&$Eł2 E6ҋB \@(#@pH,^ ]'cP0aS|G0&Jy\y0)!;(HJ|χOWC}LaVȯ%$){ 1v6 2V;0HC^;T6: `xD@={nN|\wb^oGV68a' 4 zl ?^ƒE0:@lpz(N+ed@J ȨJ?YBq`FEQ Y&[)qV&,*/UyY8QϹT:{koJeRSN8u޶$#p*[Khg!ӧap>Y`>Ȭ4/ZVͺl]6k!Het۠lj dflEp%qfNCj'm8*8_Ą\fBqBHi:Sgi; S%$A{_~!o~cՄM轊:&j&Sx7rS::i(ؘX)kvt=@6}zmd]X,}8iP /衩oZ]Ɋ]JG֓;1S/?{DMB[mvZm TSDm×g}[a20M*F5SKܿ…;출U r_V1,Rq6o-wR Wr=7[V_K2)w U'CÍ5j [ilj@ߕ"U`ȴvZCoIV3+= =Mȴm j^Hf./v- gg~0E[}C%s4f㔟̀zϽ{Uf3@ĸ:8s@<f&e~ uUR551-u7Gɏ*l\ygνgua˃BdFJg`sd5c͸a€M?.#ՍbG4+fkٛwOVٝOZ g vիnهcB݊.Kf `@?BDU+ݰf߰֐YUE=V4W0n7c1frzz>e%7Lx_2rqj\ͪIR4@ng3i4ـ.x u5dm߉rKi:7PK2rAIX>fY .-n-3D,D$k Q9ަH]QTZёE䬺C+qBbۺѝB vФE>m gjbW ղ[,\`Kd\'=|& -Be-vw`n %~ňśCl,#jNT}ޙD'DB o8du$>c:Ȇ<(a4;N{b XYwx6#kj"U6I!O^BgϿDE7v#&a갢(9d :OY _AJX0,e8{3q2N'CUS=W]|Ur M*Q-f? 5]%\͐mxG bLeW4X&a6~Dv޵khW2G-}dҌci1B.pGnqU2ckzh접OlXj0p/J5Y&6i sqMoM]Ƃ~hz\ӛCQvѵD dI+V۪Z XNndoĹdžʰJ$]wt"\u*_!ݒ|GdtVSWr>fi*Gzv&Lsݡ+J?tl= x {eH';,a5fLԄ(_%6' ]$ :)1AjnAXae\@;_ Γ|(=_vV*R׵P }ِo?kB QEP>B@òz:n})f1R}DvaL(.Ie=. FsPdÅtq}܏ Hc{<P6]}\joqN!؂睏G%s-A5ݣe4ȔЀҮzL-Ea09g W"#c/PK0 exAQPӅQT0`{ r 6hqA4kl`,PKpMq3+3[lT|T[5:1â@CVbQ**ouFlפ=Y7>%g& JyfhteVqqk@Rli@v㉱C!5 fj9|>` 1 [4q_'3PN"E0A $ v<t:UCs,&+饈$CTi8N̪$T^QiN"2@yyʉ9*5Z:3˔_ps cEJ[% 8xi ~EwN|GWz=h  q n=3W^ Ÿv_gQ]["a"`ŷA/xj|'EuNFS ͇3\Ua(Jۈ{{"s/CxۉBnb9lpMVBN` _c"l dN ƁRm"/үUsZK[іଁtR aR'bL'v%xKRV?3)>7?S*"5c?gʃ| ihB4Z`F f'*/@*+jD7_ v}VaEm/Q eWT#\0gJ-ҥAZjYr[pgtsvL3-"ND;.1H˓ 7sSr]'} ~p1:b)6Z}،m& F\F$Qe(a1#? 4s2zΙ&EYa&W܋EYcrƖ([b;ƵY 4?Œ%vBZׁ-'0θg9}7,iZG?UG-ycB6D̒rדrgQ/*( S`QF)8B )VO iwYCF0<`{NjjjE5UCdxnSpphvvOKHJ\/r;XJt'8e@0pp.6&"83S=$ !! MlݬUhUJLh-@gf.%;9̄Jηd%3AB Ij`.XשS({Z9UM' pKn c?׬tCdpPV CE0g+jb.V0ș~hmo=7f(ܲ zy+ƲSD뉬7c1QAGfO V':Wx /?@U\=y# C=t=8o&*|G*c=56) ŠdyѻARƹ.gMJъB~ZBQ+ F(ď*P-j΋(OLr%AXϣ&3MVSrݦКZ>*gq"wDkK/!OKյZBpdd*|*,E+ZQPuVJ4j%lf0o/)AJ&*_E@?SB/.z ,93r7 ]\rg8<_ڲ#>y X;a>Ѷh_L-T!" ^A#WV]Q#M]Td}*GzκS3JD%! E] ̲ H}o>9`ݿdz@&dgDBFTt\NF`JYԛe"k6ǔ-ʴ2/K:XA;KN)OPx;,a;Huzg0x G|;];WV$7`bàA }b{0+)DE99z.IQd<O~fsDui+AE߈QbL|4 6T@+CHQK؍"~Ip+QxF!^**AWP/Tk|8ETWՎ 2Q|_ArD7hM6i7Bd(э0 ر8׏Tb$J$4i wDyn\E߼f]29ݤ=Oj^+0fʱ["PR!MR.ydYB2HH.huŽM9_!n0'S +obj2\?$6̽w'L܍3j @KտxQj$ӇLSd*}힮v4J%{(,\@MVCSҸA Q0!|PqSѢ UzC*qBPx/ŧBOardcTj~\&?qNl]ݸ^\~b[8Ǎƿ|*y0X>%AYGq/G# rO^ÆۈޭQ/ltQIU[ 9Y+.qPBr>v`w4 6R}Q0t #J&R0Ěy 1c7G{i2n3Rg򸤞ZSk@3XP,5 <3"auν9_PAǩ3*NmJh|c%ؘۚHMMo;aD~ q}v/>?'ӛ/woo޾{n7PDX<(bҮŲxTEH4寰8 Z.]h6NPKZˤCWJՔʦ+_w&6H$ۚg&Q3ʩ͛t&|5"wI9q9De~j;,7ymVӸs Liɟ3'ՠ4UxW5_ YX$-ƒzNyjl36.S7qXj7^ >{ 1 >a7J6qaELW{lkd!jf1-v+.=_06 A킋P<.1I ل+d/M՟ I*? >y7ְݺWKf;K^ƅ͒Z={{R 5zc?a4/^I*W*׳|GO07J5G2Ę[>[]нc>e&]B5mqSମ|:]eopg7.w.pbRA2H& zh?*q8>,}$ؐs'j.%FJ)B&<PKL`qT6Z3drj#$ Bij K v9A#nsVp4ect)%ZiT1ͪGFcimD͛] +<rۨZšģ !hIl QpmZʼ_,NسŴ&S*瘔gmH8OnzjL|B;~ )-PKF;$F M2Crypto/DH.pyUKo@+F"@"(qC"VmB؆jYrzh}3;VEpϱ|z -|˺A4D.\\]]-?2,SxbUs~5Q1e%*,VJHH |Fϖ1|`#Ҳ1\*kӔְ#u=5މTSB<K/z||\)3sD5&؀a*̟g~_gd~4X:1AuhE]%]y;\v7$"qj%"t~w[89LPMož؏"؝ -{+ m٩Dz V7|%/B3Q{n\o-ݝ4[YF@(s:M4q(yA'pzMւ7Bl\/Ξ^oYMoM kVr8CHW,KҐ| ,bTTnd4A-(V4E0MÆ^DR}6)@0/m@sI؋WxgYE vd*3 JpؑI!Ͳn!QlDpo(}W2xrOhBaA> @rQ!]5g³ttbX|"W@kRzF;M$8UURtLI h`4f9(˓NT4VZxӚPʵ%hoÛ62i xvAIU50M"\e/ЀG: eYxFIsBl@d`sSwh@.\|*0> 3](eVӆ5W&f)"R@qg;l'iSi*U'y1AY{Ձyi?fІuqDК9C0x" |ޤORSEyEFRs^^Ʃ6uI24σMjCh$Z̷ v1'6%=۷qTlrŬ|KtdSQ\St>VTӪ 0Ô[*J&t*lR&P>Bof#;Z{b>7WeJZE*cx cg(3SG=3{9M4q"^e K1 l]RlBELl"9g ٛCF͂X5):Ի-<9%0!H<ջ U?'BAwR[fu8):,3gƼqjp2Z4c^wV\U`a *7|3SLZ!/Fw; vWC\x_1/yy3W7PKF;ԧ 6M2Crypto/DSA.pyZm۸_ )Ph m.$lz_AKF}g"R2lص-gLޞۺȍuYTYyu\\N˪ \5$rgϞ[+WrJ.(:ILlXn8\VI2hr2ߪUՊ.xF5-s.^' 6˄~"HWWЭ|NJmxydTJ_}Xk'礆d}6%7%KyZ׏+bA@IV ;hk\Pٖ25] 5yCɔH,)6LMw/Z;_~ʩR@G}X3)钑$/D%7TO#a8Mj_&䊞FG6mk0! dg\n@%-dwOwMO)UOOA*8jn|YU5BwU ^6$"j)Pxx6C-C7=)Dfd"EQR2HYַ׿%#U  Ҭ5ؒE[f.CyeƦA^p2{k5e+}k# Cq3qSmZl$! -x #''5iHd8D)-8[J|e||`M+JJtg_6i.P񞪩'x!FN&,W Qjd2@6TA mMF D NsfāXY ܽ ]%۹z|M&C[FLFNnw1%'-x̆}UrطO}VW*zYπ f?ʈHbd&IoGm }r:O>oNOg2T+aU} p'q,\I/j=L`aN~hztЛ :pŵGqXzK/8Z9~*Ք#O|w&|񳀬cO@ rUs1r q7[@Mc.NILhs^3A?஁|c 7s3ahX̀9 ]/ⳬ${0P[WqB&z~04<DэL1t6Gءuݾ,vS'/"/ls}~)^ꕠ< ֍ |!Мg7hS%]5UXVZec*`]2C o  2gvGZ;;kY6!7#acm[( dqdp0+qg{Pn"Krzo~?7҅eS ԲB:[g3ow'j2صXNc;v^7A}F7~RiOox\OhjJq63AKi}9fS^ cAۡ^<=%+*Wm}8)mE.Be┌Ez%z[l?А TԪySut mo^ȓ74eu5ӷz.׷f,rZ@ͣsw+ҝM-ڷH-׷b15k _1uXUhtLeu)M!*t=wyqׂˏ':)yuA!ǫbҌt,0ݧC{ZTpԢN_n=գHdJ֦8BOy7 F}C_Bk+VԐ6+ڐ,tUӎ,VRLB*'{78Px&FDߺ{kF@`UH~W0bu3I`!>L~xzӚLw(tmaMYe*jtxy=~jz u<DKuX8>+v)>SLϜ(`mra)}8E#dOU0L=XMwBqr۬@%PEMO?=+,Wa+[Z׭ށ4Նl"_{1u 0G6۸Fm"B{f׍gW8W9CB$۹o]_^,AX8}:c9bUs:ZAs1T[ChU+\ж` .ZI_H\R&fq f_%L;ZU+X u^* zQ~]K8f}g!Xbƭtd1;K$);Sv7Qg;wDYo ;݌o_qwaÚaucO @p/N3#r/k-֌ABߓyޏQ@Ϭ~yR>ڑ[;  eW욖0[xniD}S(puL;PV+Nx-I{*s@\{vqƛWĿ;kzAGłX\EwL+VhSD#2> 9z),-!aY胇U ;f81ǬR"@&A .#a 6\`S|u(~ =O(gL!B(EK$ z>LsbSxkP I<?i|+dvOb˻= 50Pq*벵<ǁJӫrR<_xLYM~42믃T}$KP{ر:tUů(}AغeYmHɒeg?s,A+ֹCUIX??|Yж#_Eȼ S^Cm0g,$>zr#Hx-Lo]ߧmc9@ mX+Sw*]Lq&%yaf.V2UUnlVYm9ڒ>X&̯yEK!&=˲4[IiiTulҭM?4V{XGl|\>WsA*AXd/DRĎ˳~nz`"ncvIQ(+RQšdL<b0IV gj gCQ!)gX1_E F[HiF 4gܫf(޴!w#"bKK2*!T>>Y'P *C$S/ 66gRꆨ!31.~Aߦ?ix( %"VW -( V򕜽3DB ;D=bm*Aq.&1@ӽ& .O(AgpJ0+]&*j1ZK}1߇ ŋuB1x^Gam21 *qA'^0%tqqxK&.cN>bшh ]1r"ۑܥ,)\TeK4Am&BZM#{^ǯW*Z?ai4+:1Q+"k7a#~VƸΙ/] Eq?SY\ʌx`G;& , UJ`7؝ ]6Ϡ'*CIg\gBBҋ۟q?tۉE8`Si:,IȫѠR;v׊R@+G 􉜸RZ6(n6V16lb|*xiTGE"ÝL"?8WCo;)N@ǎ#/72t扏!o`gճ+&X{q?_}֮iU}.qZ vWlXfi%ࡢ^,xT /t(`zT"KVX7%)|("zJ$&QvLebibQ ͚3 w~l(xhkڨIڙ^ֆ F9$ Ek[-퓥 ›% ii#Z9|*Ȟ".SCY&]Jj,' 4vNuO ?28f'x]"|%PT vAhd͒LDIa?&aGSrYɴRUI?=Ǻ"@`Y&*<6*첉{kCv ~T10Drr9YlμR2:bwR1 JaY7Pn^By@` D -m%r`"9Sk@T++2~'ƑMkNMЉ) xs~GGxxC3ra!Si6NJяU>Z\Iz؄>Ez'%y]vYkx"pYTF& B1B`4*v6>>W D74)\S @ cێ44^xay{q{{?uhfuf7E?ҽ:p%^Rh&l/`T%=ѦYy%ױf. om$b"YEQ侀.?e 4;'$I7om= robxU%8.voʼSsVkRbmR[֖*:|jj݀*M T^+<+.^m q:|ûu^j‡er/ "Q2GR .9U`.& /tsxc] Peq* GErKg3)!TO$U $!%xitJ"v*vKe}]BP^oH T댙 @//)Qw5 RM4fxXu|~ߚc.>|8f{붮ZtAС&?"HX]r9ɝG$|gKfٛӦܬq1c(.(?@{@aqﺍlͽxL ڋPb>h7.v&O=`YDG^M0:G3==hþ{I?Mbqjn|O1 a󨯑l"$ E@?1Q#s1R#S+9$) M'!u2I͝JAGΏ7lAv㌚1jz۹U<ݯOiYKP8x:3Y#hF x#$_)$BY$KUXY3VS(ۙPiqG.,obdPDȽaDNkraƷw,t}/qD$SQ\%CD xYwG}܋{?oC^"ahrvO06[-RmX/9l`?|10gދxlUYm/Z>W;M.#ܘwQV&A~{+LZ9ß(dOW0xl 'z{#)7%nԖ.V~7דc1#g$(G~/a0Ri-ۃ㧢MY^эCq+2+lN)eЏ@w { )ϰd:<" LJ,}C >qZ39&!ޔuŸft<[Ζ\6kkտPKF;uc *M2Crypto/EC.pyZmoF_砐<K@{ .Mru7"W[r&w'3ﳳ+_\\?FwTyRUMɇݛ <ܒۏo xzb.exCGʃ஬Nmw -o9y%wؒVې9f&))GAI]||[J+T?Y ABƬ`bA xA m-ER:xM\Ӵ">#@Kh-YD $p%RX\>px*p*p,#O.(rr6ȥp$R xR f.@.51Z:U,+:bJ%XXF.|#K_Ғ%,M 3R- $\۰["h-܊ܔZ" W4mTqu驐K& Z˙+A.W\ $xg{ {t)}h @#)}~c$oxŐ1H1iTk(xQQ<;JﱽG7~%ŕ[3o[3bn( _FC#Ņ/3rUR<鋡@J+>H\Ǥ6f>)üsaޫsWüxyxü/9{3̻<ǻ|"{11G8pyyd0cIR)IҴ\-Ô8w) Z:rA"# (a8T8ӌO+@ksa C4 xr\5Jw}"iR5% m Dx]y e^"k>ךBBp!p{[`GC^P(@ ;M{j" D05hwx _xY?iBWMR\0T a ?#(S #9J) q.'+ /0$NM4Lň#b/ZV \`ME(0+0mȖDiє*X-SQN _S6BrSqߠǀ-k@|-(OfkN6*ɞBu!+;WP2!jz,;¤66uT(S_z4zmCWn^7e1$T'Լa/`:-<V&e>C[QX$_cMH~kرPAi #VzA ਖ਼ӧBl+Qe#ɲɋɔ@"I I-*"r#~0n8~I]̆OP9iC2/_:>[Zqk7!2PFwHbd)7m"SWp 8g)iEIyz8@=Kã_2PejR-w%ig0""0^6[Kw|N,Xer窺&g$OoBQvqvN}0:XR4%)v%gMS8q$I rC|vay- \yF~P# +R ˕PN{hQ*NԊB*Pʺk^Z9,<56rfgthiN{s"չ 84{[ gjPS|hiL#z Gc8Qiٍ-̍=yّ3PqQƊ )Lʻa#ajf}l4ŞvJ[S4dn>7|<@ t#ذ~N Uڒ9RMͻhyףn- :Ќv>ϕ7T7g ' kJZt;v/ܙڹz{nN_Y!}kާnw*Ze>T3uMgT l=-YHl<=Y߅C.ˌ_YFę  ۼ! ']}?y؇qw0|4VDnݔv*UizK2ۀM&s0Ae=CUxNm %> kdc/?RK< 6h N&z.XnV wE]čҸ oArXD /L&YVPl֩Uv E zٗR hEok8g@=_) XQ~唣܁޹ rO"M w@ŬЩ4s. U5 j-Al 7|;SL21g VE.7PK=_(~')[ @4\Q So^@,7 r^`З1c;b  Ld` @b`43 f19N8bc3p=' >a`@<`! <OA| s&\ԪǬ~Ip' N7_SKpOMpx e/Hѷ./A/Ӟ0n'ċql$_hnU S-aQevGZV4_:]ƯjT҄5lZ}j^: xX[9-liwФy4KV[dkJ=@I~w,ZP ]SjKj %*$T%9GΈTD(3ZΒp ,;yb: R6Ndz>lvg{^Pa)nj lEg |qoK`` Q^˶X,WsՌzҔN#"۰hJg/!j%+#>Y@*1e{Iq#V>"l(%cpXy+1c^ ݳ+lK|0OhǛI:m|GFv|NÊ9BsUiıܳ\F">ۙdo"` YIwj2Ă"FөӖɞ ZVr9{r[ڸSdhF2*s;vLbП5{\_Ofw$k;mV>kECy&`i_t6L* '&e-<_) 1N['j- 4ցSu'6IkhN 2#+7ly;>WtFf?0hsP32m3Ҭ7k7xRM(!Dz6Y;R?&3f-`(,7-\\f} &ыSdBR^V4r+xd*Ûg)J8x\ZiB=f1?5tu7[oMn"L/5?Aam@G}awʭQ,@cw)"n{t%&.6sJv-Gq9;GcEu.>QF42Ui9v FݯǡkjxZ(hW!ϥ`ߏPm*z:f>\F/`~c([&cC$45x%b 8^oWfp5v렃q%@P=Dt#^@toG x8ՌH'fחN 簗&`svLp#]KXzccG"+H:fֻfu\ebK$v*a]KbKV/ d_}۹*p퓚cY&JzL(!6pwӆY7FX.d.T#]0闎W[>m40QMf؟O gnJvSVnO$-VH/4;ϘƯhyz q 8IPdqʣ4SsU1L_uҬ$.'98!Ϡ1G?TD*~:R?pMzFJ*'tIlh7A_0ӊad}*I+9BS6xtB3\Q&Q? f&p'I NmzQ@:_{ŷ]DzYNcdR&M:YJ&o<5oPsw %f^6oSBC''i.mg2Z(I?/ΟrÏ8d(Gn*+O *8p ߱ jW#IUz^[vR1{;ڵG$24;zmefDA+]d<KղS8@[\]DL1J\qd|sϸgt6c\T׿NYz󻚡AJ<''{=CN ƜqѨN8Z"-3hN*j#Ʃ Z_i~Ainr-r>#ۉW8IR;U!Rh?(2ïq¯-~Ȭ^Z lRi~M=W]HHdP&%,*/ɯ֬E ;Mm"4jt(%7}NGr023c'U&C.z:/6Oʒ٣hTbf)NƔ1{lT6Jl8igڐJ(7,?Q"H8aljg)īR+Z=7/EbZb\'0 ^n5P ˚,^pẐ`Ei $x/xYYU_W *ed%Q |8!5<7Q+2t C;b̲3Rm@ I)|^Y2R9M#E"489;۽^rZxy@2;EʝJ:0r%QgEܫʴ,ӬS%; K@d&%Ҽe)Ke"4ZVSj$ԛ3tJJWYfV9؋H Ս<X=؁f{J1¡(ҔEz/rզ||L,_zԙQl{9CuX\w8+ۖM+ehH䗋mrwl<[ zr(%6p ȸmaC˟hi(\9@$lX̆/ ʨl:E}MMR腵< S6C7ްOÅvs 5[;X~ VOmɀ 0jCgCVKwA{CYG='kDg R:#2gދjo7^7amM h+цiy/ƖyFcmɲ4ߞ1 FdTjlY1l:`cTW9PAKj6',Zȟ|]>+>q]ejcuKp!%fLG} 輛ƠHJDn 5O"ԛ3V GJfj2ӫ>j5:ë6r;Wng62]oŽaM>ϋ&4RMmx.~FnsWC@|EZ7-$ vkUN =M 0#6n]ץIB,e{#_==>PK_a;bE;XTc5fQjL6%c|IL[S,hQo` O[,X 4`lDѓlD\e^LL2 a7GCzkN2̛$/ZGg 䑪ᗧ }B a,,J8|Й| ^r x&^v؛HU-:$4S=G0 a<azOo?{{axtшj~.$\["bBSlKH"CHK0E"Ek&EhM㔟MJoqx;w|-i . _Uh o00O!õ}Ư9|wWDN_8dp8#$kpmv?'35j#&"KȤxQL $$C共Dd?R?"OB2Nbs^1Ȝ/hGtS96敞p37 ݞ2)5ka4X 8T65;[|sRbm%- Lu\ss,T]NQvN{%n #Iv8yTr^KbP )Yc6Kwp5LLXGtCC^N"ןn0Xs,ҩF!s%ASecJk0mE~Z"h}-‚3NYQ&Օ؞ݕSaHF;E&qt^# *Ql.Fav14L aA%x_JΫ4 f0<ϥPa^"#o)TIϕNŦ0IHr9Lb8"ɣ)ggOO+hݛCH2$9qQϝaa`|?Bt:SāQs6J4uCk Y[y13[>V@.!ϱw]<Zv /I,\:o!; @Òػ{ ӞGzahմm4vi7z{h%C˥99li>jBo rsN[9m6+ j^Lċee#'BNWhmw|48sqt|ЯPsg"aQIK\zK{EVݙS0ӼhrTۼc-f3|S`l`z)&OWF6ھVCEeByWff: cV/Q#1K$@Wc)~$XLSg /M#'cjgؗ5Eu! k*0[s`4]&X 7%6L5hCo}IQoC:〫 `e%Tpvo '֮iѬ7PKF; xcM2Crypto/Err.py_K0).YDp= *. I:ݷkgW7W(mr眤4ɽfƠ-Ty5TUPo5?|h<5Nk8Jzc#A^IΊ Wa鞂ʽ&ۜK'1V2@43STM^ܜ';+;љ ⣭}d4?3b8|PKM̐O9v5,gofvFǏ+?uX?M2v;ga8;sI \&k$ԘPg#cAB-ldT`p5rdv6]`3(˔2 ?/_[4=~k z?:>>>8:<|쟍oU2:M}dӹ7*WRɾ >gˁuf݀0kPsLJ:> |,lij'8VƢ݅eȌNPH# P^Td-^&3E$ $mD.m:aAE5 Tln%:=b{|QZ_D !nSX\xU"JB7C7nhxgi@5UͿq+qNEi4Pc:zCmxϒ ^/ Q 5Ѕ7Փcfx^V]/AU_.SGK-w/0(FF8$6v0w; ZnV-GRQ9 mHH6!zMkJ[bpyYҪ74O5 xx. 1)obԢN:(2K4v$QQ THafUZS`\a:MB|6=s;$S8M@i*gx _QT9xF-:e(c(Z׸=#`XXbysMHIjB⊁vHCD7˷M9]Jʂ&f3r^y=SfET;fTlHߪCmMw=f +t @7 $JmvfXX9FIWC~h2yTfd\ପyÿ젶pcStNȴxWuKi;zt5XY-\(%"לtQV_ g,P 'e%K68&[VѪ~:2fxcW%#iR3 Gut6;QvF~!0dloH8Bfhv2>bݲ 6KH(y;n$SWj!Q$kF=L[˟.D]eI/ge 9wIٴWZLKuN1mg4i{PY*MTim z$jn*Vŷh^o&]څ}wxv)< Q*x1j&PFz_rtK8z>\+lQ&b礧3([9z9?NH^/-bTXHm_^ ^\Lm"VV; >vimNP8rNyBP=ryc;#b' <'eec_ՀƂ@ਵe@ Paէ[Qr8JtbڣvT/w:ۤjh]BC붕z 6c]7 S1/;c7!Aq@R!p*vi<c.eyՓOBfEaCYT`]֐|M.E,<ؗ٩d&~Ĭ6(. hz*Nߩv$TׁAhO Ys:VukLƱuu#3OSaʄ6 rŕ?`^o1j:fKna= "/N [J .ue6WSw)SǩH9Vvm&2dgc};Q"D&Jk'o:`n6VdLj⁴²X,mai d<(aܫ.!)Yr/CicA3&~As]{ `&"HO*dTxf{1Nsjh}yԝDӟqCXBu<Vu ] ]& /r(6!!-N\]~u)\<-ys=-@/eap) RCѬ-!ݐ׌ E\3cK$!x|+CpD,ՎBPc,(@**dIZFrF|\ng>ha1+u]y$ή 0Xdžl9lh9-+"ͣ:QԨǎ}iNi2:WI\5'V-l)0L(!< hjY9|NץNU5|wg2 jm`|ԼG7C&)C;y !SS8Ci"/w ?HC=A:8 ڷbWd];8~b *Y|\MţZWV;~`~sbT *aPKڂJIA`a-TT^76vvx7街E-}.ewF '٣KW]e%0mbcǧDM,]!0Z숱#Ď˰8pm8sCx>?t:is^$i in{$ n;!0vwo:޼uF^ݬO]^|$"0|m3oFۄ9OśAotL" F0|hR0}ppNQ s6v$^<~ㇲImz*Nx]q]~v:]K;zWuJh ܈Q~ԑvc|Ɂgo{<ǵeCk MDSF~D« MIojHxXGF5CgG$!IG||QIjiĞЈBuom Pc@zݱ٧6=%PMX*އٱ @JfA\*P^Ͻ^汓3@'\TϹ+\&ݾ}inKHd*NEcG԰J\R!Jt !Q |@~ѹ2dk;^ R^tJ+1Hl%3p L >fF$7?"9Xz!Mcb̝0.3u*lyX}//XcyGȿRXu^-ܝ{75T\)r6xzq3Y+YYk5)TkVtZBcNmNKOGbS2` kHJ$LuUJYJUO\WKOυ1 $yQZSepe-xT>v~v|ڦrլBDvu_:Q}6NmqNQN" #gPkQաa=EIԮM+S,UVQq 5&-J(%h=&cVFdn./[,"N(`K`B(!MX.Ue]JiA 3D:W}x#l3juRQ@-IEu2ˎfG:HuCQ> ^E}JFͪ1EG`fQXGmMO+C*xG(=yΓ68Ża8[igv 0[m*꠵LL)N үO_MqIJ!YKmL" 7[ =Oj א13G*#as5dG촆Y9O+bތJӏ:rB7:]cd3ϋ\KX^o[Cmge› sD/ eyfiRJSui<-J}c;%ʣ2L-mEwb期cyʙlEdJd,SK/coyyy #"n!GuQnVfA%+@\ӶH d0b]`w^,1}4% N|C)\v¾9dW-fӋ~pƀ݂֜8̤wۤwse҈(K! Y|,kyc) n)Q4m/햬e J.WQfj2#OɆt(r>T݄6sZ9yvk BZ>/,3ݲ݉2ySѧ6|slP}ȟaVZq_' qS)ty`lkpxVpL.݉],~vQ,L$9Zbq&AXQBޙ)r^ȫ)L&Jk` M.O|^ԟ\_-sK4I9w{M0%D 2[i&M(&VrϥyV'~uM{Gt'OY xԼvQdV} "=O9q(Plyd0}qTrى<9G0sĉx@yJ梿Џ!p8։W V}$SkF'n+Z]:F!{r%DKD\ IĒuΪZ 9AL=mP1% \A1O-z2cLA\A4:D`(m ;#L Y!~B70T,Jk*Z䪪"0Z=4F$KSIvJƃS8AHj#y{"Y7Cq2}X=Jr .l{16E] #gnC}yV9M6GD!QѲqIۇ,r"F15r`%~du@C֐wxZ ُZ$P @;85Qo7*nVD/"&p}Oa'\\ztC;Q*!ݴ -}?`ڻa":b;F\'z'ʧӜ톥6-$ "#K%Dpr۔rkQe|6]1vԪc^l;nXDgsBڅ'h&TYj< sS*)QWlS$Vzivq"7T2[ό5(=mw )|Rœh  ijn0z%y>CHgd'k9!:Ojϣ¿&#{IzΠxPʽ M g\0=^ٙں@ &:bOKɅc-6$1u۝BM{ bW*qh\ __ ϲ+&˜QJ AR+!%Z*])s{jh ukhݢm. FmwC&2q{󢠱rl{? >ȒyKvERHyI%I = dٍArzTE%7'9XHIEV_mc5$9+t5\Q\𡹂 W8 *jHI7/c[T10(}߇5)*w4FIQ{{\ Ax!܎ReyF-o v#,h%h$8fU7 ܩ/od5e7?T>?oV$tԺ}^cMC!=&װ)v~=TQӬ+zyF,k˝`JuKN\ oW`K!!ϡ֓8R%^׳`^ӰDZ6sÒI?߅H2 Eץ/X+n58wv3kg÷e4kMgw6nM8-+Ή`qc@a6)*RS8[hXBS"V0Ey:t6ն~BG҈~B#9bE<`k eUU Y SscƉF\up/M3ZwFU zS)*sj-SA&Cڝ$-h\&A_dBɰG ɝvrT;pı}dc9NIF[2BiԐ$siGJ(c^W{SEM6)?pI S_z2&h@BW6*ORPPB$!QPo؁0,n+sc|'rD\bLPOSZ׹ EItf-~gE lhP'XxvH:JJyp?;:@0?W(AXֿPKM9+I脙srΞw ƟW~{^+ !}5dO#=JH:$,~hZVe/BVIJ9ҟNT<ϓ~ 7H gH0GY2yW|_C]5? li"cꇜE!xbokFo$dc$#0L\hzOԛ652i6KHC kq]:H1] TR!oy=۱8/z&]ulT$<=Z4 "as]/좂,eДyԪ Fxbߴ0ȬD=&^ht{o&(e2 yĄYXD#}ԡ6g6=Vs>pguz珘ZC>0px!Rkn:ߴmh'p%[N2ƻqx;sjozSڼ &-ݢI,0eyxR->v,SgeYxN#[Ay vs~=Q9֒)oR#v`Nm*{޾д,eKH U 3Ɋd}>T8؝, h4ÑSr֐X4]E!Ucj6< ,Gfs,XƩ$Jc6l&,;dM͖2G&}G60YSȬ#Z_#uD^@iL_i$H!"1Wk 55 ;t{B(Y:|FOXop?H@4j$H4^"L^i^EP#tpc%tM+qɽ Per],>TYЋIܤ9΢\Z8bʹg[ ֎)6zNgPtu堓*< u0&)тaw`q7BZcϏAñFl8\,h$ɴs򈾠-j53S?]!\ukU8/58`_ՐΨ@s>R;os̟yXyiv YN?=.7f%Б|DTc㠃ա*=ܙy9`rLy!TV^:v;"( U۝ Ewhk(ljֳ"o̠gjƓsjľvQU|] (Nӻi5 \U\fʵjbRꍩLQAvA ,cr=iZם̃ẘKj,7PKF; M2Crypto/httpslib.pyY{oܸ?FR9>h\s ?z A+VK$eg[wԃjm7męp7/z:pr)wLU%fK!N)ֺ*E&KQdZkd!{ݻ)bk+v[EY2Cx:N&WZ|1S'm@ORlY-* X U=<|TO'Q{=mr;bwB1Džn!g^_kdRV¥2 &?t`laӲ"ۚYJHI8ڄ;!eZ:1;gYI(([ #Cd/_+U!XR9#nm;ߐ! Vܝ%l3|IE >gPi״JJ!Zi I&>/_,!L uj9Kww:4ԟc~(5*B\ga/a4{؍gpsa15Y6C\m\c\Nw[,=yJ?A6e5YYp`oO2RpuHZ QP) .] r֕\3mUA|PI\ӻm%J# jh%0qAAyU͵ئ6k'\֜S0TlVBIWpS;9ۊF0nq_S4ld'Iq/j;=4.V)U#3]Y3yAQS]k:l<:k<}-V[HS^+鑗Xs>Yg4 y dkcȷ [\Ky7na.ݙ$;3J=)H CB?^]!^#c+'l+ 벐}) q-6L5ƛ,a;59FtdR¸!\J@+S)ddgK_I~zA,Eƣx?fsMmmn=nBEyuF- r3g⛢JL=n^Ҍ\<9bzalS]s(%G1W +,KMQ+0L4 m1ݜ "!X\tE=6ټ-|TMӻ +5eHt_9-(kqhoQN`U~)B&qg+bx6vcݞzV6!M?O8ֈDI"s(#&9ǖe M5pl7fC{ax0 lljV6=3̭Mv5R$~uWjR<쐝JN4K (N{^tK¡T0d1 ߱{=2@T{qե;I[#O=]D ٸe n3n8XSWEa]5X]0KQ Tx73,(ws^)ܯCK4;f5B8j0s1%??p@$?MyF} YHl 0Pih9UEttɿPKw܁c"4oO˪I(I8Qz(rt ц<8ذfWW-\ktY,:sN㹿-g=*=-;)q >)HVĵ6PǺȗ^ c`rX\۶cg+>۫0 F0J~yt1t+2+eЁ1]iJ|BEӝcT7JLQ\IPPby64+l"Դڵ6bD >xrAZXzwQT$4LIQ`UpB>-&JV55 B 2>~EZOkX38dbzN KHWhdX/:c6vjK6S"x;3"qwz"LBSwJ4y|o|OBMM8f1Jo;[iI`i|F_UFDOB-k:'Ҟ"D*jpfzO="ʀʁ cYk^g&2~cOQDA?J배ONlꂵII)xC[kAiM5E& K&ܜ4J|[Zvz~)J#gk>ËQ%K뭰0k1t2~IyB9\PmQL3NȄ 2OC @8L,SᦡPg6KiHZq;~N2P?32)# g/(N%N5)IpAN4fO2|Ӯ8LBLLDhPhYqڣEEf ^!1x%.V0˿W5E/ :gs gT#\M&3֢آ B7J m+l[=yx(o{Wq{\ E(Oq|]C~&IS7zZgS!Ҁd$ ulÑ.c꼵AN6c4*R6Wu )X8ԁ̢<)zi)$[ګ8fK6^y [0No¯@uЩ]jVsV̸o-Ażsɉ |%)d p3G.k!p 'EFJ25 gsW˚C?Jp3{g'w|(opݨ179aXfi?F > 1D [2խu:3P:hx`Lvٿų(leY}6-(@|u.YCBLd @' ?31O6&ޱPua ZD!Ӥ:tvߎypsI|#$1ŝ856YLD(G:`3җjH@  D$#5yf ē$_̰Ӭ\Nex! uBӺ$(ND.%G]Ѣ;%u>h*v{|j_X[|](s̳w3q[6{Tަ0),t΁uzEݾ7z]R m^tA}zȦ5ҏBߵ(&8LC}oZలDe[y>% k ebVnl8 #qC]yUeQ> AuR d٣!잳p-yF;pYga/K{1aqm|&4,ŞY'|nb:Ųg.y#7[OBo3uX#cn뼢C$"-54H6cl?TL/!aF]؁i>-REM}6(m|-4o[?3n7Isx kڕaP4gNSǙ_ЙV,E: /ҏ[ɶHal?e*9W|Ɵ8S1'ϴ&g|yŎ8⬳RXvݥPKF;TͨM2Crypto/m2.pymRKs0Wp3aڃssaruH„ߕLS ~,~;;A$P“AU`1h8(VnXp;?=s/xLzQ=S 0һ58wRB4QOѾmG1"h4 9ώY1vs +&E] %b PKïd읣m tkf3~{ة#.p&\Α*Pk.kõ(ԏUtaZ̹!s=.q&w8݅F;&[ūVj(uƇ^Lz;qB 4f_x3^Sej|B^I%>ˌ0[Xt䱭}{UA>~9:PKF;:{LM2Crypto/m2urllib.pyUQ8~ϯ$mȶIvw=i[PoPR]ہ~8 8 ;^off<>*+*c.G[Iht]gJ (j.JZe":LR5/+ &www?·C%ྮ hf޳" 0NImXS0GvE{{2X.OG ϏxZiSo?FAP-Hڡc B|M MHaًu;4܀( 04=XJv9E6fSwjî=OgРks(OzX&-УSIT1X4R5N8DgWQSwALA񫑖h(rKN<\AcAujyH P/a2>rHډOCFzG\~©gz;،Mc=$G 5*-_繻0}UǾQN@0k)Nd`Rl{&$<oݼVTB2&6`IL+ 9UyKDbq;tϧjX=7EN o82`MY=)T^,Ԁf?jh1_s !by#L ip㰛`'8B\i+r5."l"\DC>^Ƅa{xROH֘޵\hGm^ 3{nSm-+l'Gz.QxO|A^\5+eY°.V3Ke[amEB}.WV-pgI ]MC}]+3v|عnnF1HӴTJ*B(($6;{Sgg3ރix]ܿsΜV|C) q]p >0lA99GNC7i@w8r Dw~A%-CDF~& }q.B yt2P(T*a{cu6HZ+8?uooo767_8}h 8a SN,/o3m ?\%\\ge0AZ[YpFg\1r G &30AOQPS*ȓLSCQoxcm2LШ ᘅ)`kr m`$CO rf]4ENb!9r969!`UњZ4Т@>6alC(5BXL͂^` x%*lz`M0+t^`xTJ6ٻtx*bIũz佩zw1& *gҌF:#Cr <(AlQ8Oģ>Y!mD_WawFsTg.T"D:WB%KRbpO{.E6ΩzSꊻҥ oh*^LPUF[xy UЀlS/`'oF~;jo%L2aϴ.r#sXZbv-"f[Fᙸ5\uk {h*-G{lXc6U+E2z\Knn.]q23P݌璏i,E-XLg?PKF;5M2Crypto/m2urllib2.pyXmo6 _ 9o׽R=LUH!bQz~ezïOji`sՆD+ڱb9%=]ڪ(t lk~]Ɉ]X٬_\ʿ߿`;pT˝{JJvC݀mQ=7OLoY.^{kq~6J+ 1ef#gw6:]8I"WmooaV}hGz0=yZfPcԽs?SX dKtTU}cV7yãϓ Xpw h$Ս[!Ao H-0Z.ϟθ|XfAn+r" 3}^}CjE%a2e;k/*90^K2(gz=N)Lp{zt.j) a&WBjc፣ŸڣuKVziYzȱ0J;8 CυHTeUy7 )'D@#Bfhg$Dd` Ҁ t111EbȂ` oxn5`36Y_VSEy^] !-P5ܡp =p93RCtbc\Z=lx[TP- ȁXh d@~ko# D56 i/Gz &WDMӖbUh*u*nZp}-d6R逴i9c&6 v ,uBɨzGT@5mA_@_p2n)kJ$;9nҦQ25*}(ˏ3(s.WuG箩nhS:ūO}~c⨐6N2%:V0hv! {\ZTC xAhA6>cx~SxYhϢ?Asӊp-:gtIXGrΈG*b=" H:{=kM{!:td#/₄ LN5\ഖ@QsOnc `v6Ta=ЉѻTj%>CIgӸXS Xe:c'z1 NWczCv9ȶCZL9XbG$GtK*iB21s?Po௾~؊ {KDe3^PI#v|iC U/;zs%)"b >_,ͥA`QTtcT/Ï)Ӗ/ه0[uA߳ [6{̏sL&y=aD@<,:DGab:fj\R)N'yɣQoIu j| -L GS/6LG@H!~w:F/KgAO)Ɓxoٯ=t~쇁 7ӝNq4 P PKB:2Bo,Cē JcI!G<}s?y0ẋ2<$+&"([SG E_jr_[EaI҈b\}?7 lLc^6RpB&u!/1(Ѡu1 =0\4X0y'l@9u'y4P\tǝEf 9l70a#ߔC-N?.JqOR.궅%.;aag.sy z4JI $KtPn 3d:ΣFmBC1'JPO7|x[Ƿpޞn "\ б)kЀ cF J<9`i(Rϡeh Am A]GM2,:=z2" O-r 2SmuB\tj6n?P1pc1p$`I5uk*Q{̾:fa9m&?f0!9LRIߺ klx% 1=h"k9y<4VnJB"o:/ ӯB6GDTΒ)gnZ肅Cԡ3X‘:,% xN<86I{L^ p $@Jtgtj[ܩ]3z*yw={ 0.9631̱A}+`]X15Z0Ң2d`ƀ/s`?]EybШ8TqfhTBQ@v+ 1 hӘ"مyFa@S ^ނàe/uϵT``_%+VP?P֬sU9c5j)OG#  s-Nc=ص#@0KHț:שFLj'Є@yQR:JՓf.%^ۚ^71tY{1p/DBusai[f@ ۖ>Ka+Z}`E*#W,;䫬"-X4+v[sL|j]F@!,} =(beSP@@ІS`͆~d6Myw$9EvD+ӺF:o"ljehQ=86c4Mok[+z]ُѠ7< jGoV*IxF눈x$ʝ bcsVߓ9kWA% ڈ?=zp (\b;Bj`Վ(ꗟ .s[9z]BL*v$6 xA:ƷsxFZopzyk_%ٌ6o?F8TPP==8/UefC51Rk.#~ָM ؒvm"Z]f,ےF,O7՚8R{O"VjS]kЋJ3*v^:3Jӯ{`_ lkoG7\o!AFZ,a{p~3E1/I 5_(Mr*7.mj$4~EcDi;B⚜'XrOCIǽ_vWúQEPz.Z.47fN4f4ByMѱh_&x^1s{G}iYlM~ZL\:5F}X61h)QA/8;K#ylIL-՝U[;PKG2{*{(jngRF{_9 eH|fZ({S"c&c9%vBUK 6%Bh "H=T/Onwt`Y)^N-` U-l,7c˫$hSU<, ,h3SCy*V 󯣛NR 17&3u1YKr7h{|0"$.{FL07I5R)8q7ظdlVdNL&Hm'.G1R('KcH.uMMV9WSZ~52PVo:8dߙL)s%& ,C72$p~C4Ed1.U$;t:Íw!*( piZ 1cO`y` cBf!epy O%ɣ!6cQk*Ubr,ܵ6"Zv]ujEuy^%\$Dl)Xp#G$QJO)DZN&& E~Tz=Vw~TkK.Q {%_sMrDS<PKF;e_"jM2Crypto/Rand.pymN0{_YRBC FrccAy{BJ"I ߻+cg=7ez,c9LP۱ ZXڷ(<sSmS < X$ITH9S?tY3 ǻJ]mX֦o93圀 |g}CNg]}DD@T@1פ)Δ.Ly#q}PKhGK@.nCz{ 4dzԐtАt˗wg16OZK5ii\~7Jɘ~o!e2;Raxi*ZSy6'(:\&lr%ldAv{X-qDXZ/ElJbr[x.ы}MغKc1>zȐ$h$yiY2׳ؿC[CXw}QCa oE mQt:͌;9lJy>m{ymp[/N:x'N;PKF;D&DM2Crypto/RC4.py}n0 y uzi&mm>lD&]շ_ P`(W x_nqc@hAua kA;ɢe1yP^ߩ;HpI P p LpLjԀuMX|uTRBXKoSMSqTģgfNv5Hy,;Gn%|"‍$4ޗL".vW]K<6Nx,NgB{e /\|PYyA%|PA(/vd-P)ЋF醳N vis. O0"`9,p ӉrI=Z  N`A 9")ΗHLTc@uCKCUBqu- E4&(o,ܪCQbF~sٞ^8:_AЩAtmZٍ,!.FFglioՋ'"*޸TPFvՙwOT7c7zZM(7vs|r bbbaAmtB)vkMi4aQeL{E/4'G ;U)PKTF;"4HD H3M2Crypto/RSA.pyZo8a!wmnbt h[҉fH=7Ml>oɫٯ徐9ٕ(XIyI/G_b_$xBΟ={v:;;{J^͚9_*ˈ'H+, ONNFtS$b/JٔrfJ^lfQQ!pYh?cVH< FYX W@o{ "kQf`DlZjQR-wLLtp8VO!^:}sΦHגVd;il3F.̙\\\5E:^[S˧|: 1ҒJ<]*dNuTt^Pmi+2 ivXS4?{x730SlRq< @QE3N;Ûea1Bpұb,ӌ=PKapȐKM-6i x9jxc `0Wh<:W F.oJjJ0Am=8,Wl_L_#eђ!ډdL$ xxqAC~%(qL,AU:oˢϿ%Q]rɂk Amwq8i|#>i2eX+'ceE`$rJh/`$YVr}9;:YS>OȔ*Ff>FJ%ST\5dzY5Kg j\`%K>_yҦ$Kxw nM%Xf (^u\SV?$덥 :Hē0GR;^4T;*]7|uccf)yl=Gdj&7hEz;15ѝz]|W-1[V}ΨʂCw$yI xuL r$ 16`%vipPNp3; Igo(3@P-`H0 :2*5h#W?JC*04ৌbj (چz#p'jMthF"1N͞R~ )sOg@yBMcͪja=x}< .艋zwQqP[x@c_wH+z& w-q8 vb rh5̭,|[j"p>B;Nxт.ߕCOr'_NMл18f?x0r,iL nŮ؝jB>{Dzb=YV<Ʒg:I2ꖰ{8tŷ/ '\m̰]b7=ZuKq'CԃFcF;X(nZդ&~@+P/ 롑vcӥ^$,UXix]SWRE(d_'+2M5Q"!O76X}9>Oʡ*än\fCKyJ8f['DHd˕KآMBSٰA6zSYgq d`ع& Rus1UYB}MF)v* Ku|g Zb{+qsNqĀiq6F-\Ģ s[d9Mjq-EKFhsαS.P9,:kꠟ&݉U<Ƚ+9Pg5wf:#> ÕSo!@Hk7,݁%1-x[h8qmQX?|yǮlaT}CGX'>[>6 AxvRC >3#nPޑ\wHDŽ?ɀs6gTnƭֱ\s!)i9==Ȫh8ĸ#sm;9%OO{G:UiW˳,*2Z#Z@X 4EQfJԳN qpr$ 47L|nH` lWݮP:\w;?PK t6ݻ1v1Z^w^gsGmzY> R|np.l5CA/Yoe%5)Uw8x=Io;Als{f:wysW.L:1^$%1C5\MCh錗W&f x6/)"}O_$2/ : YBꗨduuk<aV,.xf|_w8H8F!V2D6-RvDE~tՒ=FPx kRASrx;ݶu%Pbij{^FER؂̊$Yǂdtm 74n^mHXjlI=EL9R]ڮ $tU$Dd>9rTHGz畠šB S#28ԽW/y:C}?OR6ZΡ`L!E'1ir'JDhauUh YyHϨx ;L}Y6Hӽcn$]n B ؾ!)AOr}CA[t?s$GD~gy=>hM$Rvb(j!OGGNllg֏  rbN`1 W#n#ͷo@ 0'=A7@zXpŷAl9(4R N11e``(ڳ=ߚhn,N&Py]u}9}SZoA7(kB@h喦PufHӘ ڻ/R]Ev9@oG{;v8FeK0ncy{U.^Ex[pz}l4/fD}+FD #?83 32/$P-YÖk“LK b6 %0$֮`+2*_3g)Q( I%>9B Ӽx.tJрMEF?DL|Q٩ 6Tjb(ɩ i"rb ʠgMtLK&Q7xC=ܐNB"1Z:-G>$o4K&L)r"7+S0lVΏ0Dw`ٶƁ0,=PCB8?`2v``;-ye DMU9 y8 S='3|~"o8C2~@$P\6TRzbҳIiY(i5#s?Qg˘-` JE|xK`QxBiY˟:a=7kSNgDZ$J<4&l˅5.rc:::p5nՔY FWsyſLT-u2+uTCv3L.)X$l 6##xGە2Y0jZ+$G`rꉸg2)XҖ%(`#EST(5'_J7s9!=E".X 2~:ab0+Rs1R)~z|+Vᾰڤ-*ߌw4t@W ѐL Dݙ4:0=(j|Xv|+S#Կ'dNV$qQg3Xy"s*y/u r, |'/ ,#k/|pl2ЅcƱCsN  ["\\L 9 ZenMג#ڄ ʂjH\۟7'[Nr>zF^x3:i%(a ?N4FN.>r?=?=[qc/yWCPTHCr ʴp6tlMm=`<[UJWC |]'TuFyjeıM&}pYǷ>_OΦ)B DRs+ OA(ڝ VJꂶ{:nL4⦚Q})3g1{\ ~/M:`x*MpH8Z?!5n0H.Va¢/mb$36Q{)GTAU-}]_ԫg}Z^.\_^[^_^.UWPKF;^ԋ7M2Crypto/SMIME.pyXn6}H"( qn 6퓠ȔFެ3Vv $ș93gx|w+rg4?wӻ =L7g ~#gي?GNhi]vVl:: eҜ(ti-Fm8N"׳,};<9aI3APQH*nmIqȞ "~J&:jX+*B!T[:7| Ta/}9ښϩԉjpA 7 mG#mՒ"V-ۣzL?Y֌ڑ8@2ӫCbAV)其Q.>pUXa@j<\ыo]hT N B$ jE`Ռ@&`>اE`St6( :Wy ;" 2 + ם{eLY&}SJYqIQVXY.:ْr%5|h^wYV2|)_8 :Ǣ*=& K1)U]k|t$kF13~p~ӥʘku0> ,aD PÇ#yebmaEĚ$)HS@zL}Nm&jS lΕ"\K-۠SlLH%^SwA,zPJ( ϔVF1'HGWѿrHН Rr1<vbk40Zhh{ ^Zm+3UԥɡXŋ >T[dZxw֯7,5ukqw' !BWA)ʖZ⇲ g+nƩŅ# QEV\萸ꩦBqTuxH r,Rي׭2f٣lh>\aJ);\)փiKv0#Uַ %aRk[\E $L3~E;=RR,g4h/8sˊ(ۗ<[ۦg}AV6vu:sV%uSxGsξBK|v3@3rp %HqHHvIf")p-ځJQі9lodq`oLD3;V&cG؎/xW]Y:b߼{n AH4;Bʺ UQ@Am""gONSHHJ8w#3g ;j%ž!`g;6ޗU-]w]y~ U7h,;h6D#.UwēǷ 0#@eW/ڊiM?^hçK7p[(_PK'#>a옱ghz9gر4 Kxh?)eAs.S.4'tiF|í7Z;  vMaժa&puN'*þo X*ov܁caTᕫk UgwwU{ᡅWW::QAIW::ss)@c@⅜K:bC90XV4LŖ\/$_ 7‡qYVμLrCb9^t!x=xף;<aȐyPCgзU@ ]ύ:LQ j=-FJ^ B| Opc 1d,̃&J]'FQUݎ` j2`d۱A-|%O.&i7|Dh"cz($C1V hT-o^RcvB2>okt8t:GD3ÞkB9B]<3a"ZҖtnN{VJYq{:'068xq}a`- >NM;4Ae_œǛnSFUFȑÊާi Ei b:X"Sck j ޱצ!G? 9&| t:"L+V݁C%y&?-4{cC/ #MN4N;-%pƧF09 D†p!UU;E}Fjq1!d94z"{B J%<,̈D&Mє,@UIӠz\2 h>aP]e.~ĒTc2) ~YoynD/=wyM&Z_('!^hmIwBNalygcW]&;|"'_VFJj{]VRb(\5IC!M#)fk5 cY _.+$,;s&sȖHzr2I# ;)ݝ^d^n1yjڸzah?.O2=`~ߔ| N_ItS>̣\s ۦiu8ʧrJ:{ѶYYqA|;8pū( ]z)5RL r0'H4 Snd^:̰Bk8/yR$sŇl?cRM,511YAa"C!5դ&D"+PUTP{PCԯPJ;z\{THf*SH@X0r08"KeAQvob,S yz $M!t<!aW#`]}Ҁj!V29_rV *$BHմhU&qRQ l)V1hNkPBH`X"'`4l9ĮE1zrN\#R$=a3d_1ˌcRk*jȱ>*Nc3?3|Qɥ22_'Q(,ÓD>Yʄ#~Scjiz,26*wȁm&8;Ȗ9ET߲m)ף,Ȉ\L` U=tjbT&SVj`@1@"Uo|ŏKK%˧kR_J.@ɼ~Rexi*1Y*w|RS<ʧ2:Pʓڍʲ#X=z^{TLu2upI`D\!LWN9R-G]KJRk#SDH}zSi&Z4?DHT?[S'S sz^^N;ױ0:(cD6hum4Zm493i-*WBqh9߹YUxW ,UxwKHgawp"PV*m AEU_9/U}yc( Fyq_PKF;"\1[M2Crypto/threading.pyA 0Esn4h]՝ $c;&1M=Sr ~/$m W*.ؖش޻rO.`ݚ=$p#;(X9 \VgYMt dpi >F *0W8-e:76|ꃬS&憴myme99o%PKAh [PAPtC< 5À;:=9c=4&H*R7w%.q^f}5A%"cI/T=TtvmvZCO:e} p@~!'oܪ}7(fqѷ m h+8oͧ%#q2 S:ㄫf5HB…T LX72fQc輀L2hS3ZLtgi7v[De`|㜓2'FYjM^PKF;B)4M2Crypto/util.pyTmk8_15҅-_r!((Nۦ,'igьhOٳmP{!ojwip߳6)(L7\ W s`v˳(_sPXP`,BߎGH ׺ˢ9 b)2tE.K,4JM"΂!* 8o<,O"ayf}>kpYx}& @(LUW+@FO,U^@ʼdtF7Lfwf6e{Pbc riVunh8ͶpKJmb tt;y -vII;}P0=?m28|gA BJ^1p]ly|86d?uᛥKi]{a1A з'Vu=܂%ibAa $hNM/5{ WY[L+$!O/2L38ocfAkY N?~Ǟw&t-zzg4_ 58<έX!!X߹q{.ri> ^gW*M+Wdbdn=^xĝ]a/q<UGbuXݝW88,KYߟ3,4Nn^_w:.$DX̚|EwFF7s$mm__KSCzS7z:rކ#*6R!}&z2[&%PjbӘ@)آ4aPd-L |%ciA67~MxV*x4=U^-A ?D^@ 9j2۠m,FS6YĘyqOsDi& e2k2IWP]xq+ŽLx?e^{V+jQUyUWe"HqnE MkxXri-lQ]hM~n0ziC~~^[\RN;XxlS@I<$TgS-4 (T9(6pN|3오-REͫf}C#T)*[4=99`z@`a+˾ ; m[ºwE^!F)?fѢ=qȢl"v82(zU 7iS$KA/< ԦpjI9r؉Μt/r}SzGfɥr|<1+8fF7r@gR&״t{66QeR&[<8H6q9R|)6YK/Ta0ŠңneH/FvVp_fI?5['f[ſi Y1 ZTUjVYeM>UA @0 @zi㽄I:1S;  5Goʮ3u8qh\PZ.@p_Ì; Nu߁YH}Q% eښW%k۵JX$PKF;VM2Crypto/X509.py=sƎstj{/4=qM/áɕ3E$e[/?SM[$X`]{qRMnx;[-Kv|f:uͯ6J˗߾+{?gW,SvjVW7UBcb ԲÛ ;* [5Y>?FN?#AuM\e5:K6>dKx5,^LuY3Ҁ'@i_Xo#~<"^ H.G-׳nj?OHN*u Z}#6WÓ'~f,.R"eZ7U ^2LN^>YVlpx[VqVs .X lY\,W>Wq}5SBN%'#6iw5o,f$`ll* >&x('|\FS"KJo~0oVUjvcK&~g/"PDHT;lE50"mQb{`f6l 0usٳtΛi*IPN4m; sdd5,e_RRV%bA&.YsE1EYSWy(چr f!hڗ˸[?yUj6I,xH6+ `\jx8ws~&;|dZ!I0wt + MRŭ855`* Ėk:,m,cÓaC7\a"|xqn[mYu^7Ym2 0|3ܭY_e5쟔;o5?f8V4(HNPEhn Oq: I iF` Jx1Y'4p&LjM$[<þvf xR!j;i΅޽hǶkQrœ~86$ 8,|j`:ktl x-15˖Ӻ' &A-6 - <+6R'u ج'_H`yI1;'_A' ,IuɂZL"{ U#rf2̒w* +[VgL>pg37oNaM}a RMμ2~SN0Wp6iu۶pbkqGzZ:$[wb354_ϛCo-[*46B)GghMfaLQаq%g6g dje =h'UsZ9l i.agJf&ډ׬LR~KPߨvQõRGX>zǎIER^$I7ƼwM}wU%OOsʛ9X < ur-KDL\/xt0~KUrHht-=->Hm{ [¯LFg @J Am9f"N5DF6CbSb-?Z>prTKy+:+xWH"ď&n}I_T}Kpg'҇:}f8MEɡuSj7l'7meU,kq'Lba&(yl(JˆY:7 ~_d/Ej$>d!<7M(- m{bZL3+rYGEݰ Z{t5=:7Xc( K%UqVdl*^ħG<F":挤72") o*q<_/*i4wQSrn<K2N#d(eIjtoR*:Ko!6melvɦ,i*Cb04y'})MZ+NM<=Il5j,3hIl!޴$@VE-҉>3D fݳ'<-Lƞ"(1o2IAnHq HpK-jŦNfn ZHL1p e=_<gXwET3Lu}:M8J.ď6P< Y~__ݷ,9wZ-O)&M?tʯgS)'q>)+a<:(O|_/&9ͧT5+>č6 [=L jQe1t-tU᪪xgLE-w rx>»ퟡ K.WYXJ&3(JԠDhckŭvYW-ƾVYD#͒^n=jlab4@0 W vn>pz@jeS>eu4Q2i}eΚNG?_ ܇p~2$~¡YN!~=xs25O*q ߯5 |ʁ0Oyِa6d蓞e ^auK@xAӓ`VD9qՖ]Acr,މpTȗVtHW | \5sV~sd`x|Y4M|k򏾴DZ_2H{IgJ[300L=ݍ9W}'vaJrt=pD[#e~Z]o>Uͮpu]`oAUH3?@ڲz녎LEFXFBei="~L ך*OSϖgx4041~%<V_gVYc[͛-k79[hD]"J]&ʤ]!*=@n vܪ+ q!5T;LVA! }$^CGK~k85B;0,#>TFq1yi8Ҹ4I\ڹ AnI<x~8S­C9Og/^zJ^:BS9{GK&~x4y?A1 @~yLíN鸡捎//_3{ysn| յ؜i=g=Μy\\5\\u֜kM|.2C7r{C^ ˜ޏVڱ똷#r [9鴽͞y=37^δCWs&|:I`g-ٹn7\]ϼxoӟ߸Bsˋg2=twn=yπju :D.A3Srnm?S]pxvng̾|z]]}AL ГΖoC-ypIN#}1Qz}xF1bPyʀC*+>~0e yP%qܧӷJ O);c!=O&G0}w񭹰~Ql-w';n+~ݺ~xn7Μ=K/ⓟ:on9ܥ _z7ۮ V. *Ǝšy9au&mx+>.Dnl 3Nʂ>\I 5J-ZQ@7 )c BI  YqYq }CѐI%V$he :Sn\8. H4fb"bw k1<ߋM |f o*/CMIp`@@@8 "v9 تq0)$"& B:Rpn6ϪU5h/ԄbBN,Q *q,)L0{,`Ut?y)g 6_9k^2)ش}لz|JXkVǕ7.Pcucvw1 (9;7{G?L9ZKv)vwS$qM [|Dciu6P$P=>X [?%qaXbc)\d DCˍonQ`Q} o+Cӧ7`56e16->LW30-/KWt^ADr5<)zuZw^]W!"y*A !hAy1 &`CR{Y42a}O@U]̀Uo 5./._ؘf֤,YV* _aA" Z\}go{>tT_uFv+RljRB[++n&uz4"grNd06Rc7Qݺ]-8h*A8n,@-`AW Z&DQA([D)Cpp0zC~puZjJR+Z |Rm.vn*ᤁ*"Po*چT]0/ȴl!Q [ ɷ͖j ~+OF-Jo1k8q;],|. PG9RMgK+RնMD[~٦Dr>@Ppnt zZ,$ntuY2cU~M'L;h2Ek%K2M4- r{ςU `bRjSK-hz*ya0N{J5c**).D85dOOȗ9 x\|EN}foa~sȝso MaDeW R5N\H$& (4Y 1&Kni}LI$Pp9zFHgP 0ViŤ|]zYH(ri,fsʶJ;(eN',-R󞳹ۮîQ:bwQWF#lHbAQ6BBu5jӆr:}Z,BإO^2+.~I "ߗFM\Fki]I5%*()6K6[dzn{G+lETyoQ`td1c%"^L Y ڛ"\ɿA G$Aԟ2C"t+ܐǰB*"}lX6{J݇wGwtzJ7;?;| ">f⌜t P 6)xI J:CTmw˾+-U8Kpʃ o^*ߨ25Vtsy[RpFzL7`9+BJ| V} ~k\UM]ط&sw%U_ z>OT#F 0!*3bA'<@)DGD(A : AprGA'GscT " '.z @AK쟛dz\xHZvۋ1~ھ˃o#Xޟ_D [@ op68E<~""rˋ(_#jDF,2Gw6{h%O U]tѝC%魬q,Vgq@s(>% NfDVq5u05.5/B8La a6J{ºEBQE c*VE_O丞':1nkRYf♸B`$1d5w۞_xE柩AUDHtVTIݒ8 pzME|(CS ψQgGdsP" țz!kU`l ѭ pɚ(}$#I SBH( @|E3q8F sz*;gr,E%R2: CueK՛IжpbP nYhNq t7yx\sw$*:#pXq2g*{1A<{K3@oMj8TTͰ,8 ܑÂa""PhQ N(0,a<7?fp^FQ- 0JSlVkb\ńcr / -Q3v7c-eDJیA;JB-ح54>z\cX u;61tBTT+l.-o"0\vJZm;ƺChP#*kY3 ób=.(Я=N?"QпPemWHb̘QC xq_V~^fPȜ<Lv[DFǥ?d_mE t'O\[zC_=]|܄_k?j |ܕKs7 7.~<1` kx |#Zi õZ6o*( F͓(lmFjdp'5W!#«8*c«_xDžWqBx'W*N AUdncMy\)MPsͦ=)n3S7b9JU=F0~A~W}f$oXn60!uyoR߄yՊe{ r|ov>^G0]A>>k^n..b?#Xz8PGh (lRJ%ܿʆa}RH,o)uf'B$"eؼL5~=?V|c=(Y$vN>ܩtW \$@|{B--!t[g vw?H.rg^ˤr ~?(Zxgg^M>.+S$dX#^i[W~]0BEG&ϞֹԌG@o!v>_Y<Ƅqnf+Kw4!1X` 1_4T#Q "~H*0ʽg!<0&Y t;Q_ZIFpNqqpy GXغ/,`\ݔww[ق>3d4sL r0s%!1-w[2?Ai&k԰蜌\w^uM[uش۫T2k X]篞M\ϞNX._(\Ot`w&`*zL-b u.d~(Tg^2mZ9ýDhQ]M/ =7VN$K!V+5%?zqx'P2kl`XmY-:dx˜{.I\,>ӣm(8?w #cI7GcwЍ.gf~ Ԙ̫*3e1>ٲ儬f;o%*K2U9GS醝2u;eYvtuĝ>)03RebtmkBlA˾`͛F03y&o #ph}B>0+vͮr^UY2G򭛄( |-vbPY,P'AT"u_N,_uCVW77FxL.䃙%糒^xnfc<4PFaxY/e nǡu?|P [VٌY hRn9 $g ic~C :nP7g /7A^))SBTlO.YPȂA/_W%[_opM!op aA9ipepTz R˭H0]_/́:UY듵:lX&k# M1#ae=W$aOX}((\V *Stca3v[YuMq k 4+b딖IK1yLUv(;-NEadF q02_BtB̈́۝.,e7jm.x[";]G~A։QXleS5 ٘ԊPIEرaQ5e|".F1s?/眇t1$Zn"Xrpqٽwp8ԁq3Y 풜~ÁRtr0E% S f.NT5aŝ5ڇOu@$ddtZtD֐1>@QF~QT8S,c~gGjAJX`!0 JؽlPHV0)ILHR&.%|VSNŝ>D嶃ʛ!IZDϗb@gi%i$pe$fS-A1>% ' 7R\tY1QH40(QzDGyFAn9b' %N_N'  'w(̆)%a@`TOq;ZV_+x Is>ch̐F;5*-wťrMN\7Κ-<e2dLjZ S]YT\^P6qU=&r:w"6 !_πIv TDK;P_5WvAWC/ ?QV֪:XiĂ ;23aK\/q|WL$VbTTXB]d@Y9jCA*$=0HqT!qTx/aH*;Y);;8~yd,t}Cm +YAdZ)6D(Mh8@q!aOnpˀ `Z+ʹX)C &2%)NٵpC@a/{3^BP_B`4i+_*`0V|+L<٣1U7*MP0wf\"5O%,X+~3GWy3a!6r;3G {)s}8gSo3[% db'&:Ƣ:XE(|wƊ;؝,wgwgݝjD"F? :]c~m8 6ZJ6Z曝- nb  bw.~.]ܣ'yr]w[4 1s%v[ TծM>Rzgdwh^!#r:Wމ'}驼pV[*lA" m-k输lG.@4Sa?^s;t*;/Oɝg#wc>]щ~X8ǙJ%69[!+w'26@BbNjcg--Aذ#!*&7 E^B 3ΤFlKA-vOMUs&I܉rVG[%4oϋ6i\\,W%,$J"'sl[A %5$IM>mi:iU"H[7Dv[t^Vu\ŵ3d9=el2T.&Ȗ]!ȰJˬBoL54ٳ«lݍA_6k]؍sGkI,W;*ՎryZjT#gk>wF/\}/{O2J`dȔ^( Wڣ0 )U6u`Z9=>TϽ&އ;LNY Q{:H~jKqJ.b]GU "EJc,Pd$ ~IC&tRэ'P4ԥ"ZGRb9b*4E,%8)FZXTldJfqI4|ӡ0?Lrur .dgW,%=afEs$Ǖk0mUUH?Qi0 fr"+ V(?=չzv[-7p ^wvDNL@OZhz)&Ns>F \ي2'I =K??'/]y^VO 2خ?-\ˮp.sH[b$F+sG{,W)c=ǿYܓ/ň?p|%8em"Ɏh삝f1 0&[ /-<1 nBGq<>e dbG;_=MpƞXZXlO9]1%6M8h;A9|?ˤ\zTޓ4 '.$x"Pl0.#Twcԝ£?"B>O`@c>sr^p0In^NoJϼ*4.a'+bM0sl@I|V41d'k4gm~R4Rix SgE0o!  !S0 z^ւ!8!i-.NɧfzjTKSg'6'M֧ꣵsZ}!22qPK F;#]M2Crypto/PGP/__init__.pyUPj1 +OIid{c!:c6_yAB30hJm#.93]׽;| L@xjBQcȬB!O^DR@.8 7m,XJ=#Idq[&Ue$<̄ߓsOIKۖs?PK!@!x&Eti?d~.xׇT2_GV"ظZ\,^2w cB Xt?UuJ녑APK F;NY}M2Crypto/PGP/constants.pye=o0Ew'O(8I,$XKN( !Ŀ' 9WqK it#|$M7 Tum]n0vm"X@1,Qy$\x^sRakxz׋BgW<٪4v¼%ɑړz^hB+;PۼZ߉u݊)c PKn+=eEQUVyoPK F; +M2Crypto/PGP/packet.pyZms۸_*)QXI~iƙN؞ܤEB|+ѵ@|L\ܙS&ECmjdPQclۏ|aۃb6Y/0 Gm#"-,c?9:]pC81 g\:1K#tRr?B rKY ǟ+xAu9,l(1X89,m!4r`Heɩ!g"LJ$yr|d"gTAMjI2kL,c߀dٶm Oz_I!:Zm{=݋mbHTɡi&l)kQS"IJ9~~XuaKC;\ܹؗLC:Ԏ kjx ӽ7Ɣ4t Ƙ_| E͸)v[ LF9 U8jߴbʍ;bgڪZ^/ Pc^^:Kވ޾?8~7\l}VJku2mNy"g7 [Ŵlw,i§mp VCZQ4X? AS^'ۺj#*9DُۼR9ٲ~k"rƞX!v٩"Wqe)}2đ:"vwiv,G1&ʌ.jUtXiw*waY_ʲ" -,GhZY$yqX蠡_t(5}0Ѹ|#=p m2sx9^^%yTW/]DÝG/?7uIUr)zG^*jJ%ܦDzlCRӰɈH+~jȥ[-ŕ-fg:Dc`ŗͬq xZ~z Z%=vX 3Kez/9֭PKXp'xnגk/dmNGNآpoNl4pJR(JpҦ٨)@8"tC˜C{ҁaapY3ڨXջ|1B-RH64x5^?x'4 ^xPf@M5S}] j|a3$o,&H#l'e{[FFܣ~նҺOm7RֈGi\ uRUDmDf5y8+28WS %gu eڍF laFdkZyUY!&+X^ahVa[GI&)'C}Zm"q8W'ݡ L69qHF_4_]Vۜ+BcaZjw%+؇câ0TucFmnm[i[$P+æZqWR1cҏ2Vkۉ+\䝕m!ߒMgqL{o/>9=On[*Y/172Z"2֛*<68l }kXB%W403= be{/>>:G_솝m(b9RNuUueeYYjO,U{ `c®늤US] Z9XρE< vINqm IY[kq43v6{NZ|^.ĵs`/3qm%Y{65Y{.]uYǵ o3ěD!q#q$.1e"h1$p0EM$f8q:X 2SD4& 3pg W [ԗF?ς}Ho9 gӟ|Jmmw[N=ջMkA[{Kovl}^/[nA:imG}pt}'[כjrv|w7}WmGonM+/Ee&N4h[NOS'8'ze/;^۞kYӵ N;6qv7+:6|7W'NgK_ñVKve{?冰.PgJ5?7VD1,<+|nE%W*U.5`8!0 W~p~NH'XQ H@D^dv1u/\O5@. a /80Jv 'yR<+@H5=H> m$'}MtIuG{tmlc+H.7R\_7+sяp:VK 7*jkó9+pZ1NK{n˾AnZ^߹Z7r7B}i74͋4ʸRSJUQ-V" .J<jSk\tRyj49yZ%J`\ 3X^ iƕPh;g a~y5L3ks>+fB̲үuk B{ 5"n0Y<6gcdG/Ք)u[b74{bRZę~FfB2 3tfk.3'fitz=gL:4Sc'~gd3q}o,PHi+b$r<Φ;JOk1 ^;-69S-}c7G;hI8g9R71^Pj 6A$<7SED 5QPuI.Z۫.-(٥ebō=۾O+GX,Vk3}NUuV ]|x!pInQ&L TxƗ|&$ gC!8>x<%l;)i$cRȨSA8K9*ޤRs6H/ւ2^)k)DrHm\uͣHUFjDž3i),yE4qcDq`TpTj#F,cçeF<,b;QEAk|PCCRCd (!Ds.j`)4.5wQ$]lHr0;h U|$*lȄ(LPl4XI4='>FTY)?8KS/mUҺg W* WJUpWuV ţ?TPC5LFwLuR\ Ө؉X [T\:϶c$0* ԘlTEceը>f3L+IexWCsYe^bU ȒLD;D:"6< bX|Ác@F{F,av߅L16?(A̯k![ #!" a } 1/ }<Ծlc _sO<|Oف v#?RPC⭇kg$w$Uf*1/̙]YO_wS(&cH/E˥)JAKͱAC=*nzJ~ qN:.T}JㅒWNr ^Ů?o,M)#Mf䰅wSiV~Ԏ韮}pFnUGx}D[RB4Þ#FTFUSjNЍ^U9XM1 ƼKkLepİlf67Cĉ06g}U0M0Yܣ5yQ;q6u^#*ܵQRVȆ ZuApoƨX(GK8Ч,aRI" + CէhDhǟ?4=^)@=?~Od&_AT|&ݽf- E9 Q0y6*oNy;m0o7bQ.:-Fqv  ߨ)|d9*>9ӱc8.]?Ʈ I{c#|Ufˆa(bb`6KN5:b3K]5)ƨ)뷒{]UPXK}sxۈ7r^ q)$6iVgcn+\!F$@'ƍ*Ck| Me e 1&lIh:i4| JOҶytdSCqeَ0[Pt fBvCY)B-Dh_fym8CxE*D!CiUԚ̡*|4X?. oI&0 UN6Wsr6?f;;*;}Dpe;?E4 [R_-aMrU!qK/92#j}7Woh'-wD#Ƕj }'ĐLMVK$6L `\m"]gx[.5fh.=q) ^4)^W1)xoot/Zth.IқdR9/"( dj{^ rCZ8؎oZ=Gwz0u}t{av"W2~vlLT|kG<ub!n1\u&L>Nة^EZ'5[=Y\?_/cE%jM>}5cwPK F;Q_c~ZnM2Crypto/PGP/PublicKey.pyTMk@W !rpĽԁBZJ(19`H#yZvWqMh%k%IK3o޾L&{}Tշ՜}Zd; aD#NZ lYcم׀QN Z+Mt;h~?gI'==NYo`{w׏Lbo$yGi/Z/詿׵pWCa8x9U\q`FbEOm C%ESsڡYaŽ ag*h88 (x~46 hijު6 r\qХ,Qh0]^?&J {iN$g,zS-=3T3/oTL9c=̬D%6Ƽt*rsA6=Y!'3@t QJ|$4\rЌ@9O.Ni.TF99J$Rj{ovE;2ww0A;sA*Q& [nَur*gW|oik3I|J _r Wc.M{ٌUY)Q0뀅,*AIuFP]ڞSJ H_0da4P4>"M|i7LJF<%j (s6lr"0 p)T`/IIRH.Q(S6,)oB0fZtsM;#+Yg6EiR}psTXoV bJ_L&ulR|Ǻf޽̳JQ]r3<0A-fWg -Uݽ:Cq#ڱa" VjpRk ;>nSP%ŧ:\'2T`sdHaA k̫ZDyy\ Ezz% .VGH‰ J0It[m2[d#CɿOC2&? kS֜*mqı8?흢:UQ_yYe[u#Ք'E$=baPK F;]fY{M2Crypto/PGP/PublicKeyRing.pyUM0W !6+6P,!b{ؒޑ$mf̛7h6}}0G?oY<(}4";www׷77o`z2BɌ<ςAS6͂`gTqIFQhe\'tuE ~Em8R8-,΍$ &uX)(~h`" 9rZI.tX6_@V'4ܷ'vhDJ?O VFҒm4rK+VKJc7)zٗ}uy*z"GX*yeLU*a+2c:!HXQCSת)54֨5,*uggN<354lѧޯ-޴nZe밋/ uQ%s{!HWO樟g6 bE&+ t(Cல$W1` E7[E: 1~is3)ql'!0 4@W=4L 4?0HJ=ȣH=r|(圵'x>|k{va:\]IE4ƋO4=?&#U4 Ǿ7ԚCCyc)-yfL `3Ͽˡ֡07yެzdeS>/PKJ z2=ݍ~<΋RlK>lnnn? wʆγa?j4 y KeTR}zZl`gt)vJS\ K!Ҭ!RZ*mZh+( 'FHa1R1vtK^HU\f;cUޜ9xX!~ء`Dx`SW)&'-*&8^Fy25f DҵTInNV?]-dM kGk SwHgFQ@&3'ĂO! K-M2nAĮ'mqp֠ſ!omA${$"K>͐g H 3;;3۰"hV$J6=GK|4c:Y :BCWMAݨ3oce#cKz{%|љq(K Ɠ̪sǜBe)X0rI~"Yl'V{nrz $$j1YN,0RK5lϔ/'dW4Kނ%#v[Ƅ:zK4tKuաڪq}FV%OTVɜ;.\WnXe+)˼:/. .o>syYՈi%+4$ImU&L^ ?^xpcNT[5> JŎs.c2\Ee{КV(NUmrS^ K&evU"2Kť' Tpe>6JdUq qNFI$VH|H#/`rIPJ/:Ag  3mK4/L?V\DFՖQJuFl.JF)J>;2RxHQ\^^*<cQ}Ea_ώ;kV˽xmna6HWG`@d3KOkBiIexIB1ӛgL>k0Ļ̣1c% :K:HА=cIטxo4 ukeQ3E'֘_c(#Z^bІa _ɓ4d| pOX8'wRlײfCDe'b1 BMV : _PKHRqwQ]^T Ya[e "r<,ƹͰv |\z͝=<`DvT! LBqD-i}Zpۣu>38"8v73]>"-RE4KFCbgRKbFj4z26LEݝv 94i6(g]<7;x:3G %eP7  2T4S&>(*E <*=A0 e0we.x!bctlߣ=6Vhy0}~(.PoCIbsr!#ut*FygZjkЦ4ͥtl)-8HrT(-ף' sHSm%µ:<3J J;Q S3LҖ& ܌!8HW- bfKP1\+: 挽kgmaD噽5i#QWN48:Fw,;;g:o }Jq1S9ǶVV;%򮺷"bz7ؿWU}W_&޴]z₸Lq*oPKF; -zM2Crypto/SSL/__init__.pym]k0+iaNv.LpZu*HPMBJ5ʜ<=8·立fYNXypFy\>y K9P^Œ ^pUU;q` JcY-Ұ U43RJ:*Bi2ܦ0Lj>vL̻Lskz#؅cSb{#L86Gud(Ab7:ӎ5d2ڑT!Ɗ/ N9ޢ4 qh$5t3ń6*p(R15OG!s> ||܂)QEB#l*l2g=yW9y< PKYx !Q;^ H ; , cd3 d;]иn,\f̓O @ WOXR +)' n (bM UYm)<X ;@jR+= {Rx]^"|ny4{Z̢bqCUA8ɔkuv|rrr::z'tN=O~1y ayz Q2< .,u&(G3F)W2 kkkdzWldQaZM646&pJuYNYK+|dI_9>gn 6OۣCve80=um]ujy9 |E)͏_g}` w/E]Xד"V=kF%{D3ֈiK*'.W!jJ9]SLPKF;oh:6 M2Crypto/SSL/cb.pyT]o0}bc]=4R'1nXRڵEli^g 4IQMxI{{l0Ї$βaB+VO8}n J4=m ^k7+neg 15lPfOi2Kյ0*`J9U٬}hΚܴڇݓp؇ЎlfZzcաZeS%OC:}x/H2tb.Kadm;,U/K-[a0"?{\:{=ERy\To5sH#ۭw=O$Q[l7VS+h5zEf;L"jkt)*Oȣ٨a#wnJr&˺׮qox}.zŷۓ`,m4 #JoA-MrI(QcZ{#*:gnCPK,3@*s5fe,Nyviie[d?,,0z%Vfn s˄,jZA`wN74#iTύ/8yа(֌z'^|5&i]bG܋)OoBNr͇ӷ7 N_&~x-׮*p&UgN>p2 wULTd)K>,}p{^3rDDs-.ܭ Y;;ٮ=-o8zۈcmAAm<ИeGL%Ljh"c^s3^+l*f*>")3( D_1ERNa:2+PWÈ)!Axs]%<,/i%vl (#xd̗ V}ѧHlFJRXB6.'|dSAY҅lH EKϢd&bhxLH$cJhUZ> mI=Q~rT%5)+jZ|#$J+}L®<)UjS a -\R,#j~)e ђv̥^޿ҰڐR~|_K|Z ~}3xRHDUyC!ZP jRܔJ``A C._J_oU-OB#}ǁ )d_zFҌVs4{B8͖yp\agC{}ע9z˴)1UQp9y~FMB7:V0 c&Z.I/cGSZyș/]Yț Y?9jq97p5b|_lۋ+7$|'g꒲ K gyVՔSh])d!np09Bcz;%68_pӲ$;3!Oo`xzܮ?nO.oi#dQo^oxaZWe7Իǖ)7nv:E4(jױKh~s^095++j1S_֊+PKF;s`4!M2Crypto/SSL/Checker.pyZO8}w(F@;TףTTXzBQ{N20\TL~޳ݭW!by< ќEw\ eSщ9 p1?_2&RyC%ĀK,Eч$=YAY,|f7`‡Y[($ |gy9 ;s;8򤵈>'bf}ATß*rf42|xɼJ ҃Q1gQJNc29>ւ3l&XʺC&u z}3t&Z3U!3g94nb č #]k2C{C'CCZ^v=ttySzMVzd͢!]L#:@nhM33G)TQ诙m-v\C0ZզEcU!a?LpoLzH4ה8DNb0֜ qE?T~q i$؜gLp*Z.eZiP0XP/˨x `pk(4ARb- y՜_`"cOaX]ZBCnow屒)#Wk(E633Vp{ZDpwRƸKyZoyoX҈tE7v}T?m%yM76T7Uʻ2өo[1lMd-LЍ!Cu6[CG@ D JkWꈆ3.c7=Ͽ| Zpr@ՑBZ, D 0"7f:L! ]7\ρ7Oq\.Re+yβlۜwtfɥjel!ͨYկtnuy޳o-5i\=x"WgU7rt8I,эbg+k]l :|9e|@WceWCwu|xwPb=B;F͊^ĬuoJvzwpӐ1Y(Ⴚ+ĞkFq㸰I˱5y*9i:Ǟ&dR`up[U [YNnK̏.vsgfmȆWYZ, #FvxW:B'T\T⎂ofR]skȷy3 wT\{FYFhBI=/TZM܆PxT@]ʣZZ/S}g=^vS0mȎnlla`&z" bc{п~O*c\PK3?ssNbA"ڭ|埣kş |W5h9,楘f^52l F˱ Rl-Zif-0syekY\,r %c/Z<tG(5-uGGPoyݡcO,0Ƙcra&Vh⟡)޶C!"u:ZnSoQX\­H6.sg|PjЉ\a_ 2쯂QRjٴXّP"z&J?K}Sȍ i ~w>ZEd7((L=34Z F`!(skvO9: ڂs+kK<4፨qk5iZMG;:-izGTv@m.=S9ihsn9 up ?[ ȀfWja@^: rG+QhW\%gk:&884hH7 _tb疑-hSOr,`\33t7͙6!ͱWKmq-v2,-XϽTtE~.'օ9R ϖu[7ŀFBw"ַpˈ|lTAd;'u _MX aHl7}DPT :-5: 4Rlt"MjL؎֎\ i!E$l!;0v^B҇}lӷIbA2BW5T-ػ֥hyx.ڱP17XPh颩0/0U~Zj&#$,W'ZzU1$ Q[=IBALVՠS;N.җk }ЊYRZN']VBL / A&(/ؓN6ɨ5}Slf3 i 2 Ȏ -0dԊ(~B^ ^!hI7y>'Ԣ݀*F%mJJ;+3;tOh$>vM0Dz4V gKIXtTN)~OiNC:쐱02Iv$ɯh",&S)pNbSȗ)79 7ywb@3K?iatN-|bo9E"+PsD_X/< 5Z-Rzo(D=Jn#MtTD2p,dɃPR%d`ؓ^݃mIl0RkL0` |!MB5 Ih ُ )Iov8[JSV]Ú(mfS,Z ,D:,S~J8{fXuOY|jѯ>2,cZrwQQ&g1 -Nݹ{ư ~. dzs*J8-fJlF8 u[Tya1މTޤ<#?PDh2*mo$)A/G+1ZT!M/W8;c?`PKF;@?A.M2Crypto/SSL/Cipher.pyRQk0~ϯ8 8C0Ơ2B^k0MJKѪ,iyIKVQiB)V Y|2Nᣄ5&E3iPQGBi9w/4_JV>[]m$@5#$qؓc2 F^!ӡw G.`9=tQh%lHkNh73:C*d=Ë 'TSk}ܭoX)t/աsb˞:M4Up$;ٺ3|; dvqM#(eU}QJgdc@6-J/۔78h&Qc M2Crypto/SSL/Cipher.pycUQOP>mǀaB DAd&$%R{GF/?ok|6sm_e[N{O=k{6\&F\|X؄Ђw\5 484(4*%صRi;&J{^\X[{f}v##jCI;񤗜xnU&hV8W1`H- 0+ MRb J DMI1%Q!mTx Ùg5l0FSh&(\zcaR,{A@jՎeu}|m<߯}NN*5;=Uc8U ɼF~Lpa H Z/ v !e 1 "- 5LqLku9qI.KVS7vzu!9w3~gKgomV9];ܒ֟ [8% g J|҉^!p&(Letk`4\&q+^O&{%w^I%us s/7Tѭ}CxNh9^҃n,#90?hitx /~3 2B:K\pn1I`"qO񁝴"!d+Fޡ$N.GVxZTҋ[t)xO:P^G'Tn\1dwL@$ bH 2 ,,J$H>+sEK73 ׷BM *2ze)<|qzɛ6b}&}q9M kh#C{5jNes_PKF;#Hw -M2Crypto/SSL/Connection.pyo۸wDR7łk"ΊEAmkE){$lh,K:;;ޑ( T({`DWT;-W$C˗8?,ɇ/d;crD)"^QDrVwL8,8P2spDrث~! |A6 TZFs8xR+cɫ,ȋ^/əRwieH, jg`)*elC9͹LЅ^U*wV;hЬI#Oyh*cܒ(vPa-4f)R#W />BЀ/e+0]7Wzuv;.pJ ;[=L_7oF ]d9/ 6-inAtwѱ's'j>!VEΖ;B?یS[;v#'xDP&8G4?! S-;De'H#2]F2ѪyԞ TNgg2܇q^ 6'[1)SCBD2 V, t*h\Ȝ l3@4O$:&,YqSCЭ`p|@B)|`-e䳴p6ϊrTAК^CvgG}Kn 0C\sb9&ҴF5kCp7"[UOTᮾbUm$m#4k41-[AjMFK{:M3`ܟ$e9 L/Tǁa$P.#8)dQYGPr4Sjf+D" 295}){Z@h@U%lmѽ"o157rʒ\ Fc:gc@Q@4P42m1ZK%Wbx9z ⪗-0nerֶj2&B)[36VsM#$(=3H:scQD.Q̃t-Y>|u?8Ә/S`:Y BPϴKBoD~?i0[ZWqD·wY[ZiO$!ĿƅY$-PnoP|C얒#}ZP@{G\ /%Ljgp$Ȫ?i&Qeg Jz}߹do1Db^v`? ƌe " @٪F"+]%p:5d N9  T]ଆA=.'Nt{0>w< km.$r{v^|\PhSl%iɹ>1 |%t|G`\ kp`~ȍcu~ ÅT p(Y85`$bD* %B evyWYշwZ?$B;̕n0l-" UqOQwjؑilrH$dV,5f=*NERJ>0禍ӽ¶>w.39LY:v4OBQ{*O^_O12qTC_cJShB-\vSy=l'?6g    s#:;%a *G VX7 Pqpk'=QMUj3n1匑qvS7[H\˧_cae6 xZFю)!,)]`+G۪%u]}EJ+rx#̔Ş#U=bfQ?fw+wcxY_;!CJ-[pNKGȐpG}BI lf.K)q ftG,"=ޓöϑQ,EP3{S T+28 SN,\}g aqk?$˽hb+#{EfS:nID:[Pƺq>DFɑ0NVt6fd\dPzPZ7&;ك8ugFSOU {\SH_Wg鱼;zTu8$]FGsȂǁΓXyyXo-kq'}9M ]e!$ni_q؏;p2m]s5л -`,nn_F'鯶lK7`؆83}};ھTH;xs/=N]"U+ Z )\߾,4z#)wv|CxBBH0_O h61(ܲ QUV$ K\.%u Ԝz}>ua&T>n`f6k+q]* Ӏ7o_5:):ġF;Ӱc&V"@6Gƚ~~u; b XӰi&?͛STi;B ʬ&H@Cto[?z<.1v0ׁMS쀻_TܣC/DG53>Fwp=c|%`}`|?'?@)`1~W?LiƏ*G xcq0>AC'iړ"xq*e,+.c+bTiW k\buf?C7$2SYfW?OU/ue|kph>1>C/g!.O}[:o0W cn2.Fm#C'@Ưg >(fE2 JFQe@`O@A |_`OL9}B5 0#)1İ̞8PYE~V>]XK!xG=v96qtA&0l4TosgdڜN}kX$X3l%X*PF}K[7oAH: f+04dؖХЈK~ aj,7i*4]O NqOg*fT#Ƙ1f7&}>38' H~J)jfjE]M2 de(t*!2HBq,($=b-Drf9>϶q,},WYFO{PzH.sMo39e: 9ՑjD$5Ob&5d0^m'<$^nxt߅j;V61ĖK?5MNYt'ɪXaSVZ"?%֒˭Xz/CGKB%d9 aoUe+ZٰȪ$Fi;♊h 6,u؛JHT\7QZ"5i~HV+DVr5iU@ NċN4]A2/y48ͬct} p!Y"4R"kW{d,'&dNeF"g'egz.$n7Y@zѤ47%ځ-smD~",Xb]uLI]9OLbJ"I.Fr\`ёna띚W4ݨi4rdi-` \]jAEXsk]--noG Ѯ8˅ގk}Tqܦ^7 es1`\teC5߂Z0d ~F6Kƪ A$$9 $37`}#[5PcbVspLT eDLS:!?bo%# 8ٱhUpT%X\0z;5GG{4:D-84q -ۍ5oIJKHMqh EbSA t^olZ\,&8IbAb5.gΐ !|.zτ&>c)L=u~n*$$ϣ(Xw`ڧD3EJGq7l\Y&1U2FTܪ&>ْ XU7䊝%2nE@l82WAΔ}(rQ.gu5y\uAQX2 ŮhQrqqZ2!?د⨪BC:0添]kY.I=5S%/}|R@]t  JFUVK% )4)bB&- h|AZhku}*AE/,xwjֺ'Gh:g5 8o4>Df㦬G+\zy둓ck `iȥDc!7e1Uw/Y<>ggk 9Khfc)is.xk%b#F&M%D;Ff*j"@DK6u =2c}5:+4J1%挍b^em:FK4Um8z~n26 M~(7N'\ъ"kFi$g[d[ hBԦ%i*a2yBYne[~I3wOăr{N`II}+j@Y~y?A224T/uRjT3M=)tj8}ftzG_ى@J؝ #&KN`y9Ur:ȵ8zHb`]2Eܪ` 􃕡ScAa9tqrM4&ry @oe7pI O>^DP``&e4lS"6^L&kYQ ~Æ h(dY#o }:P~W58?o6>8\ýi*Fl/kͱXwJ wЈ a//{%vqkEs@wDNa^=Lp69};t!2X>u{6(G4{X?S.m'Au[$:Z[_nv黿S;U? 37ɄcOGN1=,.P PASӡ:LPT&1:@}}&>>^$Z]fҙaSiXrd&S'ǻ}>0ZQw)ځVdl8Q&'vup"> dl/_4?DG_Av$Ta+/GEGh}xNn̳p{49=hB>~>},ƮgTKg%t (B0kU%O >žxK%Q|(C=O%z*Sy$syYiڨz&/mI7.=3$Zyr"*QAKRԐEyǓ4qZx^Iw!Xy"Yᡳ<^uׄ}蝅eMj$1h.h{y}sROY, Jq8cԌqcN8!FB75-/3K&5@rLm33OM[oToɕ5Jɷ;M|͛W_O>l˷䞋|4%f")&, ΂ hF g-fK2sgF<1(نR_4-55p_WPheRW+RtI~nIn\OK/o?~qJ"Q6O|2-rPșyBQshXYņnC a4,sLQ>¾$,P޶Yܭca,>%3gnoaPť* T' I05cZ ÕyT|d g$RhbAb>FI sY !Tk9Ǽ8Y1 0158ВfBbĤ=Qek^yY?hlۜY:_,KST揹xʣz Jz^]`FM ,i߶:Eh^T[EaBbDw[ojļ1\ hW/.߭^zqՋ??|ûYå]Z}[ Y6TI+IMƩP++w0d}MSA(fR;rSf/Lsȏ5/GIXTC[ËoxL5#4O |w  9l45Z_Q+W,EZ"o`Jqx<lCT[%bc 'F=ee k87(ؚKC8f]Sc=\5+tx`&kG=b\&)^a?h*X{tQ`29M_)}HQZϼ!8HW "-*޹cЍ[t4D9ڞ$@6~.uYQS7*:͂><($_r4\z[h~=ו:0w^@t2.AЩ B?2@@'*:1•W ha{rGe\GWfpsVQb.٪=a{f $&qD($)%8p~(_O@0,oȽL6f\]'^!'/a275сi$ozJ`WPdHbeۖS}ɔN37( l z=/ с[ AjT{\Iaow4Z/׊\s+ h_IVVh2Xͫ#,i{c47p g s/*KU1t9O1ie>Uny@0UiB !?U|$Yn(O#ra鱛͍U{N0\UN1)V}ۦ0XUpay׮wCTbLTZd kX5\+-` Lb+ cf@'@3=jsh[CSQs슑;1ISraz<V#ᕔh_BQ0]W6û¶dID#n @KANobAS'8d! }2$U5cd|98ꯎ!)MxdsU-] ;TX.D&@ŽiЀJ7_ sB3o/SeI*LFɁ(:=qƁyTxmطLxSflFPf23DעCZ烥`Έ16,'wZKGE% ZnfN`l?zX5\`'h?Ddyi0T'MJRY IXc-DL3 6jg 9D4%-;1! aiSq=ATijT[gɮӱ춯Q|(:Z\DeO[ ݟ7O"B176;ӠD%++άw`&%&Ks:Bi–b4 TV=a괣:܇W'{N:20PK+ \s| oئpk$*t$߽Koն7D 'D>| M& G:QKpFJnA ;#TQةxT&N6I'&VpF r"!wvW6tmZ۱Pv \x oVkΥZUPBקpIj"61hCS1J@Q{pH -.l;> ſ XJټei9TGvqUhp3<R'JF_nK9nʣɔǭ,\u]4Kdz~E \..e4S /Wp?V6ίfnȜK8I4< ? Gx%hK]!$+g OJ#"`wyc8fÚ1EUXsYtqY9ɬ)Ꜣa44f]g?5G_Ĭ4Fe7ț(TǔFR T%f۞a&kZ>!SmWxhs=Fs7C_W߆1=dnH3ܻywM9ġ3T1dI|n]R8U15-b/4ptgO0KUc=<1̽uuz| '`i.sa#`>%Z"o *;>:k8Bq _~WP$r!f3~wDZ͵,;loQ̡ 2Υ|ǃxTNET|:Ds/L-x|7"uREE=|) `gl># >rq*|qҧ99_Bӱ s/ %EEE+4yq 'j"Ʈ@ԓ&{6V"(Ӓh LɊ4aPr:,e*>wY$j\,!6M-tH9 c<$|h7, =C+(P֣|7$r/Wr+*I~)Pw<CUiHx'ml h/-FV_H-i c<,gq n4b$Lj_4|Dj=`؎Ԣۑ{lAbTP!ۀؒkҋ6 ]h ¶~p1$FQ\%22;jgb#2Ю%4jه<,G[%f:2.=3=_/NOMHhC}bpF)FZM_ ,f'LƎו«Vr]p W4"Yxb9Xv"p,qZ&,؉g4*9E,L9zLbhZ]룓{%X5'fݹDSR5{vqCȚF1CD OD;>ωygXSkFK`a)F /OчߎZR1o+P[CN_ > 7Y)5j Rxf3 :Ba[BSzzT4B*ni*gA]69~+eRƙ؄Or0LRvtp`[@Nlݽ|*|a293Kμ ǶTlyqNSp[TPL^Bз^G^ba|OflYnyKv3Y${/aGNZ9~oo 2CYw|s44|]c ̐oL-k6asؼͷN̳@|^0S4M i ΁EVy$ Qj-l*2XiSVIkjx*TK͠.ҹ^-c@O]tO$EHuo % 2)Æ"P48@K=tjdA_asc{ap\*4Š/wxGgsڳZh;XK1dZl`{jrt3A8>3^{mo#m6%{\*5<4%KBfpPK"JH@XW"DV ° k@Xχ5kt1CXݧ(;e&͋dHxΣӓǝ O:|l<?uR7vml (~/9jI?LS+zM\BCB& H  +%[BndRvRZBdeA(8悢yJP/]AXvK8xߣZ$v'eDLYl1NΉhjٷ(.& {lw: 7dzYӓIE٤9L]2V#@"EQ{ k [^{$bBsdLY6ϲ,03JMۃʤ|UaNQ'82{ˍDUT.$z92n7,7S גKh l6|I8:U\Ė4 ŨLR{xE,)}{9o1A̷=\bK # f4s]\Q@_:b|Ld !=&tg#jSFl \jD2yP% (;|k7$ywbGkF옕L|8W9A~t} LvrNn# Mvyꭐxr7M&_LpeF|:R'W[,!!;>"<5ǚteV\ǭɿA99,x=UXiwmZRFp!Q>JxUo,^*"[)J \VGXr"to'}[$x:abPK?Y䕲 vtq'aj`|o<Z2ZgN>uk$TJ7IxCQ A Yf -15^wc cN"4bDAyRS#ANJ8tfwId j,/gءe ㉴B_V6eƼ\%G~/s?v_Կy@XbɌygf>Q‡MuQK6OLMnA/=)eϪc;uƮBXU+RJ?}kΉ*-``yQJҀO1rECѱ _nN#e=2 T^6~qppkP? wy UmkQu>i1|6%gqdz5y02s6 ]t/E8 N`i  3}dPK\m0IbVڲNӛIk{VsY!.ObۍόDXqBR!9~vez&nn?a{F5#ɵ7s?x]BdRk# ~E@E*3k2$Wi&5 D%Jȟuvm`X`FU® ~S1upأ=m~;Tg('(ǥ]PKHJ6= DHܝy<{l:ydͅ%.n#Y0N^ lr%(?~ "7Z;V񔒋ͦD%L}EA ,<YR_)+KJ8nH[i2 N_r&Yʊw\>k/$pÔ'_PtMK))?7f\E?1q1dtA@id'44LK.P:PeDh#+q*+^S*~M##PE.|YJN;6m&Fw46|IH{Z-R H3)ѩA5\\n+DRrVTbml-`3" 4~/]p"Yϳ*EmYm䟍~XҒ{UrM8I+-@rrgfAvHǽkb<4}!sעq~/B~'˛9ȺS4l9>3Ct)K{i*/HHپiM_$5_;{]|)癶#*|W'zMZds6[pJsCZmIe.g+3n-c Ё12A>?([$AC8 A8`GzPhտzB-ϏO t~'S0An:bTE8 \ m.W6&OwNaAز85sTTSbN_i؉Gdfd&I ^^)ɜ7lb=bD#o4C_ZZ:2#ErW07 O50W!v +JCI1IOQ Ӡ})²%1dS GŀnlCⴸ}|S,7CU &fwcml ֺL+-%@zKo+7ӔN@Jn4nաs(LOqI (>(e߰K*!S)st -TrY Y"cJʥ"H$_V莇ZWIFFJ*A0'D=%2Y!ĝyxaTG3D)!%ݒ$rvENaś[MOA%q9\'_υ\.DEL 9'UL8*&Б0p$8`xSћƆ7 9,nVl66ƃd2@W40吃Iȓ_[@S䁛A@0-КJLtKm8LM cúPE YUjn(R'ȏ;+}\w v}zK%u2AUkn;cbsqC-*yE@`]%p1bpA! uDo¬kGD &ZvmؽYm5RVֱn4fԘ{drD d;5z I4vD؆9w&窳Bu.KrC!>u^?cNyBmGGMA] EBJu"z_#Qj}jSc&i`\MTіuGz v^ab J*]֓K%ӊ| ]j X͵y,dujrJi*Iex&oe[55 :)UJc) ?ݫݽƮ Or0ǔna^!n@0nd4 3@y32_ܨRߧr幤yo|a6Qk1Ǫ{u@ոd伃nL@bM~@pɌ̌!k5_6:t`\L H;mh!ډhUOkJ tjG⯅҂ 4ǪaXRJdh_hwVMbAjfQfx_446rA_>[fo^\ͮ\<u5r}j@Ћ˫)|r/ކꥷS;SR4?S̾d-t~S9Kt*x5 N왹;j#wkeh-N#e%xlCKƗK1%qx%ыM/ThӪ+ƣϨ0a2XmƩ OOh > cG藓_]DKgpR+| 6@5czX4{ykUɌm˖6c8Km^'&8Ąv),LxIcLwo?░C0J-MifO}Xֵ0eg8NjSf+Θ#'v"Rov,[[OvG $h+'x->P>| OM¯FA ;QnD6*瑚3Ivm8΅.-} >+UYJy[T `{9qQ{BK36`d*[oƸcFdXwۖSf3Zy8ҰL,N]pB|`ةF; El|C15T Шa$W׎J9~a#*BKŸF6UUI*qBO_&`t iU@` OMu:`KwfUJ 4y%8ƲTz+ymq^l Ķ6\^O=UqEG RJ H]͸s80 ' pDe7/Wg cwԨĖJߨÚ\Gm5Ѡ>#_&䤻(H3 `K';nr5*|d nPI8(SșW"3|4R35?ƃ~6Iw#cZG?ËcGi=x5j>w ¦5o0Xnc!=FOt#bb:4 :i}7l/ ai>NuzGD3b}F"s_Έ`ʺϊs"hֳKD@DK,w]":9ZQ, Ql+"X0M_!6i.+ v)]Y.gIKa?O$uVWKI0 sgѿܾuTq>YM/{~^&q'q,>Q~\[Z[aF1:O蛧G+i'~}zLNj$6"b[u@һkg$U3>#'<[цbv^>};j`mc $틐iEIE_iz}Z d"{@ -Ivp[ݷoMuS3&m͂sc׈TϋluR }lIm3r_^0S8[A1!K%'B6c!g:xDZ&Պ~asC~*P$7'e;GoJ[ZNLU)9aO?K[5m,^ 3C y{$767@{En,V8'PfH6Sy]d =R>!lV$y{I ^,YqVB 0YBfcתI,U>tfpw < Q" v⒙%ǽ#_3fLeDFf۳|#)[?Ez16[ z^s_it)g)JaH8)c${,^X+Fg4F;UfOiRP^2RdMZ/;XդuĪ:.P+ &=)NUtT/;,Ti:սzLOVz_MoTbRQ~C16RpJ-Vk`:J,D)UބgZz׶:J't0sP*$b2 %uI d9{2Ng3Murzg9sH{; oKC0"C{*o|a6D9"mdb 02ab*b4H<_5319bz<}z̮.іDrNXxVRVL 07޻ݣmRR Mze }CQšׯx{{h#8Xq~Ssbqd/]7!jM̳^-UiUPϲ"4݉r7O])mmǤWq5wP45ܛQ R?< YҊ_(i5J\ɹBG 62L!݌4̨t`FedTf@:i#Y,2)vUU;:

    9;Jo 3 ;m4yh#'d> QeOwIvBkziwL@GܒyAnډi}ln@nF-_hR,fb;M0D|^؆T;7xDncM* >th#'rW oƠo)&n pQw/vpQ:'U/D D$)+CV"}dw`W ̣lG/alږQĒ8c[,Xq{\8OM9h"(ZVFO5l }ָ3 *"E}5rkZִʭ*БڭjпM]ir*u[LJfEI.EKP1msp5!o5r&dJBZ(x( xy+{ڥ9n<# ydXG C<gɀ_0NuGӋ2'Mhg|>>}~~-S}z{辠dO\6,BrL1rە{ajHn"qqNn?a}VIg~h ۣ2?W|*uЇH5aj7qGsI 1lt[~|=wGO܇kW֨@0 0"Z6[uպdb_?m}~Z,X4e¼ZHE*…2:΃*:OP1#)\$~( mv"#,ª$d7FeVH]s4Y"&rmU5Xڕ?jPUQ?Fw9MTB!L^*A+Y&_}3atbއ_N+p~|M~ӮXoǺPYLJȘ!^b0˰i L_a˭߆,h'n$a3.eޜv?#Rsؚf5hkNvMTUb4@m P0d)NJ2'Ln򺪑[xIso:QUvSUU#@*ljeO߅=]H] Jke/dM8ڔcSTxZaN(2y8˱C?OPRt`p)g I+͢<'JGҗSa Y17pCT)㮢XEzH<&g?6`Ux]OĄ⏉pJJo^Ś5gٗQXrՈ x}s~:ce& "u2ݶ +uYtJxDZUL#}5X9mnm & A%gMRN2XsDxŻÚ@gV{BCQrU#:Oܤ>#:{Ls4hiM>k*;IW5uCOĿ*iٛzL[𫓑?!R 78$+y2cTQqxSn'`aiͪ!ґFJd"3F2mf?dXJԙR5U b#"}0`P?l}L)&=x9Q z ń:WJ|QiWZ-1` ީdNgSsQ$CRcCj쬺*LJO'FE}u_2z>5D֌8h*=ߡzlk_ap}Xm @Y25j7Q!ys\JêbxRu 420bFl=Q45ʳղʟVY<9xa3*M榆 ."⾍n_ӉpUDެU?EVޘً|0v c:w!Cʱ:ڲƜ 2}e)% ^ݺޚo-f[VUk]̾V+zm /z'yq_Pi 7TN_ ^:~6f{/V zEΩ#>u"׳6,.TP5㉱S+X:Ia<<{.XsVP˚?SGy4קPKEGG-INFO/native_libs.txtPKM2Crypto/__m2crypto.soPKF;QW-M2Crypto/ASN1.pyPKM2Crypto/DH.pyPKeM2Crypto/EC.pyPKQc M2Crypto/SSL/Cipher.pycPKF;#Hw -M2Crypto/SSL/Connection.pyPKsbindir should be # cleaned out from. sed -i '' "s|{BINDIR}|${BINDIR}|g" "${PKGTMP}/scripts/${PREFLIGHT}" sed -i '' "s|{BINDIR}|${BINDIR}|g" "${PKGTMP}/scripts/${POSTFLIGHT}" sed -i '' "s|{LAUNCHD}|${LAUNCHD}|g" "${PKGTMP}/scripts/${POSTFLIGHT}" # {pre,post}flight scripts must be 770 to execute chmod 0770 "${PKGTMP}/scripts/${PREFLIGHT}" chmod 0770 "${PKGTMP}/scripts/${POSTFLIGHT}" # add in M2Crypto if python version is less than 2.6 if [[ ${PYMAJORVERSION} == 2 ]]; then if [[ ${PYMINORVERSION} -lt 6 ]]; then cp "M2Crypto-0.20.2-py2.5-macosx-10.5-ppc.egg" "${PKGROOT}/${SITELIBDIR}" && cp "easy-install.pth" "${PKGROOT}/${SITELIBDIR}"; fi ; fi # add default bcfg2.conf mkdir -p "${PKGROOT}/etc" cp "${CONF}" "${PKGROOT}/etc/${CONF}" # add default launchd cron job mkdir -p "${PKGROOT}/Library/LaunchDaemons" cp "${LAUNCHD}" "${PKGROOT}/Library/LaunchDaemons/${LAUNCHD}" client: prepare rm -rf `pwd`/bcfg2-${BCFGVER}.pkg echo "Building package" echo "Note that packagemaker is reknowned for spurious errors. Don't panic." "${PACKAGEMAKER}" --root "${PKGROOT}" \ --info "${PKGTMP}/${PROTO_PLIST}" \ --scripts "${PKGTMP}/scripts" \ ${FILTERS} \ --verbose \ --title "bcfg2" \ --out `pwd`/bcfg2-${BCFGVER}.pkg server: prepare rm -rf `pwd`/bcfg2-${BCFGVER}.pkg echo "Building package" echo "Note that packagemaker is reknowned for spurious errors. Don't panic." "${PACKAGEMAKER}" --root "${PKGROOT}" \ --info "${PKGTMP}/${PROTO_PLIST}" \ --scripts "${PKGTMP}/scripts" \ --verbose \ --title "bcfg2" \ --out `pwd`/bcfg2-${BCFGVER}.pkg clean: rm -rf bcfg2tmp bcfg2pkg bcfg2-1.3.3/osx/PackageInfo.plist000066400000000000000000000023031223671746500165510ustar00rootroot00000000000000 CFBundleIdentifier gov.anl.mcs.bcfg2 CFBundleShortVersionString {SHORTVERSION} IFMajorVersion {MAJORVERSION} IFMinorVersion {MINORVERSION} IFPkgFlagAllowBackRev IFPkgFlagAuthorizationAction RootAuthorization IFPkgFlagDefaultLocation / IFPkgFlagFollowLinks IFPkgFlagInstallFat IFPkgFlagIsRequired IFPkgFlagOverwritePermissions IFPkgFlagRelocatable IFPkgFlagRestartAction None IFPkgFlagRootVolumeOnly IFPkgFlagUpdateInstalledLanguages bcfg2-1.3.3/osx/bcfg2.conf000066400000000000000000000002331223671746500151570ustar00rootroot00000000000000[communication] protocol = xmlrpc/ssl password = foobat # certificate = /etc/bcfg2.key # key = /etc/bcfg2.key [components] bcfg2 = https://localhost:6789 bcfg2-1.3.3/osx/easy-install.pth000066400000000000000000000003451223671746500164530ustar00rootroot00000000000000import sys; sys.__plen = len(sys.path) ./M2Crypto-0.20.2-py2.5-macosx-10.5-ppc.egg import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new) bcfg2-1.3.3/osx/gov.anl.mcs.bcfg2-daily.plist000066400000000000000000000011011223671746500206040ustar00rootroot00000000000000 Label gov.anl.mcs.bcfg2-daily ProgramArguments /usr/local/bin/bcfg2 -n LowPriorityIO Nice 1 StartCalendarInterval Hour 3 Minute 15 bcfg2-1.3.3/osx/macports/000077500000000000000000000000001223671746500151575ustar00rootroot00000000000000bcfg2-1.3.3/osx/macports/Portfile000066400000000000000000000024251223671746500166710ustar00rootroot00000000000000# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4 # $Id$ PortSystem 1.0 PortGroup python26 1.0 name bcfg2 version 1.3.3 categories sysutils python maintainers gmail.com:sol.jerome license BSD supported_archs noarch description Bcfg2 configuration management system long_description Bcfg2 helps system administrators deploy complex \ changes across large numbers of systems in a \ coherent and transparent fashion. homepage http://www.bcfg2.org/ platforms darwin master_sites ftp://ftp.mcs.anl.gov/pub/bcfg checksums rmd160 db89ee0b8975bf50ad68bfac122e50253ded1906 \ sha256 138d792423475ae6516a95578a3df191504afaff31007877c7c2b36830d1a260 patchfiles patch-setup.py.diff post-destroot { ln -s ${python.prefix}/bin/bcfg2 ${destroot}${prefix}/bin/bcfg2 set pyman ${python.prefix}/share/man/ set manroot ${destroot}${prefix}/share/man/ xinstall -d ${manroot}/man5 xinstall -d ${manroot}/man1 ln -s ${pyman}man5/bcfg2.conf.5 ${manroot}/man5/ ln -s ${pyman}/man1/bcfg2.1 ${manroot}/man1/ } bcfg2-1.3.3/osx/macports/files/000077500000000000000000000000001223671746500162615ustar00rootroot00000000000000bcfg2-1.3.3/osx/macports/files/patch-setup.py.diff000066400000000000000000000057231223671746500220060ustar00rootroot00000000000000--- setup.py 2010-11-15 15:30:28.000000000 -0600 +++ setup.py.macports 2010-11-18 19:06:49.155292524 -0600 @@ -11,47 +11,22 @@ setup(cmdclass=cmdclass, name="Bcfg2", version="1.1.1", - description="Bcfg2 Server", + description="Bcfg2 Client", author="Narayan Desai", author_email="desai@mcs.anl.gov", - packages=["Bcfg2", - "Bcfg2.Client", + packages=["Bcfg2.Client", "Bcfg2.Client.Tools", - 'Bcfg2.Server', - "Bcfg2.Server.Admin", - "Bcfg2.Server.Hostbase", - "Bcfg2.Server.Hostbase.hostbase", - "Bcfg2.Server.Plugins", - "Bcfg2.Server.Reports", - "Bcfg2.Server.Reports.reports", - "Bcfg2.Server.Reports.reports.templatetags", - "Bcfg2.Server.Snapshots", ], + py_modules = ["Bcfg2.Options", + "Bcfg2.Proxy", + "Bcfg2.Logger", + ], package_dir = {'Bcfg2':'src/lib'}, - package_data = {'Bcfg2.Server.Reports.reports':['fixtures/*.xml']}, - scripts = glob('src/sbin/*'), - data_files = [('share/bcfg2/schemas', - glob('schemas/*.xsd')), - ('share/bcfg2/xsl-transforms', - glob('reports/xsl-transforms/*.xsl')), - ('share/bcfg2/xsl-transforms/xsl-transform-includes', - glob('reports/xsl-transforms/xsl-transform-includes/*.xsl')), - ('share/man/man1', glob("man/bcfg2.1")), + package_data = {'Bcfg2.Server.Reports.reports':['fixtures/*.xml', + 'templates/*.html', 'templates/*/*.html', + 'templates/*/*.inc' ] }, + scripts = glob('src/sbin/bcfg2'), + data_files = [('share/man/man1', glob("man/bcfg2.1")), ('share/man/man5', glob("man/*.5")), - ('share/man/man8', glob("man/*.8")), - ('share/bcfg2/Reports/templates', - glob('src/lib/Server/Reports/reports/templates/*.html')), - ('share/bcfg2/Reports/templates/displays', - glob('src/lib/Server/Reports/reports/templates/displays/*')), - ('share/bcfg2/Reports/templates/clients', - glob('src/lib/Server/Reports/reports/templates/clients/*')), - ('share/bcfg2/Reports/templates/config_items', - glob('src/lib/Server/Reports/reports/templates/config_items/*')), - ('share/bcfg2/Hostbase/templates', - glob('src/lib/Server/Hostbase/hostbase/webtemplates/*.*')), - ('share/bcfg2/Hostbase/templates/hostbase', - glob('src/lib/Server/Hostbase/hostbase/webtemplates/hostbase/*')), - ('share/bcfg2/Hostbase/repo', - glob('src/lib/Server/Hostbase/templates/*')), ] ) bcfg2-1.3.3/osx/postflight000066400000000000000000000007751223671746500154460ustar00rootroot00000000000000#!/bin/bash # # ${3} is the destination volume so that this works correctly # when being installed to volumes other than the current OS. # set proper perms /usr/bin/find "${3}"{SITELIBDIR}/Bcfg2* -type f -exec chmod 0644 {} \; chmod 0644 "${3}"{DATADIR}/share/man/man1/bcfg2.1 chmod 0644 "${3}"{DATADIR}/share/man/man5/bcfg2.conf.5 chmod 0644 "${3}"/Library/LaunchDaemons/{LAUNCHD} chmod 0755 "${3}"/usr/local/bin/bcfg2 # add the launchd script /bin/launchctl load -w "${3}"/Library/LaunchDaemons/{LAUNCHD} bcfg2-1.3.3/osx/preflight000066400000000000000000000006671223671746500152470ustar00rootroot00000000000000#!/bin/bash # # Remove old bcfg2 cruft # # ${3} is the destination volume so that this works correctly # when being installed to volumes other than the current OS. /bin/rm -Rvf "${3}"{SITELIBDIR}/Bcfg2* /bin/rm -Rvf "${3}"/usr/local/bin/bcfg2* /bin/rm -Rvf "${3}{DATADIR}/share/bcfg2" /bin/rm -Rvf "${3}{DATADIR}/share/man/man1/bcfg2*" /bin/rm -Rvf "${3}{DATADIR}/share/man/man5/bcfg2*" /bin/rm -Rvf "${3}{DATADIR}/share/man/man8/bcfg2*" bcfg2-1.3.3/redhat/000077500000000000000000000000001223671746500137655ustar00rootroot00000000000000bcfg2-1.3.3/redhat/scripts/000077500000000000000000000000001223671746500154545ustar00rootroot00000000000000bcfg2-1.3.3/redhat/scripts/bcfg2-report-collector.init000077500000000000000000000045671223671746500226400ustar00rootroot00000000000000#!/bin/sh # # bcfg-report-collector - Bcfg2 reporting collector daemon # # chkconfig: 2345 19 81 # description: bcfg2 server for reporting data # ### BEGIN INIT INFO # Provides: bcfg2-report-collector # Required-Start: $network $remote_fs $named # Required-Stop: $network $remote_fs $named # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Configuration management Server # Description: Bcfg2 is a configuration management system that builds # installs configuration files served by bcfg2-server ### END INIT INFO # Include lsb functions . /etc/init.d/functions # Commonly used stuff DAEMON=/usr/sbin/bcfg2-report-collector PIDFILE=/var/run/bcfg2-server/bcfg2-report-collector.pid PARAMS="-D $PIDFILE" # Include default startup configuration if exists test -f "/etc/sysconfig/bcfg2-report-collector" && . /etc/sysconfig/bcfg2-report-collector # Exit if $DAEMON doesn't exist and is not executable test -x $DAEMON || exit 5 # Internal variables BINARY=$(basename $DAEMON) RETVAL=0 start () { echo -n "Starting Configuration Report Collector: " daemon ${DAEMON} ${PARAMS} ${BCFG2_REPORT_OPTIONS} && success || failure STATUS=$? if [ "$STATUS" = 0 ] then test -d /var/lock/subsys && touch /var/lock/subsys/$BINARY else log_failure_msg "bcfg2-report-collector" fi return $STATUS } stop () { echo -n "Stopping Configuration Report Collector: " if [ -f $PIDFILE ]; then killproc -p $PIDFILE ${BINARY} else killproc ${BINARY} fi STATUS=$? test -e /var/lock/subsys/bcfg2-report-collector && rm /var/lock/subsys/$BINARY return $STATUS } status () { PID=$(pidofproc -p "$PIDFILE" $BINARY) if [ -n "$PID" ]; then echo "$BINARY (pid $PID) is running..." return 0 fi if [ -f $PIDFILE ]; then if [ -n "$PID" ]; then echo "$BINARY dead but pid file exists..." return 1 fi fi echo "$BINARY is not running" return 3 } case "$1" in start) start RETVAL=$? ;; stop) stop RETVAL=$? ;; status) status RETVAL=$? ;; restart|reload|force-reload) stop sleep 5 start RETVAL=$? ;; *) echo "Usage: $0 {start|stop|status|reload|restart|force-reload}" RETVAL=1 ;; esac exit $RETVAL bcfg2-1.3.3/redhat/scripts/bcfg2-server.init000077500000000000000000000033241223671746500206350ustar00rootroot00000000000000#!/bin/sh # # bcfg2-server - bcfg2 configuration daemon # # chkconfig: 345 80 20 # description: bcfg2 is a configuration management system that builds \ # and installs configuration files. \ # This is the server that provides the configurations \ # to clients. DAEMON=/usr/sbin/bcfg2-server PIDFILE=/var/run/bcfg2-server/bcfg2-server.pid PARAMS="-D $PIDFILE" prog=$(basename $DAEMON) conf="/etc/bcfg2.conf" # Disabled per default BCFG2_SERVER_OPTIONS="" BCFG2_SERVER_ENABLED=0 PATH=/sbin:/bin:/usr/bin:/usr/sbin # Source function library . /etc/init.d/functions # Include default startup configuration if exists test -f /etc/sysconfig/$prog && . /etc/sysconfig/$prog if [ "$BCFG2_SERVER_ENABLED" -eq 0 ] ; then failure $"bcfg2-server is disabled - see /etc/sysconfig/bcfg2-server" echo exit 0 fi RETVAL=0 start () { test -x $DAEMON || exit 5 test -f $conf || exit 6 echo -n $"Starting $prog: " daemon $DAEMON ${PARAMS} ${BCFG2_SERVER_OPTIONS} && success || failure RETVAL=$? echo if test $RETVAL = 0 ; then test -d /var/lock/subsys && touch /var/lock/subsys/$prog fi return $RETVAL } stop () { echo -n $"Stopping $prog: " killproc ${prog} && success || failure RETVAL=$? echo rm -f /var/lock/subsys/$prog return $RETVAL } case "$1" in start) start RETVAL=$? ;; stop) stop RETVAL=$? ;; status) status $prog RETVAL=$? ;; restart|reload|force-reload) stop sleep 5 start RETVAL=$? ;; *) echo $"Usage: $0 {start|stop|status|restart|reload|force-reload}" RETVAL=3 ;; esac exit $RETVAL bcfg2-1.3.3/redhat/scripts/bcfg2.init000077500000000000000000000027311223671746500173320ustar00rootroot00000000000000#!/bin/sh # # bcfg2 - bcfg2 configuration client # # chkconfig: 345 80 20 # description: bcfg2 is a configuration management system that builds \ # and installs configuration files served by bcfg2-server. \ # This is a client that installs the server-provided \ # configuration. # DAEMON=/usr/sbin/bcfg2 PIDFILE=/var/run/bcfg2-agent.pid prog=$(basename $DAEMON) # Set default options # You can set script specific options with BCFG2_OPTIONS_INIT PARAMS="-q" # Disabled per default BCFG2_ENABLED=0 BCFG2_INIT=0 BCFG2_AGENT=0 PATH=/sbin:/bin:/usr/bin:/usr/sbin # Source function library . /etc/init.d/functions # Include default startup configuration if exists test -f /etc/sysconfig/$prog && . /etc/sysconfig/$prog [ "$BCFG2_ENABLED" -eq 0 ] && exit 0 [ "$BCFG2_INIT" -eq 0 ] && exit 0 if [ "$BCFG2_AGENT" != 0 ]; then echo "Bcfg2 no longer supports agent mode, please update your configuration!" exit 1 fi RETVAL=0 start () { test -x $DAEMON || exit 5 echo -n $"Starting $prog: " if test $BCFG2_INIT = 1 ; then $DAEMON $PARAMS ${BCFG2_OPTIONS_INIT} && success || failure RETVAL=$? echo fi return $RETVAL } case "$1" in start) start RETVAL=$? ;; stop|status) RETVAL=0 ;; restart|force-reload) start RETVAL=$? ;; *) echo "Usage: $0 {start|stop|status|restart|force-reload}" RETVAL=3 esac exit $RETVAL bcfg2-1.3.3/redhat/selinux/000077500000000000000000000000001223671746500154545ustar00rootroot00000000000000bcfg2-1.3.3/redhat/selinux/bcfg2.fc000066400000000000000000000022411223671746500167500ustar00rootroot00000000000000/etc/rc\.d/init\.d/bcfg2-server -- gen_context(system_u:object_r:bcfg2_server_initrc_exec_t,s0) /etc/rc\.d/init\.d/bcfg2 -- gen_context(system_u:object_r:bcfg2_initrc_exec_t,s0) /usr/sbin/bcfg2 -- gen_context(system_u:object_r:bcfg2_exec_t,s0) /usr/sbin/bcfg2-server -- gen_context(system_u:object_r:bcfg2_server_exec_t,s0) /usr/sbin/bcfg2-yum-helper -- gen_context(system_u:object_r:bcfg2_yum_helper_exec_t,s0) /usr/lib/bcfg2/bcfg2-cron -- gen_context(system_u:object_r:bcfg2_exec_t,s0) /var/lib/bcfg2(/.*)? gen_context(system_u:object_r:bcfg2_var_lib_t,s0) /var/lib/bcfg2/Trigger/.* -- gen_context(system_u:object_r:bcfg2_server_script_exec_t,s0) /var/lib/bcfg2/PuppetENC/.* -- gen_context(system_u:object_r:bcfg2_server_script_exec_t,s0) /var/lib/bcfg2/Cfg/.*/:test -- gen_context(system_u:object_r:bcfg2_server_script_exec_t,s0) /var/run/bcfg2-server.* -- gen_context(system_u:object_r:bcfg2_var_run_t,s0) /var/lock/bcfg2\.run -- gen_context(system_u:object_r:bcfg2_lock_t,s0) /etc/bcfg2.*\.conf -- gen_context(system_u:object_r:bcfg2_conf_t,s0) bcfg2-1.3.3/redhat/selinux/bcfg2.if000066400000000000000000000106011223671746500167550ustar00rootroot00000000000000##

    bcfg2-server daemon which serves configurations to clients based on the data in its repository ######################################## ## ## Execute bcfg2-server in the bcfg2 server domain. ## ## ## ## Domain allowed to transition. ## ## # interface(`bcfg2_server_domtrans',` gen_require(` type bcfg2_server_t, bcfg2_server_exec_t; ') corecmd_search_bin($1) domtrans_pattern($1, bcfg2_server_exec_t, bcfg2_server_t) ') ######################################## ## ## Execute bcfg2-server server in the bcfg2-server domain. ## ## ## ## Domain allowed access. ## ## # interface(`bcfg2_server_initrc_domtrans',` gen_require(` type bcfg2_server_initrc_exec_t; ') init_labeled_script_domtrans($1, bcfg2_server_initrc_exec_t) ') ######################################## ## ## Search bcfg2 lib directories. ## ## ## ## Domain allowed access. ## ## # interface(`bcfg2_search_lib',` gen_require(` type bcfg2_var_lib_t; ') allow $1 bcfg2_var_lib_t:dir search_dir_perms; files_search_var_lib($1) ') ######################################## ## ## Read bcfg2 lib files. ## ## ## ## Domain allowed access. ## ## # interface(`bcfg2_read_lib_files',` gen_require(` type bcfg2_var_lib_t; ') files_search_var_lib($1) read_files_pattern($1, bcfg2_var_lib_t, bcfg2_var_lib_t) ') ######################################## ## ## Manage bcfg2 lib files. ## ## ## ## Domain allowed access. ## ## # interface(`bcfg2_manage_lib_files',` gen_require(` type bcfg2_var_lib_t; ') files_search_var_lib($1) manage_files_pattern($1, bcfg2_var_lib_t, bcfg2_var_lib_t) ') ######################################## ## ## Manage bcfg2 lib directories. ## ## ## ## Domain allowed access. ## ## # interface(`bcfg2_manage_lib_dirs',` gen_require(` type bcfg2_var_lib_t; ') files_search_var_lib($1) manage_dirs_pattern($1, bcfg2_var_lib_t, bcfg2_var_lib_t) ') ######################################## ## ## All of the rules required to administer ## a bcfg2-server environment ## ## ## ## Domain allowed access. ## ## ## ## ## Role allowed access. ## ## ## # interface(`bcfg2_server_admin',` gen_require(` type bcfg2_server_t; type bcfg2_server_initrc_exec_t; type bcfg2_server_var_lib_t; ') allow $1 bcfg2_server_t:process { ptrace signal_perms }; ps_process_pattern($1, bcfg2_server_t) bcfg2_server_initrc_domtrans($1) domain_system_change_exemption($1) role_transition $2 bcfg2_server_initrc_exec_t system_r; allow $2 system_r; files_search_var_lib($1) admin_pattern($1, bcfg2_server_var_lib_t) ') ######################################## ## ## Execute bcfg2 in the bcfg2 domain. ## ## ## ## Domain allowed to transition. ## ## # interface(`bcfg2_domtrans',` gen_require(` type bcfg2_t, bcfg2_exec_t; ') corecmd_search_bin($1) domtrans_pattern($1, bcfg2_exec_t, bcfg2_t) ') ######################################## ## ## Execute bcfg2 in the bcfg2 domain. ## ## ## ## Domain allowed access. ## ## # interface(`bcfg2_initrc_domtrans',` gen_require(` type bcfg2_initrc_exec_t; ') init_labeled_script_domtrans($1, bcfg2_initrc_exec_t) ') ######################################## ## ## All of the rules required to administer ## a bcfg2 client ## ## ## ## Domain allowed access. ## ## ## ## ## Role allowed access. ## ## ## # interface(`bcfg2_client_admin',` gen_require(` type bcfg2_t; type bcfg2_initrc_exec_t; type bcfg2_var_lib_t; ') allow $1 bcfg2_t:process { ptrace signal_perms }; ps_process_pattern($1, bcfg2_t) bcfg2_initrc_domtrans($1) domain_system_change_exemption($1) role_transition $2 bcfg2_initrc_exec_t system_r; allow $2 system_r; ') bcfg2-1.3.3/redhat/selinux/bcfg2.te000066400000000000000000000145441223671746500170010ustar00rootroot00000000000000policy_module(bcfg2, 1.1.0) ######################################## # # Declarations # gen_tunable(bcfg2_server_exec_scripts, false) gen_tunable(bcfg2_server_can_network_connect_db, false) type bcfg2_t; type bcfg2_exec_t; init_daemon_domain(bcfg2_t, bcfg2_exec_t) type bcfg2_server_t; type bcfg2_server_exec_t; init_daemon_domain(bcfg2_server_t, bcfg2_server_exec_t) type bcfg2_initrc_exec_t; init_script_file(bcfg2_initrc_exec_t) type bcfg2_server_initrc_exec_t; init_script_file(bcfg2_server_initrc_exec_t) type bcfg2_var_lib_t; files_type(bcfg2_var_lib_t) type bcfg2_server_script_t; type bcfg2_server_script_exec_t; application_domain(bcfg2_server_script_t, bcfg2_server_script_exec_t) role system_r types bcfg2_server_script_t; type bcfg2_yum_helper_exec_t; application_domain(bcfg2_server_t, bcfg2_server_script_exec_t) type bcfg2_var_run_t; files_pid_file(bcfg2_var_run_t) type bcfg2_lock_t; files_lock_file(bcfg2_lock_t) type bcfg2_conf_t; files_config_file(bcfg2_conf_t) type bcfg2_tmp_t; files_tmp_file(bcfg2_tmp_t) ######################################## # # bcfg2-server local policy # allow bcfg2_server_t self:fifo_file rw_fifo_file_perms; allow bcfg2_server_t self:tcp_socket create_stream_socket_perms; allow bcfg2_server_t self:unix_stream_socket { connectto create_stream_socket_perms }; allow bcfg2_server_t self:process { setrlimit setsched }; allow bcfg2_server_t self:capability { setgid setuid sys_nice }; manage_dirs_pattern(bcfg2_server_t, bcfg2_var_lib_t, bcfg2_var_lib_t) manage_files_pattern(bcfg2_server_t, bcfg2_var_lib_t, bcfg2_var_lib_t) files_var_lib_filetrans(bcfg2_server_t, bcfg2_var_lib_t, dir ) manage_files_pattern(bcfg2_server_t, bcfg2_server_script_t, bcfg2_server_script_t) manage_files_pattern(bcfg2_server_t, bcfg2_var_run_t, bcfg2_var_run_t) files_pid_filetrans(bcfg2_server_t, bcfg2_var_run_t, file ) files_search_etc(bcfg2_server_t) read_files_pattern(bcfg2_server_t, bcfg2_conf_t, bcfg2_conf_t) read_lnk_files_pattern(bcfg2_server_t, bcfg2_conf_t, bcfg2_conf_t) manage_files_pattern(bcfg2_server_t, bcfg2_tmp_t, bcfg2_tmp_t) files_tmp_filetrans(bcfg2_server_t, bcfg2_tmp_t, file) can_exec(bcfg2_server_t, bcfg2_tmp_t) kernel_read_system_state(bcfg2_server_t) corecmd_exec_bin(bcfg2_server_t) corecmd_exec_shell(bcfg2_server_t) dev_read_urand(bcfg2_server_t) fs_list_inotifyfs(bcfg2_server_t) domain_use_interactive_fds(bcfg2_server_t) files_read_usr_files(bcfg2_server_t) logging_send_syslog_msg(bcfg2_server_t) miscfiles_read_localization(bcfg2_server_t) miscfiles_read_certs(bcfg2_server_t) auth_use_nsswitch(bcfg2_server_t) libs_exec_ldconfig(bcfg2_server_t) # let bcfg2-server run bcfg2-yum-helper in the exact same context can_exec(bcfg2_server_t, bcfg2_yum_helper_exec_t) # port 6789 was somehow already claimed by cyphesis, whatever that is corenet_tcp_bind_cyphesis_port(bcfg2_server_t) corenet_tcp_connect_http_port(bcfg2_server_t) corenet_tcp_sendrecv_http_port(bcfg2_server_t) optional_policy(` postgresql_stream_connect(bcfg2_server_t) postgresql_unpriv_client(bcfg2_server_t) tunable_policy(`bcfg2_server_can_network_connect_db',` postgresql_tcp_connect(bcfg2_server_t) ') ') optional_policy(` mysql_stream_connect(bcfg2_server_t) mysql_rw_db_sockets(bcfg2_server_t) tunable_policy(`bcfg2_server_can_network_connect_db',` mysql_tcp_connect(bcfg2_server_t) ') ') optional_policy(` unconfined_domain(bcfg2_server_script_t) ') tunable_policy(`bcfg2_server_exec_scripts', ` domtrans_pattern(bcfg2_server_t, bcfg2_server_script_exec_t, bcfg2_server_script_t) can_exec(bcfg2_server_t, bcfg2_server_script_t) ') ######################################## # # bcfg2 (client) local policy # allow bcfg2_t self:capability { fowner fsetid setuid setgid dac_override sys_nice sys_ptrace sys_tty_config }; allow bcfg2_t self:process { signal signull getsched setsched }; allow bcfg2_t self:fifo_file rw_fifo_file_perms; allow bcfg2_t self:netlink_route_socket create_netlink_socket_perms; allow bcfg2_t self:tcp_socket create_stream_socket_perms; allow bcfg2_t self:udp_socket create_socket_perms; files_search_etc(bcfg2_t) read_files_pattern(bcfg2_t, bcfg2_conf_t, bcfg2_conf_t) read_lnk_files_pattern(bcfg2_t, bcfg2_conf_t, bcfg2_conf_t) allow bcfg2_t bcfg2_lock_t:file manage_file_perms; files_lock_filetrans(bcfg2_t, bcfg2_lock_t, file) kernel_dontaudit_search_sysctl(bcfg2_t) kernel_dontaudit_search_kernel_sysctl(bcfg2_t) kernel_read_system_state(bcfg2_t) kernel_read_crypto_sysctls(bcfg2_t) cron_system_entry(bcfg2_t, bcfg2_exec_t) corecmd_exec_bin(bcfg2_t) corecmd_exec_shell(bcfg2_t) corenet_all_recvfrom_netlabel(bcfg2_t) corenet_all_recvfrom_unlabeled(bcfg2_t) corenet_tcp_sendrecv_generic_if(bcfg2_t) corenet_tcp_sendrecv_generic_node(bcfg2_t) corenet_tcp_bind_generic_node(bcfg2_t) corenet_tcp_connect_cyphesis_port(bcfg2_t) corenet_sendrecv_cyphesis_client_packets(bcfg2_t) dev_read_rand(bcfg2_t) dev_read_sysfs(bcfg2_t) dev_read_urand(bcfg2_t) domain_read_all_domains_state(bcfg2_t) domain_interactive_fd(bcfg2_t) files_manage_config_files(bcfg2_t) files_manage_config_dirs(bcfg2_t) files_manage_etc_dirs(bcfg2_t) files_manage_etc_files(bcfg2_t) files_read_usr_symlinks(bcfg2_t) files_relabel_config_dirs(bcfg2_t) files_relabel_config_files(bcfg2_t) selinux_search_fs(bcfg2_t) selinux_set_all_booleans(bcfg2_t) selinux_set_generic_booleans(bcfg2_t) selinux_validate_context(bcfg2_t) term_dontaudit_getattr_unallocated_ttys(bcfg2_t) term_dontaudit_getattr_all_ttys(bcfg2_t) init_all_labeled_script_domtrans(bcfg2_t) init_domtrans_script(bcfg2_t) init_read_utmp(bcfg2_t) init_signull_script(bcfg2_t) logging_send_syslog_msg(bcfg2_t) miscfiles_read_hwdata(bcfg2_t) miscfiles_read_localization(bcfg2_t) mount_domtrans(bcfg2_t) auth_use_nsswitch(bcfg2_t) seutil_domtrans_setfiles(bcfg2_t) seutil_domtrans_semanage(bcfg2_t) sysnet_dns_name_resolve(bcfg2_t) sysnet_run_ifconfig(bcfg2_t, system_r) optional_policy(` consoletype_domtrans(bcfg2_t) ') optional_policy(` hostname_exec(bcfg2_t) ') optional_policy(` files_rw_var_files(bcfg2_t) rpm_domtrans(bcfg2_t) rpm_domtrans_script(bcfg2_t) rpm_manage_db(bcfg2_t) rpm_manage_log(bcfg2_t) ') optional_policy(` unconfined_domain(bcfg2_t) ') optional_policy(` usermanage_domtrans_groupadd(bcfg2_t) usermanage_domtrans_useradd(bcfg2_t) ') bcfg2-1.3.3/redhat/systemd/000077500000000000000000000000001223671746500154555ustar00rootroot00000000000000bcfg2-1.3.3/redhat/systemd/bcfg2-server.service000066400000000000000000000005571223671746500213350ustar00rootroot00000000000000[Unit] Description=Bcfg2 configuration daemon After=syslog.target network.target [Service] Type=forking StandardOutput=syslog StandardError=syslog EnvironmentFile=-/etc/sysconfig/bcfg2-server PIDFile=/run/bcfg2-server/bcfg2-server.pid ExecStart=/usr/sbin/bcfg2-server -D /run/bcfg2-server/bcfg2-server.pid $BCFG2_SERVER_OPTIONS [Install] WantedBy=multi-user.target bcfg2-1.3.3/redhat/systemd/bcfg2.service000066400000000000000000000004031223671746500200170ustar00rootroot00000000000000[Unit] Description=Bcfg2 configuration client After=syslog.target network.target [Service] Type=forking StandardOutput=syslog StandardError=syslog EnvironmentFile=-/etc/sysconfig/bcfg2 ExecStart=/usr/sbin/bcfg2 $OPTIONS [Install] WantedBy=multi-user.target bcfg2-1.3.3/reports/000077500000000000000000000000001223671746500142145ustar00rootroot00000000000000bcfg2-1.3.3/reports/reports.wsgi000066400000000000000000000005311223671746500166040ustar00rootroot00000000000000import os import Bcfg2.settings os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.settings' import django.core.handlers.wsgi def application(environ, start_response): if 'BCFG2_CONFIG_FILE' in environ: Bcfg2.settings.read_config(cfile=environ['BCFG2_CONFIG_FILE']) return django.core.handlers.wsgi.WSGIHandler()(environ, start_response) bcfg2-1.3.3/reports/site_media/000077500000000000000000000000001223671746500163175ustar00rootroot00000000000000bcfg2-1.3.3/reports/site_media/AnchorPosition.js000066400000000000000000000124141223671746500216160ustar00rootroot00000000000000// =================================================================== // Author: Matt Kruse // WWW: http://www.mattkruse.com/ // // NOTICE: You may use this code for any purpose, commercial or // private, without any further permission from the author. You may // remove this notice from your final code if you wish, however it is // appreciated by the author if at least my web site address is kept. // // You may *NOT* re-distribute this code in any way except through its // use. That means, you can include it in your product, or your web // site, or any other form where the code is actually being used. You // may not put the plain javascript up on your site for download or // include it in your javascript libraries for download. // If you wish to share this code with others, please just point them // to the URL instead. // Please DO NOT link directly to my .js files from your site. Copy // the files to your server and use them there. Thank you. // =================================================================== /* AnchorPosition.js Author: Matt Kruse Last modified: 10/11/02 DESCRIPTION: These functions find the position of an tag in a document, so other elements can be positioned relative to it. COMPATABILITY: Netscape 4.x,6.x,Mozilla, IE 5.x,6.x on Windows. Some small positioning errors - usually with Window positioning - occur on the Macintosh platform. FUNCTIONS: getAnchorPosition(anchorname) Returns an Object() having .x and .y properties of the pixel coordinates of the upper-left corner of the anchor. Position is relative to the PAGE. getAnchorWindowPosition(anchorname) Returns an Object() having .x and .y properties of the pixel coordinates of the upper-left corner of the anchor, relative to the WHOLE SCREEN. NOTES: 1) For popping up separate browser windows, use getAnchorWindowPosition. Otherwise, use getAnchorPosition 2) Your anchor tag MUST contain both NAME and ID attributes which are the same. For example: 3) There must be at least a space between for IE5.5 to see the anchor tag correctly. Do not do with no space. */ // getAnchorPosition(anchorname) // This function returns an object having .x and .y properties which are the coordinates // of the named anchor, relative to the page. function getAnchorPosition(anchorname) { // This function will return an Object with x and y properties var useWindow=false; var coordinates=new Object(); var x=0,y=0; // Browser capability sniffing var use_gebi=false, use_css=false, use_layers=false; if (document.getElementById) { use_gebi=true; } else if (document.all) { use_css=true; } else if (document.layers) { use_layers=true; } // Logic to find position if (use_gebi && document.all) { x=AnchorPosition_getPageOffsetLeft(document.all[anchorname]); y=AnchorPosition_getPageOffsetTop(document.all[anchorname]); } else if (use_gebi) { var o=document.getElementById(anchorname); x=AnchorPosition_getPageOffsetLeft(o); y=AnchorPosition_getPageOffsetTop(o); } else if (use_css) { x=AnchorPosition_getPageOffsetLeft(document.all[anchorname]); y=AnchorPosition_getPageOffsetTop(document.all[anchorname]); } else if (use_layers) { var found=0; for (var i=0; i // WWW: http://www.mattkruse.com/ // // NOTICE: You may use this code for any purpose, commercial or // private, without any further permission from the author. You may // remove this notice from your final code if you wish, however it is // appreciated by the author if at least my web site address is kept. // // You may *NOT* re-distribute this code in any way except through its // use. That means, you can include it in your product, or your web // site, or any other form where the code is actually being used. You // may not put the plain javascript up on your site for download or // include it in your javascript libraries for download. // If you wish to share this code with others, please just point them // to the URL instead. // Please DO NOT link directly to my .js files from your site. Copy // the files to your server and use them there. Thank you. // =================================================================== // HISTORY // ------------------------------------------------------------------ // Feb 7, 2005: Fixed a CSS styles to use px unit // March 29, 2004: Added check in select() method for the form field // being disabled. If it is, just return and don't do anything. // March 24, 2004: Fixed bug - when month name and abbreviations were // changed, date format still used original values. // January 26, 2004: Added support for drop-down month and year // navigation (Thanks to Chris Reid for the idea) // September 22, 2003: Fixed a minor problem in YEAR calendar with // CSS prefix. // August 19, 2003: Renamed the function to get styles, and made it // work correctly without an object reference // August 18, 2003: Changed showYearNavigation and // showYearNavigationInput to optionally take an argument of // true or false // July 31, 2003: Added text input option for year navigation. // Added a per-calendar CSS prefix option to optionally use // different styles for different calendars. // July 29, 2003: Fixed bug causing the Today link to be clickable // even though today falls in a disabled date range. // Changed formatting to use pure CSS, allowing greater control // over look-and-feel options. // June 11, 2003: Fixed bug causing the Today link to be unselectable // under certain cases when some days of week are disabled // March 14, 2003: Added ability to disable individual dates or date // ranges, display as light gray and strike-through // March 14, 2003: Removed dependency on graypixel.gif and instead /// use table border coloring // March 12, 2003: Modified showCalendar() function to allow optional // start-date parameter // March 11, 2003: Modified select() function to allow optional // start-date parameter /* DESCRIPTION: This object implements a popup calendar to allow the user to select a date, month, quarter, or year. COMPATABILITY: Works with Netscape 4.x, 6.x, IE 5.x on Windows. Some small positioning errors - usually with Window positioning - occur on the Macintosh platform. The calendar can be modified to work for any location in the world by changing which weekday is displayed as the first column, changing the month names, and changing the column headers for each day. USAGE: // Create a new CalendarPopup object of type WINDOW var cal = new CalendarPopup(); // Create a new CalendarPopup object of type DIV using the DIV named 'mydiv' var cal = new CalendarPopup('mydiv'); // Easy method to link the popup calendar with an input box. cal.select(inputObject, anchorname, dateFormat); // Same method, but passing a default date other than the field's current value cal.select(inputObject, anchorname, dateFormat, '01/02/2000'); // This is an example call to the popup calendar from a link to populate an // input box. Note that to use this, date.js must also be included!! Select // Set the type of date select to be used. By default it is 'date'. cal.setDisplayType(type); // When a date, month, quarter, or year is clicked, a function is called and // passed the details. You must write this function, and tell the calendar // popup what the function name is. // Function to be called for 'date' select receives y, m, d cal.setReturnFunction(functionname); // Function to be called for 'month' select receives y, m cal.setReturnMonthFunction(functionname); // Function to be called for 'quarter' select receives y, q cal.setReturnQuarterFunction(functionname); // Function to be called for 'year' select receives y cal.setReturnYearFunction(functionname); // Show the calendar relative to a given anchor cal.showCalendar(anchorname); // Hide the calendar. The calendar is set to autoHide automatically cal.hideCalendar(); // Set the month names to be used. Default are English month names cal.setMonthNames("January","February","March",...); // Set the month abbreviations to be used. Default are English month abbreviations cal.setMonthAbbreviations("Jan","Feb","Mar",...); // Show navigation for changing by the year, not just one month at a time cal.showYearNavigation(); // Show month and year dropdowns, for quicker selection of month of dates cal.showNavigationDropdowns(); // Set the text to be used above each day column. The days start with // sunday regardless of the value of WeekStartDay cal.setDayHeaders("S","M","T",...); // Set the day for the first column in the calendar grid. By default this // is Sunday (0) but it may be changed to fit the conventions of other // countries. cal.setWeekStartDay(1); // week is Monday - Sunday // Set the weekdays which should be disabled in the 'date' select popup. You can // then allow someone to only select week end dates, or Tuedays, for example cal.setDisabledWeekDays(0,1); // To disable selecting the 1st or 2nd days of the week // Selectively disable individual days or date ranges. Disabled days will not // be clickable, and show as strike-through text on current browsers. // Date format is any format recognized by parseDate() in date.js // Pass a single date to disable: cal.addDisabledDates("2003-01-01"); // Pass null as the first parameter to mean "anything up to and including" the // passed date: cal.addDisabledDates(null, "01/02/03"); // Pass null as the second parameter to mean "including the passed date and // anything after it: cal.addDisabledDates("Jan 01, 2003", null); // Pass two dates to disable all dates inbetween and including the two cal.addDisabledDates("January 01, 2003", "Dec 31, 2003"); // When the 'year' select is displayed, set the number of years back from the // current year to start listing years. Default is 2. // This is also used for year drop-down, to decide how many years +/- to display cal.setYearSelectStartOffset(2); // Text for the word "Today" appearing on the calendar cal.setTodayText("Today"); // The calendar uses CSS classes for formatting. If you want your calendar to // have unique styles, you can set the prefix that will be added to all the // classes in the output. // For example, normal output may have this: // Today // But if you set the prefix like this: cal.setCssPrefix("Test"); // The output will then look like: // Today // And you can define that style somewhere in your page. // When using Year navigation, you can make the year be an input box, so // the user can manually change it and jump to any year cal.showYearNavigationInput(); // Set the calendar offset to be different than the default. By default it // will appear just below and to the right of the anchorname. So if you have // a text box where the date will go and and anchor immediately after the // text box, the calendar will display immediately under the text box. cal.offsetX = 20; cal.offsetY = 20; NOTES: 1) Requires the functions in AnchorPosition.js and PopupWindow.js 2) Your anchor tag MUST contain both NAME and ID attributes which are the same. For example: 3) There must be at least a space between for IE5.5 to see the anchor tag correctly. Do not do with no space. 4) When a CalendarPopup object is created, a handler for 'onmouseup' is attached to any event handler you may have already defined. Do NOT define an event handler for 'onmouseup' after you define a CalendarPopup object or the autoHide() will not work correctly. 5) The calendar popup display uses style sheets to make it look nice. */ // CONSTRUCTOR for the CalendarPopup Object function CalendarPopup() { var c; if (arguments.length>0) { c = new PopupWindow(arguments[0]); } else { c = new PopupWindow(); c.setSize(150,175); } c.offsetX = -152; c.offsetY = 25; c.autoHide(); // Calendar-specific properties c.monthNames = new Array("January","February","March","April","May","June","July","August","September","October","November","December"); c.monthAbbreviations = new Array("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"); c.dayHeaders = new Array("S","M","T","W","T","F","S"); c.returnFunction = "CP_tmpReturnFunction"; c.returnMonthFunction = "CP_tmpReturnMonthFunction"; c.returnQuarterFunction = "CP_tmpReturnQuarterFunction"; c.returnYearFunction = "CP_tmpReturnYearFunction"; c.weekStartDay = 0; c.isShowYearNavigation = false; c.displayType = "date"; c.disabledWeekDays = new Object(); c.disabledDatesExpression = ""; c.yearSelectStartOffset = 2; c.currentDate = null; c.todayText="Today"; c.cssPrefix=""; c.isShowNavigationDropdowns=false; c.isShowYearNavigationInput=false; window.CP_calendarObject = null; window.CP_targetInput = null; window.CP_dateFormat = "MM/dd/yyyy"; // Method mappings c.copyMonthNamesToWindow = CP_copyMonthNamesToWindow; c.setReturnFunction = CP_setReturnFunction; c.setReturnMonthFunction = CP_setReturnMonthFunction; c.setReturnQuarterFunction = CP_setReturnQuarterFunction; c.setReturnYearFunction = CP_setReturnYearFunction; c.setMonthNames = CP_setMonthNames; c.setMonthAbbreviations = CP_setMonthAbbreviations; c.setDayHeaders = CP_setDayHeaders; c.setWeekStartDay = CP_setWeekStartDay; c.setDisplayType = CP_setDisplayType; c.setDisabledWeekDays = CP_setDisabledWeekDays; c.addDisabledDates = CP_addDisabledDates; c.setYearSelectStartOffset = CP_setYearSelectStartOffset; c.setTodayText = CP_setTodayText; c.showYearNavigation = CP_showYearNavigation; c.showCalendar = CP_showCalendar; c.hideCalendar = CP_hideCalendar; c.getStyles = getCalendarStyles; c.refreshCalendar = CP_refreshCalendar; c.getCalendar = CP_getCalendar; c.select = CP_select; c.setCssPrefix = CP_setCssPrefix; c.showNavigationDropdowns = CP_showNavigationDropdowns; c.showYearNavigationInput = CP_showYearNavigationInput; c.copyMonthNamesToWindow(); // Return the object return c; } function CP_copyMonthNamesToWindow() { // Copy these values over to the date.js if (typeof(window.MONTH_NAMES)!="undefined" && window.MONTH_NAMES!=null) { window.MONTH_NAMES = new Array(); for (var i=0; i\n"; result += '
    \n'; } else { result += '
    \n'; result += '
    \n'; result += '
    \n'; } // Code for DATE display (default) // ------------------------------- if (this.displayType=="date" || this.displayType=="week-end") { if (this.currentDate==null) { this.currentDate = now; } if (arguments.length > 0) { var month = arguments[0]; } else { var month = this.currentDate.getMonth()+1; } if (arguments.length > 1 && arguments[1]>0 && arguments[1]-0==arguments[1]) { var year = arguments[1]; } else { var year = this.currentDate.getFullYear(); } var daysinmonth= new Array(0,31,28,31,30,31,30,31,31,30,31,30,31); if ( ( (year%4 == 0)&&(year%100 != 0) ) || (year%400 == 0) ) { daysinmonth[2] = 29; } var current_month = new Date(year,month-1,1); var display_year = year; var display_month = month; var display_date = 1; var weekday= current_month.getDay(); var offset = 0; offset = (weekday >= this.weekStartDay) ? weekday-this.weekStartDay : 7-this.weekStartDay+weekday ; if (offset > 0) { display_month--; if (display_month < 1) { display_month = 12; display_year--; } display_date = daysinmonth[display_month]-offset+1; } var next_month = month+1; var next_month_year = year; if (next_month > 12) { next_month=1; next_month_year++; } var last_month = month-1; var last_month_year = year; if (last_month < 1) { last_month=12; last_month_year--; } var date_class; if (this.type!="WINDOW") { result += ""; } result += '\n'; var refresh = windowref+'CP_refreshCalendar'; var refreshLink = 'javascript:' + refresh; if (this.isShowNavigationDropdowns) { result += ''; result += ''; result += ''; } else { if (this.isShowYearNavigation) { result += ''; result += ''; result += ''; result += ''; result += ''; if (this.isShowYearNavigationInput) { result += ''; } else { result += ''; } result += ''; } else { result += '\n'; result += '\n'; result += '\n'; } } result += '
     <'+this.monthNames[month-1]+'> <'+year+'><<'+this.monthNames[month-1]+' '+year+'>>
    \n'; result += '\n'; result += '\n'; for (var j=0; j<7; j++) { result += '\n'; } result += '\n'; for (var row=1; row<=6; row++) { result += '\n'; for (var col=1; col<=7; col++) { var disabled=false; if (this.disabledDatesExpression!="") { var ds=""+display_year+LZ(display_month)+LZ(display_date); eval("disabled=("+this.disabledDatesExpression+")"); } var dateClass = ""; if ((display_month == this.currentDate.getMonth()+1) && (display_date==this.currentDate.getDate()) && (display_year==this.currentDate.getFullYear())) { dateClass = "cpCurrentDate"; } else if (display_month == month) { dateClass = "cpCurrentMonthDate"; } else { dateClass = "cpOtherMonthDate"; } if (disabled || this.disabledWeekDays[col-1]) { result += ' \n'; } else { var selected_date = display_date; var selected_month = display_month; var selected_year = display_year; if (this.displayType=="week-end") { var d = new Date(selected_year,selected_month-1,selected_date,0,0,0,0); d.setDate(d.getDate() + (7-col)); selected_year = d.getYear(); if (selected_year < 1000) { selected_year += 1900; } selected_month = d.getMonth()+1; selected_date = d.getDate(); } result += ' \n'; } display_date++; if (display_date > daysinmonth[display_month]) { display_date=1; display_month++; } if (display_month > 12) { display_month=1; display_year++; } } result += ''; } var current_weekday = now.getDay() - this.weekStartDay; if (current_weekday < 0) { current_weekday += 7; } result += '\n'; result += '
    '+this.dayHeaders[(this.weekStartDay+j)%7]+'
    '+display_date+''+display_date+'
    \n'; if (this.disabledDatesExpression!="") { var ds=""+now.getFullYear()+LZ(now.getMonth()+1)+LZ(now.getDate()); eval("disabled=("+this.disabledDatesExpression+")"); } if (disabled || this.disabledWeekDays[current_weekday+1]) { result += ' '+this.todayText+'\n'; } else { result += ' '+this.todayText+'\n'; } result += '
    \n'; result += '
    \n'; } // Code common for MONTH, QUARTER, YEAR // ------------------------------------ if (this.displayType=="month" || this.displayType=="quarter" || this.displayType=="year") { if (arguments.length > 0) { var year = arguments[0]; } else { if (this.displayType=="year") { var year = now.getFullYear()-this.yearSelectStartOffset; } else { var year = now.getFullYear(); } } if (this.displayType!="year" && this.isShowYearNavigation) { result += ""; result += '\n'; result += ' \n'; result += ' \n'; result += ' \n'; result += '
    <<'+year+'>>
    \n'; } } // Code for MONTH display // ---------------------- if (this.displayType=="month") { // If POPUP, write entire HTML document result += '\n'; for (var i=0; i<4; i++) { result += ''; for (var j=0; j<3; j++) { var monthindex = ((i*3)+j); result += ''; } result += ''; } result += '
    '+this.monthAbbreviations[monthindex]+'
    \n'; } // Code for QUARTER display // ------------------------ if (this.displayType=="quarter") { result += '
    \n'; for (var i=0; i<2; i++) { result += ''; for (var j=0; j<2; j++) { var quarter = ((i*2)+j+1); result += ''; } result += ''; } result += '

    Q'+quarter+'

    \n'; } // Code for YEAR display // --------------------- if (this.displayType=="year") { var yearColumnSize = 4; result += ""; result += '\n'; result += ' \n'; result += ' \n'; result += '
    <<>>
    \n'; result += '\n'; for (var i=0; i'+currentyear+''; } result += ''; } result += '
    \n'; } // Common if (this.type == "WINDOW") { result += "\n"; } return result; } bcfg2-1.3.3/reports/site_media/PopupWindow.js000066400000000000000000000252421223671746500211550ustar00rootroot00000000000000// =================================================================== // Author: Matt Kruse // WWW: http://www.mattkruse.com/ // // NOTICE: You may use this code for any purpose, commercial or // private, without any further permission from the author. You may // remove this notice from your final code if you wish, however it is // appreciated by the author if at least my web site address is kept. // // You may *NOT* re-distribute this code in any way except through its // use. That means, you can include it in your product, or your web // site, or any other form where the code is actually being used. You // may not put the plain javascript up on your site for download or // include it in your javascript libraries for download. // If you wish to share this code with others, please just point them // to the URL instead. // Please DO NOT link directly to my .js files from your site. Copy // the files to your server and use them there. Thank you. // =================================================================== /* PopupWindow.js Author: Matt Kruse Last modified: 02/16/04 DESCRIPTION: This object allows you to easily and quickly popup a window in a certain place. The window can either be a DIV or a separate browser window. COMPATABILITY: Works with Netscape 4.x, 6.x, IE 5.x on Windows. Some small positioning errors - usually with Window positioning - occur on the Macintosh platform. Due to bugs in Netscape 4.x, populating the popup window with bcfg2-1.3.3/reports/xsl-transforms/xsl-transform-includes/html-templates.xsl000066400000000000000000000147001223671746500273330ustar00rootroot00000000000000
    Time Ran: ()

    Node:

    Revision:
    &nbsp;
    &nbsp;
    Node is clean; Everything has been satisfactorily configured.
    This node did not run within the last 24 hours-- it may be out of date.
    items did not verify and are considered Dirty.
    items were modified in the last run.
    extra configuration elements on node.
  • Config File:
  • Package:
  • Directory:
  • Service:
  • SymLink:
  • bcfg2-1.3.3/reports/xsl-transforms/xsl-transform-includes/main-js.xsl000066400000000000000000000021061223671746500257260ustar00rootroot00000000000000 bcfg2-1.3.3/reports/xsl-transforms/xsl-transform-includes/sorttable-js.xsl000066400000000000000000000155341223671746500270120ustar00rootroot00000000000000 bcfg2-1.3.3/reports/xsl-transforms/xsl-transform-includes/text-templates.xsl000066400000000000000000000074731223671746500273640ustar00rootroot00000000000000 Node: Time Ran: . () Node is clean; Everything has been satisfactorily configured. This node did not run within the last 24 hours-- it may be out of date. items did not verify and are considered Dirty: items were modified in the last run. extra configuration elements on node. Config File: Package: Directory: Service: SymLink: bcfg2-1.3.3/schemas/000077500000000000000000000000001223671746500141415ustar00rootroot00000000000000bcfg2-1.3.3/schemas/atom.xsd000066400000000000000000000012401223671746500156160ustar00rootroot00000000000000 atomic configuration types schema for bcfg2 Narayan Desai, Argonne National Laboratory bcfg2-1.3.3/schemas/authorizedkeys.xsd000066400000000000000000000152271223671746500177420ustar00rootroot00000000000000 Schema for :ref:`server-plugins-generators-cfg-sshkeys` ``authorizedkeys.xml`` An **AuthorizedKeysGroupType** is a tag used to provide logic. Child entries of an AuthorizedKeysGroupType tag only apply to machines that match the condition specified -- either membership in a group, or a matching client name. :xml:attribute:`AuthorizedKeysGroupType:negate` can be set to negate the sense of the match. The name of the client or group to match on. Child entries will only apply to this client or group (unless :xml:attribute:`AuthorizedKeysGroupType:negate` is set). Negate the sense of the match, so that child entries only apply to a client if it is not a member of the given group or does not have the given name. An **OptionContainerType** is a tag used to provide logic. Child entries of an OptionContainerType tag only apply to machines that match the condition specified -- either membership in a group, or a matching client name. :xml:attribute:`OptionContainerType:negate` can be set to negate the sense of the match. The name of the client or group to match on. Child entries will only apply to this client or group (unless :xml:attribute:`OptionContainerType:negate` is set). Negate the sense of the match, so that child entries only apply to a client if it is not a member of the given group or does not have the given name. Allow access from a public key, given either as text content, or described by the attributes. The path of the public key to allow. Use a public key specific to the given group, instead of the public key specific to the appropriate category group of the current client. Use a public key specific to the group in the given category, instead of the category specified in ``bcfg2.conf``. Use a public key specific to the given host. Specify options for public key authentication and connection. See :manpage:`sshd(8)` for details on allowable options. The name of the sshd option. The value of the sshd option. This can be omitted for options that take no value. **Deprecated** way to specify options for public key authentication and connection. See :manpage:`sshd(8)` for details on allowable parameters. Top-level tag for describing a generated SSH key pair. bcfg2-1.3.3/schemas/awstags.xsd000066400000000000000000000050541223671746500163360ustar00rootroot00000000000000 :ref:`AWSTags <server-plugins-connectors-awstags>` config schema for bcfg2 The group to assign to machines with tags that match the enclosing Tag expression. More than one group can be specified. The name pattern to match against. This is a regular expression. It is not anchored. The value pattern to match against. This is a regular expression. It is not anchored. Top-level tag for ``AWSTags/config.xml``. Representation of a pattern that matches AWS tags. Tags can be matched in one of two ways: * If only :xml:attribute:`TagType:name` is specified, then AWSTags will only look for a tag with a matching name, and the value of tags is ignored. * If both :xml:attribute:`TagType:name` and :xml:attribute:`TagType:value` are specified, a tag must have a matching name *and* a matching value. bcfg2-1.3.3/schemas/base.xsd000066400000000000000000000025401223671746500155740ustar00rootroot00000000000000 base schema for bcfg2 Narayan Desai, Argonne National Laboratory bcfg2-1.3.3/schemas/bundle.xsd000066400000000000000000000325621223671746500161420ustar00rootroot00000000000000 bundle schema for bcfg2 Narayan Desai, Argonne National Laboratory Abstract implementation of a Package entry. The full specification will be generated by a plugin such as Packages. Abstract implementation of a Path entry. The entry will either be handled by Cfg, TGenshi, or another Generator plugin; or handled by Rules, in which case the full specification of this entry will be included in Rules. Abstract implementation of a Service entry. The full specification will be included in Rules. Abstract implementation of an Action entry. The full specification will be included in Rules. Abstract description of a POSIXUser entry. Abstract description of a POSIXGroup entry. PostInstall entries are deprecated in favor of Action entries. Actions can do everything PostInstall entries can do and more. Abstract SELinux boolean entry. Abstract SELinux port entry. Abstract SELinux file context ("fcontext") entry. Abstract SELinux node entry. Abstract SELinux login entry. Abstract SELinux user entry. Abstract SELinux interface entry. Abstract SELinux permissive domain entry. Abstract SELinux module entry. Fully bound description of a software package to be managed. Fully bound description of a filesystem path to be handled by the POSIX driver. Fully bound description of a system service to be managed. Fully bound description of a command to be run. Fully bound description of an SELinux boolean entry. Fully bound description of an SELinux port entry. Fully bound description of an SELinux file context entry. Fully bound description of an SELinux node entry. Fully bound description of an SELinux login entry. Fully bound description of an SELinux user entry. Fully bound description of an SELinux interface entry. Fully bound description of an SELinux permissive domain entry. Fully bound description of an SELinux module entry. Fully bound description of a POSIXUser entry. Fully bound description of a POSIXGroup entry. Elements within Group tags only apply to clients that are members of that group (or vice-versa; see #element_negate below) Elements within Client tags only apply to the named client (or vice-versa; see #element_negate below) Nesting Bundle tags is allowed in order to support XInclude within Bundles. A **BundlerGroupType** is a tag used to provide logic. Child entries of a BundlerGroupType tag only apply to machines that match the condition specified -- either membership in a group, or a matching client name. :xml:attribute:`BundlerGroupType:negate` can be set to negate the sense of the match. The group name Negate the sense of this group or client; i.e., entries within this tag are only used on clients that are not members of the group, or that have hostnames that do not match. Freeform description of the bundle. The name of the bundle. This must match the bundle filename, minus the extension. Bundle schema version. URL of master version (for common repo) Master version control revision. A bundle describes a group of inter-dependent configuration entries, such as the combination of packages, configuration files, and service activations that comprise typical Unix daemons. Bundles are used to add groups of configuration entries to the inventory of client configurations, as opposed to describing particular versions of those entries. For example, a bundle could say that the configuration file ``/etc/passwd`` should be included in a configuration, but will not describe the particular version of ``/etc/passwd`` that a given client will receive. bcfg2-1.3.3/schemas/clients.xsd000066400000000000000000000135761223671746500163360ustar00rootroot00000000000000 Bcfg2 client list schema Describe a Bcfg2 client machine. **Alias** allows you to set alternative hostname and IP address pairs that also resolve to this client. Hostname of the alternative client name-address pair. IP address of the alternative client name-address pair. Hostname of client. This needs to be the name (probably FQDN) returned by a reverse lookup on the connecting IP address. Profile group naem to associate this client with. Deprecated. Authentication mode for the client. See :ref:`appendix-guides-authentication` for details on the values available. Establishes a name for this cilent that can be used to bypass dns-based client resolution. Establishes a per-client password that can be used instead of the global password. Deprecated. Use :xml:attribute:`ClientType:floating` instead. Allows requests to come from any IP address, rather than requiring requests to come from an IP associated with this client. Note that, since this forces the Bcfg2 server to trust any connection that claims to be from this hostname, it can introduce security issues. Requires the use of :xml:attribute:`ClientType:password` for this client. Deprecated. Establishes an extra IP address that resolves to this client. The version of the Bcfg2 client running on this machine. You should not have to set this manually, but can let the Bcfg2 server set it automatically. Metadata client list top-level tag Client schema version bcfg2-1.3.3/schemas/decisions.xsd000066400000000000000000000012741223671746500166450ustar00rootroot00000000000000 decision list schema for bcfg2 Narayan Desai, Argonne National Laboratory bcfg2-1.3.3/schemas/defaults.xsd000066400000000000000000000030251223671746500164700ustar00rootroot00000000000000 string enumeration definitions for bcfg2 Narayan Desai, Argonne National Laboratory bcfg2-1.3.3/schemas/deps.xsd000066400000000000000000000027331223671746500156210ustar00rootroot00000000000000 dependency schema for bcfg2 Narayan Desai, Argonne National Laboratory bcfg2-1.3.3/schemas/fileprobes.xsd000066400000000000000000000024031223671746500170120ustar00rootroot00000000000000 FileProbes plugin config schema for bcfg2 Chris St. Pierre bcfg2-1.3.3/schemas/genshi.xsd000066400000000000000000000277161223671746500161530ustar00rootroot00000000000000 Genshi XML templating language schema `for directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id3>`_ The loop iterator `if directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id1>`_ The statement giving the value to test `match directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id5>`_ XPath expression to search for in the template. Whether the engine should stop looking for more matching elements after the first match. Use this on match templates that match elements that can only occur once in the stream, such as the <head> or <body> elements in an HTML template, or elements with a specific ID. Whether the matched content should be buffered in memory. Buffering can improve performance a bit at the cost of needing more memory during rendering. Buffering is *required* for match templates that contain more than one invocation of the ``select()`` function. If there is only one call, and the matched content can potentially be very long, consider disabling buffering to avoid excessive memory use. Whether the match template should be applied to its own output. Note that once implies non-recursive behavior, so this attribute only needs to be set for match templates that don't also have once set. `def directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id4>`_ The function prototype `with directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#py-with>`_ A semicolon-delimited list of variables to define and their values. `replace directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id8>`_ The value to replace the contents with. `choose directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id2>`_ The ``when`` directive is used inside :xml:type:`py:chooseType` or :xml:attribute:`py:genshiAttrs:choose` to handle a single specific condition. If ``test`` is set, the child :xml:element:`py:when` directives are tested for equality to the value of the expression. The ``otherwise`` directive is used inside :xml:type:`py:chooseType` or :xml:attribute:`py:genshiAttrs:choose` to handle all conditions not handled by a :xml:element:`py:when`. Most Genshi templating directives can be used either as standalone elements or as attributes on existing elements. This element group defines the standalone tags. Most Genshi templating directives can be used either as standalone elements or as attributes on existing elements. This attribute group defines the attribute directives. `if directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id1>`_ `choose directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id2>`_ The ``when`` directive is used inside :xml:type:`py:chooseType` or :xml:attribute:`py:genshiAttrs:choose` to handle a single specific condition. The ``otherwise`` directive is used inside :xml:type:`py:chooseType` or :xml:attribute:`py:genshiAttrs:choose` to handle all conditions not handled by a :xml:element:`py:when`. `for directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id3>`_ `def directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id4>`_ `match directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id5>`_ `with directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#py-with>`_ `attrs directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id6>`_ `content directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id7>`_ `replace directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id8>`_ `strip directive <http://genshi.edgewall.org/wiki/Documentation/xml-templates.html#id9>`_ bcfg2-1.3.3/schemas/grouplogic.xsd000066400000000000000000000075071223671746500170440ustar00rootroot00000000000000 GroupLogic schema for bcfg2 A **GroupLogicDeclarationType** declares a Group to be added to a client. The group name The top-level tag of a GroupLogic configuration file. Elements within Group tags only apply to clients that are members of that group (or vice-versa; see #element_negate below) Elements within Client tags only apply to the named client (or vice-versa; see #element_negate below) Nesting GroupLogic tags is allowed in order to support XInclude. A **GroupLogicContainerType** is a tag used to provide logic. Child entries of a GroupLogicContainerType tag only apply to machines that match the condition specified -- either membership in a group, or a matching client name. :xml:attribute:`GroupLogicContainerType:negate` can be set to negate the sense of the match. The group name Negate the sense of this group or client; i.e., entries within this tag are only used on clients that are not members of the group, or that have hostnames that do not match. A GroupLogic file is a genshi file that can be used to dynamically add additional groups to a client. bcfg2-1.3.3/schemas/grouppatterns.xsd000066400000000000000000000021041223671746500175730ustar00rootroot00000000000000 group patterns config schema for bcfg2 Narayan Desai, Argonne National Laboratory bcfg2-1.3.3/schemas/info.xsd000066400000000000000000000121321223671746500156130ustar00rootroot00000000000000 ``info.xml`` schema for Bcfg2 The Info tag specifies metadata (ownership, permissions, etc.) for entries that are generated by various plugins. Encoding of the file for tranfer to the client. Use ``base64`` for binary files. Sets group of the file. Important entries are installed first during client execution. Sets owner of the file. Sets the mode of the file from the octal value given. Sets the SELinux context of the file, or sets to the default context for that path set by policy if set to the special value ``__default__``. If true, files that are replaced will be backed up first. The contents of sensitive entries aren't included in reports. An **InfoGroupType** is a ``info.xml`` tag used to provide logic. Child entries of such a tag only apply to machines that match the condition specified -- membership in a group, a matching client name, or a matching path for the file being generated. :xml:attribute:`InfoGroupType:negate` can be set to negate the sense of the match. The name of the client or group, or the full path to match on. Child entries will only apply to this client or group (unless :xml:attribute:`InfoGroupType:negate` is set). Negate the sense of the match, so that child entries only apply to a client if it is not a member of the given group, does not have the given client name, or the path names do not match. Top-level tag for ``info.xml``. bcfg2-1.3.3/schemas/metadata.xsd000066400000000000000000000152221223671746500164430ustar00rootroot00000000000000 Bcfg2 schema for declaring groups and associating groups with bundles. Declaration of a bundle as a member of a group. The bundle name The Group tag serves two purposes: * If it is at the top level of ``groups.xml`` (i.e., its direct parent is :xml:element:`Groups`), or if it has no children, then it is considered to declare a new group, and :xml:attribute:`MetadataGroupType:profile`, :xml:attribute:`MetadataGroupType:public`, :xml:attribute:`MetadataGroupType:category`, and :xml:attribute:`MetadataGroupType:default` are parsed. * If it is not at the top level of ``groups.xml`` *and* it has children, then it is considered to be a conditional; its children only apply to clients that are already members in the group. The attributes listed above are not parsed. Name of the group Mark the group as a profile, which allows a client to be directly associated with this group in :ref:`server-plugins-grouping-metadata-clients-xml`. Mark the group as public, which allows any client to assert membership in the group with ``bcfg2 -p``. Set as the profile to use for clients that are not associated with any profile explicitly in :ref:`server-plugins-grouping-metadata-clients-xml`. Setting ``default`` to ``true`` requires setting :xml:attribute:`MetadataGroupType:profile` to ``true`` as well. Assign the group to the given category. A client can only be a member of one group in a given category. When the Group tag is used as a conditional, only apply the child elements if the named group does not match. When the Group tag is used as a declaration, do not apply the named group to matching clients. Client tags are conditionals, and can be used to set per-client groups and bundles. The name of the client. Only apply the child tags if the named client does not match. Metadata group list top-level tag Nested ``Groups`` tags allowed to support XInclude Group schema version URI of master version (for common repository) Master version control revision bcfg2-1.3.3/schemas/nagiosgen.xsd000066400000000000000000000021201223671746500166260ustar00rootroot00000000000000 NagiosGen config schema for bcfg2 Chris St. Pierre bcfg2-1.3.3/schemas/packages.xsd000066400000000000000000000223441223671746500164440ustar00rootroot00000000000000 packages config schema for bcfg2 Narayan Desai, Argonne National Laboratory **RepoOptionsType** can be used to specify arbitrary repository options. The options given in this tag will only be used on the Bcfg2 server, not on the clients. The options given in this tag will only be used on the Bcfg2 clients, not on the server. All other (arbitrary) attributes will be added to the repository configuration. **SourceType** elements are used to specify software sources (i.e., repositories) for the Packages plugin. Components are used to build multiple repository URLs from a single :xml:element:`Source` tag. This is only meaningful if the :xml:attribute:`SourceType:url` attribute is specified; see that attribute above for more detail. The architecture(s) of the repository. A client must be a member of one of the listed architecture groups in order for this source to apply to the client. Additionally, if the :xml:attribute:`SourceType:url` attribute is specified, the :xml:element:`Arch` tag is used to generate URLs. See :xml:attribute:`the url attribute <SourceType:url>` for more detail. The GPG key(s) for the repository. This only applies to sources with :xml:attribute:`SourceType:type` = ``yum``. If GPG keys are specified, then GPG checking will be automatically enabled for the repository, both on the Bcfg2 server (if :ref:`yum libraries <native-yum-libraries>` are in use) and on the Bcfg2 client (if you use :ref:`server-plugins-generators-packages` to :ref:`generate your Yum config <generating-client-configs>`). Arbitrary options to be used in the repository configuration. Blacklist the given package(s) from the :ref:`server-plugins-generators-packages` plugin. This prevents them from being included in automatically-resolved dependencies. If **Whitelist** is specified, *only* packages listed will be included by the :ref:`server-plugins-generators-packages` plugin. Include packages recommended as dependencies by APT. This only applies to sources with :xml:attribute:`SourceType:type` = ``apt``. You must regenerate the Packages cache after changing this attribute. Include essential packages from this repo by default (i.e., without needing to specify them in a bundle). This only applies to sources with :xml:attribute:`SourceType:type` = ``apt``. The type of the repository. This corresponds to the Packages plugin driver that will handle the source. The :ref:`Pulp <pulp-source-support>` repository ID for this repo. This only applies to sources with :xml:attribute:`SourceType:type` = ``yum``. Due to the amount of data that can be queried directly from Pulp, there's rarely a need to supply other attributes. Include ``deb-src`` lines in the generated APT configuration. This only applies to sources with :xml:attribute:`SourceType:type` = ``apt``. The base URL to use when generating URLs for this source. If :xml:attribute:`SourceType:url` is used, you must also provide the :xml:element:`Arch` tag, at least one :xml:element:`Component` tag, and the :xml:attribute:`SourceType:version` attribute. You must not specify :xml:attribute:`SourceType:rawurl`. For each combination of component and Arch tag, a URL is created in the format:: <url>/<version>/<component>/<arch> The raw URL to the (single) repository defined by this source. :xml:element:`Component` and :xml:attribute:`SourceType:version` are ignored if this is given. The OS version this source applies to. This is used to generate URLs if the :xml:attribute:`SourceType:url` attribute is given, and ignored otherwise. bcfg2-1.3.3/schemas/pathentry.xsd000066400000000000000000000012101223671746500166710ustar00rootroot00000000000000 path entry schema for bcfg2 Narayan Desai, Argonne National Laboratory bcfg2-1.3.3/schemas/pkglist.xsd000066400000000000000000000075411223671746500163450ustar00rootroot00000000000000 package list schema for bcfg2 Narayan Desai, Argonne National Laboratory An **PackageContainerType** is a tag used to provide logic. Child entries of an PackageContainerType tag only apply to machines that match the condition specified -- either membership in a group, or a matching client name. :xml:attribute:`PackageContainerType:negate` can be set to negate the sense of the match. The name of the client or group to match on. Child entries will only apply to this client or group (unless :xml:attribute:`PackageContainerType:negate` is set). Negate the sense of the match, so that child entries only apply to a client if it is not a member of the given group or does not have the given name. The top-level tag in a :ref:`server-plugins-generators-pkgmgr` XML package list. Sets the priority for rules in this file for :ref:`server-plugins-generators-rules`. The higher value wins. The package type, which determines which client driver will handle installation of packages in this package list. The URI to the repository that data in this package list file was parsed from. Comma-separated list of architectures of packages in this package list that should be installed. Filename creation rules for multiarch packages. bcfg2-1.3.3/schemas/pkgtype.xsd000066400000000000000000000311051223671746500163440ustar00rootroot00000000000000 package list schema for bcfg2 Narayan Desai, Argonne National Laboratory Abstract description of a package or package group to be installed. Install the named package. Either ``name`` or :xml:attribute:`PackageStructure:group` must be specified. Install the named package group. Package groups are only supported for Yum :xml:element:`Source` repositories, and only if the :ref:`yum libraries <native-yum-libraries>` are in use. Either ``group`` or :xml:attribute:`PackageStructure:name` must be specified. Whether or not to verify the package. The package set to select from a given package group. Only meaningful if :xml:attribute:`PackageStructure:group` is specified. Concrete specification of a package to be installed. A package can be specified in one of two ways: * A single Package tag that lists all of the attributes for the single instance of the package that should be installed. * A Package tag with any number of :xml:element:`Instance` children, each of which lists the attributes of an instance of the package that should be installed. In this case, the Package tag should only have :xml:attribute:`PackageType:name` and :xml:attribute:`PackageType:type`. Note that many of the attributes listed below are specific to one or a few package drivers. An Instance element describes a single instance of a package that may have several different versions, arches, etc., installed at once. The architecture of the package to be installed. The epoch of the package to be installed. The version of the package to be installed. See :xml:attribute:`PackageType:version` for details. The release of the package to be installed. Package file name. No name parsing is performed, so no extra fields get set. This is only used for manual maintenance of ``gpg-pubkey`` packages with the :ref:`YUM, YUM24, or RPM <client-tools-yum>` driver. Whether or not to perform full package verification (file integrity, etc.) on this package with the :ref:`YUM, YUM24, or RPM <client-tools-yum>` driver. Comma-separated list of flags to pass to the package verification routines of the :ref:`YUM, YUM24, or RPM <client-tools-yum>` driver. See ``man rpm`` for details on the flags. If this is set to any value other than "install", package installation will be suppressed with the :ref:`YUM24 and RPM <client-tools-yum>` drivers. If this is set to any value other than "upgrade", a package that has the incorrect version installed will not be fixed with the :ref:`YUM24 and RPM <client-tools-yum>` drivers. Note that "upgrade" is misleading; if a package is installed that is newer than the desired version, it will not be downgraded if this attribute is set to anything other than "upgrade". If this is set to any value other than "reinstall", a package that fails package verification will not be reinstalled with the :ref:`YUM24 and RPM <client-tools-yum>` drivers. Package name. The architecture of the package to be installed. The version of the package to be installed. This should *only* be the version, i.e., not the release. Release should be specified in :xml:attribute:`PackageType:release`, and it is an error to append the release to this. This can also be one of two "special" values: * ``auto`` will select the newest version of the package available. * ``any`` will select any version of the package, and can be used to ensure that a package is installed without requiring any particular version. The release of the package to be installed. Package file name. Several other attributes (name, version) can be automatically defined based on regular expressions defined in the :ref:`server-plugins-generators-pkgmgr` plugin, which is the only plugin with which this is useful. Whether or not to perform package verification. This is not supported by the :ref:`YUM <client-tools-yum>` driver. Package file name. No name parsing is performed, so no extra fields get set. This is only used for some edge cases with :ref:`server-plugins-generators-pkgmgr`. Comma-separated list of architectures of this package that should be installed. This is only used by the :ref:`server-plugins-generators-pkgmgr` plugin. Filename creation rules for multiarch packages. This is only used by the :ref:`server-plugins-generators-pkgmgr` plugin. The package type, which determines which client driver will handle installation of this package. The name of the package for installing (as opposed to the name when verifying) with the Blast and OpenCSW drivers. Whether or not to perform basic package checks (version, release, etc.) on this package with the :ref:`YUM, YUM24, or RPM <client-tools-yum>` driver. Whether or not to perform full package verification (file integrity, etc.) on this package with the :ref:`YUM, YUM24, or RPM <client-tools-yum>` driver. Flags to pass to the package verification routines of the :ref:`YUM, YUM24, or RPM <client-tools-yum>` driver. bcfg2-1.3.3/schemas/privkey.xsd000066400000000000000000000123121223671746500163510ustar00rootroot00000000000000 Schema for :ref:`server-plugins-generators-cfg-sshkeys` ``privkey.xml`` An **PrivateKeyGroupType** is a tag used to provide logic. Child entries of a PrivateKeyGroupType tag only apply to machines that match the condition specified -- either membership in a group, or a matching client name. :xml:attribute:`PrivateKeyGroupType:negate` can be set to negate the sense of the match. The name of the client or group to match on. Child entries will only apply to this client or group (unless :xml:attribute:`PrivateKeyGroupType:negate` is set). Negate the sense of the match, so that child entries only apply to a client if it is not a member of the given group or does not have the given name. Available private key formats Specify the private key passphrase. The name of the passphrase to use to encrypt this private key on the filesystem (in Bcfg2). Specify parameters for creating the private key Number of bits in the key. See :manpage:`ssh-keygen(1)` for defaults. Key type to create. Top-level tag for describing a generated SSH key pair. Create keys on a per-host basis (rather than on a per-group basis). Create keys specific to the given category, instead of specific to the category given in ``bcfg2.conf``. Create group-specific keys with the given priority. Override the global strict/lax decryption setting in ``bcfg2.conf``. bcfg2-1.3.3/schemas/pubkey.xsd000066400000000000000000000007441223671746500161650ustar00rootroot00000000000000 Schema for :ref:`server-plugins-generators-cfg-sshkeys` ``pubkey.xml`` Top-level tag for flagging a generated SSH public key. bcfg2-1.3.3/schemas/report-configuration.xsd000066400000000000000000000052101223671746500210370ustar00rootroot00000000000000 statistical report configuration schema for bcfg2 Joey Hagedorn, Argonne National Laboratory bcfg2-1.3.3/schemas/rules.xsd000066400000000000000000000174331223671746500160230ustar00rootroot00000000000000 string enumeration definitions for bcfg2 Narayan Desai, Argonne National Laboratory Fully bound description of a software package to be managed. Fully bound description of a filesystem path to be handled by the POSIX driver. Fully bound description of a system service to be managed. Fully bound description of a command to be run. Fully bound description of an SELinux boolean entry. Fully bound description of an SELinux port entry. Fully bound description of an SELinux file context entry. Fully bound description of an SELinux node entry. Fully bound description of an SELinux login entry. Fully bound description of an SELinux user entry. Fully bound description of an SELinux interface entry. Fully bound description of an SELinux permissive domain entry. Fully bound description of an SELinux module entry. Fully bound description of a POSIXUser entry. Fully bound description of a POSIXGroup entry. PostInstall entries are deprecated in favor of Action entries. Actions can do everything PostInstall entries can do and more. Elements within Group tags only apply to clients that are members of that group (or vice-versa, if :xml:attribute:`RContainerType:negate` is set) Elements within Client tags only apply to the named client (or vice-versa, if :xml:attribute:`RContainerType:negate` is set) An **RContainerType** is a Rules tag used to provide logic. Child entries of an RContainerType tag only apply to machines that match the condition specified -- either membership in a group, or a matching client name. :xml:attribute:`RContainerType:negate` can be set to negate the sense of the match. The name of the client or group to match on. Child entries will only apply to this client or group (unless :xml:attribute:`RContainerType:negate` is set). Negate the sense of the match, so that child entries only apply to a client if it is not a member of the given group or does not have the given name. The top-level tag for concrete descriptions of entries in :ref:`server-plugins-generators-rules`. Sets the priority for rules in this file for :ref:`server-plugins-generators-rules`. The higher value wins. bcfg2-1.3.3/schemas/selinux.xsd000066400000000000000000000251471223671746500163610ustar00rootroot00000000000000 SELinux element definitions for bcfg2 Concrete SELinux boolean entry Name of the boolean Value of the boolean Concrete SELinux port entry Port number or range and protocol SELinux type to apply to this port SELinux MLS range to apply to this port Port number or range and protocol for SEPort entries. ``<port>/<proto>`` or ``<start>-<end>/<proto>`` Concrete SELinux file context ("fcontext") entry Regular expression file specification SELinux type to apply to files matching this specification File type to match SELinux MLS range to apply to files matching this specification Concrete SELinux node entry IP address and netmask of node SELinux type to apply to this node Protocol SELinux MLS range to apply to this node IP address and netmask for SENode entries. Netmask can be numeric or dotted-quad. ``<addr>/<netmask>``. Netmask can be numeric (``/16``) or dotted-quad (``/255.255.0.0``). Concrete SELinux login entry Unix username SELinux username SELinux MLS range to apply to this user Concrete SELinux user entry SELinux username Space-separated list of rules Home directory context prefix SELinux MLS range to apply to this user Concrete SELinux interface entry Interface name SELinux type to apply to this interface SELinux MLS range to apply to this interface Concrete SELinux permissive domain entry SELinux type to make permissive Concrete SELinux module entry SELinux module name or filename Disable this module bcfg2-1.3.3/schemas/servicetype.xsd000066400000000000000000000077141223671746500172340ustar00rootroot00000000000000 services schema for bcfg2 Narayan Desai, Argonne National Laboratory Concrete description of a service entry. Note that, due to the great proliferation of init systems, many of the attributes listed only apply to one or a few client tools. The name of the service. Whether the service should start at boot. The default value corresponds to the value of the status attribute. Whether the service should be on or off when the bcfg2 client is run. This attribute may have different behavior depending on the characteristics of the client tool. If set to "ignore", then the status of the service will not be checked. Whether or not to restart the service when the bundle is modified. (New in 1.3; replaces "mode" attribute.) Whether or not to install the service initially. (New in 1.3; replaces "mode" attribute.) Driver to use on the client to manage this service. The resource identifier for SMF services. Order for service startup. Only meaningful for DebInit services. Command to pass to the service management system when restarting a service. Parameters to pass to the service. Only meaningful for Upstart services. bcfg2-1.3.3/schemas/sslca-cert.xsd000066400000000000000000000136271223671746500167320ustar00rootroot00000000000000 Schema for :ref:`server-plugins-generators-sslca` ``cert.xml`` An **SSLCACertGroupType** is a tag used to provide logic. Child entries of an SSLCACertGroupType tag only apply to machines that match the condition specified -- either membership in a group, or a matching client name. :xml:attribute:`SSLCACertGroupType:negate` can be set to negate the sense of the match. The name of the client or group to match on. Child entries will only apply to this client or group (unless :xml:attribute:`SSLCACertGroupType:negate` is set). Negate the sense of the match, so that child entries only apply to a client if it is not a member of the given group or does not have the given name. Available certificate formats Explicitly specify subject alternative names for the generated certificate. The full path to the key entry to use for this certificate. This is the *client* path; e.g., for a key defined at ``/var/lib/bcfg2/SSLCA/etc/pki/tls/private/foo.key/key.xml``, **key** should be ``/etc/pki/tls/private/foo.key``. The certificate format to produce. The name of the CA (from :ref:`bcfg2.conf <sslca-configuration>`) to use to generate this certificate. Time (in days) the certificate will be valid for. Override the country set in the CA config Override the location set in the CA config Override the state set in the CA config Override the organizational unit set in the CA config Override the organization set in the CA config Override the email address set in the CA config Append the CA chain certificate to the generated certificate (e.g., to produce a certificate in the format required by Nginx.) Top-level tag for describing an SSLCA generated certificate. bcfg2-1.3.3/schemas/sslca-key.xsd000066400000000000000000000057371223671746500165700ustar00rootroot00000000000000 Schema for :ref:`server-plugins-generators-sslca` ``key.xml`` An **SSLCAKeyGroupType** is a tag used to provide logic. Child entries of an SSLCAKeyGroupType tag only apply to machines that match the condition specified -- either membership in a group, or a matching client name. :xml:attribute:`SSLCAKeyGroupType:negate` can be set to negate the sense of the match. The name of the client or group to match on. Child entries will only apply to this client or group (unless :xml:attribute:`SSLCAKeyGroupType:negate` is set). Negate the sense of the match, so that child entries only apply to a client if it is not a member of the given group or does not have the given name. Available generated key types The key type The key length Top-level tag for describing an SSLCA generated key. bcfg2-1.3.3/schemas/types.xsd000066400000000000000000000410001223671746500160200ustar00rootroot00000000000000 string enumeration definitions for bcfg2 Narayan Desai, Argonne National Laboratory Action entries are external shell commands that are executed either before bundle installation, after bundle installation or both. When the action is run. Actions with "pre" timing are run after important entries have been installed and before bundle entries are installed. Actions with "post" timing are run after bundle entries are installed. If the action is always run, or is only run when a bundle has been modified. Whether or not to check the return code of the action. If this is "check", then a non-zero return code will result in the entry being flagged as bad. Also execute the action in build mode. The freeform name of the action. The command to run. Whether the command string should be executeed within a shell. If enabled flow control and other shell-specific things can be used. Define POSIX ACLs for a Path entry. ACL type ACL scope. This is omitted for :xml:attribute:`ACLType:type` = ``default``. Permissions for the ACL. This can either be a single octal digit (e.g., ``6`` would indicate read and write, but not execute), or a symbolic mode including 'r', 'w', and 'x'. You can include '-' for operations that are not permitted, but it's not required. I.e., all of the following are identical:: perms="5" perms="rx" perms="r-x" User the ACL applies to (with :xml:attribute:`ACLType:scope` = ``user``). Group the ACL applies to (with :xml:attribute:`ACLType:scope` = ``group``). Manage filesystem paths -- files, directories, symlinks, etc. Type of path to manage. Full path. Type of device. Major device number (``block`` and ``char`` devices only). Minor device number (``block`` and ``char`` devices only). Permissions mode in octal format. Owner username or UID number Group name or GID number SELinux context for the path. This should be a full context, not just the type. E.g., ``system_u:object_r:etc_t:s0``, not just ``etc_t``. You can also specify ``__default__``, which will restore the context of the file to the default set by policy. See :ref:`server-selinux` for more information. Recursively remove files or set permissions, as appropriate. Remove entries that are not in the Bcfg2 specification from the directory. File to link to The file entry has no content. This must be set as a safeguard against accidentally empty content. The VCS backend to checkout contents from. The revision to checkout. The VCS URL to checkout. Specify additional supplementary groups for the POSIXUser The name of the supplementary group. This can also be specified as content of the tag, although that is deprecated. The POSIXUser tag allows you to create users on client machines. Username User ID number. If this is not specified, each client is allowed to set the UID. Name of the user's primary group. If this is not set, the user's primary group will be the same as the username. This field is typically used to record general information about the account or its user(s) such as their real name and phone number. If this is not set, the GECOS will be the same as the username. User's home directory. Default is ``/root`` for the root user, ``/home/<username>`` otherwise. User's shell The POSIXGroup tag allows you to create groups on client machines. Username Group ID number. If this is not specified, each client is allowed to set the GID. bcfg2-1.3.3/schemas/xinclude.xsd000066400000000000000000000031521223671746500164750ustar00rootroot00000000000000 The official XInclude XML Schema document is not normative or deterministic. This schema implements only the features of XInclude that are used in Bcfg2 in a manner that is deterministic (i.e., passes XML validation). bcfg2-1.3.3/schemas/xml.xsd000066400000000000000000000133051223671746500154630ustar00rootroot00000000000000 See http://www.w3.org/XML/1998/namespace.html and http://www.w3.org/TR/REC-xml for information about this namespace. This schema document describes the XML namespace, in a form suitable for import by other schema documents. Note that local names in this namespace are intended to be defined only by the World Wide Web Consortium or its subgroups. The following names are currently defined in this namespace and should not be used with conflicting semantics by any Working Group, specification, or document instance: base (as an attribute name): denotes an attribute whose value provides a URI to be used as the base for interpreting any relative URIs in the scope of the element on which it appears; its value is inherited. This name is reserved by virtue of its definition in the XML Base specification. id (as an attribute name): denotes an attribute whose value should be interpreted as if declared to be of type ID. The xml:id specification is not yet a W3C Recommendation, but this attribute is included here to facilitate experimentation with the mechanisms it proposes. Note that it is _not_ included in the specialAttrs attribute group. lang (as an attribute name): denotes an attribute whose value is a language code for the natural language of the content of any element; its value is inherited. This name is reserved by virtue of its definition in the XML specification. space (as an attribute name): denotes an attribute whose value is a keyword indicating what whitespace processing discipline is intended for the content of the element; its value is inherited. This name is reserved by virtue of its definition in the XML specification. Father (in any context at all): denotes Jon Bosak, the chair of the original XML Working Group. This name is reserved by the following decision of the W3C XML Plenary and XML Coordination groups: In appreciation for his vision, leadership and dedication the W3C XML Plenary on this 10th day of February, 2000 reserves for Jon Bosak in perpetuity the XML name xml:Father This schema defines attributes and an attribute group suitable for use by schemas wishing to allow xml:base, xml:lang, xml:space or xml:id attributes on elements they define. To enable this, such a schema must import this schema for the XML namespace, e.g. as follows: <schema . . .> . . . <import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/> Subsequently, qualified reference to any of the attributes or the group defined below will have the desired effect, e.g. <type . . .> . . . <attributeGroup ref="xml:specialAttrs"/> will define a type which will schema-validate an instance element with any of those attributes In keeping with the XML Schema WG's standard versioning policy, this schema document will persist at http://www.w3.org/2005/08/xml.xsd. At the date of issue it can also be found at http://www.w3.org/2001/xml.xsd. The schema document at that URI may however change in the future, in order to remain compatible with the latest version of XML Schema itself, or with the XML namespace itself. In other words, if the XML Schema or XML namespaces change, the version of this document at http://www.w3.org/2001/xml.xsd will change accordingly; the version at http://www.w3.org/2005/08/xml.xsd will not change. Attempting to install the relevant ISO 2- and 3-letter codes as the enumerated possible values is probably never going to be a realistic possibility. See RFC 3066 at http://www.ietf.org/rfc/rfc3066.txt and the IANA registry at http://www.iana.org/assignments/lang-tag-apps.htm for further information. The union allows for the 'un-declaration' of xml:lang with the empty string. See http://www.w3.org/TR/xmlbase/ for information about this attribute. See http://www.w3.org/TR/xml-id/ for information about this attribute. bcfg2-1.3.3/setup.py000077500000000000000000000057661223671746500142510ustar00rootroot00000000000000#!/usr/bin/env python from setuptools import setup from glob import glob import sys version_file = 'src/lib/Bcfg2/version.py' try: # python 2 execfile(version_file) except NameError: # py3k exec(compile(open(version_file).read(), version_file, 'exec')) inst_reqs = [ 'lockfile', 'lxml', 'python-daemon', ] # we only need m2crypto on < python2.6 if sys.version_info[:2] < (2, 6): inst_reqs.append('M2Crypto') setup(name="Bcfg2", version=__version__, # Defined in src/lib/Bcfg2/version.py description="Bcfg2 Server", author="Narayan Desai", author_email="desai@mcs.anl.gov", # nosetests test_suite='nose.collector', packages=["Bcfg2", "Bcfg2.Client", "Bcfg2.Client.Tools", "Bcfg2.Client.Tools.POSIX", "Bcfg2.Reporting", "Bcfg2.Reporting.Storage", "Bcfg2.Reporting.Transport", "Bcfg2.Reporting.migrations", "Bcfg2.Reporting.templatetags", 'Bcfg2.Server', "Bcfg2.Server.Admin", "Bcfg2.Server.FileMonitor", "Bcfg2.Server.Hostbase", "Bcfg2.Server.Hostbase.hostbase", "Bcfg2.Server.Lint", "Bcfg2.Server.Plugin", "Bcfg2.Server.Plugins", "Bcfg2.Server.Plugins.Packages", "Bcfg2.Server.Plugins.Cfg", "Bcfg2.Server.Reports", "Bcfg2.Server.Reports.reports", "Bcfg2.Server.Snapshots", ], install_requires=inst_reqs, tests_require=['mock', 'nose', 'sqlalchemy'], package_dir={'': 'src/lib', }, package_data={'Bcfg2.Reporting': ['templates/*.html', 'templates/*/*.html', 'templates/*/*.inc']}, scripts=glob('src/sbin/*'), data_files=[('share/bcfg2/schemas', glob('schemas/*.xsd')), ('share/bcfg2/xsl-transforms', glob('reports/xsl-transforms/*.xsl')), ('share/bcfg2/xsl-transforms/xsl-transform-includes', glob('reports/xsl-transforms/xsl-transform-includes/*.xsl')), ('share/bcfg2', glob('reports/reports.wsgi')), ('share/man/man1', glob("man/bcfg2.1")), ('share/man/man5', glob("man/*.5")), ('share/man/man8', glob("man/*.8")), ('share/bcfg2/Hostbase/templates', glob('src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/*.*')), ('share/bcfg2/Hostbase/templates/hostbase', glob('src/lib/Bcfg2/Server/Hostbase/hostbase/webtemplates/hostbase/*')), ('share/bcfg2/Hostbase/repo', glob('src/lib/Bcfg2/Server/Hostbase/templates/*')), ('share/bcfg2/site_media', glob('reports/site_media/*')), ] ) bcfg2-1.3.3/solaris-ips/000077500000000000000000000000001223671746500147635ustar00rootroot00000000000000bcfg2-1.3.3/solaris-ips/MANIFEST.bcfg2-server.header000066400000000000000000000003411223671746500216270ustar00rootroot00000000000000license ../../LICENSE license=simplified_bsd set name=description value="Configuration management server" set name=pkg.summary value="Configuration management server" set name=pkg.fmri value="pkg://bcfg2/bcfg2-server@1.3.3" bcfg2-1.3.3/solaris-ips/MANIFEST.bcfg2.header000066400000000000000000000004371223671746500203310ustar00rootroot00000000000000license ../../LICENSE license=simplified_bsd set name=description value="Configuration management client" set name=pkg.summary value="Configuration management client" set name=pkg.fmri value="pkg://bcfg2/bcfg2@1.3.3" file usr/bin/bcfg2 group=bin mode=0755 owner=root path=usr/bin/bcfg2 bcfg2-1.3.3/solaris-ips/Makefile000066400000000000000000000011441223671746500164230ustar00rootroot00000000000000#!/usr/bin/gmake VERS=1.3.3-1 PYVERSION := $(shell python -c "import sys; print sys.version[0:3]") default: clean package package: -mkdir tmp tmp/bcfg2-server tmp/bcfg2 -mkdir -p build/lib/$(PYVERSION)/site-packages -cd ../ && PYTHONPATH=$(PYTHONPATH):$(PWD)/build/lib/python2.6/site-packages/ python setup.py install --single-version-externally-managed --record=/dev/null --prefix=$(PWD)/build/usr #setuptools appears to use a restictive umask -chmod -R o+r build/ -chmod +x build/usr/bin/bcfg2 -sh ./gen-manifests.sh clean: -rm -rf tmp build -rm -rf MANIFEST.bcfg2 -rm -rf MANIFEST.bcfg2-server bcfg2-1.3.3/solaris-ips/README000066400000000000000000000007541223671746500156510ustar00rootroot00000000000000BUILDING -------- Dependancies: gmake Usage: gmake PUBLISHING ---------- Modify MANIFEST.bcfg2 and MANIFEST.bcfg2-server to set your publisher name in the fmri, e.g. Change set name=pkg.fmri value="pkg://bcfg2/bcfg2@1.2.4" to set name=pkg.fmri value="pkg://example.com/bcfg2@1.2.4" Then run the pkgsend publish, i.e. pkgsend publish -s http://example.com/path/to/repo -d build MANIFEST.bcfg2 pkgsend publish -s http://example.com/path/to/repo -d build MANIFEST.bcfg2-server bcfg2-1.3.3/solaris-ips/gen-manifests.sh000066400000000000000000000011271223671746500200600ustar00rootroot00000000000000#!/usr/bin/sh #bcfg2 cat MANIFEST.bcfg2.header > MANIFEST.bcfg2 pkgsend generate build | grep man[15] >> MANIFEST.bcfg2 pkgsend generate build | grep Bcfg2/[^/]*.py$ >> MANIFEST.bcfg2 pkgsend generate build | grep Bcfg2/Client/.*.py$ >> MANIFEST.bcfg2 #bcfg2-server cat MANIFEST.bcfg2-server.header > MANIFEST.bcfg2-server pkgsend generate build | grep man[8] >> MANIFEST.bcfg2-server pkgsend generate build | grep share/bcfg2 >> MANIFEST.bcfg2-server pkgsend generate build | grep bin/bcfg2- >> MANIFEST.bcfg2-server pkgsend generate build | grep Bcfg2/Server/.*.py$ >> MANIFEST.bcfg2-server bcfg2-1.3.3/solaris-ips/pkginfo.bcfg2000066400000000000000000000003141223671746500173230ustar00rootroot00000000000000PKG="SCbcfg2" NAME="bcfg2" ARCH="sparc" VERSION="1.3.3" CATEGORY="application" VENDOR="Argonne National Labratory" EMAIL="bcfg-dev@mcs.anl.gov" PSTAMP="Bcfg2 Developers" BASEDIR="/opt/csw" CLASSES="none" bcfg2-1.3.3/solaris-ips/pkginfo.bcfg2-server000066400000000000000000000003261223671746500206320ustar00rootroot00000000000000PKG="SCbcfg2-server" NAME="bcfg2-server" ARCH="sparc" VERSION="1.3.3" CATEGORY="application" VENDOR="Argonne National Labratory" EMAIL="bcfg-dev@mcs.anl.gov" PSTAMP="Bcfg2 Developers" BASEDIR="/usr" CLASSES="none" bcfg2-1.3.3/solaris/000077500000000000000000000000001223671746500141725ustar00rootroot00000000000000bcfg2-1.3.3/solaris/Makefile000066400000000000000000000022571223671746500156400ustar00rootroot00000000000000#!/usr/sfw/bin/gmake PYTHON="/usr/local/bin/python" VERS=1.3.3-1 PYVERSION := $(shell $(PYTHON) -c "import sys; print sys.version[0:3]") default: clean package package: -mkdir tmp tmp/bcfg2-server tmp/bcfg2 -mkdir -p build/lib/$(PYVERSION)/site-packages -cd ../ && PYTHONPATH=$(PYTHONPATH):$(PWD)/build/lib/python2.6/site-packages/ $(PYTHON) setup.py install --single-version-externally-managed --record=/dev/null --prefix=$(PWD)/build #setuptools appears to use a restictive umask -chmod -R o+r build/ -cat build/bin/bcfg2 | sed -e 's!/usr/bin/python!$(PYTHON)!' > build/bin/bcfg2.new && mv build/bin/bcfg2.new build/bin/bcfg2 -chmod +x build/bin/bcfg2 -sh ./gen-prototypes.sh -pkgmk -o -a `uname -m` -f prototype.bcfg2 -d $(PWD)/tmp -r $(PWD)/build -pkgmk -o -a `uname -m` -f prototype.bcfg2-server -d $(PWD)/tmp -r $(PWD)/build -pkgtrans -o -s $(PWD)/tmp $(PWD)/bcfg2-$(VERS) SCbcfg2 -pkgtrans -o -s $(PWD)/tmp $(PWD)/bcfg2-server-$(VERS) SCbcfg2-server -gzip -f $(PWD)/bcfg2-$(VERS) -gzip -f $(PWD)/bcfg2-server-$(VERS) clean: -rm -rf tmp build -rm -rf bcfg2-$(VERS).gz bcfg2-server-$(VERS).gz -rm -rf prototype.bcfg2.fixed prototype.bcfg2-server.fixed -rm -f prototype.* bcfg2-1.3.3/solaris/bcfg2-server000077500000000000000000000006211223671746500164060ustar00rootroot00000000000000#!/bin/sh # # This file belongs in /lib/svc/method . /lib/svc/share/smf_include.sh ACTION="$1" test "x$ACTION" = x && exit $SMF_EXIT_ERR_CONFIG test "x$ACTION" = xrefresh && ACTION="reload" if test "x$ACTION" = xmanifest; then echo "/var/svc/manifest/site/bcfg2-server.xml" exit $SMF_EXIT_OK; fi /etc/init.d/bcfg2-server $ACTION RC=$? test $RC == 0 && exit $SMF_EXIT_OK exit $SMF_EXIT_ERR_FATAL bcfg2-1.3.3/solaris/bcfg2-server.xml000066400000000000000000000035401223671746500172050ustar00rootroot00000000000000 bcfg2-1.3.3/solaris/gen-prototypes.sh000066400000000000000000000020141223671746500175220ustar00rootroot00000000000000#!/bin/sh cd build PP="./lib/python/site-packages/" #bcfg2 echo "i pkginfo=./pkginfo.bcfg2" > ../prototype.tmp find . | grep man[15] | pkgproto >> ../prototype.tmp echo "./bin" | pkgproto >> ../prototype.tmp echo "./bin/bcfg2" | pkgproto >> ../prototype.tmp echo "${PP}Bcfg2" | pkgproto >> ../prototype.tmp ls -1 ${PP}Bcfg2/*.py | pkgproto >> ../prototype.tmp find ${PP}Bcfg2/Client/ ! -name "*.pyc" | pkgproto >> ../prototype.tmp sed "s/`id | sed 's/uid=[0-9]*(\(.*\)) gid=[0-9]*(\(.*\))/\1 \2/'`/bin bin/" ../prototype.tmp > ../prototype.bcfg2 #bcfg2-server echo "i pkginfo=./pkginfo.bcfg2-server" > ../prototype.tmp find . | grep man8 | pkgproto >> ../prototype.tmp find share/bcfg2 | pkgproto >> ../prototype.tmp echo "./bin" | pkgproto >> ../prototype.tmp ls -1 bin/bcfg2-* | pkgproto >> ../prototype.tmp find ${PP}Bcfg2/Server/ ! -name "*.pyc" | pkgproto >> ../prototype.tmp sed "s/`id | sed 's/uid=[0-9]*(\(.*\)) gid=[0-9]*(\(.*\))/\1 \2/'`/bin bin/" ../prototype.tmp > ../prototype.bcfg2-server rm ../prototype.tmp bcfg2-1.3.3/solaris/pkginfo.bcfg2000066400000000000000000000003141223671746500165320ustar00rootroot00000000000000PKG="SCbcfg2" NAME="bcfg2" ARCH="sparc" VERSION="1.3.3" CATEGORY="application" VENDOR="Argonne National Labratory" EMAIL="bcfg-dev@mcs.anl.gov" PSTAMP="Bcfg2 Developers" BASEDIR="/opt/csw" CLASSES="none" bcfg2-1.3.3/solaris/pkginfo.bcfg2-server000066400000000000000000000003261223671746500200410ustar00rootroot00000000000000PKG="SCbcfg2-server" NAME="bcfg2-server" ARCH="sparc" VERSION="1.3.3" CATEGORY="application" VENDOR="Argonne National Labratory" EMAIL="bcfg-dev@mcs.anl.gov" PSTAMP="Bcfg2 Developers" BASEDIR="/usr" CLASSES="none" bcfg2-1.3.3/solaris/prototype.bcfg2000066400000000000000000000051351223671746500171500ustar00rootroot00000000000000i pkginfo=./pkginfo.bcfg2 d none lib 0755 root bin d none lib/PYVERSION 0755 root bin d none lib/PYVERSION/site-packages 0755 root bin d none lib/PYVERSION/site-packages/Bcfg2 0755 bin bin f none lib/PYVERSION/site-packages/Bcfg2/__init__.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Statistics.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/SSLServer.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Component.py 0644 bin bin d none lib/PYVERSION/site-packages/Bcfg2/Client 0755 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/XML.py 0644 bin bin d none lib/PYVERSION/site-packages/Bcfg2/Client/Tools 0755 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/Action.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/IPS.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/FreeBSDInit.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/RPMng.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/Chkconfig.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/RcUpdate.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/APT.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/POSIX.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/SYSV.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/__init__.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/rpmtools.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/launchd.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/FreeBSDPackage.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/Blast.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/YUMng.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/Portage.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/DebInit.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/Encap.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Tools/SMF.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/__init__.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Client/Frame.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Logger.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Proxy.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Options.py 0644 bin bin d none bin 0755 root bin f none bin/bcfg2 0755 bin bin d none share 0755 root bin d none share/man 0755 root bin d none share/man/man1 0755 root bin f none share/man/man1/bcfg2.1 0444 root bin bcfg2-1.3.3/solaris/prototype.bcfg2-server000066400000000000000000000140151223671746500204510ustar00rootroot00000000000000i pkginfo=./pkginfo.bcfg2-server d none lib 0755 bin bin d none lib/PYVERSION 0755 bin bin d none lib/PYVERSION/site-packages 0755 bin bin d none lib/PYVERSION/site-packages/Bcfg2 0755 bin bin d none lib/PYVERSION/site-packages/Bcfg2/Server 0755 bin bin d none lib/PYVERSION/site-packages/Bcfg2/Server/Admin 0755 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Admin/Tidy.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Admin/Minestruct.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Admin/__init__.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Admin/Snapshots.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Admin/Init.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Admin/Group.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Admin/Compare.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Admin/Perf.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Admin/Query.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Admin/Viz.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Admin/Pull.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Admin/Bundle.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugin.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/__init__.py 0644 bin bin d none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins 0755 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Probes.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Decisions.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Rules.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Packages.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/GroupPatterns.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/SSHbase.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Trigger.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/__init__.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Snapshots.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/TCheetah.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Account.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Cfg.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Statistics.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Metadata.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Base.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Pkgmgr.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Ohai.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Properties.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Editor.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Bundler.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/NagiosGen.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Deps.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Svn.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/DBStats.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/TGenshi.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Plugins/Git.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/FileMonitor.py 0644 bin bin d none lib/PYVERSION/site-packages/Bcfg2/Server/Snapshots 0755 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Snapshots/model.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Snapshots/__init__.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Server/Core.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/__init__.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Statistics.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/SSLServer.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Component.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Logger.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Proxy.py 0644 bin bin f none lib/PYVERSION/site-packages/Bcfg2/Options.py 0644 bin bin d none bin 0755 bin bin f none bin/bcfg2-server 0755 bin bin f none bin/bcfg2-repo-validate 0755 bin bin f none bin/bcfg2-admin 0755 bin bin f none bin/bcfg2-build-reports 0755 bin bin f none bin/bcfg2-info 0755 bin bin f none bin/bcfg2-reports 0755 bin bin d none share 0755 bin bin d none share/bcfg2 0755 bin bin d none share/bcfg2/schemas 0755 bin bin f none share/bcfg2/schemas/packages.xsd 0444 bin bin f none share/bcfg2/schemas/metadata.xsd 0444 bin bin f none share/bcfg2/schemas/base.xsd 0444 bin bin f none share/bcfg2/schemas/services.xsd 0444 bin bin f none share/bcfg2/schemas/decisions.xsd 0444 bin bin f none share/bcfg2/schemas/info.xsd 0444 bin bin f none share/bcfg2/schemas/xml.xsd 0444 bin bin f none share/bcfg2/schemas/atom.xsd 0444 bin bin f none share/bcfg2/schemas/clients.xsd 0444 bin bin f none share/bcfg2/schemas/grouppatterns.xsd 0444 bin bin f none share/bcfg2/schemas/types.xsd 0444 bin bin f none share/bcfg2/schemas/rules.xsd 0444 bin bin f none share/bcfg2/schemas/pkglist.xsd 0444 bin bin f none share/bcfg2/schemas/bundle.xsd 0444 bin bin f none share/bcfg2/schemas/deps.xsd 0444 bin bin f none share/bcfg2/schemas/pkgtype.xsd 0444 bin bin f none share/bcfg2/schemas/servicetype.xsd 0444 bin bin f none share/bcfg2/schemas/report-configuration.xsd 0444 bin bin d none share/man 0755 bin bin d none share/man/man1 0755 bin bin d none share/man/man5 0755 bin bin d none share/man/man8 0755 bin bin f none share/man/man5/bcfg2.conf.5 0444 bin bin f none share/man/man8/bcfg2-admin.8 0444 bin bin f none share/man/man8/bcfg2-build-reports.8 0444 bin bin f none share/man/man1/bcfg2.1 0444 bin bin f none share/man/man8/bcfg2-repo-validate.8 0444 bin bin f none share/man/man8/bcfg2-server.8 0444 bin bin f none share/man/man8/bcfg2-info.8 0444 bin bin bcfg2-1.3.3/src/000077500000000000000000000000001223671746500133055ustar00rootroot00000000000000bcfg2-1.3.3/src/lib/000077500000000000000000000000001223671746500140535ustar00rootroot00000000000000bcfg2-1.3.3/src/lib/Bcfg2/000077500000000000000000000000001223671746500147765ustar00rootroot00000000000000bcfg2-1.3.3/src/lib/Bcfg2/Cache.py000066400000000000000000000007111223671746500163520ustar00rootroot00000000000000""" An implementation of a simple memory-backed cache. Right now this doesn't provide many features, but more (time-based expiration, etc.) can be added as necessary. """ class Cache(dict): """ an implementation of a simple memory-backed cache """ def expire(self, key=None): """ expire all items, or a specific item, from the cache """ if key is None: self.clear() elif key in self: del self[key] bcfg2-1.3.3/src/lib/Bcfg2/Client/000077500000000000000000000000001223671746500162145ustar00rootroot00000000000000bcfg2-1.3.3/src/lib/Bcfg2/Client/Client.py000066400000000000000000000322671223671746500200160ustar00rootroot00000000000000""" The main Bcfg2 client class """ import os import sys import stat import time import fcntl import socket import logging import tempfile import Bcfg2.Proxy import Bcfg2.Logger import Bcfg2.Options import Bcfg2.Client.XML import Bcfg2.Client.Frame import Bcfg2.Client.Tools from Bcfg2.Utils import locked, Executor from Bcfg2.Compat import xmlrpclib from Bcfg2.version import __version__ class Client(object): """ The main Bcfg2 client class """ def __init__(self, setup): self.toolset = None self.tools = None self.config = None self._proxy = None self.setup = setup if self.setup['debug']: level = logging.DEBUG elif self.setup['verbose']: level = logging.INFO else: level = logging.WARNING Bcfg2.Logger.setup_logging('bcfg2', to_syslog=self.setup['syslog'], level=level, to_file=self.setup['logging']) self.logger = logging.getLogger('bcfg2') self.logger.debug(self.setup) self.cmd = Executor(self.setup['command_timeout']) if self.setup['bundle_quick']: if not self.setup['bundle'] and not self.setup['skipbundle']: self.logger.error("-Q option requires -b or -B") raise SystemExit(1) elif self.setup['remove']: self.logger.error("-Q option incompatible with -r") raise SystemExit(1) if 'drivers' in self.setup and self.setup['drivers'] == 'help': self.logger.info("The following drivers are available:") self.logger.info(Bcfg2.Client.Tools.drivers) raise SystemExit(0) if self.setup['remove'] and 'services' in self.setup['remove'].lower(): self.logger.error("Service removal is nonsensical; " "removed services will only be disabled") if (self.setup['remove'] and self.setup['remove'].lower() not in ['all', 'services', 'packages', 'users']): self.logger.error("Got unknown argument %s for -r" % self.setup['remove']) if self.setup["file"] and self.setup["cache"]: print("cannot use -f and -c together") raise SystemExit(1) if not self.setup['server'].startswith('https://'): self.setup['server'] = 'https://' + self.setup['server'] def _probe_failure(self, probename, msg): """ handle failure of a probe in the way the user wants us to (exit or continue) """ message = "Failed to execute probe %s: %s" % (probename, msg) if self.setup['probe_exit']: self.fatal_error(message) else: self.logger.error(message) def run_probe(self, probe): """Execute probe.""" name = probe.get('name') self.logger.info("Running probe %s" % name) ret = Bcfg2.Client.XML.Element("probe-data", name=name, source=probe.get('source')) try: scripthandle, scriptname = tempfile.mkstemp() script = os.fdopen(scripthandle, 'w') try: script.write("#!%s\n" % (probe.attrib.get('interpreter', '/bin/sh'))) if sys.hexversion >= 0x03000000: script.write(probe.text) else: script.write(probe.text.encode('utf-8')) script.close() os.chmod(scriptname, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH | stat.S_IWUSR) # 0755 rv = self.cmd.run(scriptname, timeout=self.setup['timeout']) if rv.stderr: self.logger.warning("Probe %s has error output: %s" % (name, rv.stderr)) if not rv.success: self._probe_failure(name, "Return value %s" % rv) self.logger.info("Probe %s has result:" % name) self.logger.info(rv.stdout) if sys.hexversion >= 0x03000000: ret.text = rv.stdout else: ret.text = rv.stdout.decode('utf-8') finally: os.unlink(scriptname) except SystemExit: raise except: self._probe_failure(name, sys.exc_info()[1]) return ret def fatal_error(self, message): """Signal a fatal error.""" self.logger.error("Fatal error: %s" % (message)) raise SystemExit(1) @property def proxy(self): """ get an XML-RPC proxy to the server """ if self._proxy is None: self._proxy = Bcfg2.Proxy.ComponentProxy( self.setup['server'], self.setup['user'], self.setup['password'], key=self.setup['key'], cert=self.setup['certificate'], ca=self.setup['ca'], allowedServerCNs=self.setup['serverCN'], timeout=self.setup['timeout'], retries=int(self.setup['retries']), delay=int(self.setup['retry_delay'])) return self._proxy def run_probes(self, times=None): """ run probes and upload probe data """ if times is None: times = dict() try: probes = Bcfg2.Client.XML.XML(str(self.proxy.GetProbes())) except (Bcfg2.Proxy.ProxyError, Bcfg2.Proxy.CertificateError, socket.gaierror, socket.error): err = sys.exc_info()[1] self.fatal_error("Failed to download probes from bcfg2: %s" % err) except Bcfg2.Client.XML.ParseError: err = sys.exc_info()[1] self.fatal_error("Server returned invalid probe requests: %s" % err) times['probe_download'] = time.time() # execute probes probedata = Bcfg2.Client.XML.Element("ProbeData") for probe in probes.findall(".//probe"): probedata.append(self.run_probe(probe)) if len(probes.findall(".//probe")) > 0: try: # upload probe responses self.proxy.RecvProbeData( Bcfg2.Client.XML.tostring( probedata, xml_declaration=False).decode('utf-8')) except Bcfg2.Proxy.ProxyError: err = sys.exc_info()[1] self.fatal_error("Failed to upload probe data: %s" % err) times['probe_upload'] = time.time() def get_config(self, times=None): """ load the configuration, either from the cached configuration file (-f), or from the server """ if times is None: times = dict() if self.setup['file']: # read config from file try: self.logger.debug("Reading cached configuration from %s" % self.setup['file']) return open(self.setup['file'], 'r').read() except IOError: self.fatal_error("Failed to read cached configuration from: %s" % (self.setup['file'])) else: # retrieve config from server if self.setup['profile']: try: self.proxy.AssertProfile(self.setup['profile']) except Bcfg2.Proxy.ProxyError: err = sys.exc_info()[1] self.fatal_error("Failed to set client profile: %s" % err) try: self.proxy.DeclareVersion(__version__) except xmlrpclib.Fault: err = sys.exc_info()[1] if (err.faultCode == xmlrpclib.METHOD_NOT_FOUND or (err.faultCode == 7 and err.faultString.startswith("Unknown method"))): self.logger.debug("Server does not support declaring " "client version") else: self.logger.error("Failed to declare version: %s" % err) except (Bcfg2.Proxy.ProxyError, Bcfg2.Proxy.CertificateError, socket.gaierror, socket.error): err = sys.exc_info()[1] self.logger.error("Failed to declare version: %s" % err) self.run_probes(times=times) if self.setup['decision'] in ['whitelist', 'blacklist']: try: self.setup['decision_list'] = \ self.proxy.GetDecisionList(self.setup['decision']) self.logger.info("Got decision list from server:") self.logger.info(self.setup['decision_list']) except Bcfg2.Proxy.ProxyError: err = sys.exc_info()[1] self.fatal_error("Failed to get decision list: %s" % err) try: rawconfig = self.proxy.GetConfig().encode('utf-8') except Bcfg2.Proxy.ProxyError: err = sys.exc_info()[1] self.fatal_error("Failed to download configuration from " "Bcfg2: %s" % err) times['config_download'] = time.time() return rawconfig def run(self): """Perform client execution phase.""" times = {} # begin configuration times['start'] = time.time() self.logger.info("Starting Bcfg2 client run at %s" % times['start']) rawconfig = self.get_config(times=times).decode('utf-8') if self.setup['cache']: try: open(self.setup['cache'], 'w').write(rawconfig) os.chmod(self.setup['cache'], 33152) except IOError: self.logger.warning("Failed to write config cache file %s" % (self.setup['cache'])) times['caching'] = time.time() try: self.config = Bcfg2.Client.XML.XML(rawconfig) except Bcfg2.Client.XML.ParseError: syntax_error = sys.exc_info()[1] self.fatal_error("The configuration could not be parsed: %s" % syntax_error) times['config_parse'] = time.time() if self.config.tag == 'error': self.fatal_error("Server error: %s" % (self.config.text)) return(1) if self.setup['bundle_quick']: newconfig = Bcfg2.Client.XML.XML('') for bundle in self.config.getchildren(): if (bundle.tag == 'Bundle' and ((self.setup['bundle'] and bundle.get('name') in self.setup['bundle']) or (self.setup['skipbundle'] and bundle.get('name') not in self.setup['skipbundle']))): newconfig.append(bundle) self.config = newconfig self.tools = Bcfg2.Client.Frame.Frame(self.config, self.setup, times, self.setup['drivers'], self.setup['dryrun']) if not self.setup['omit_lock_check']: #check lock here try: lockfile = open(self.setup['lockfile'], 'w') if locked(lockfile.fileno()): self.fatal_error("Another instance of Bcfg2 is running. " "If you want to bypass the check, run " "with the %s option" % Bcfg2.Options.OMIT_LOCK_CHECK.cmd) except SystemExit: raise except: lockfile = None self.logger.error("Failed to open lockfile %s: %s" % (self.setup['lockfile'], sys.exc_info()[1])) # execute the configuration self.tools.Execute() if not self.setup['omit_lock_check']: # unlock here if lockfile: try: fcntl.lockf(lockfile.fileno(), fcntl.LOCK_UN) os.remove(self.setup['lockfile']) except OSError: self.logger.error("Failed to unlock lockfile %s" % lockfile.name) if not self.setup['file'] and not self.setup['bundle_quick']: # upload statistics feedback = self.tools.GenerateStats() try: self.proxy.RecvStats( Bcfg2.Client.XML.tostring( feedback, xml_declaration=False).decode('utf-8')) except Bcfg2.Proxy.ProxyError: err = sys.exc_info()[1] self.logger.error("Failed to upload configuration statistics: " "%s" % err) raise SystemExit(2) self.logger.info("Finished Bcfg2 client run at %s" % time.time()) bcfg2-1.3.3/src/lib/Bcfg2/Client/Frame.py000066400000000000000000000552131223671746500176260ustar00rootroot00000000000000""" Frame is the Client Framework that verifies and installs entries, and generates statistics. """ import copy import time import fnmatch import logging import Bcfg2.Client.Tools from Bcfg2.Client import prompt from Bcfg2.Compat import any, all # pylint: disable=W0622 def matches_entry(entryspec, entry): """ Determine if the Decisions-style entry specification matches the entry. Both are tuples of (tag, name). The entryspec can handle the wildcard * in either position. """ if entryspec == entry: return True return all(fnmatch.fnmatch(entry[i], entryspec[i]) for i in [0, 1]) def matches_white_list(entry, whitelist): """ Return True if (, ) is in the given whitelist. """ return any(matches_entry(we, (entry.tag, entry.get('name'))) for we in whitelist) def passes_black_list(entry, blacklist): """ Return True if (, ) is not in the given blacklist. """ return not any(matches_entry(be, (entry.tag, entry.get('name'))) for be in blacklist) # pylint: disable=W0702 # in frame we frequently want to catch all exceptions, regardless of # type, so disable the pylint rule that catches that. class Frame(object): """Frame is the container for all Tool objects and state information.""" def __init__(self, config, setup, times, drivers, dryrun): self.config = config self.times = times self.dryrun = dryrun self.times['initialization'] = time.time() self.setup = setup self.tools = [] self.states = {} self.whitelist = [] self.blacklist = [] self.removal = [] self.logger = logging.getLogger(__name__) for driver in drivers[:]: if (driver not in Bcfg2.Client.Tools.drivers and isinstance(driver, str)): self.logger.error("Tool driver %s is not available" % driver) drivers.remove(driver) tclass = {} for tool in drivers: if not isinstance(tool, str): tclass[time.time()] = tool tool_class = "Bcfg2.Client.Tools.%s" % tool try: tclass[tool] = getattr(__import__(tool_class, globals(), locals(), ['*']), tool) except ImportError: continue except: self.logger.error("Tool %s unexpectedly failed to load" % tool, exc_info=1) for tool in list(tclass.values()): try: self.tools.append(tool(self.logger, setup, config)) except Bcfg2.Client.Tools.ToolInstantiationError: continue except: self.logger.error("Failed to instantiate tool %s" % tool, exc_info=1) for tool in self.tools[:]: for conflict in getattr(tool, 'conflicts', []): for item in self.tools: if item.name == conflict: self.tools.remove(item) self.logger.info("Loaded tool drivers:") self.logger.info([tool.name for tool in self.tools]) deprecated = [tool.name for tool in self.tools if tool.deprecated] if deprecated: self.logger.warning("Loaded deprecated tool drivers:") self.logger.warning(deprecated) experimental = [tool.name for tool in self.tools if tool.experimental] if experimental: self.logger.info("Loaded experimental tool drivers:") self.logger.info(experimental) # find entries not handled by any tools self.unhandled = [entry for struct in config for entry in struct if entry not in self.handled] if self.unhandled: self.logger.error("The following entries are not handled by any " "tool:") for entry in self.unhandled: self.logger.error("%s:%s:%s" % (entry.tag, entry.get('type'), entry.get('name'))) self.find_dups(config) pkgs = [(entry.get('name'), entry.get('origin')) for struct in config for entry in struct if entry.tag == 'Package'] if pkgs: self.logger.debug("The following packages are specified in bcfg2:") self.logger.debug([pkg[0] for pkg in pkgs if pkg[1] is None]) self.logger.debug("The following packages are prereqs added by " "Packages:") self.logger.debug([pkg[0] for pkg in pkgs if pkg[1] == 'Packages']) def find_dups(self, config): """ Find duplicate entries and warn about them """ entries = dict() for struct in config: for entry in struct: for tool in self.tools: if tool.handlesEntry(entry): pkey = tool.primarykey(entry) if pkey in entries: entries[pkey] += 1 else: entries[pkey] = 1 multi = [e for e, c in entries.items() if c > 1] if multi: self.logger.debug("The following entries are included multiple " "times:") for entry in multi: self.logger.debug(entry) def promptFilter(self, msg, entries): """Filter a supplied list based on user input.""" ret = [] entries.sort(key=lambda e: e.tag + ":" + e.get('name')) for entry in entries[:]: if entry in self.unhandled: # don't prompt for entries that can't be installed continue if 'qtext' in entry.attrib: iprompt = entry.get('qtext') else: iprompt = msg % (entry.tag, entry.get('name')) if prompt(iprompt): ret.append(entry) return ret def __getattr__(self, name): if name in ['extra', 'handled', 'modified', '__important__']: ret = [] for tool in self.tools: ret += getattr(tool, name) return ret elif name in self.__dict__: return self.__dict__[name] raise AttributeError(name) def InstallImportant(self): """Install important entries We also process the decision mode stuff here because we want to prevent non-whitelisted/blacklisted 'important' entries from being installed prior to determining the decision mode on the client. """ # Need to process decision stuff early so that dryrun mode # works with it self.whitelist = [entry for entry in self.states if not self.states[entry]] if not self.setup['file']: if self.setup['decision'] == 'whitelist': dwl = self.setup['decision_list'] w_to_rem = [e for e in self.whitelist if not matches_white_list(e, dwl)] if w_to_rem: self.logger.info("In whitelist mode: " "suppressing installation of:") self.logger.info(["%s:%s" % (e.tag, e.get('name')) for e in w_to_rem]) self.whitelist = [x for x in self.whitelist if x not in w_to_rem] elif self.setup['decision'] == 'blacklist': b_to_rem = \ [e for e in self.whitelist if not passes_black_list(e, self.setup['decision_list'])] if b_to_rem: self.logger.info("In blacklist mode: " "suppressing installation of:") self.logger.info(["%s:%s" % (e.tag, e.get('name')) for e in b_to_rem]) self.whitelist = [x for x in self.whitelist if x not in b_to_rem] # take care of important entries first if not self.dryrun: parent_map = dict((c, p) for p in self.config.getiterator() for c in p) for cfile in self.config.findall(".//Path"): if (cfile.get('name') not in self.__important__ or cfile.get('type') != 'file' or cfile not in self.whitelist): continue parent = parent_map[cfile] if ((parent.tag == "Bundle" and ((self.setup['bundle'] and parent.get("name") not in self.setup['bundle']) or (self.setup['skipbundle'] and parent.get("name") in self.setup['skipbundle']))) or (parent.tag == "Independent" and (self.setup['bundle'] or self.setup['skipindep']))): continue tools = [t for t in self.tools if t.handlesEntry(cfile) and t.canVerify(cfile)] if tools: if (self.setup['interactive'] and not self.promptFilter("Install %s: %s? (y/N):", [cfile])): self.whitelist.remove(cfile) continue try: self.states[cfile] = tools[0].InstallPath(cfile) if self.states[cfile]: tools[0].modified.append(cfile) except: self.logger.error("Unexpected tool failure", exc_info=1) cfile.set('qtext', '') if tools[0].VerifyPath(cfile, []): self.whitelist.remove(cfile) def Inventory(self): """ Verify all entries, find extra entries, and build up workqueues """ # initialize all states for struct in self.config.getchildren(): for entry in struct.getchildren(): self.states[entry] = False for tool in self.tools: try: tool.Inventory(self.states) except: self.logger.error("%s.Inventory() call failed:" % tool.name, exc_info=1) def Decide(self): # pylint: disable=R0912 """Set self.whitelist based on user interaction.""" iprompt = "Install %s: %s? (y/N): " rprompt = "Remove %s: %s? (y/N): " if self.setup['remove']: if self.setup['remove'] == 'all': self.removal = self.extra elif self.setup['remove'].lower() == 'services': self.removal = [entry for entry in self.extra if entry.tag == 'Service'] elif self.setup['remove'].lower() == 'packages': self.removal = [entry for entry in self.extra if entry.tag == 'Package'] elif self.setup['remove'].lower() == 'users': self.removal = [entry for entry in self.extra if entry.tag in ['POSIXUser', 'POSIXGroup']] candidates = [entry for entry in self.states if not self.states[entry]] if self.dryrun: if self.whitelist: self.logger.info("In dryrun mode: " "suppressing entry installation for:") self.logger.info(["%s:%s" % (entry.tag, entry.get('name')) for entry in self.whitelist]) self.whitelist = [] if self.removal: self.logger.info("In dryrun mode: " "suppressing entry removal for:") self.logger.info(["%s:%s" % (entry.tag, entry.get('name')) for entry in self.removal]) self.removal = [] # Here is where most of the work goes # first perform bundle filtering all_bundle_names = [b.get('name') for b in self.config.findall('./Bundle')] bundles = self.config.getchildren() if self.setup['bundle']: # warn if non-existent bundle given for bundle in self.setup['bundle']: if bundle not in all_bundle_names: self.logger.info("Warning: Bundle %s not found" % bundle) bundles = [b for b in bundles if b.get('name') in self.setup['bundle']] elif self.setup['indep']: bundles = [b for b in bundles if b.tag != 'Bundle'] if self.setup['skipbundle']: # warn if non-existent bundle given if not self.setup['bundle_quick']: for bundle in self.setup['skipbundle']: if bundle not in all_bundle_names: self.logger.info("Warning: Bundle %s not found" % bundle) bundles = [b for b in bundles if b.get('name') not in self.setup['skipbundle']] if self.setup['skipindep']: bundles = [b for b in bundles if b.tag == 'Bundle'] self.whitelist = [e for e in self.whitelist if any(e in b for b in bundles)] # first process prereq actions for bundle in bundles[:]: if bundle.tag != 'Bundle': continue bmodified = len([item for item in bundle if item in self.whitelist or item in self.modified]) actions = [a for a in bundle.findall('./Action') if (a.get('timing') != 'post' and (bmodified or a.get('when') == 'always'))] # now we process all "pre" and "both" actions that are either # always or the bundle has been modified if self.setup['interactive']: self.promptFilter(iprompt, actions) self.DispatchInstallCalls(actions) # need to test to fail entries in whitelist if False in [self.states[a] for a in actions]: # then display bundles forced off with entries self.logger.info("Bundle %s failed prerequisite action" % (bundle.get('name'))) bundles.remove(bundle) b_to_remv = [ent for ent in self.whitelist if ent in bundle] if b_to_remv: self.logger.info("Not installing entries from Bundle %s" % (bundle.get('name'))) self.logger.info(["%s:%s" % (e.tag, e.get('name')) for e in b_to_remv]) for ent in b_to_remv: self.whitelist.remove(ent) self.logger.debug("Installing entries in the following bundle(s):") self.logger.debug(" %s" % ", ".join(b.get("name") for b in bundles if b.get("name"))) if self.setup['interactive']: self.whitelist = self.promptFilter(iprompt, self.whitelist) self.removal = self.promptFilter(rprompt, self.removal) for entry in candidates: if entry not in self.whitelist: self.blacklist.append(entry) def DispatchInstallCalls(self, entries): """Dispatch install calls to underlying tools.""" for tool in self.tools: handled = [entry for entry in entries if tool.canInstall(entry)] if not handled: continue try: tool.Install(handled, self.states) except: self.logger.error("%s.Install() call failed:" % tool.name, exc_info=1) def Install(self): """Install all entries.""" self.DispatchInstallCalls(self.whitelist) mods = self.modified mbundles = [struct for struct in self.config.findall('Bundle') if any(True for mod in mods if mod in struct)] if self.modified: # Handle Bundle interdeps if mbundles: self.logger.info("The Following Bundles have been modified:") self.logger.info([mbun.get('name') for mbun in mbundles]) tbm = [(t, b) for t in self.tools for b in mbundles] for tool, bundle in tbm: try: tool.Inventory(self.states, [bundle]) except: self.logger.error("%s.Inventory() call failed:" % tool.name, exc_info=1) clobbered = [entry for bundle in mbundles for entry in bundle if (not self.states[entry] and entry not in self.blacklist)] if clobbered: self.logger.debug("Found clobbered entries:") self.logger.debug(["%s:%s" % (entry.tag, entry.get('name')) for entry in clobbered]) if not self.setup['interactive']: self.DispatchInstallCalls(clobbered) for bundle in self.config.findall('.//Bundle'): if (self.setup['bundle'] and bundle.get('name') not in self.setup['bundle']): # prune out unspecified bundles when running with -b continue if bundle in mbundles: self.logger.debug("Bundle %s was modified" % bundle.get('name')) func = "BundleUpdated" else: self.logger.debug("Bundle %s was not modified" % bundle.get('name')) func = "BundleNotUpdated" for tool in self.tools: try: getattr(tool, func)(bundle, self.states) except: self.logger.error("%s.%s() call failed:" % (tool.name, func), exc_info=1) def Remove(self): """Remove extra entries.""" for tool in self.tools: extras = [entry for entry in self.removal if tool.handlesEntry(entry)] if extras: try: tool.Remove(extras) except: self.logger.error("%s.Remove() failed" % tool.name, exc_info=1) def CondDisplayState(self, phase): """Conditionally print tracing information.""" self.logger.info('Phase: %s' % phase) self.logger.info('Correct entries: %d' % list(self.states.values()).count(True)) self.logger.info('Incorrect entries: %d' % list(self.states.values()).count(False)) if phase == 'final' and list(self.states.values()).count(False): for entry in sorted(self.states.keys(), key=lambda e: e.tag + ":" + e.get('name')): if not self.states[entry]: etype = entry.get('type') if etype: self.logger.info("%s:%s:%s" % (entry.tag, etype, entry.get('name'))) else: self.logger.info("%s:%s" % (entry.tag, entry.get('name'))) self.logger.info('Total managed entries: %d' % len(list(self.states.values()))) self.logger.info('Unmanaged entries: %d' % len(self.extra)) if phase == 'final' and self.setup['extra']: for entry in sorted(self.extra, key=lambda e: e.tag + ":" + e.get('name')): etype = entry.get('type') if etype: self.logger.info("%s:%s:%s" % (entry.tag, etype, entry.get('name'))) else: self.logger.info("%s:%s" % (entry.tag, entry.get('name'))) if ((list(self.states.values()).count(False) == 0) and not self.extra): self.logger.info('All entries correct.') def ReInventory(self): """Recheck everything.""" if not self.dryrun and self.setup['kevlar']: self.logger.info("Rechecking system inventory") self.Inventory() def Execute(self): """Run all methods.""" self.Inventory() self.times['inventory'] = time.time() self.CondDisplayState('initial') self.InstallImportant() self.Decide() self.Install() self.times['install'] = time.time() self.Remove() self.times['remove'] = time.time() if self.modified: self.ReInventory() self.times['reinventory'] = time.time() self.times['finished'] = time.time() self.CondDisplayState('final') def GenerateStats(self): """Generate XML summary of execution statistics.""" feedback = Bcfg2.Client.XML.Element("upload-statistics") stats = Bcfg2.Client.XML.SubElement( feedback, 'Statistics', total=str(len(self.states)), version='2.0', revision=self.config.get('revision', '-1')) good_entries = [key for key, val in list(self.states.items()) if val] good = len(good_entries) stats.set('good', str(good)) if any(not val for val in list(self.states.values())): stats.set('state', 'dirty') else: stats.set('state', 'clean') # List bad elements of the configuration for (data, ename) in [(self.modified, 'Modified'), (self.extra, "Extra"), (good_entries, "Good"), ([entry for entry in self.states if not self.states[entry]], "Bad")]: container = Bcfg2.Client.XML.SubElement(stats, ename) for item in data: item.set('qtext', '') container.append(copy.deepcopy(item)) item.text = None timeinfo = Bcfg2.Client.XML.Element("OpStamps") feedback.append(stats) for (event, timestamp) in list(self.times.items()): timeinfo.set(event, str(timestamp)) stats.append(timeinfo) return feedback bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/000077500000000000000000000000001223671746500173145ustar00rootroot00000000000000bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/APK.py000066400000000000000000000046731223671746500203130ustar00rootroot00000000000000"""This provides Bcfg2 support for Alpine Linux APK packages.""" import Bcfg2.Client.Tools class APK(Bcfg2.Client.Tools.PkgTool): """Support for Apk packages.""" name = 'APK' __execs__ = ["/sbin/apk"] __handles__ = [('Package', 'apk')] __req__ = {'Package': ['name', 'version']} pkgtype = 'apk' pkgtool = ("/sbin/apk add %s", ("%s", ["name"])) def __init__(self, logger, setup, config): Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config) self.installed = {} self.RefreshPackages() def RefreshPackages(self): """Refresh memory hashes of packages.""" names = self.cmd.run("/sbin/apk info").stdout.splitlines() nameversions = self.cmd.run("/sbin/apk info -v").stdout.splitlines() for pkg in zip(names, nameversions): pkgname = pkg[0] version = pkg[1][len(pkgname) + 1:] self.logger.debug(" pkgname: %s" % pkgname) self.logger.debug(" version: %s" % version) self.installed[pkgname] = version def VerifyPackage(self, entry, _): """Verify Package status for entry.""" if not 'version' in entry.attrib: self.logger.info("Cannot verify unversioned package %s" % entry.attrib['name']) return False if entry.attrib['name'] in self.installed: if entry.attrib['version'] in \ ['auto', self.installed[entry.attrib['name']]]: #if not self.setup['quick'] and \ # entry.get('verify', 'true') == 'true': #FIXME: Does APK have any sort of verification mechanism? return True else: self.logger.info(" pkg %s at version %s, not %s" % (entry.attrib['name'], self.installed[entry.attrib['name']], entry.attrib['version'])) entry.set('current_version', self.installed[entry.get('name')]) return False entry.set('current_exists', 'false') return False def Remove(self, packages): """Remove extra packages.""" names = [pkg.get('name') for pkg in packages] self.logger.info("Removing packages: %s" % " ".join(names)) self.cmd.run("/sbin/apk del %s" % " ".join(names)) self.RefreshPackages() self.extra = self.FindExtra() bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/APT.py000066400000000000000000000304061223671746500203150ustar00rootroot00000000000000"""This is the Bcfg2 support for apt-get.""" # suppress apt API warnings import warnings warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning) import apt.cache import os import Bcfg2.Client.Tools class APT(Bcfg2.Client.Tools.Tool): """The Debian toolset implements package and service operations and inherits the rest from Toolset.Toolset. """ name = 'APT' __execs__ = [] __handles__ = [('Package', 'deb'), ('Path', 'ignore')] __req__ = {'Package': ['name', 'version'], 'Path': ['type']} def __init__(self, logger, setup, config): Bcfg2.Client.Tools.Tool.__init__(self, logger, setup, config) self.install_path = setup.get('apt_install_path', '/usr') self.var_path = setup.get('apt_var_path', '/var') self.etc_path = setup.get('apt_etc_path', '/etc') self.debsums = '%s/bin/debsums' % self.install_path self.aptget = '%s/bin/apt-get' % self.install_path self.dpkg = '%s/bin/dpkg' % self.install_path self.__execs__ = [self.debsums, self.aptget, self.dpkg] path_entries = os.environ['PATH'].split(':') for reqdir in ['/sbin', '/usr/sbin']: if reqdir not in path_entries: os.environ['PATH'] = os.environ['PATH'] + ':' + reqdir self.pkgcmd = '%s ' % self.aptget + \ '-o DPkg::Options::=--force-confold ' + \ '-o DPkg::Options::=--force-confmiss ' + \ '--reinstall ' + \ '--force-yes ' if not self.setup['debug']: self.pkgcmd += '-q=2 ' self.pkgcmd += '-y install %s' self.ignores = [entry.get('name') for struct in config \ for entry in struct \ if entry.tag == 'Path' and \ entry.get('type') == 'ignore'] self.__important__ = self.__important__ + \ ["%s/cache/debconf/config.dat" % self.var_path, "%s/cache/debconf/templates.dat" % self.var_path, '/etc/passwd', '/etc/group', '%s/apt/apt.conf' % self.etc_path, '%s/dpkg/dpkg.cfg' % self.etc_path] + \ [entry.get('name') for struct in config for entry in struct \ if entry.tag == 'Path' and \ entry.get('name').startswith('%s/apt/sources.list' % self.etc_path)] self.nonexistent = [entry.get('name') for struct in config for entry in struct \ if entry.tag == 'Path' and entry.get('type') == 'nonexistent'] os.environ["DEBIAN_FRONTEND"] = 'noninteractive' self.actions = {} if self.setup['kevlar'] and not self.setup['dryrun']: self.cmd.run("%s --force-confold --configure --pending" % self.dpkg) self.cmd.run("%s clean" % self.aptget) try: self.pkg_cache = apt.cache.Cache() except SystemError: e = sys.exc_info()[1] self.logger.info("Failed to initialize APT cache: %s" % e) raise Bcfg2.Client.Tools.ToolInstantiationError self.pkg_cache.update() self.pkg_cache = apt.cache.Cache() if 'req_reinstall_pkgs' in dir(self.pkg_cache): self._newapi = True else: self._newapi = False def FindExtra(self): """Find extra packages.""" packages = [entry.get('name') for entry in self.getSupportedEntries()] if self._newapi: extras = [(p.name, p.installed.version) for p in self.pkg_cache if p.is_installed and p.name not in packages] else: extras = [(p.name, p.installedVersion) for p in self.pkg_cache if p.isInstalled and p.name not in packages] return [Bcfg2.Client.XML.Element('Package', name=name, \ type='deb', version=version) \ for (name, version) in extras] def VerifyDebsums(self, entry, modlist): output = \ self.cmd.run("%s -as %s" % (self.debsums, entry.get('name'))).stdout.splitlines() if len(output) == 1 and "no md5sums for" in output[0]: self.logger.info("Package %s has no md5sums. Cannot verify" % \ entry.get('name')) entry.set('qtext', "Reinstall Package %s-%s to setup md5sums? (y/N) " % (entry.get('name'), entry.get('version'))) return False files = [] for item in output: if "checksum mismatch" in item: files.append(item.split()[-1]) elif "changed file" in item: files.append(item.split()[3]) elif "can't open" in item: if item.split()[5] not in self.nonexistent: files.append(item.split()[5]) elif "missing file" in item and \ item.split()[3] in self.nonexistent: # these files should not exist continue elif "is not installed" in item or "missing file" in item: self.logger.error("Package %s is not fully installed" \ % entry.get('name')) else: self.logger.error("Got Unsupported pattern %s from debsums" \ % item) files.append(item) files = list(set(files) - set(self.ignores)) # We check if there is file in the checksum to do if files: # if files are found there we try to be sure our modlist is sane # with erroneous symlinks modlist = [os.path.realpath(filename) for filename in modlist] bad = [filename for filename in files if filename not in modlist] if bad: self.logger.debug("It is suggested that you either manage these " "files, revert the changes, or ignore false " "failures:") self.logger.info("Package %s failed validation. Bad files are:" % \ entry.get('name')) self.logger.info(bad) entry.set('qtext', "Reinstall Package %s-%s to fix failing files? (y/N) " % \ (entry.get('name'), entry.get('version'))) return False return True def VerifyPackage(self, entry, modlist, checksums=True): """Verify package for entry.""" if not 'version' in entry.attrib: self.logger.info("Cannot verify unversioned package %s" % (entry.attrib['name'])) return False pkgname = entry.get('name') if self.pkg_cache.has_key(pkgname): if self._newapi: is_installed = self.pkg_cache[pkgname].is_installed else: is_installed = self.pkg_cache[pkgname].isInstalled if not self.pkg_cache.has_key(pkgname) or not is_installed: self.logger.info("Package %s not installed" % (entry.get('name'))) entry.set('current_exists', 'false') return False pkg = self.pkg_cache[pkgname] if self._newapi: installed_version = pkg.installed.version candidate_version = pkg.candidate.version else: installed_version = pkg.installedVersion candidate_version = pkg.candidateVersion if entry.get('version') == 'auto': if self._newapi: is_upgradable = self.pkg_cache._depcache.is_upgradable(pkg._pkg) else: is_upgradable = self.pkg_cache._depcache.IsUpgradable(pkg._pkg) if is_upgradable: desiredVersion = candidate_version else: desiredVersion = installed_version elif entry.get('version') == 'any': desiredVersion = installed_version else: desiredVersion = entry.get('version') if desiredVersion != installed_version: entry.set('current_version', installed_version) entry.set('qtext', "Modify Package %s (%s -> %s)? (y/N) " % \ (entry.get('name'), entry.get('current_version'), desiredVersion)) return False else: # version matches if not self.setup['quick'] and entry.get('verify', 'true') == 'true' \ and checksums: pkgsums = self.VerifyDebsums(entry, modlist) return pkgsums return True def Remove(self, packages): """Deal with extra configuration detected.""" pkgnames = " ".join([pkg.get('name') for pkg in packages]) self.pkg_cache = apt.cache.Cache() if len(packages) > 0: self.logger.info('Removing packages:') self.logger.info(pkgnames) for pkg in pkgnames.split(" "): try: if self._newapi: self.pkg_cache[pkg].mark_delete(purge=True) else: self.pkg_cache[pkg].markDelete(purge=True) except: if self._newapi: self.pkg_cache[pkg].mark_delete() else: self.pkg_cache[pkg].markDelete() try: self.pkg_cache.commit() except SystemExit: # thank you python-apt 0.6 pass self.pkg_cache = apt.cache.Cache() self.modified += packages self.extra = self.FindExtra() def Install(self, packages, states): # it looks like you can't install arbitrary versions of software # out of the pkg cache, we will still need to call apt-get ipkgs = [] bad_pkgs = [] for pkg in packages: if not self.pkg_cache.has_key(pkg.get('name')): self.logger.error("APT has no information about package %s" % (pkg.get('name'))) continue if pkg.get('version') in ['auto', 'any']: if self._newapi: try: ipkgs.append("%s=%s" % (pkg.get('name'), self.pkg_cache[pkg.get('name')].candidate.version)) except AttributeError: self.logger.error("Failed to find %s in apt package cache" % pkg.get('name')) continue else: ipkgs.append("%s=%s" % (pkg.get('name'), self.pkg_cache[pkg.get('name')].candidateVersion)) continue if self._newapi: avail_vers = [x.ver_str for x in \ self.pkg_cache[pkg.get('name')]._pkg.version_list] else: avail_vers = [x.VerStr for x in \ self.pkg_cache[pkg.get('name')]._pkg.VersionList] if pkg.get('version') in avail_vers: ipkgs.append("%s=%s" % (pkg.get('name'), pkg.get('version'))) continue else: self.logger.error("Package %s: desired version %s not in %s" \ % (pkg.get('name'), pkg.get('version'), avail_vers)) bad_pkgs.append(pkg.get('name')) if bad_pkgs: self.logger.error("Cannot find correct versions of packages:") self.logger.error(bad_pkgs) if not ipkgs: return if not self.cmd.run(self.pkgcmd % (" ".join(ipkgs))): self.logger.error("APT command failed") self.pkg_cache = apt.cache.Cache() self.extra = self.FindExtra() for package in packages: states[package] = self.VerifyPackage(package, [], checksums=False) if states[package]: self.modified.append(package) def VerifyPath(self, entry, _): """Do nothing here since we only verify Path type=ignore.""" return True bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/Action.py000066400000000000000000000106711223671746500211100ustar00rootroot00000000000000"""Action driver""" import os import sys import select import Bcfg2.Client.Tools from Bcfg2.Client.Frame import matches_white_list, passes_black_list from Bcfg2.Compat import input # pylint: disable=W0622 class Action(Bcfg2.Client.Tools.Tool): """Implement Actions""" name = 'Action' __handles__ = [('PostInstall', None), ('Action', None)] __req__ = {'PostInstall': ['name'], 'Action': ['name', 'timing', 'when', 'command', 'status']} def _action_allowed(self, action): """ Return true if the given action is allowed to be run by the whitelist or blacklist """ if self.setup['decision'] == 'whitelist' and \ not matches_white_list(action, self.setup['decision_list']): self.logger.info("In whitelist mode: suppressing Action: %s" % action.get('name')) return False if self.setup['decision'] == 'blacklist' and \ not passes_black_list(action, self.setup['decision_list']): self.logger.info("In blacklist mode: suppressing Action: %s" % action.get('name')) return False return True def RunAction(self, entry): """This method handles command execution and status return.""" shell = False shell_string = '' if entry.get('shell', 'false') == 'true': shell = True shell_string = '(in shell) ' if not self.setup['dryrun']: if self.setup['interactive']: prompt = ('Run Action %s%s, %s: (y/N): ' % (shell_string, entry.get('name'), entry.get('command'))) # flush input buffer while len(select.select([sys.stdin.fileno()], [], [], 0.0)[0]) > 0: os.read(sys.stdin.fileno(), 4096) ans = input(prompt) if ans not in ['y', 'Y']: return False if self.setup['servicemode'] == 'build': if entry.get('build', 'true') == 'false': self.logger.debug("Action: Deferring execution of %s due " "to build mode" % entry.get('command')) return False self.logger.debug("Running Action %s %s" % (shell_string, entry.get('name'))) rv = self.cmd.run(entry.get('command'), shell=shell) self.logger.debug("Action: %s got return code %s" % (entry.get('command'), rv.retval)) entry.set('rc', str(rv.retval)) return entry.get('status', 'check') == 'ignore' or rv.success else: self.logger.debug("In dryrun mode: not running action: %s" % (entry.get('name'))) return False def VerifyAction(self, dummy, _): """Actions always verify true.""" return True def VerifyPostInstall(self, dummy, _): """Actions always verify true.""" return True def InstallAction(self, entry): """Run actions as pre-checks for bundle installation.""" if entry.get('timing') != 'post': return self.RunAction(entry) return True def InstallPostInstall(self, entry): """ Install a deprecated PostInstall entry """ self.logger.warning("Installing deprecated PostInstall entry %s" % entry.get("name")) return self.InstallAction(entry) def BundleUpdated(self, bundle, states): """Run postinstalls when bundles have been updated.""" for postinst in bundle.findall("PostInstall"): if not self._action_allowed(postinst): continue self.cmd.run(postinst.get('name')) for action in bundle.findall("Action"): if action.get('timing') in ['post', 'both']: if not self._action_allowed(action): continue states[action] = self.RunAction(action) def BundleNotUpdated(self, bundle, states): """Run Actions when bundles have not been updated.""" for action in bundle.findall("Action"): if action.get('timing') in ['post', 'both'] and \ action.get('when') != 'modified': if not self._action_allowed(action): continue states[action] = self.RunAction(action) bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/Blast.py000066400000000000000000000021211223671746500207270ustar00rootroot00000000000000"""This provides Bcfg2 support for Blastwave.""" import tempfile import Bcfg2.Client.Tools.SYSV class Blast(Bcfg2.Client.Tools.SYSV.SYSV): """Support for Blastwave packages.""" pkgtype = 'blast' pkgtool = ("/opt/csw/bin/pkg-get install %s", ("%s", ["bname"])) name = 'Blast' __execs__ = ['/opt/csw/bin/pkg-get', "/usr/bin/pkginfo"] __handles__ = [('Package', 'blast')] __req__ = {'Package': ['name', 'version', 'bname']} def __init__(self, logger, setup, config): # dont use the sysv constructor Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config) noaskfile = tempfile.NamedTemporaryFile() self.noaskname = noaskfile.name try: noaskfile.write(Bcfg2.Client.Tools.SYSV.noask) except: pass # VerifyPackage comes from Bcfg2.Client.Tools.SYSV # Install comes from Bcfg2.Client.Tools.PkgTool # Extra comes from Bcfg2.Client.Tools.Tool # Remove comes from Bcfg2.Client.Tools.SYSV def FindExtra(self): """Pass through to null FindExtra call.""" return [] bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/Chkconfig.py000066400000000000000000000113361223671746500215650ustar00rootroot00000000000000# This is the bcfg2 support for chkconfig """This is chkconfig support.""" import os import Bcfg2.Client.Tools import Bcfg2.Client.XML class Chkconfig(Bcfg2.Client.Tools.SvcTool): """Chkconfig support for Bcfg2.""" name = 'Chkconfig' __execs__ = ['/sbin/chkconfig'] __handles__ = [('Service', 'chkconfig')] __req__ = {'Service': ['name', 'status']} os.environ['LC_ALL'] = 'C' def get_svc_command(self, service, action): return "/sbin/service %s %s" % (service.get('name'), action) def verify_bootstatus(self, entry, bootstatus): """Verify bootstatus for entry.""" rv = self.cmd.run("/sbin/chkconfig --list %s " % entry.get('name')) if rv.success: srvdata = rv.stdout.splitlines()[0].split() else: # service not installed entry.set('current_bootstatus', 'service not installed') return False if len(srvdata) == 2: # This is an xinetd service if bootstatus == srvdata[1]: return True else: entry.set('current_bootstatus', srvdata[1]) return False try: onlevels = [level.split(':')[0] for level in srvdata[1:] if level.split(':')[1] == 'on'] except IndexError: onlevels = [] if bootstatus == 'on': current_bootstatus = (len(onlevels) > 0) else: current_bootstatus = (len(onlevels) == 0) return current_bootstatus def VerifyService(self, entry, _): """Verify Service status for entry.""" entry.set('target_status', entry.get('status')) # for reporting bootstatus = self.get_bootstatus(entry) if bootstatus is None: return True current_bootstatus = self.verify_bootstatus(entry, bootstatus) if entry.get('status') == 'ignore': # 'ignore' should verify current_svcstatus = True svcstatus = True else: svcstatus = self.check_service(entry) if entry.get('status') == 'on': if svcstatus: current_svcstatus = True else: current_svcstatus = False elif entry.get('status') == 'off': if svcstatus: current_svcstatus = False else: current_svcstatus = True if svcstatus: entry.set('current_status', 'on') else: entry.set('current_status', 'off') return current_bootstatus and current_svcstatus def InstallService(self, entry): """Install Service entry.""" self.cmd.run("/sbin/chkconfig --add %s" % (entry.get('name'))) self.logger.info("Installing Service %s" % (entry.get('name'))) bootstatus = self.get_bootstatus(entry) if bootstatus is not None: if bootstatus == 'on': # make sure service is enabled on boot bootcmd = '/sbin/chkconfig %s %s' % \ (entry.get('name'), bootstatus) elif bootstatus == 'off': # make sure service is disabled on boot bootcmd = '/sbin/chkconfig %s %s' % (entry.get('name'), bootstatus) bootcmdrv = self.cmd.run(bootcmd).success if self.setup['servicemode'] == 'disabled': # 'disabled' means we don't attempt to modify running svcs return bootcmdrv buildmode = self.setup['servicemode'] == 'build' if (entry.get('status') == 'on' and not buildmode) and \ entry.get('current_status') == 'off': svccmdrv = self.start_service(entry) elif (entry.get('status') == 'off' or buildmode) and \ entry.get('current_status') == 'on': svccmdrv = self.stop_service(entry) else: svccmdrv = True # ignore status attribute return bootcmdrv and svccmdrv else: # when bootstatus is 'None', status == 'ignore' return True def FindExtra(self): """Locate extra chkconfig Services.""" allsrv = [line.split()[0] for line in self.cmd.run("/sbin/chkconfig --list").stdout.splitlines() if ":on" in line] self.logger.debug('Found active services:') self.logger.debug(allsrv) specified = [srv.get('name') for srv in self.getSupportedEntries()] return [Bcfg2.Client.XML.Element('Service', type='chkconfig', name=name) for name in allsrv if name not in specified] bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/DebInit.py000066400000000000000000000152371223671746500212140ustar00rootroot00000000000000"""Debian Init Support for Bcfg2""" import glob import os import re import Bcfg2.Client.Tools # Debian squeeze and beyond uses a dependecy based boot sequence DEBIAN_OLD_STYLE_BOOT_SEQUENCE = ('etch', '4.0', 'lenny') class DebInit(Bcfg2.Client.Tools.SvcTool): """Debian Service Support for Bcfg2.""" name = 'DebInit' __execs__ = ['/usr/sbin/update-rc.d', '/usr/sbin/invoke-rc.d'] __handles__ = [('Service', 'deb')] __req__ = {'Service': ['name', 'status']} svcre = \ re.compile(r'/etc/.*/(?P[SK])(?P\d+)(?P\S+)') def get_svc_command(self, service, action): return '/usr/sbin/invoke-rc.d %s %s' % (service.get('name'), action) def verify_bootstatus(self, entry, bootstatus): """Verify bootstatus for entry.""" rawfiles = glob.glob("/etc/rc*.d/[SK]*%s" % (entry.get('name'))) files = [] try: deb_version = open('/etc/debian_version').read().split('/', 1)[0] except IOError: deb_version = 'unknown' if entry.get('sequence'): if (deb_version in DEBIAN_OLD_STYLE_BOOT_SEQUENCE or deb_version.startswith('5') or os.path.exists('/etc/init.d/.legacy-bootordering')): start_sequence = int(entry.get('sequence')) kill_sequence = 100 - start_sequence else: start_sequence = None self.logger.warning("Your debian version boot sequence is " "dependency based \"sequence\" attribute " "will be ignored.") else: start_sequence = None for filename in rawfiles: match = self.svcre.match(filename) if not match: self.logger.error("Failed to match file: %s" % filename) continue if match.group('name') == entry.get('name'): files.append(filename) if bootstatus == 'off': if files: entry.set('current_bootstatus', 'on') return False else: return True elif files: if start_sequence: for filename in files: match = self.svcre.match(filename) file_sequence = int(match.group('sequence')) if ((match.group('action') == 'S' and file_sequence != start_sequence) or (match.group('action') == 'K' and file_sequence != kill_sequence)): return False return True else: entry.set('current_bootstatus', 'off') return False def VerifyService(self, entry, _): """Verify Service status for entry.""" entry.set('target_status', entry.get('status')) # for reporting bootstatus = self.get_bootstatus(entry) if bootstatus is None: return True current_bootstatus = self.verify_bootstatus(entry, bootstatus) if entry.get('status') == 'ignore': # 'ignore' should verify current_svcstatus = True svcstatus = True else: svcstatus = self.check_service(entry) if entry.get('status') == 'on': if svcstatus: current_svcstatus = True else: current_svcstatus = False elif entry.get('status') == 'off': if svcstatus: current_svcstatus = False else: current_svcstatus = True if svcstatus: entry.set('current_status', 'on') else: entry.set('current_status', 'off') return current_bootstatus and current_svcstatus def InstallService(self, entry): """Install Service entry.""" self.logger.info("Installing Service %s" % (entry.get('name'))) bootstatus = self.get_bootstatus(entry) # check if init script exists try: os.stat('/etc/init.d/%s' % entry.get('name')) except OSError: self.logger.debug("Init script for service %s does not exist" % entry.get('name')) return False if bootstatus is not None: seqcmdrv = True if bootstatus == 'on': # make sure service is enabled on boot bootcmd = '/usr/sbin/update-rc.d %s defaults' % \ entry.get('name') if entry.get('sequence'): seqcmd = '/usr/sbin/update-rc.d -f %s remove' % \ entry.get('name') seqcmdrv = self.cmd.run(seqcmd) start_sequence = int(entry.get('sequence')) kill_sequence = 100 - start_sequence bootcmd = '%s %d %d' % (bootcmd, start_sequence, kill_sequence) elif bootstatus == 'off': # make sure service is disabled on boot bootcmd = '/usr/sbin/update-rc.d -f %s remove' % \ entry.get('name') bootcmdrv = self.cmd.run(bootcmd) if self.setup['servicemode'] == 'disabled': # 'disabled' means we don't attempt to modify running svcs return bootcmdrv and seqcmdrv buildmode = self.setup['servicemode'] == 'build' if (entry.get('status') == 'on' and not buildmode) and \ entry.get('current_status') == 'off': svccmdrv = self.start_service(entry) elif (entry.get('status') == 'off' or buildmode) and \ entry.get('current_status') == 'on': svccmdrv = self.stop_service(entry) else: svccmdrv = True # ignore status attribute return bootcmdrv and svccmdrv and seqcmdrv else: # when bootstatus is 'None', status == 'ignore' return True def FindExtra(self): """Find Extra Debian Service entries.""" specified = [entry.get('name') for entry in self.getSupportedEntries()] extra = set() for fname in glob.glob("/etc/rc[12345].d/S*"): name = self.svcre.match(fname).group('name') if name not in specified: extra.add(name) return [Bcfg2.Client.XML.Element('Service', name=name, type='deb') for name in list(extra)] def Remove(self, _): """Remove extra service entries.""" # Extra service removal is nonsensical # Extra services need to be reflected in the config return bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/Encap.py000066400000000000000000000036571223671746500207270ustar00rootroot00000000000000"""Bcfg2 Support for Encap Packages""" import glob import re import Bcfg2.Client.Tools class Encap(Bcfg2.Client.Tools.PkgTool): """Support for Encap packages.""" name = 'Encap' __execs__ = ['/usr/local/bin/epkg'] __handles__ = [('Package', 'encap')] __req__ = {'Package': ['version', 'url']} pkgtype = 'encap' pkgtool = ("/usr/local/bin/epkg -l -f -q %s", ("%s", ["url"])) splitter = re.compile(r'.*/(?P[\w-]+)\-(?P[\w\.+-]+)') def RefreshPackages(self): """Try to find encap packages.""" self.installed = {} for pkg in glob.glob("/usr/local/encap/*"): match = self.splitter.match(pkg) if match: self.installed[match.group('name')] = match.group('version') else: print("Failed to split name %s" % pkg) self.logger.debug("Encap: RefreshPackages: self.installed.keys() are:") self.logger.debug("%s" % list(self.installed.keys())) def VerifyPackage(self, entry, _): """Verify Package status for entry.""" if not entry.get('version'): self.logger.info("Insufficient information of Package %s; " "cannot Verify" % entry.get('name')) return False success = self.cmd.run("/usr/local/bin/epkg -q -S -k %s-%s" % (entry.get('name'), entry.get('version'))).success if not success: self.logger.debug("Package %s version incorrect" % entry.get('name')) return success def Remove(self, packages): """Deal with extra configuration detected.""" names = " ".join([pkg.get('name') for pkg in packages]) self.logger.info("Removing packages: %s" % (names)) self.cmd.run("/usr/local/bin/epkg -l -q -r %s" % (names)) self.RefreshPackages() self.extra = self.FindExtra() bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/FreeBSDInit.py000066400000000000000000000014101223671746500217200ustar00rootroot00000000000000"""FreeBSD Init Support for Bcfg2.""" __revision__ = '$Rev$' # TODO # - hardcoded path to ports rc.d # - doesn't know about /etc/rc.d/ import os import Bcfg2.Client.Tools class FreeBSDInit(Bcfg2.Client.Tools.SvcTool): """FreeBSD service support for Bcfg2.""" name = 'FreeBSDInit' __handles__ = [('Service', 'freebsd')] __req__ = {'Service': ['name', 'status']} def __init__(self, logger, cfg, setup): Bcfg2.Client.Tools.Tool.__init__(self, logger, cfg, setup) if os.uname()[0] != 'FreeBSD': raise Bcfg2.Client.Tools.ToolInstantiationError def VerifyService(self, entry, _): return True def get_svc_command(self, service, action): return "/usr/local/etc/rc.d/%s %s" % (service.get('name'), action) bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/FreeBSDPackage.py000066400000000000000000000032551223671746500223610ustar00rootroot00000000000000"""This is the Bcfg2 tool for the FreeBSD package system.""" # TODO # - actual package installation # - verification of package files import re import Bcfg2.Client.Tools class FreeBSDPackage(Bcfg2.Client.Tools.PkgTool): """The FreeBSD toolset implements package operations and inherits the rest from Toolset.Toolset.""" name = 'FreeBSDPackage' __execs__ = ['/usr/sbin/pkg_add', '/usr/sbin/pkg_info'] __handles__ = [('Package', 'freebsdpkg')] __req__ = {'Package': ['name', 'version']} pkgtool = ('/usr/sbin/pkg_add -r %s', ('%s-%s', ['name', 'version'])) pkgtype = 'freebsdpkg' def RefreshPackages(self): self.installed = {} packages = self.cmd.run("/usr/sbin/pkg_info -a -E").stdout.splitlines() pattern = re.compile(r'(.*)-(\d.*)') for pkg in packages: if pattern.match(pkg): name = pattern.match(pkg).group(1) version = pattern.match(pkg).group(2) self.installed[name] = version def VerifyPackage(self, entry, _): if not 'version' in entry.attrib: self.logger.info("Cannot verify unversioned package %s" % entry.attrib['name']) return False if entry.attrib['name'] in self.installed: if self.installed[entry.attrib['name']] == entry.attrib['version']: # TODO: verfification return True else: entry.set('current_version', self.installed[entry.get('name')]) return False self.logger.info("Package %s not installed" % (entry.get('name'))) entry.set('current_exists', 'false') return False bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/IPS.py000066400000000000000000000042331223671746500203230ustar00rootroot00000000000000"""This is the Bcfg2 support for OpenSolaris packages.""" import pkg.client.image as image import pkg.client.progress as progress import Bcfg2.Client.Tools class IPS(Bcfg2.Client.Tools.PkgTool): """The IPS driver implements OpenSolaris package operations.""" name = 'IPS' pkgtype = 'ips' conflicts = ['SYSV'] __handles__ = [('Package', 'ips')] __req__ = {'Package': ['name', 'version']} pkgtool = ('pkg install --no-refresh %s', ('%s', ['name'])) def __init__(self, logger, setup, cfg): self.installed = {} self.pending_upgrades = set() self.image = image.Image() self.image.find_root('/', False) self.image.load_config() Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, cfg) self.cfg = cfg def RefreshPackages(self): self.installed = dict() self.image.history.operation_name = "list" self.image.load_catalogs(progress.NullProgressTracker()) for (pfmri, pinfo) in self.image.inventory([], False): pname = pfmri.pkg_name pversion = pfmri.version.get_short_version() self.installed[pname] = pversion if pinfo['upgradable']: self.pending_upgrades.add(pname) def VerifyPackage(self, entry, _): """Verify package for entry.""" pname = entry.get('name') if not 'version' in entry.attrib: self.logger.info("Cannot verify unversioned package %s" % (pname)) return False if pname not in self.installed: self.logger.debug("IPS: Package %s not installed" % pname) return False if entry.get('version') == 'auto': if pname in self.pending_upgrades: return False elif entry.get('version') == 'any': pass else: if entry.get('version') != self.installed[pname]: self.logger.debug("IPS: Package %s: have %s want %s" % (pname, self.installed[pname], entry.get('version'))) return False # need to implement pkg chksum validation return True bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/MacPorts.py000066400000000000000000000056301223671746500214220ustar00rootroot00000000000000"""This provides Bcfg2 support for macports packages.""" import Bcfg2.Client.Tools class MacPorts(Bcfg2.Client.Tools.PkgTool): """macports package support.""" name = 'MacPorts' __execs__ = ["/opt/local/bin/port"] __handles__ = [('Package', 'macport')] __req__ = {'Package': ['name', 'version']} pkgtype = 'macport' pkgtool = ('/opt/local/bin/port install %s', ('%s', ['name'])) def __init__(self, logger, setup, config): Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config) self.installed = {} self.RefreshPackages() def RefreshPackages(self): """Refresh memory hashes of packages.""" pkgcache = self.cmd.run(["/opt/local/bin/port", "installed"]).stdout.splitlines() self.installed = {} for pkg in pkgcache: if pkg.startswith("Warning:"): continue if pkg.startswith("The following ports are currently installed"): continue if pkg.startswith("No ports are installed"): return pkgname = pkg.split('@')[0].strip() version = pkg.split('@')[1].split(' ')[0] self.logger.info(" pkgname: %s version: %s" % (pkgname, version)) self.installed[pkgname] = version def VerifyPackage(self, entry, _): """Verify Package status for entry.""" if not 'version' in entry.attrib: self.logger.info("Cannot verify unversioned package %s" % entry.attrib['name']) return False if entry.attrib['name'] in self.installed: if (self.installed[entry.attrib['name']] == entry.attrib['version'] or entry.attrib['version'] == 'any'): #if not self.setup['quick'] and \ # entry.get('verify', 'true') == 'true': #FIXME: We should be able to check this once # http://trac.macports.org/ticket/15709 is implemented return True else: self.logger.info(" %s: Wrong version installed. " "Want %s, but have %s" % (entry.get("name"), entry.get("version"), self.installed[entry.get("name")], )) entry.set('current_version', self.installed[entry.get('name')]) return False entry.set('current_exists', 'false') return False def Remove(self, packages): """Remove extra packages.""" names = [pkg.get('name') for pkg in packages] self.logger.info("Removing packages: %s" % " ".join(names)) self.cmd.run("/opt/local/bin/port uninstall %s" % " ".join(names)) self.RefreshPackages() self.extra = self.FindExtra() bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/OpenCSW.py000066400000000000000000000022271223671746500211470ustar00rootroot00000000000000# This is the bcfg2 support for opencsw packages (pkgutil) """This provides Bcfg2 support for OpenCSW packages.""" import tempfile import Bcfg2.Client.Tools.SYSV class OpenCSW(Bcfg2.Client.Tools.SYSV.SYSV): """Support for OpenCSW packages.""" pkgtype = 'opencsw' pkgtool = ("/opt/csw/bin/pkgutil -y -i %s", ("%s", ["bname"])) name = 'OpenCSW' __execs__ = ['/opt/csw/bin/pkgutil', "/usr/bin/pkginfo"] __handles__ = [('Package', 'opencsw')] __req__ = {'Package': ['name', 'version', 'bname']} def __init__(self, logger, setup, config): # dont use the sysv constructor Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config) noaskfile = tempfile.NamedTemporaryFile() self.noaskname = noaskfile.name try: noaskfile.write(Bcfg2.Client.Tools.SYSV.noask) except: pass # VerifyPackage comes from Bcfg2.Client.Tools.SYSV # Install comes from Bcfg2.Client.Tools.PkgTool # Extra comes from Bcfg2.Client.Tools.Tool # Remove comes from Bcfg2.Client.Tools.SYSV def FindExtra(self): """Pass through to null FindExtra call.""" return [] bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/POSIX/000077500000000000000000000000001223671746500202165ustar00rootroot00000000000000bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/POSIX/Device.py000066400000000000000000000052621223671746500217740ustar00rootroot00000000000000""" Handle entries """ import os import sys from Bcfg2.Client.Tools.POSIX.base import POSIXTool, device_map class POSIXDevice(POSIXTool): """ Handle entries """ __req__ = ['name', 'dev_type', 'mode', 'owner', 'group'] def fully_specified(self, entry): if entry.get('dev_type') in ['block', 'char']: # check if major/minor are properly specified if (entry.get('major') is None or entry.get('minor') is None): return False return True def verify(self, entry, modlist): """Verify device entry.""" ondisk = self._exists(entry) if not ondisk: return False # attempt to verify device properties as specified in config rv = True dev_type = entry.get('dev_type') if dev_type in ['block', 'char']: major = int(entry.get('major')) minor = int(entry.get('minor')) if major != os.major(ondisk.st_rdev): msg = ("Major number for device %s is incorrect. " "Current major is %s but should be %s" % (entry.get("name"), os.major(ondisk.st_rdev), major)) self.logger.debug('POSIX: ' + msg) entry.set('qtext', entry.get('qtext', '') + "\n" + msg) rv = False if minor != os.minor(ondisk.st_rdev): msg = ("Minor number for device %s is incorrect. " "Current minor is %s but should be %s" % (entry.get("name"), os.minor(ondisk.st_rdev), minor)) self.logger.debug('POSIX: ' + msg) entry.set('qtext', entry.get('qtext', '') + "\n" + msg) rv = False return POSIXTool.verify(self, entry, modlist) and rv def install(self, entry): if not self._exists(entry, remove=True): try: dev_type = entry.get('dev_type') mode = device_map[dev_type] | int(entry.get('mode'), 8) if dev_type in ['block', 'char']: major = int(entry.get('major')) minor = int(entry.get('minor')) device = os.makedev(major, minor) os.mknod(entry.get('name'), mode, device) else: os.mknod(entry.get('name'), mode) except (KeyError, OSError, ValueError): err = sys.exc_info()[1] self.logger.error('POSIX: Failed to install %s: %s' % (entry.get('name'), err)) return False return POSIXTool.install(self, entry) bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/POSIX/Directory.py000066400000000000000000000057031223671746500225410ustar00rootroot00000000000000""" Handle entries """ import os import sys import stat import Bcfg2.Client.XML from Bcfg2.Client.Tools.POSIX.base import POSIXTool class POSIXDirectory(POSIXTool): """ Handle entries """ __req__ = ['name', 'mode', 'owner', 'group'] def verify(self, entry, modlist): ondisk = self._exists(entry) if not ondisk: return False if not stat.S_ISDIR(ondisk[stat.ST_MODE]): self.logger.info("POSIX: %s is not a directory" % entry.get('name')) return False prune = True if entry.get('prune', 'false').lower() == 'true': # check for any extra entries when prune='true' attribute is set try: extras = [os.path.join(entry.get('name'), ent) for ent in os.listdir(entry.get('name')) if os.path.join(entry.get('name'), ent) not in modlist] if extras: prune = False msg = "Directory %s contains extra entries: %s" % \ (entry.get('name'), "; ".join(extras)) self.logger.info("POSIX: " + msg) entry.set('qtext', entry.get('qtext', '') + '\n' + msg) for extra in extras: Bcfg2.Client.XML.SubElement(entry, 'Prune', name=extra) except OSError: prune = True return POSIXTool.verify(self, entry, modlist) and prune def install(self, entry): """Install directory entries.""" fmode = self._exists(entry) if fmode and not stat.S_ISDIR(fmode[stat.ST_MODE]): self.logger.info("POSIX: Found a non-directory entry at %s, " "removing" % entry.get('name')) try: os.unlink(entry.get('name')) fmode = False except OSError: err = sys.exc_info()[1] self.logger.error("POSIX: Failed to unlink %s: %s" % (entry.get('name'), err)) return False elif fmode: self.logger.debug("POSIX: Found a pre-existing directory at %s" % entry.get('name')) rv = True if not fmode: rv &= self._makedirs(entry) if entry.get('prune', 'false') == 'true': for pent in entry.findall('Prune'): pname = pent.get('name') try: self.logger.debug("POSIX: Removing %s" % pname) self._remove(pent) except OSError: err = sys.exc_info()[1] self.logger.error("POSIX: Failed to unlink %s: %s" % (pname, err)) rv = False return POSIXTool.install(self, entry) and rv bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/POSIX/File.py000066400000000000000000000243571223671746500214620ustar00rootroot00000000000000""" Handle entries """ import os import sys import stat import time import difflib import tempfile from Bcfg2.Client.Tools.POSIX.base import POSIXTool from Bcfg2.Compat import unicode, b64encode, b64decode # pylint: disable=W0622 class POSIXFile(POSIXTool): """ Handle entries """ __req__ = ['name', 'mode', 'owner', 'group'] def fully_specified(self, entry): return entry.text is not None or entry.get('empty', 'false') == 'true' def _is_string(self, strng, encoding): """ Returns true if the string contains no ASCII control characters and can be decoded from the specified encoding. """ for char in strng: if ord(char) < 9 or ord(char) > 13 and ord(char) < 32: return False if not hasattr(strng, "decode"): # py3k return True try: strng.decode(encoding) return True except: # pylint: disable=W0702 return False def _get_data(self, entry): """ Get a tuple of (, ) for the given entry """ is_binary = entry.get('encoding', 'ascii') == 'base64' if entry.get('empty', 'false') == 'true' or not entry.text: tempdata = '' elif is_binary: tempdata = b64decode(entry.text) else: tempdata = entry.text if isinstance(tempdata, unicode) and unicode != str: try: tempdata = tempdata.encode(self.setup['encoding']) except UnicodeEncodeError: err = sys.exc_info()[1] self.logger.error("POSIX: Error encoding file %s: %s" % (entry.get('name'), err)) return (tempdata, is_binary) def verify(self, entry, modlist): ondisk = self._exists(entry) tempdata, is_binary = self._get_data(entry) if isinstance(tempdata, str) and str != unicode: tempdatasize = len(tempdata) else: tempdatasize = len(tempdata.encode(self.setup['encoding'])) different = False content = None if not ondisk: # first, see if the target file exists at all; if not, # they're clearly different different = True content = "" elif tempdatasize != ondisk[stat.ST_SIZE]: # next, see if the size of the target file is different # from the size of the desired content different = True else: # finally, read in the target file and compare them # directly. comparison could be done with a checksum, # which might be faster for big binary files, but slower # for everything else try: content = open(entry.get('name')).read() except UnicodeDecodeError: content = open(entry.get('name'), encoding=self.setup['encoding']).read() except IOError: self.logger.error("POSIX: Failed to read %s: %s" % (entry.get("name"), sys.exc_info()[1])) return False different = content != tempdata if different: self.logger.debug("POSIX: %s has incorrect contents" % entry.get("name")) self._get_diffs( entry, interactive=self.setup['interactive'], sensitive=entry.get('sensitive', 'false').lower() == 'true', is_binary=is_binary, content=content) return POSIXTool.verify(self, entry, modlist) and not different def _write_tmpfile(self, entry): """ Write the file data to a temp file """ filedata = self._get_data(entry)[0] # get a temp file to write to that is in the same directory as # the existing file in order to preserve any permissions # protections on that directory, and also to avoid issues with # /tmp set nosetuid while creating files that are supposed to # be setuid try: (newfd, newfile) = \ tempfile.mkstemp(prefix=os.path.basename(entry.get("name")), dir=os.path.dirname(entry.get("name"))) except OSError: err = sys.exc_info()[1] self.logger.error("POSIX: Failed to create temp file in %s: %s" % (os.path.dirname(entry.get('name')), err)) return False try: if isinstance(filedata, str) and str != unicode: os.fdopen(newfd, 'w').write(filedata) else: os.fdopen(newfd, 'wb').write( filedata.encode(self.setup['encoding'])) except (OSError, IOError): err = sys.exc_info()[1] self.logger.error("POSIX: Failed to open temp file %s for writing " "%s: %s" % (newfile, entry.get("name"), err)) return False return newfile def _rename_tmpfile(self, newfile, entry): """ Rename the given file to the appropriate filename for entry """ try: os.rename(newfile, entry.get('name')) return True except OSError: err = sys.exc_info()[1] self.logger.error("POSIX: Failed to rename temp file %s to %s: %s" % (newfile, entry.get('name'), err)) try: os.unlink(newfile) except OSError: err = sys.exc_info()[1] self.logger.error("POSIX: Could not remove temp file %s: %s" % (newfile, err)) return False def install(self, entry): """Install device entries.""" if not os.path.exists(os.path.dirname(entry.get('name'))): if not self._makedirs(entry, path=os.path.dirname(entry.get('name'))): return False newfile = self._write_tmpfile(entry) if not newfile: return False rv = self._set_perms(entry, path=newfile) if not self._rename_tmpfile(newfile, entry): return False return POSIXTool.install(self, entry) and rv def _get_diffs(self, entry, interactive=False, # pylint: disable=R0912 sensitive=False, is_binary=False, content=None): """ generate the necessary diffs for entry """ if not interactive and sensitive: return prompt = [entry.get('qtext', '')] attrs = dict() if content is None: # it's possible that we figured out the files are # different without reading in the local file. if the # supplied version of the file is not binary, we now have # to read in the local file to figure out if _it_ is # binary, and either include that fact or the diff in our # prompts for -I and the reports try: content = open(entry.get('name')).read() except UnicodeDecodeError: content = open(entry.get('name'), encoding='utf-8').read() except IOError: self.logger.error("POSIX: Failed to read %s: %s" % (entry.get("name"), sys.exc_info()[1])) return False if not is_binary: is_binary |= not self._is_string(content, self.setup['encoding']) if is_binary: # don't compute diffs if the file is binary prompt.append('Binary file, no printable diff') attrs['current_bfile'] = b64encode(content) else: if interactive: diff = self._diff(content, self._get_data(entry)[0], difflib.unified_diff, filename=entry.get("name")) if diff: udiff = '\n'.join(l.rstrip('\n') for l in diff) if hasattr(udiff, "decode"): udiff = udiff.decode(self.setup['encoding']) try: prompt.append(udiff) except UnicodeEncodeError: prompt.append("Could not encode diff") elif entry.get("empty", "true"): # the file doesn't exist on disk, but there's no # expected content prompt.append("%s does not exist" % entry.get("name")) else: prompt.append("Diff took too long to compute, no " "printable diff") if not sensitive: diff = self._diff(content, self._get_data(entry)[0], difflib.ndiff, filename=entry.get("name")) if diff: attrs["current_bdiff"] = b64encode("\n".join(diff)) else: attrs['current_bfile'] = b64encode(content) if interactive: entry.set("qtext", "\n".join(prompt)) if not sensitive: for attr, val in attrs.items(): entry.set(attr, val) def _diff(self, content1, content2, difffunc, filename=None): """ Return a diff of the two strings, as produced by difffunc. warns after 5 seconds and times out after 30 seconds. """ rv = [] start = time.time() longtime = False for diffline in difffunc(content1.split('\n'), content2.split('\n')): now = time.time() rv.append(diffline) if now - start > 5 and not longtime: if filename: self.logger.info("POSIX: Diff of %s taking a long time" % filename) else: self.logger.info("POSIX: Diff taking a long time") longtime = True elif now - start > 30: if filename: self.logger.error("POSIX: Diff of %s took too long; " "giving up" % filename) else: self.logger.error("POSIX: Diff took too long; giving up") return False return rv bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/POSIX/Hardlink.py000066400000000000000000000006521223671746500223270ustar00rootroot00000000000000""" Handle entries """ import os from Bcfg2.Client.Tools.POSIX.base import POSIXLinkTool class POSIXHardlink(POSIXLinkTool): """ Handle entries """ __linktype__ = "hardlink" def _verify(self, entry): return os.path.samefile(entry.get('name'), entry.get('to')) def _link(self, entry): return os.link(entry.get('to'), entry.get('name')) bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/POSIX/Nonexistent.py000066400000000000000000000030031223671746500231020ustar00rootroot00000000000000""" Handle entries """ import os import sys from Bcfg2.Client.Tools.POSIX.base import POSIXTool class POSIXNonexistent(POSIXTool): """ Handle entries """ __req__ = ['name'] def verify(self, entry, _): if os.path.lexists(entry.get('name')): self.logger.debug("POSIX: %s exists but should not" % entry.get("name")) return False return True def install(self, entry): ename = entry.get('name') recursive = entry.get('recursive', '').lower() == 'true' if recursive: # ensure that configuration spec is consistent first for struct in self.config.getchildren(): for el in struct.getchildren(): if (el.tag == 'Path' and el.get('type') != 'nonexistent' and el.get('name').startswith(ename)): self.logger.error('POSIX: Not removing %s. One or ' 'more files in this directory are ' 'specified in your configuration.' % ename) return False try: self._remove(entry, recursive=recursive) return True except OSError: err = sys.exc_info()[1] self.logger.error('POSIX: Failed to remove %s: %s' % (ename, err)) return False bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/POSIX/Permissions.py000066400000000000000000000003711223671746500231040ustar00rootroot00000000000000""" Handle entries """ from Bcfg2.Client.Tools.POSIX.base import POSIXTool class POSIXPermissions(POSIXTool): """ Handle entries """ __req__ = ['name', 'mode', 'owner', 'group'] bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/POSIX/Symlink.py000066400000000000000000000010161223671746500222140ustar00rootroot00000000000000""" Handle entries """ import os from Bcfg2.Client.Tools.POSIX.base import POSIXLinkTool class POSIXSymlink(POSIXLinkTool): """ Handle entries """ __linktype__ = "symlink" def _verify(self, entry): sloc = os.readlink(entry.get('name')) if sloc != entry.get('to'): entry.set('current_to', sloc) return False return True def _link(self, entry): return os.symlink(entry.get('to'), entry.get('name')) bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/POSIX/__init__.py000066400000000000000000000143571223671746500223410ustar00rootroot00000000000000"""All POSIX Type client support for Bcfg2.""" import os import re import sys import shutil from datetime import datetime import Bcfg2.Client.Tools from Bcfg2.Compat import walk_packages from Bcfg2.Client.Tools.POSIX.base import POSIXTool class POSIX(Bcfg2.Client.Tools.Tool): """POSIX File support code.""" name = 'POSIX' def __init__(self, logger, setup, config): Bcfg2.Client.Tools.Tool.__init__(self, logger, setup, config) self.ppath = setup['ppath'] self.max_copies = setup['max_copies'] self._handlers = self._load_handlers() self.logger.debug("POSIX: Handlers loaded: %s" % (", ".join(self._handlers.keys()))) self.__req__ = dict(Path=dict()) for etype, hdlr in self._handlers.items(): self.__req__['Path'][etype] = hdlr.__req__ self.__handles__.append(('Path', etype)) # Tool.__init__() sets up the list of handled entries, but we # need to do it again after __handles__ has been populated. we # can't populate __handles__ when the class is created because # _load_handlers() _must_ be called at run-time, not at # compile-time. This also has to _extend_ self.handled, not # set it, because self.handled has some really crazy # semi-global thing going that, frankly, scares the crap out # of me. for struct in config: self.handled.extend([e for e in struct if (e not in self.handled and self.handlesEntry(e))]) def _load_handlers(self): """ load available POSIX tool handlers. this must be called at run-time, not at compile-time, or we get wierd circular import issues. """ rv = dict() for submodule in walk_packages(path=__path__, prefix=__name__ + "."): mname = submodule[1].rsplit('.', 1)[-1] if mname == 'base': continue module = getattr(__import__(submodule[1]).Client.Tools.POSIX, mname) hdlr = getattr(module, "POSIX" + mname) if POSIXTool in hdlr.__mro__: # figure out what entry type this handler handles etype = hdlr.__name__[5:].lower() rv[etype] = hdlr(self.logger, self.setup, self.config) return rv def canVerify(self, entry): if not Bcfg2.Client.Tools.Tool.canVerify(self, entry): return False if not self._handlers[entry.get("type")].fully_specified(entry): self.logger.error('POSIX: Cannot verify incomplete entry %s. ' 'Try running bcfg2-lint.' % entry.get('name')) return False return True def canInstall(self, entry): """Check if entry is complete for installation.""" if not Bcfg2.Client.Tools.Tool.canInstall(self, entry): return False if not self._handlers[entry.get("type")].fully_specified(entry): self.logger.error('POSIX: Cannot install incomplete entry %s. ' 'Try running bcfg2-lint.' % entry.get('name')) return False return True def InstallPath(self, entry): """Dispatch install to the proper method according to type""" self.logger.debug("POSIX: Installing entry %s:%s:%s" % (entry.tag, entry.get("type"), entry.get("name"))) self._paranoid_backup(entry) return self._handlers[entry.get("type")].install(entry) def VerifyPath(self, entry, modlist): """Dispatch verify to the proper method according to type""" self.logger.debug("POSIX: Verifying entry %s:%s:%s" % (entry.tag, entry.get("type"), entry.get("name"))) ret = self._handlers[entry.get("type")].verify(entry, modlist) if self.setup['interactive'] and not ret: entry.set('qtext', '%s\nInstall %s %s: (y/N) ' % (entry.get('qtext', ''), entry.get('type'), entry.get('name'))) return ret def _prune_old_backups(self, entry): """ Remove old paranoid backup files """ bkupnam = entry.get('name').replace('/', '_') bkup_re = re.compile( bkupnam + r'_\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6}$') # current list of backups for this file try: bkuplist = [f for f in os.listdir(self.ppath) if bkup_re.match(f)] except OSError: err = sys.exc_info()[1] self.logger.error("POSIX: Failed to create backup list in %s: %s" % (self.ppath, err)) return bkuplist.sort() while len(bkuplist) >= int(self.max_copies): # remove the oldest backup available oldest = bkuplist.pop(0) self.logger.info("POSIX: Removing old backup %s" % oldest) try: os.remove(os.path.join(self.ppath, oldest)) except OSError: err = sys.exc_info()[1] self.logger.error("POSIX: Failed to remove old backup %s: %s" % (os.path.join(self.ppath, oldest), err)) def _paranoid_backup(self, entry): """ Take a backup of the specified entry for paranoid mode """ if (entry.get("paranoid", 'false').lower() == 'true' and self.setup.get("paranoid", False) and entry.get('current_exists', 'true') == 'true' and not os.path.isdir(entry.get("name"))): self._prune_old_backups(entry) bkupnam = "%s_%s" % (entry.get('name').replace('/', '_'), datetime.isoformat(datetime.now())) bfile = os.path.join(self.ppath, bkupnam) try: shutil.copy(entry.get('name'), bfile) self.logger.info("POSIX: Backup of %s saved to %s" % (entry.get('name'), bfile)) except IOError: err = sys.exc_info()[1] self.logger.error("POSIX: Failed to create backup file for " "%s: %s" % (entry.get('name'), err)) bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/POSIX/base.py000066400000000000000000000742531223671746500215150ustar00rootroot00000000000000""" Base class for tools that handle POSIX (Path) entries """ import os import sys import pwd import grp import stat import copy import shutil import Bcfg2.Client.Tools import Bcfg2.Client.XML from Bcfg2.Compat import oct_mode try: import selinux HAS_SELINUX = selinux.is_selinux_enabled() except ImportError: HAS_SELINUX = False try: import posix1e HAS_ACLS = True # map between permissions characters and numeric ACL constants ACL_MAP = dict(r=posix1e.ACL_READ, w=posix1e.ACL_WRITE, x=posix1e.ACL_EXECUTE) except ImportError: HAS_ACLS = False ACL_MAP = dict(r=4, w=2, x=1) # map between dev_type attribute and stat constants device_map = dict(block=stat.S_IFBLK, # pylint: disable=C0103 char=stat.S_IFCHR, fifo=stat.S_IFIFO) class POSIXTool(Bcfg2.Client.Tools.Tool): """ Base class for tools that handle POSIX (Path) entries """ def fully_specified(self, entry): # pylint: disable=W0613 """ return True if the entry is fully specified """ # checking is done by __req__ return True def verify(self, entry, modlist): # pylint: disable=W0613 """ return True if the entry is correct on disk """ if not self._verify_metadata(entry): return False if entry.get('recursive', 'false').lower() == 'true': # verify ownership information recursively for root, dirs, files in os.walk(entry.get('name')): for path in dirs + files: if not self._verify_metadata(entry, path=os.path.join(root, path)): return False return True def install(self, entry): """ Install the given entry. Return True on success. """ rv = True rv &= self._set_perms(entry) if entry.get('recursive', 'false').lower() == 'true': # set metadata recursively for root, dirs, files in os.walk(entry.get('name')): for path in dirs + files: rv &= self._set_perms(entry, path=os.path.join(root, path)) return rv def _remove(self, entry, recursive=True): """ Remove a Path entry, whatever that takes """ if os.path.islink(entry.get('name')): os.unlink(entry.get('name')) elif os.path.isdir(entry.get('name')): if recursive: shutil.rmtree(entry.get('name')) else: os.rmdir(entry.get('name')) else: os.unlink(entry.get('name')) def _exists(self, entry, remove=False): """ check for existing paths and optionally remove them. if the path exists, return the lstat of it """ try: ondisk = os.lstat(entry.get('name')) if remove: try: self._remove(entry) return None except OSError: err = sys.exc_info()[1] self.logger.warning('POSIX: Failed to unlink %s: %s' % (entry.get('name'), err)) return ondisk # probably still exists else: return ondisk except OSError: return None def _set_perms(self, entry, path=None): """ set permissions on the given entry, or on the given path according to the given entry """ if path is None: path = entry.get("name") rv = True if entry.get("owner") and entry.get("group"): try: self.logger.debug("POSIX: Setting ownership of %s to %s:%s" % (path, self._norm_entry_uid(entry), self._norm_entry_gid(entry))) os.chown(path, self._norm_entry_uid(entry), self._norm_entry_gid(entry)) except KeyError: self.logger.error('POSIX: Failed to change ownership of %s' % path) rv = False os.chown(path, 0, 0) except OSError: self.logger.error('POSIX: Failed to change ownership of %s' % path) rv = False if entry.get("mode"): wanted_mode = int(entry.get('mode'), 8) if entry.get('dev_type'): wanted_mode |= device_map[entry.get('dev_type')] try: self.logger.debug("POSIX: Setting mode on %s to %s" % (path, oct_mode(wanted_mode))) os.chmod(path, wanted_mode) except (OSError, KeyError): self.logger.error('POSIX: Failed to change mode on %s' % path) rv = False if entry.get('mtime'): try: os.utime(entry.get('name'), (int(entry.get('mtime')), int(entry.get('mtime')))) except OSError: self.logger.error("POSIX: Failed to set mtime of %s" % path) rv = False rv &= self._set_secontext(entry, path=path) rv &= self._set_acls(entry, path=path) return rv def _apply_acl(self, acl, path, atype=None): """ Apply the given ACL to the given path """ if atype is None: # the default value for atype is set this way (rather than # in the argument list) because posix1e libs may not be # installed, and this code is executed at run-time (and # thus will never be reached if ACLs aren't supported), # but argument lists are parsed at compile-time atype = posix1e.ACL_TYPE_ACCESS if atype == posix1e.ACL_TYPE_ACCESS: atype_str = "access" else: atype_str = "default" if acl.valid(): self.logger.debug("POSIX: Applying %s ACL to %s:" % (atype_str, path)) for line in str(acl).splitlines(): self.logger.debug(" " + line) try: acl.applyto(path, atype) return True except OSError: err = sys.exc_info()[1] self.logger.error("POSIX: Failed to set ACLs on %s: %s" % (path, err)) return False else: self.logger.warning("POSIX: %s ACL created for %s was invalid:" % (atype_str.title(), path)) for line in str(acl).splitlines(): self.logger.warning(" " + line) return False def _set_acls(self, entry, path=None): # pylint: disable=R0912 """ set POSIX ACLs on the file on disk according to the config """ if not HAS_ACLS: if entry.findall("ACL"): self.logger.debug("POSIX: ACLs listed for %s but no pylibacl " "library installed" % entry.get('name')) return True acls = self._list_entry_acls(entry) if path is None: path = entry.get("name") try: acl = posix1e.ACL(file=path) except IOError: err = sys.exc_info()[1] if err.errno == 95: # fs is mounted noacl if acls: self.logger.error("POSIX: Cannot set ACLs on filesystem " "mounted without ACL support: %s" % path) else: # no ACLs on the entry, no ACLs on the filesystem. # all is well in the world. return True else: self.logger.error("POSIX: Error getting current ACLS on %s: %s" % (path, err)) return False # clear ACLs out so we start fresh -- way easier than trying # to add/remove/modify ACLs for aclentry in acl: if aclentry.tag_type in [posix1e.ACL_USER, posix1e.ACL_GROUP]: acl.delete_entry(aclentry) if os.path.isdir(path): defacl = posix1e.ACL(filedef=path) if not defacl.valid(): # when a default ACL is queried on a directory that # has no default ACL entries at all, you get an empty # ACL, which is not valid. in this circumstance, we # just copy the access ACL to get a base valid ACL # that we can add things to. defacl = posix1e.ACL(acl=acl) else: for aclentry in defacl: if aclentry.tag_type in [posix1e.ACL_USER, posix1e.ACL_GROUP]: defacl.delete_entry(aclentry) else: defacl = None if not acls: self.logger.debug("POSIX: Removed ACLs from %s" % entry.get("name")) return True for aclkey, perms in acls.items(): atype, scope, qualifier = aclkey if atype == "default": if defacl is None: self.logger.warning("POSIX: Cannot set default ACLs on " "non-directory %s" % path) continue aclentry = posix1e.Entry(defacl) else: aclentry = posix1e.Entry(acl) for perm in ACL_MAP.values(): if perm & perms: aclentry.permset.add(perm) aclentry.tag_type = scope try: if scope == posix1e.ACL_USER: scopename = "user" aclentry.qualifier = self._norm_uid(qualifier) elif scope == posix1e.ACL_GROUP: scopename = "group" aclentry.qualifier = self._norm_gid(qualifier) except (OSError, KeyError): err = sys.exc_info()[1] self.logger.error("POSIX: Could not resolve %s %s: %s" % (scopename, qualifier, err)) continue acl.calc_mask() rv = self._apply_acl(acl, path) if defacl: defacl.calc_mask() rv &= self._apply_acl(defacl, path, posix1e.ACL_TYPE_DEFAULT) return rv def _set_secontext(self, entry, path=None): """ set the SELinux context of the file on disk according to the config""" if not HAS_SELINUX: return True if path is None: path = entry.get("name") context = entry.get("secontext") if not context: # no context listed return True if context == '__default__': try: selinux.restorecon(path) rv = True except OSError: err = sys.exc_info()[1] self.logger.error("POSIX: Failed to restore SELinux context " "for %s: %s" % (path, err)) rv = False else: try: rv = selinux.lsetfilecon(path, context) == 0 except OSError: err = sys.exc_info()[1] self.logger.error("POSIX: Failed to restore SELinux context " "for %s: %s" % (path, err)) rv = False return rv def _norm_gid(self, gid): """ This takes a group name or gid and returns the corresponding gid. """ try: return int(gid) except ValueError: return int(grp.getgrnam(gid)[2]) def _norm_entry_gid(self, entry): """ Given an entry, return the GID number of the desired group """ try: return self._norm_gid(entry.get('group')) except (OSError, KeyError): err = sys.exc_info()[1] self.logger.error('POSIX: GID normalization failed for %s on %s: ' '%s' % (entry.get('group'), entry.get('name'), err)) return 0 def _norm_uid(self, uid): """ This takes a username or uid and returns the corresponding uid. """ try: return int(uid) except ValueError: return int(pwd.getpwnam(uid)[2]) def _norm_entry_uid(self, entry): """ Given an entry, return the UID number of the desired owner """ try: return self._norm_uid(entry.get("owner")) except (OSError, KeyError): err = sys.exc_info()[1] self.logger.error('POSIX: UID normalization failed for %s on %s: ' '%s' % (entry.get('owner'), entry.get('name'), err)) return 0 def _norm_acl_perms(self, perms): """ takes a representation of an ACL permset and returns a digit representing the permissions entailed by it. representations can either be a single octal digit, a string of up to three 'r', 'w', 'x', or '-' characters, or a posix1e.Permset object""" if perms is None: return 0 elif hasattr(perms, 'test'): # Permset object return sum([p for p in ACL_MAP.values() if perms.test(p)]) try: # single octal digit rv = int(perms) if rv > 0 and rv < 8: return rv else: self.logger.error("POSIX: Permissions digit out of range in " "ACL: %s" % perms) return 0 except ValueError: # couldn't be converted to an int; process as a string if len(perms) > 3: self.logger.error("POSIX: Permissions string too long in ACL: " "%s" % perms) return 0 rv = 0 for char in perms: if char == '-': continue elif char not in ACL_MAP: self.logger.warning("POSIX: Unknown permissions character " "in ACL: %s" % char) elif rv & ACL_MAP[char]: self.logger.warning("POSIX: Duplicate permissions " "character in ACL: %s" % perms) else: rv |= ACL_MAP[char] return rv def _acl2string(self, aclkey, perms): """ Get a string representation of the given ACL. aclkey must be a tuple of (, , ) """ atype, scope, qualifier = aclkey acl_str = [] if atype == 'default': acl_str.append(atype) if scope == posix1e.ACL_USER: acl_str.append("user") elif scope == posix1e.ACL_GROUP: acl_str.append("group") acl_str.append(qualifier) acl_str.append(self._acl_perm2string(perms)) return ":".join(acl_str) def _acl_perm2string(self, perm): """ Turn an octal permissions integer into a string suitable for use with ACLs """ rv = [] for char in 'rwx': if ACL_MAP[char] & perm: rv.append(char) else: rv.append('-') return ''.join(rv) def _gather_data(self, path): """ Get data on the existing state of -- e.g., whether or not it exists, owner, group, permissions, etc. """ try: ondisk = os.stat(path) except OSError: self.logger.debug("POSIX: %s does not exist" % path) return (False, None, None, None, None, None) try: owner = str(ondisk[stat.ST_UID]) except OSError: err = sys.exc_info()[1] self.logger.debug("POSIX: Could not get current owner of %s: %s" % (path, err)) owner = None except KeyError: self.logger.error('POSIX: User resolution failed for %s' % path) owner = None try: group = str(ondisk[stat.ST_GID]) except (OSError, KeyError): err = sys.exc_info()[1] self.logger.debug("POSIX: Could not get current group of %s: %s" % (path, err)) group = None except KeyError: self.logger.error('POSIX: Group resolution failed for %s' % path) group = None try: mode = oct_mode(ondisk[stat.ST_MODE])[-4:] except (OSError, KeyError, TypeError): err = sys.exc_info()[1] self.logger.debug("POSIX: Could not get current permissions of " "%s: %s" % (path, err)) mode = None if HAS_SELINUX: try: secontext = selinux.getfilecon(path)[1].split(":")[2] except (OSError, KeyError): err = sys.exc_info()[1] self.logger.debug("POSIX: Could not get current SELinux " "context of %s: %s" % (path, err)) secontext = None else: secontext = None if HAS_ACLS: acls = self._list_file_acls(path) else: acls = None return (ondisk, owner, group, mode, secontext, acls) def _verify_metadata(self, entry, path=None): # pylint: disable=R0912 """ generic method to verify mode, owner, group, secontext, acls, and mtime """ # allow setting an alternate path for recursive permissions checking if path is None: path = entry.get('name') attrib = dict() ondisk, attrib['current_owner'], attrib['current_group'], \ attrib['current_mode'], attrib['current_secontext'] = \ self._gather_data(path)[0:5] if not ondisk: entry.set('current_exists', 'false') return False # we conditionally verify every bit of metadata only if it's # specified on the entry. consequently, canVerify() and # fully_specified() are preconditions of _verify_metadata(), # since they will ensure that everything that needs to be # specified actually is. this lets us gracefully handle # symlink and hardlink entries, which have SELinux contexts # but not other permissions, optional secontext and mtime # attrs, and so on. wanted_owner, wanted_group, wanted_mode, mtime = None, None, None, -1 if entry.get('mtime', '-1') != '-1': mtime = str(ondisk[stat.ST_MTIME]) if entry.get("owner"): wanted_owner = str(self._norm_entry_uid(entry)) if entry.get("group"): wanted_group = str(self._norm_entry_gid(entry)) if entry.get("mode"): while len(entry.get('mode', '')) < 4: entry.set('mode', '0' + entry.get('mode', '')) wanted_mode = int(entry.get('mode'), 8) errors = [] if wanted_owner and attrib['current_owner'] != wanted_owner: errors.append("Owner for path %s is incorrect. " "Current owner is %s but should be %s" % (path, attrib['current_owner'], entry.get('owner'))) if wanted_group and attrib['current_group'] != wanted_group: errors.append("Group for path %s is incorrect. " "Current group is %s but should be %s" % (path, attrib['current_group'], entry.get('group'))) if (wanted_mode and oct_mode(int(attrib['current_mode'], 8)) != oct_mode(wanted_mode)): errors.append("Permissions for path %s are incorrect. " "Current permissions are %s but should be %s" % (path, attrib['current_mode'], entry.get('mode'))) if entry.get('mtime'): attrib['current_mtime'] = mtime if mtime != entry.get('mtime', '-1'): errors.append("mtime for path %s is incorrect. " "Current mtime is %s but should be %s" % (path, mtime, entry.get('mtime'))) if HAS_SELINUX: wanted_secontext = None if entry.get("secontext") == "__default__": try: wanted_secontext = \ selinux.matchpathcon( path, ondisk[stat.ST_MODE])[1].split(":")[2] except OSError: errors.append("%s has no default SELinux context" % entry.get("name")) else: wanted_secontext = entry.get("secontext") if (wanted_secontext and attrib['current_secontext'] != wanted_secontext): errors.append("SELinux context for path %s is incorrect. " "Current context is %s but should be %s" % (path, attrib['current_secontext'], wanted_secontext)) if errors: for error in errors: self.logger.debug("POSIX: " + error) entry.set('qtext', "\n".join([entry.get('qtext', '')] + errors)) if path == entry.get("name"): for attr, val in attrib.items(): if val is not None: entry.set(attr, str(val)) return self._verify_acls(entry, path=path) and len(errors) == 0 def _list_entry_acls(self, entry): """ Given an entry, get a dict of POSIX ACLs described in that entry. """ wanted = dict() for acl in entry.findall("ACL"): if acl.get("scope") == "user": scope = posix1e.ACL_USER elif acl.get("scope") == "group": scope = posix1e.ACL_GROUP else: self.logger.error("POSIX: Unknown ACL scope %s" % acl.get("scope")) continue if acl.get('perms') is None: self.logger.error("POSIX: No permissions set for ACL: %s" % Bcfg2.Client.XML.tostring(acl)) continue wanted[(acl.get("type"), scope, acl.get(acl.get("scope")))] = \ self._norm_acl_perms(acl.get('perms')) return wanted def _list_file_acls(self, path): """ Given a path, get a dict of existing POSIX ACLs on that path. The dict keys are a tuple of (, , . values are the permissions of the described ACL. """ def _process_acl(acl, atype): """ Given an ACL object, process it appropriately and add it to the return value """ try: if acl.tag_type == posix1e.ACL_USER: qual = pwd.getpwuid(acl.qualifier)[0] elif acl.tag_type == posix1e.ACL_GROUP: qual = grp.getgrgid(acl.qualifier)[0] else: return except (OSError, KeyError): err = sys.exc_info()[1] self.logger.error("POSIX: Lookup of %s %s failed: %s" % (atype, acl.qualifier, err)) qual = acl.qualifier existing[(atype, acl.tag_type, qual)] = \ self._norm_acl_perms(acl.permset) existing = dict() try: for acl in posix1e.ACL(file=path): _process_acl(acl, "access") except IOError: err = sys.exc_info()[1] if err.errno == 95: # fs is mounted noacl self.logger.debug("POSIX: Filesystem mounted without ACL " "support: %s" % path) else: self.logger.error("POSIX: Error getting current ACLS on %s: %s" % (path, err)) return existing if os.path.isdir(path): for acl in posix1e.ACL(filedef=path): _process_acl(acl, "default") return existing def _verify_acls(self, entry, path=None): """ verify POSIX ACLs on the given entry. return True if all ACLS are correct, false otherwise """ if not HAS_ACLS: if entry.findall("ACL"): self.logger.debug("POSIX: ACLs listed for %s but no pylibacl " "library installed" % entry.get('name')) return True if path is None: path = entry.get("name") # create lists of normalized representations of the ACLs we want # and the ACLs we have. this will make them easier to compare # than trying to mine that data out of the ACL objects and XML # objects and compare it at the same time. wanted = self._list_entry_acls(entry) existing = self._list_file_acls(path) missing = [] extra = [] wrong = [] for aclkey, perms in wanted.items(): if aclkey not in existing: missing.append(self._acl2string(aclkey, perms)) elif existing[aclkey] != perms: wrong.append((self._acl2string(aclkey, perms), self._acl2string(aclkey, existing[aclkey]))) if path == entry.get("name"): atype, scope, qual = aclkey aclentry = Bcfg2.Client.XML.Element("ACL", type=atype, perms=str(perms)) if scope == posix1e.ACL_USER: aclentry.set("scope", "user") elif scope == posix1e.ACL_GROUP: aclentry.set("scope", "group") else: self.logger.debug("POSIX: Unknown ACL scope %s on %s" % (scope, path)) continue aclentry.set(aclentry.get("scope"), qual) entry.append(aclentry) for aclkey, perms in existing.items(): if aclkey not in wanted: extra.append(self._acl2string(aclkey, perms)) msg = [] if missing: msg.append("%s ACLs are missing: %s" % (len(missing), ", ".join(missing))) if wrong: msg.append("%s ACLs are wrong: %s" % (len(wrong), "; ".join(["%s should be %s" % (e, w) for w, e in wrong]))) if extra: msg.append("%s extra ACLs: %s" % (len(extra), ", ".join(extra))) if msg: msg.insert(0, "POSIX: ACLs for %s are incorrect." % path) self.logger.debug(msg[0]) for line in msg[1:]: self.logger.debug(" " + line) entry.set('qtext', "\n".join([entry.get("qtext", '')] + msg)) return False return True def _makedirs(self, entry, path=None): """ os.makedirs helpfully creates all parent directories for us, but it sets permissions according to umask, which is probably wrong. we need to find out which directories were created and try to set permissions on those (http://trac.mcs.anl.gov/projects/bcfg2/ticket/1125 and http://trac.mcs.anl.gov/projects/bcfg2/ticket/1134) """ created = [] if path is None: path = entry.get("name") cur = path while cur and cur != '/': if not os.path.exists(cur): created.append(cur) cur = os.path.dirname(cur) rv = True try: os.makedirs(path) except OSError: err = sys.exc_info()[1] self.logger.error('POSIX: Failed to create directory %s: %s' % (path, err)) rv = False # set auto-created directories to mode 755 and use best effort for # permissions. If you need something else, you should specify it in # your config. tmpentry = copy.deepcopy(entry) tmpentry.set('mode', '0755') for acl in tmpentry.findall('ACL'): acl.set('perms', oct_mode(self._norm_acl_perms(acl.get('perms')) | ACL_MAP['x'])) for cpath in created: self._set_perms(tmpentry, path=cpath) return rv class POSIXLinkTool(POSIXTool): """ Base handler for link (symbolic and hard) entries """ __req__ = ['name', 'to'] __linktype__ = None def verify(self, entry, modlist): rv = True try: if not self._verify(entry): msg = "%s %s is incorrect" % (self.__linktype__.title(), entry.get('name')) self.logger.debug("POSIX: " + msg) entry.set('qtext', "\n".join([entry.get('qtext', ''), msg])) rv = False except OSError: self.logger.debug("POSIX: %s %s does not exist" % (entry.tag, entry.get("name"))) entry.set('current_exists', 'false') return False return POSIXTool.verify(self, entry, modlist) and rv def _verify(self, entry): """ perform actual verification of the link entry """ raise NotImplementedError def install(self, entry): ondisk = self._exists(entry, remove=True) if ondisk: self.logger.info("POSIX: %s %s cleanup failed" % (self.__linktype__.title(), entry.get('name'))) try: self._link(entry) rv = True except OSError: err = sys.exc_info()[1] self.logger.error("POSIX: Failed to create %s %s to %s: %s" % (self.__linktype__, entry.get('name'), entry.get('to'), err)) rv = False return POSIXTool.install(self, entry) and rv def _link(self, entry): """ create the link """ raise NotImplementedError bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/POSIXUsers.py000066400000000000000000000322761223671746500216240ustar00rootroot00000000000000""" A tool to handle creating users and groups with useradd/mod/del and groupadd/mod/del """ import pwd import grp import Bcfg2.Client.XML import Bcfg2.Client.Tools from Bcfg2.Utils import PackedDigitRange class POSIXUsers(Bcfg2.Client.Tools.Tool): """ A tool to handle creating users and groups with useradd/mod/del and groupadd/mod/del """ __execs__ = ['/usr/sbin/useradd', '/usr/sbin/usermod', '/usr/sbin/userdel', '/usr/sbin/groupadd', '/usr/sbin/groupmod', '/usr/sbin/groupdel'] __handles__ = [('POSIXUser', None), ('POSIXGroup', None)] __req__ = dict(POSIXUser=['name'], POSIXGroup=['name']) experimental = True #: A mapping of XML entry attributes to the indexes of #: corresponding values in the get{pw|gr}all data structures attr_mapping = dict(POSIXUser=dict(name=0, uid=2, gecos=4, home=5, shell=6), POSIXGroup=dict(name=0, gid=2)) #: A mapping that describes the attribute name of the id of a given #: user or group id_mapping = dict(POSIXUser="uid", POSIXGroup="gid") def __init__(self, logger, setup, config): Bcfg2.Client.Tools.Tool.__init__(self, logger, setup, config) self.set_defaults = dict(POSIXUser=self.populate_user_entry, POSIXGroup=lambda g: g) self._existing = None self._whitelist = dict(POSIXUser=None, POSIXGroup=None) self._blacklist = dict(POSIXUser=None, POSIXGroup=None) if self.setup['posix_uid_whitelist']: self._whitelist['POSIXUser'] = \ PackedDigitRange(*self.setup['posix_uid_whitelist']) else: self._blacklist['POSIXUser'] = \ PackedDigitRange(*self.setup['posix_uid_blacklist']) if self.setup['posix_gid_whitelist']: self._whitelist['POSIXGroup'] = \ PackedDigitRange(*self.setup['posix_gid_whitelist']) else: self._blacklist['POSIXGroup'] = \ PackedDigitRange(*self.setup['posix_gid_blacklist']) @property def existing(self): """ Get a dict of existing users and groups """ if self._existing is None: self._existing = dict(POSIXUser=dict([(u[0], u) for u in pwd.getpwall()]), POSIXGroup=dict([(g[0], g) for g in grp.getgrall()])) return self._existing def _in_managed_range(self, tag, eid): """ Check if the given uid or gid is in the appropriate managed range. This means that either a) a whitelist is defined, and the uid/gid is in that whitelist; or b) no whitelist is defined, and the uid/gid is not in the blacklist. """ if self._whitelist[tag] is None: return eid not in self._blacklist[tag] else: return eid in self._whitelist[tag] def canInstall(self, entry): if not Bcfg2.Client.Tools.Tool.canInstall(self, entry): return False eid = entry.get(self.id_mapping[entry.tag]) if eid is not None and not self._in_managed_range(entry.tag, eid): if self._whitelist[entry.tag] is not None: err = "not in whitelist" else: # blacklisted err = "in blacklist" self.logger.debug("%s: %s %s %s: %s" % (self.primarykey(entry), err, self.id_mapping[entry.tag], eid, self._blacklist[entry.tag])) return False return True def Inventory(self, states, structures=None): if not structures: structures = self.config.getchildren() # we calculate a list of all POSIXUser and POSIXGroup entries, # and then add POSIXGroup entries that are required to create # the primary group for each user to the structures. this is # sneaky and possibly evil, but it works great. groups = [] for struct in structures: groups.extend([e.get("name") for e in struct.findall("POSIXGroup")]) for struct in structures: for entry in struct.findall("POSIXUser"): group = self.set_defaults[entry.tag](entry).get('group') if group and group not in groups: self.logger.debug("POSIXUsers: Adding POSIXGroup entry " "'%s' for user '%s'" % (group, entry.get("name"))) struct.append(Bcfg2.Client.XML.Element("POSIXGroup", name=group)) return Bcfg2.Client.Tools.Tool.Inventory(self, states, structures) def FindExtra(self): extra = [] for handles in self.__handles__: tag = handles[0] specified = [] for entry in self.getSupportedEntries(): if entry.tag == tag: specified.append(entry.get("name")) for name, data in self.existing[tag].items(): eid = data[self.attr_mapping[tag][self.id_mapping[tag]]] if name not in specified and self._in_managed_range(tag, eid): extra.append(Bcfg2.Client.XML.Element(tag, name=name)) return extra def populate_user_entry(self, entry): """ Given a POSIXUser entry, set all of the 'missing' attributes with their defaults """ defaults = dict(group=entry.get('name'), gecos=entry.get('name'), shell='/bin/bash') if entry.get('name') == 'root': defaults['home'] = '/root' else: defaults['home'] = '/home/%s' % entry.get('name') for key, val in defaults.items(): if entry.get(key) is None: entry.set(key, val) if entry.get('group') in self.existing['POSIXGroup']: entry.set('gid', str(self.existing['POSIXGroup'][entry.get('group')][2])) return entry def user_supplementary_groups(self, entry): """ Get a list of supplmentary groups that the user in the given entry is a member of """ return [g for g in self.existing['POSIXGroup'].values() if entry.get("name") in g[3] and g[0] != entry.get("group")] def VerifyPOSIXUser(self, entry, _): """ Verify a POSIXUser entry """ rv = self._verify(self.populate_user_entry(entry)) if entry.get("current_exists", "true") == "true": # verify supplemental groups actual = [g[0] for g in self.user_supplementary_groups(entry)] expected = [e.get("group", e.text).strip() for e in entry.findall("MemberOf")] if set(expected) != set(actual): entry.set('qtext', "\n".join([entry.get('qtext', '')] + ["%s %s has incorrect supplemental group " "membership. Currently: %s. Should be: %s" % (entry.tag, entry.get("name"), actual, expected)])) rv = False if self.setup['interactive'] and not rv: entry.set('qtext', '%s\nInstall %s %s: (y/N) ' % (entry.get('qtext', ''), entry.tag, entry.get('name'))) return rv def VerifyPOSIXGroup(self, entry, _): """ Verify a POSIXGroup entry """ rv = self._verify(entry) if self.setup['interactive'] and not rv: entry.set('qtext', '%s\nInstall %s %s: (y/N) ' % (entry.get('qtext', ''), entry.tag, entry.get('name'))) return rv def _verify(self, entry): """ Perform most of the actual work of verification """ errors = [] if entry.get("name") not in self.existing[entry.tag]: entry.set('current_exists', 'false') errors.append("%s %s does not exist" % (entry.tag, entry.get("name"))) else: for attr, idx in self.attr_mapping[entry.tag].items(): val = str(self.existing[entry.tag][entry.get("name")][idx]) entry.set("current_%s" % attr, val.decode(self.setup['encoding'])) if attr in ["uid", "gid"]: if entry.get(attr) is None: # no uid/gid specified, so we let the tool # automatically determine one -- i.e., it always # verifies continue entval = entry.get(attr) if not isinstance(entval, str): entval = entval.encode('utf-8') if val != entval: errors.append("%s for %s %s is incorrect. Current %s is " "%s, but should be %s" % (attr.title(), entry.tag, entry.get("name"), attr, val, entry.get(attr))) if errors: for error in errors: self.logger.debug("%s: %s" % (self.name, error)) entry.set('qtext', "\n".join([entry.get('qtext', '')] + errors)) return len(errors) == 0 def Install(self, entries, states): for entry in entries: # install groups first, so that all groups exist for # users that might need them if entry.tag == 'POSIXGroup': states[entry] = self._install(entry) for entry in entries: if entry.tag == 'POSIXUser': states[entry] = self._install(entry) self._existing = None def _install(self, entry): """ add or modify a user or group using the appropriate command """ if entry.get("name") not in self.existing[entry.tag]: action = "add" else: action = "mod" rv = self.cmd.run(self._get_cmd(action, self.set_defaults[entry.tag](entry))) if rv.success: self.modified.append(entry) else: self.logger.error("POSIXUsers: Error creating %s %s: %s" % (entry.tag, entry.get("name"), rv.error)) return rv.success def _get_cmd(self, action, entry): """ Get a command to perform the appropriate action (add, mod, del) on the given entry. The command is always the same; we set all attributes on a given user or group when modifying it rather than checking which ones need to be changed. This makes things fail as a unit (e.g., if a user is logged in, you can't change its home dir, but you could change its GECOS, but the whole operation fails), but it also makes this function a lot, lot easier and simpler.""" cmd = ["/usr/sbin/%s%s" % (entry.tag[5:].lower(), action)] if action != 'del': if entry.tag == 'POSIXGroup': if entry.get('gid'): cmd.extend(['-g', entry.get('gid')]) elif entry.tag == 'POSIXUser': if entry.get('uid'): cmd.extend(['-u', entry.get('uid')]) cmd.extend(['-g', entry.get('group')]) extras = [e.get("group", e.text).strip() for e in entry.findall("MemberOf")] if extras: cmd.extend(['-G', ",".join(extras)]) cmd.extend(['-d', entry.get('home')]) cmd.extend(['-s', entry.get('shell')]) cmd.extend(['-c', entry.get('gecos')]) cmd.append(entry.get('name')) return cmd def Remove(self, entries): for entry in entries: # remove users first, so that all users have been removed # from groups before we remove them if entry.tag == 'POSIXUser': self._remove(entry) for entry in entries: if entry.tag == 'POSIXGroup': try: grp.getgrnam(entry.get("name")) self._remove(entry) except KeyError: # at least some versions of userdel automatically # remove the primary group for a user if the group # name is the same as the username, and no other # users are in the group self.logger.info("POSIXUsers: Group %s does not exist. " "It may have already been removed when " "its users were deleted" % entry.get("name")) self._existing = None self.extra = self.FindExtra() def _remove(self, entry): """ Remove an entry """ rv = self.cmd.run(self._get_cmd("del", entry)) if not rv.success: self.logger.error("POSIXUsers: Error deleting %s %s: %s" % (entry.tag, entry.get("name"), rv.error)) return rv.success bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/Pacman.py000066400000000000000000000062421223671746500210710ustar00rootroot00000000000000"""This is the bcfg2 support for pacman""" import sys import Bcfg2.Client.Tools class Pacman(Bcfg2.Client.Tools.PkgTool): '''Archlinux package support''' name = 'Pacman' __execs__ = ["/usr/bin/pacman"] __handles__ = [('Package', 'pacman')] __req__ = {'Package': ['name', 'version']} pkgtype = 'pacman' pkgtool = ("/usr/bin/pacman --needed --noconfirm --noprogressbar") def __init__(self, logger, setup, config): Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config) self.installed = {} self.RefreshPackages() def RefreshPackages(self): '''Refresh memory hashes of packages''' self.installed = {} for pkg in self.cmd.run("/usr/bin/pacman -Q").stdout.splitlines(): pkgname = pkg.split(' ')[0].strip() version = pkg.split(' ')[1].strip() #self.logger.info(" pkgname: %s, version: %s" % (pkgname, version)) self.installed[pkgname] = version def VerifyPackage(self, entry, _): '''Verify Package status for entry''' self.logger.info("VerifyPackage: %s : %s" % (entry.get('name'), entry.get('version'))) if not 'version' in entry.attrib: self.logger.info("Cannot verify unversioned package %s" % entry.attrib['name']) return False if entry.attrib['name'] in self.installed: if entry.attrib['version'] == 'auto': return True elif self.installed[entry.attrib['name']] == \ entry.attrib['version']: #if not self.setup['quick'] and \ # entry.get('verify', 'true') == 'true': #FIXME: need to figure out if pacman # allows you to verify packages return True else: entry.set('current_version', self.installed[entry.get('name')]) self.logger.info("attribname: %s" % (entry.attrib['name'])) self.logger.info("attribname: %s" % (entry.attrib['name'])) return False entry.set('current_exists', 'false') self.logger.info("attribname: %s" % (entry.attrib['name'])) return False def Remove(self, packages): '''Remove extra packages''' names = [pkg.get('name') for pkg in packages] self.logger.info("Removing packages: %s" % " ".join(names)) self.cmd.run("%s --noconfirm --noprogressbar -R %s" % (self.pkgtool, " ".join(names))) self.RefreshPackages() self.extra = self.FindExtra() def Install(self, packages, states): ''' Pacman Install ''' pkgline = "" for pkg in packages: pkgline += " " + pkg.get('name') self.logger.info("packages : " + pkgline) try: self.logger.debug("Running : %s -S %s" % (self.pkgtool, pkgline)) self.cmd.run("%s -S %s" % (self.pkgtool, pkgline)) except: # pylint: disable=W0702 err = sys.exc_info()[1] self.logger.error("Error occurred during installation: %s" % err) bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/Portage.py000066400000000000000000000103221223671746500212650ustar00rootroot00000000000000"""This is the Bcfg2 tool for the Gentoo Portage system.""" import re import Bcfg2.Client.Tools class Portage(Bcfg2.Client.Tools.PkgTool): """The Gentoo toolset implements package and service operations and inherits the rest from Toolset.Toolset.""" name = 'Portage' __execs__ = ['/usr/bin/emerge', '/usr/bin/equery'] __handles__ = [('Package', 'ebuild')] __req__ = {'Package': ['name', 'version']} pkgtype = 'ebuild' # requires a working PORTAGE_BINHOST in make.conf _binpkgtool = ('emerge --getbinpkgonly %s', ('=%s-%s', ['name', 'version'])) pkgtool = ('emerge %s', ('=%s-%s', ['name', 'version'])) def __init__(self, logger, cfg, setup): self._initialised = False Bcfg2.Client.Tools.PkgTool.__init__(self, logger, cfg, setup) self._initialised = True self.__important__ = self.__important__ + ['/etc/make.conf'] self._pkg_pattern = re.compile(r'(.*)-(\d.*)') self._ebuild_pattern = re.compile('(ebuild|binary)') self.cfg = cfg self.installed = {} self._binpkgonly = self.setup.get('portage_binpkgonly', False) if self._binpkgonly: self.pkgtool = self._binpkgtool self.RefreshPackages() def RefreshPackages(self): """Refresh memory hashes of packages.""" if not self._initialised: return self.logger.info('Getting list of installed packages') self.installed = {} for pkg in self.cmd.run(["equery", "-q", "list", "*"]).stdout.splitlines(): if self._pkg_pattern.match(pkg): name = self._pkg_pattern.match(pkg).group(1) version = self._pkg_pattern.match(pkg).group(2) self.installed[name] = version else: self.logger.info("Failed to parse pkg name %s" % pkg) def VerifyPackage(self, entry, modlist): """Verify package for entry.""" if not 'version' in entry.attrib: self.logger.info("Cannot verify unversioned package %s" % (entry.get('name'))) return False if not (entry.get('name') in self.installed): # Can't verify package that isn't installed entry.set('current_exists', 'false') return False # get the installed version version = self.installed[entry.get('name')] entry.set('current_version', version) if not self.setup['quick']: if ('verify' not in entry.attrib or entry.get('verify').lower() == 'true'): # Check the package if: # - Not running in quick mode # - No verify option is specified in the literal configuration # OR # - Verify option is specified and is true self.logger.debug('Running equery check on %s' % entry.get('name')) for line in self.cmd.run( ["/usr/bin/equery", "-N", "check", '=%s-%s' % (entry.get('name'), entry.get('version'))]).stdout.splitlines(): if '!!!' in line and line.split()[1] not in modlist: return False # By now the package must be in one of the following states: # - Not require checking # - Have no files modified at all # - Have modified files in the modlist only if self.installed[entry.get('name')] == version: # Specified package version is installed # Specified package version may be any in literal configuration return True # Something got skipped. Indicates a bug return False def Remove(self, packages): """Deal with extra configuration detected.""" pkgnames = " ".join([pkg.get('name') for pkg in packages]) if len(packages) > 0: self.logger.info('Removing packages:') self.logger.info(pkgnames) self.cmd.run("emerge --unmerge --quiet %s" % " ".join(pkgnames.split(' '))) self.RefreshPackages() self.extra = self.FindExtra() bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/RPM.py000066400000000000000000001456001223671746500203320ustar00rootroot00000000000000"""Bcfg2 Support for RPMS""" import os.path import rpm import rpmtools import Bcfg2.Client.Tools class RPM(Bcfg2.Client.Tools.PkgTool): """Support for RPM packages.""" __execs__ = ['/bin/rpm', '/var/lib/rpm'] __handles__ = [('Package', 'rpm')] __req__ = {'Package': ['name', 'version']} __ireq__ = {'Package': ['url']} __new_req__ = {'Package': ['name'], 'Instance': ['version', 'release', 'arch']} __new_ireq__ = {'Package': ['uri'], \ 'Instance': ['simplefile']} __gpg_req__ = {'Package': ['name', 'version']} __gpg_ireq__ = {'Package': ['name', 'version']} __new_gpg_req__ = {'Package': ['name'], 'Instance': ['version', 'release']} __new_gpg_ireq__ = {'Package': ['name'], 'Instance': ['version', 'release']} conflicts = ['RPMng'] pkgtype = 'rpm' pkgtool = ("rpm --oldpackage --replacepkgs --quiet -U %s", ("%s", ["url"])) def __init__(self, logger, setup, config): Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config) # create a global ignore list used when ignoring particular # files during package verification self.ignores = [entry.get('name') for struct in config for entry in struct \ if entry.get('type') == 'ignore'] self.instance_status = {} self.extra_instances = [] self.modlists = {} self.gpg_keyids = self.getinstalledgpg() opt_prefix = self.name.lower() self.installOnlyPkgs = self.setup["%s_installonly" % opt_prefix] if 'gpg-pubkey' not in self.installOnlyPkgs: self.installOnlyPkgs.append('gpg-pubkey') self.erase_flags = self.setup['%s_erase_flags' % opt_prefix] self.pkg_checks = self.setup['%s_pkg_checks' % opt_prefix] self.pkg_verify = self.setup['%s_pkg_verify' % opt_prefix] self.installed_action = self.setup['%s_installed_action' % opt_prefix] self.version_fail_action = self.setup['%s_version_fail_action' % opt_prefix] self.verify_fail_action = self.setup['%s_verify_fail_action' % opt_prefix] self.verify_flags = self.setup['%s_verify_flags' % opt_prefix] if '' in self.verify_flags: self.verify_flags.remove('') self.logger.debug('%s: installOnlyPackages = %s' % (self.name, self.installOnlyPkgs)) self.logger.debug('%s: erase_flags = %s' % (self.name, self.erase_flags)) self.logger.debug('%s: pkg_checks = %s' % (self.name, self.pkg_checks)) self.logger.debug('%s: pkg_verify = %s' % (self.name, self.pkg_verify)) self.logger.debug('%s: installed_action = %s' % (self.name, self.installed_action)) self.logger.debug('%s: version_fail_action = %s' % (self.name, self.version_fail_action)) self.logger.debug('%s: verify_fail_action = %s' % (self.name, self.verify_fail_action)) self.logger.debug('%s: verify_flags = %s' % (self.name, self.verify_flags)) # Force a re- prelink of all packages if prelink exists. # Many, if not most package verifies can be caused by out of # date prelinking. if os.path.isfile('/usr/sbin/prelink') and not self.setup['dryrun']: rv = self.cmd.run('/usr/sbin/prelink -a -mR') if rv.success: self.logger.debug('Pre-emptive prelink succeeded') else: # FIXME : this is dumb - what if the output is huge? self.logger.error('Pre-emptive prelink failed: %s' % rv.error) def RefreshPackages(self): """ Creates self.installed{} which is a dict of installed packages. The dict items are lists of nevra dicts. This loosely matches the config from the server and what rpmtools uses to specify pacakges. e.g. self.installed['foo'] = [ {'name':'foo', 'epoch':None, 'version':'1', 'release':2, 'arch':'i386'}, {'name':'foo', 'epoch':None, 'version':'1', 'release':2, 'arch':'x86_64'} ] """ self.installed = {} refresh_ts = rpmtools.rpmtransactionset() # Don't bother with signature checks at this stage. The GPG keys might # not be installed. refresh_ts.setVSFlags(rpm._RPMVSF_NODIGESTS|rpm._RPMVSF_NOSIGNATURES) for nevra in rpmtools.rpmpackagelist(refresh_ts): self.installed.setdefault(nevra['name'], []).append(nevra) if self.setup['debug']: print("The following package instances are installed:") for name, instances in list(self.installed.items()): self.logger.debug(" " + name) for inst in instances: self.logger.debug(" %s" %self.str_evra(inst)) refresh_ts.closeDB() del refresh_ts def VerifyPackage(self, entry, modlist, pinned_version=None): """ Verify Package status for entry. Performs the following: - Checks for the presence of required Package Instances. - Compares the evra 'version' info against self.installed{}. - RPM level package verify (rpm --verify). - Checks for the presence of unrequired package instances. Produces the following dict and list for RPM.Install() to use: For installs/upgrades/fixes of required instances: instance_status = { : { 'installed': True|False, 'version_fail': True|False, 'verify_fail': True|False, 'pkg': , 'modlist': [ , ... ], 'verify' : [ ] }, ...... } For deletions of unrequired instances: extra_instances = [ , ..... ] Constructs the text prompts for interactive mode. """ instances = [inst for inst in entry if inst.tag == 'Instance' or inst.tag == 'Package'] if instances == []: # We have an old style no Instance entry. Convert it to new style. instance = Bcfg2.Client.XML.SubElement(entry, 'Package') for attrib in list(entry.attrib.keys()): instance.attrib[attrib] = entry.attrib[attrib] if (self.pkg_checks and entry.get('pkg_checks', 'true').lower() == 'true'): if 'any' in [entry.get('version'), pinned_version]: version, release = 'any', 'any' elif entry.get('version') == 'auto': if pinned_version != None: version, release = pinned_version.split('-') else: return False else: version, release = entry.get('version').split('-') instance.set('version', version) instance.set('release', release) if entry.get('verify', 'true') == 'false': instance.set('verify', 'false') instances = [ instance ] self.logger.debug("Verifying package instances for %s" % entry.get('name')) package_fail = False qtext_versions = '' if entry.get('name') in self.installed: # There is at least one instance installed. if (self.pkg_checks and entry.get('pkg_checks', 'true').lower() == 'true'): rpmTs = rpm.TransactionSet() rpmHeader = None for h in rpmTs.dbMatch(rpm.RPMTAG_NAME, entry.get('name')): if rpmHeader is None or rpm.versionCompare(h, rpmHeader) > 0: rpmHeader = h rpmProvides = [ h['provides'] for h in \ rpmTs.dbMatch(rpm.RPMTAG_NAME, entry.get('name')) ] rpmIntersection = set(rpmHeader['provides']) & \ set(self.installOnlyPkgs) if len(rpmIntersection) > 0: # Packages that should only be installed or removed. # e.g. kernels. self.logger.debug(" Install only package.") for inst in instances: self.instance_status.setdefault(inst, {})['installed'] = False self.instance_status[inst]['version_fail'] = False if inst.tag == 'Package' and len(self.installed[entry.get('name')]) > 1: self.logger.error("WARNING: Multiple instances of package %s are installed." % \ (entry.get('name'))) for pkg in self.installed[entry.get('name')]: if inst.get('version') == 'any' or self.pkg_vr_equal(inst, pkg) \ or self.inst_evra_equal(inst, pkg): if inst.get('version') == 'any': self.logger.error("got any version") self.logger.debug(" %s" % self.str_evra(inst)) self.instance_status[inst]['installed'] = True if (self.pkg_verify and inst.get('pkg_verify', 'true').lower() == 'true'): flags = inst.get('verify_flags', '').split(',') + self.verify_flags if pkg.get('gpgkeyid', '')[-8:] not in self.gpg_keyids and \ entry.get('name') != 'gpg-pubkey': flags += ['nosignature', 'nodigest'] self.logger.debug('WARNING: Package %s %s requires GPG Public key with ID %s'\ % (pkg.get('name'), self.str_evra(pkg), \ pkg.get('gpgkeyid', ''))) self.logger.debug(' Disabling signature check.') if self.setup.get('quick', False): if rpmtools.prelink_exists: flags += ['nomd5', 'nosize'] else: flags += ['nomd5'] self.logger.debug(" verify_flags = %s" % flags) if inst.get('verify', 'true') == 'false': self.instance_status[inst]['verify'] = None else: vp_ts = rpmtools.rpmtransactionset() self.instance_status[inst]['verify'] = \ rpmtools.rpm_verify( vp_ts, pkg, flags) vp_ts.closeDB() del vp_ts if self.instance_status[inst]['installed'] == False: self.logger.info(" Package %s %s not installed." % \ (entry.get('name'), self.str_evra(inst))) qtext_versions = qtext_versions + 'I(%s) ' % self.str_evra(inst) entry.set('current_exists', 'false') else: # Normal Packages that can be upgraded. for inst in instances: self.instance_status.setdefault(inst, {})['installed'] = False self.instance_status[inst]['version_fail'] = False # Only installed packages with the same architecture are # relevant. if inst.get('arch', None) == None: arch_match = self.installed[entry.get('name')] else: arch_match = [pkg for pkg in self.installed[entry.get('name')] \ if pkg.get('arch', None) == inst.get('arch', None)] if len(arch_match) > 1: self.logger.error("Multiple instances of package %s installed with the same achitecture." % \ (entry.get('name'))) elif len(arch_match) == 1: # There is only one installed like there should be. # Check that it is the right version. for pkg in arch_match: if inst.get('version') == 'any' or self.pkg_vr_equal(inst, pkg) or \ self.inst_evra_equal(inst, pkg): self.logger.debug(" %s" % self.str_evra(inst)) self.instance_status[inst]['installed'] = True if (self.pkg_verify and inst.get('pkg_verify', 'true').lower() == 'true'): flags = inst.get('verify_flags', '').split(',') + self.verify_flags if pkg.get('gpgkeyid', '')[-8:] not in self.gpg_keyids and \ 'nosignature' not in flags: flags += ['nosignature', 'nodigest'] self.logger.info('WARNING: Package %s %s requires GPG Public key with ID %s'\ % (pkg.get('name'), self.str_evra(pkg), \ pkg.get('gpgkeyid', ''))) self.logger.info(' Disabling signature check.') if self.setup.get('quick', False): if rpmtools.prelink_exists: flags += ['nomd5', 'nosize'] else: flags += ['nomd5'] self.logger.debug(" verify_flags = %s" % flags) if inst.get('verify', 'true') == 'false': self.instance_status[inst]['verify'] = None else: vp_ts = rpmtools.rpmtransactionset() self.instance_status[inst]['verify'] = \ rpmtools.rpm_verify( vp_ts, pkg, flags ) vp_ts.closeDB() del vp_ts else: # Wrong version installed. self.instance_status[inst]['version_fail'] = True self.logger.info(" Wrong version installed. Want %s, but have %s"\ % (self.str_evra(inst), self.str_evra(pkg))) qtext_versions = qtext_versions + 'U(%s -> %s) ' % \ (self.str_evra(pkg), self.str_evra(inst)) elif len(arch_match) == 0: # This instance is not installed. self.instance_status[inst]['installed'] = False self.logger.info(" %s is not installed." % self.str_evra(inst)) qtext_versions = qtext_versions + 'I(%s) ' % self.str_evra(inst) # Check the rpm verify results. for inst in instances: instance_fail = False # Dump the rpm verify results. #****Write something to format this nicely.***** if self.setup['debug'] and self.instance_status[inst].get('verify', None): self.logger.debug(self.instance_status[inst]['verify']) self.instance_status[inst]['verify_fail'] = False if self.instance_status[inst].get('verify', None): if len(self.instance_status[inst].get('verify')) > 1: self.logger.info("WARNING: Verification of more than one package instance.") for result in self.instance_status[inst]['verify']: # Check header results if result.get('hdr', None): instance_fail = True self.instance_status[inst]['verify_fail'] = True # Check dependency results if result.get('deps', None): instance_fail = True self.instance_status[inst]['verify_fail'] = True # Check the rpm verify file results against the modlist # and entry and per Instance Ignores. ignores = [ig.get('name') for ig in entry.findall('Ignore')] + \ [ig.get('name') for ig in inst.findall('Ignore')] + \ self.ignores for file_result in result.get('files', []): if file_result[-1] not in modlist + ignores: instance_fail = True self.instance_status[inst]['verify_fail'] = True else: self.logger.debug(" Modlist/Ignore match: %s" % \ (file_result[-1])) if instance_fail == True: self.logger.debug("*** Instance %s failed RPM verification ***" % \ self.str_evra(inst)) qtext_versions = qtext_versions + 'R(%s) ' % self.str_evra(inst) self.modlists[entry] = modlist # Attach status structure for return to server for reporting. inst.set('verify_status', str(self.instance_status[inst])) if self.instance_status[inst]['installed'] == False or \ self.instance_status[inst].get('version_fail', False)== True or \ self.instance_status[inst].get('verify_fail', False) == True: package_fail = True self.instance_status[inst]['pkg'] = entry self.modlists[entry] = modlist # Find Installed Instances that are not in the Config. extra_installed = self.FindExtraInstances(entry, self.installed[entry.get('name')]) if extra_installed != None: package_fail = True self.extra_instances.append(extra_installed) for inst in extra_installed.findall('Instance'): qtext_versions = qtext_versions + 'D(%s) ' % self.str_evra(inst) self.logger.debug("Found Extra Instances %s" % qtext_versions) if package_fail == True: self.logger.info(" Package %s failed verification." % \ (entry.get('name'))) qtext = 'Install/Upgrade/delete Package %s instance(s) - %s (y/N) ' % \ (entry.get('name'), qtext_versions) entry.set('qtext', qtext) bcfg2_versions = '' for bcfg2_inst in [inst for inst in instances if inst.tag == 'Instance']: bcfg2_versions = bcfg2_versions + '(%s) ' % self.str_evra(bcfg2_inst) if bcfg2_versions != '': entry.set('version', bcfg2_versions) installed_versions = '' for installed_inst in self.installed[entry.get('name')]: installed_versions = installed_versions + '(%s) ' % \ self.str_evra(installed_inst) entry.set('current_version', installed_versions) return False else: # There are no Instances of this package installed. self.logger.debug("Package %s has no instances installed" % (entry.get('name'))) entry.set('current_exists', 'false') bcfg2_versions = '' for inst in instances: qtext_versions = qtext_versions + 'I(%s) ' % self.str_evra(inst) self.instance_status.setdefault(inst, {})['installed'] = False self.modlists[entry] = modlist self.instance_status[inst]['pkg'] = entry if inst.tag == 'Instance': bcfg2_versions = bcfg2_versions + '(%s) ' % self.str_evra(inst) if bcfg2_versions != '': entry.set('version', bcfg2_versions) entry.set('qtext', "Install Package %s Instance(s) %s? (y/N) " % \ (entry.get('name'), qtext_versions)) return False return True def Remove(self, packages): """ Remove specified entries. packages is a list of Package Entries with Instances generated by FindExtra(). """ self.logger.debug('Running RPM.Remove()') pkgspec_list = [] for pkg in packages: for inst in pkg: if pkg.get('name') != 'gpg-pubkey': pkgspec = { 'name':pkg.get('name'), 'epoch':inst.get('epoch', None), 'version':inst.get('version'), 'release':inst.get('release'), 'arch':inst.get('arch') } pkgspec_list.append(pkgspec) else: pkgspec = { 'name':pkg.get('name'), 'version':inst.get('version'), 'release':inst.get('release')} self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\ % (pkgspec.get('name'), self.str_evra(pkgspec))) self.logger.info(" This package will be deleted in a future version of the RPM driver.") #pkgspec_list.append(pkg_spec) erase_results = rpmtools.rpm_erase(pkgspec_list, self.erase_flags) if erase_results == []: self.modified += packages for pkg in pkgspec_list: self.logger.info("Deleted %s %s" % (pkg.get('name'), self.str_evra(pkg))) else: self.logger.info("Bulk erase failed with errors:") self.logger.debug("Erase results = %s" % erase_results) self.logger.info("Attempting individual erase for each package.") pkgspec_list = [] for pkg in packages: pkg_modified = False for inst in pkg: if pkg.get('name') != 'gpg-pubkey': pkgspec = { 'name':pkg.get('name'), 'epoch':inst.get('epoch', None), 'version':inst.get('version'), 'release':inst.get('release'), 'arch':inst.get('arch') } pkgspec_list.append(pkgspec) else: pkgspec = { 'name':pkg.get('name'), 'version':inst.get('version'), 'release':inst.get('release')} self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\ % (pkgspec.get('name'), self.str_evra(pkgspec))) self.logger.info(" This package will be deleted in a future version of the RPM driver.") continue # Don't delete the gpg-pubkey packages for now. erase_results = rpmtools.rpm_erase([pkgspec], self.erase_flags) if erase_results == []: pkg_modified = True self.logger.info("Deleted %s %s" % \ (pkgspec.get('name'), self.str_evra(pkgspec))) else: self.logger.error("unable to delete %s %s" % \ (pkgspec.get('name'), self.str_evra(pkgspec))) self.logger.debug("Failure = %s" % erase_results) if pkg_modified == True: self.modified.append(pkg) self.RefreshPackages() self.extra = self.FindExtra() def FixInstance(self, instance, inst_status): """ Control if a reinstall of a package happens or not based on the results from RPM.VerifyPackage(). Return True to reinstall, False to not reintstall. """ fix = False if inst_status.get('installed', False) == False: if instance.get('installed_action', 'install') == "install" and \ self.installed_action == "install": fix = True else: self.logger.debug('Installed Action for %s %s is to not install' % \ (inst_status.get('pkg').get('name'), self.str_evra(instance))) elif inst_status.get('version_fail', False) == True: if instance.get('version_fail_action', 'upgrade') == "upgrade" and \ self.version_fail_action == "upgrade": fix = True else: self.logger.debug('Version Fail Action for %s %s is to not upgrade' % \ (inst_status.get('pkg').get('name'), self.str_evra(instance))) elif inst_status.get('verify_fail', False) == True and self.name == "RPM": # yum can't reinstall packages so only do this for rpm. if instance.get('verify_fail_action', 'reinstall') == "reinstall" and \ self.verify_fail_action == "reinstall": for inst in inst_status.get('verify'): # This needs to be a for loop rather than a straight get() # because the underlying routines handle multiple packages # and return a list of results. self.logger.debug('reinstall_check: %s %s:%s-%s.%s' % inst.get('nevra')) if inst.get("hdr", False): fix = True elif inst.get('files', False): # Parse rpm verify file results for file_result in inst.get('files', []): self.logger.debug('reinstall_check: file: %s' % file_result) if file_result[-2] != 'c': fix = True break # Shouldn't really need this, but included for clarity. elif inst.get("deps", False): fix = False else: self.logger.debug('Verify Fail Action for %s %s is to not reinstall' % \ (inst_status.get('pkg').get('name'), self.str_evra(instance))) return fix def Install(self, packages, states): """ Try and fix everything that RPM.VerifyPackages() found wrong for each Package Entry. This can result in individual RPMs being installed (for the first time), reinstalled, deleted, downgraded or upgraded. packages is a list of Package Elements that has states[] == False The following effects occur: - states{} is conditionally updated for each package. - self.installed{} is rebuilt, possibly multiple times. - self.instance_statusi{} is conditionally updated for each instance of a package. - Each package will be added to self.modified[] if its states{} entry is set to True. """ self.logger.info('Runing RPM.Install()') install_only_pkgs = [] gpg_keys = [] upgrade_pkgs = [] # Remove extra instances. # Can not reverify because we don't have a package entry. if len(self.extra_instances) > 0: if (self.setup.get('remove') == 'all' or \ self.setup.get('remove') == 'packages') and\ not self.setup.get('dryrun'): self.Remove(self.extra_instances) else: self.logger.info("The following extra package instances will be removed by the '-r' option:") for pkg in self.extra_instances: for inst in pkg: self.logger.info(" %s %s" % (pkg.get('name'), self.str_evra(inst))) # Figure out which instances of the packages actually need something # doing to them and place in the appropriate work 'queue'. for pkg in packages: for inst in [instn for instn in pkg if instn.tag \ in ['Instance', 'Package']]: if self.FixInstance(inst, self.instance_status[inst]): if pkg.get('name') == 'gpg-pubkey': gpg_keys.append(inst) elif pkg.get('name') in self.installOnlyPkgs: install_only_pkgs.append(inst) else: upgrade_pkgs.append(inst) # Fix installOnlyPackages if len(install_only_pkgs) > 0: self.logger.info("Attempting to install 'install only packages'") install_args = \ " ".join(os.path.join(self.instance_status[inst].get('pkg').get('uri'), inst.get('simplefile')) for inst in install_only_pkgs) if self.cmd.run("rpm --install --quiet --oldpackage --replacepkgs " "%s" % install_args): # The rpm command succeeded. All packages installed. self.logger.info("Single Pass for InstallOnlyPkgs Succeded") self.RefreshPackages() else: # The rpm command failed. No packages installed. # Try installing instances individually. self.logger.error("Single Pass for InstallOnlyPackages Failed") installed_instances = [] for inst in install_only_pkgs: install_args = \ os.path.join(self.instance_status[inst].get('pkg').get('uri'), inst.get('simplefile')) if self.cmd.run("rpm --install --quiet --oldpackage " "--replacepkgs %s" % install_args): installed_instances.append(inst) else: self.logger.debug("InstallOnlyPackage %s %s would not install." % \ (self.instance_status[inst].get('pkg').get('name'), \ self.str_evra(inst))) install_pkg_set = set([self.instance_status[inst].get('pkg') \ for inst in install_only_pkgs]) self.RefreshPackages() # Install GPG keys. if len(gpg_keys) > 0: for inst in gpg_keys: self.logger.info("Installing GPG keys.") key_arg = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ inst.get('simplefile')) if not self.cmd.run("rpm --import %s" % key_arg): self.logger.debug("Unable to install %s-%s" % (self.instance_status[inst].get('pkg').get('name'), self.str_evra(inst))) else: self.logger.debug("Installed %s-%s-%s" % (self.instance_status[inst].get('pkg').get('name'), inst.get('version'), inst.get('release'))) self.RefreshPackages() self.gpg_keyids = self.getinstalledgpg() pkg = self.instance_status[gpg_keys[0]].get('pkg') states[pkg] = self.VerifyPackage(pkg, []) # Fix upgradeable packages. if len(upgrade_pkgs) > 0: self.logger.info("Attempting to upgrade packages") upgrade_args = " ".join([os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ inst.get('simplefile')) \ for inst in upgrade_pkgs]) if self.cmd.run("rpm --upgrade --quiet --oldpackage --replacepkgs " "%s" % upgrade_args): # The rpm command succeeded. All packages upgraded. self.logger.info("Single Pass for Upgraded Packages Succeded") upgrade_pkg_set = set([self.instance_status[inst].get('pkg') for inst in upgrade_pkgs]) self.RefreshPackages() else: # The rpm command failed. No packages upgraded. # Try upgrading instances individually. self.logger.error("Single Pass for Upgrading Packages Failed") upgraded_instances = [] for inst in upgrade_pkgs: upgrade_args = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ inst.get('simplefile')) #self.logger.debug("rpm --upgrade --quiet --oldpackage --replacepkgs %s" % \ # upgrade_args) if self.cmd.run("rpm --upgrade --quiet --oldpackage " "--replacepkgs %s" % upgrade_args): upgraded_instances.append(inst) else: self.logger.debug("Package %s %s would not upgrade." % (self.instance_status[inst].get('pkg').get('name'), self.str_evra(inst))) upgrade_pkg_set = set([self.instance_status[inst].get('pkg') \ for inst in upgrade_pkgs]) self.RefreshPackages() if not self.setup['kevlar']: for pkg_entry in packages: self.logger.debug("Reverifying Failed Package %s" % (pkg_entry.get('name'))) states[pkg_entry] = self.VerifyPackage(pkg_entry, \ self.modlists.get(pkg_entry, [])) for entry in [ent for ent in packages if states[ent]]: self.modified.append(entry) def canInstall(self, entry): """Test if entry has enough information to be installed.""" if not self.handlesEntry(entry): return False if 'failure' in entry.attrib: self.logger.error("Cannot install entry %s:%s with bind failure" % \ (entry.tag, entry.get('name'))) return False instances = entry.findall('Instance') # If the entry wasn't verifiable, then we really don't want to try and fix something # that we don't know is broken. if not self.canVerify(entry): self.logger.debug("WARNING: Package %s was not verifiable, not passing to Install()" \ % entry.get('name')) return False if not instances: # Old non Instance format, unmodified. if entry.get('name') == 'gpg-pubkey': # gpg-pubkey packages aren't really pacakges, so we have to do # something a little different. # Check that the Package Level has what we need for verification. if [attr for attr in self.__gpg_ireq__[entry.tag] if attr not in entry.attrib]: self.logger.error("Incomplete information for entry %s:%s; cannot install" \ % (entry.tag, entry.get('name'))) return False else: if [attr for attr in self.__ireq__[entry.tag] if attr not in entry.attrib]: self.logger.error("Incomplete information for entry %s:%s; cannot install" \ % (entry.tag, entry.get('name'))) return False else: if entry.get('name') == 'gpg-pubkey': # gpg-pubkey packages aren't really pacakges, so we have to do # something a little different. # Check that the Package Level has what we need for verification. if [attr for attr in self.__new_gpg_ireq__[entry.tag] if attr not in entry.attrib]: self.logger.error("Incomplete information for entry %s:%s; cannot install" \ % (entry.tag, entry.get('name'))) return False # Check that the Instance Level has what we need for verification. for inst in instances: if [attr for attr in self.__new_gpg_ireq__[inst.tag] \ if attr not in inst.attrib]: self.logger.error("Incomplete information for entry %s:%s; cannot install"\ % (inst.tag, entry.get('name'))) return False else: # New format with Instances. # Check that the Package Level has what we need for verification. if [attr for attr in self.__new_ireq__[entry.tag] if attr not in entry.attrib]: self.logger.error("Incomplete information for entry %s:%s; cannot install" \ % (entry.tag, entry.get('name'))) self.logger.error(" Required attributes that may not be present are %s" \ % (self.__new_ireq__[entry.tag])) return False # Check that the Instance Level has what we need for verification. for inst in instances: if inst.tag == 'Instance': if [attr for attr in self.__new_ireq__[inst.tag] \ if attr not in inst.attrib]: self.logger.error("Incomplete information for %s of package %s; cannot install" \ % (inst.tag, entry.get('name'))) self.logger.error(" Required attributes that may not be present are %s" \ % (self.__new_ireq__[inst.tag])) return False return True def canVerify(self, entry): """ Test if entry has enough information to be verified. Three types of entries are checked. Old style Package New style Package with Instances pgp-pubkey packages Also the old style entries get modified after the first VerifyPackage() run, so there needs to be a second test. """ if not self.handlesEntry(entry): return False if 'failure' in entry.attrib: self.logger.error("Entry %s:%s reports bind failure: %s" % \ (entry.tag, entry.get('name'), entry.get('failure'))) return False # We don't want to do any checks so we don't care what the entry has in it. if (not self.pkg_checks or entry.get('pkg_checks', 'true').lower() == 'false'): return True instances = entry.findall('Instance') if not instances: # Old non Instance format, unmodified. if entry.get('name') == 'gpg-pubkey': # gpg-pubkey packages aren't really pacakges, so we have to do # something a little different. # Check that the Package Level has what we need for verification. if [attr for attr in self.__gpg_req__[entry.tag] if attr not in entry.attrib]: self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ % (entry.tag, entry.get('name'))) return False elif entry.tag == 'Path' and entry.get('type') == 'ignore': # ignored Paths are only relevant during failed package # verification pass else: if [attr for attr in self.__req__[entry.tag] if attr not in entry.attrib]: self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ % (entry.tag, entry.get('name'))) return False else: if entry.get('name') == 'gpg-pubkey': # gpg-pubkey packages aren't really pacakges, so we have to do # something a little different. # Check that the Package Level has what we need for verification. if [attr for attr in self.__new_gpg_req__[entry.tag] if attr not in entry.attrib]: self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ % (entry.tag, entry.get('name'))) return False # Check that the Instance Level has what we need for verification. for inst in instances: if [attr for attr in self.__new_gpg_req__[inst.tag] \ if attr not in inst.attrib]: self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ % (inst.tag, inst.get('name'))) return False else: # New format with Instances, or old style modified. # Check that the Package Level has what we need for verification. if [attr for attr in self.__new_req__[entry.tag] if attr not in entry.attrib]: self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ % (entry.tag, entry.get('name'))) return False # Check that the Instance Level has what we need for verification. for inst in instances: if inst.tag == 'Instance': if [attr for attr in self.__new_req__[inst.tag] \ if attr not in inst.attrib]: self.logger.error("Incomplete information for entry %s:%s; cannot verify" \ % (inst.tag, inst.get('name'))) return False return True def FindExtra(self): """Find extra packages.""" packages = [entry.get('name') for entry in self.getSupportedEntries()] extras = [] for (name, instances) in list(self.installed.items()): if name not in packages: extra_entry = Bcfg2.Client.XML.Element('Package', name=name, type=self.pkgtype) for installed_inst in instances: if self.setup['extra']: self.logger.info("Extra Package %s %s." % \ (name, self.str_evra(installed_inst))) tmp_entry = Bcfg2.Client.XML.SubElement(extra_entry, 'Instance', \ version = installed_inst.get('version'), \ release = installed_inst.get('release')) if installed_inst.get('epoch', None) != None: tmp_entry.set('epoch', str(installed_inst.get('epoch'))) if installed_inst.get('arch', None) != None: tmp_entry.set('arch', installed_inst.get('arch')) extras.append(extra_entry) return extras def FindExtraInstances(self, pkg_entry, installed_entry): """ Check for installed instances that are not in the config. Return a Package Entry with Instances to remove, or None if there are no Instances to remove. """ name = pkg_entry.get('name') extra_entry = Bcfg2.Client.XML.Element('Package', name=name, type=self.pkgtype) instances = [inst for inst in pkg_entry if inst.tag == 'Instance' or inst.tag == 'Package'] if name in self.installOnlyPkgs: for installed_inst in installed_entry: not_found = True for inst in instances: if self.pkg_vr_equal(inst, installed_inst) or \ self.inst_evra_equal(inst, installed_inst): not_found = False break if not_found == True: # Extra package. self.logger.info("Extra InstallOnlyPackage %s %s." % \ (name, self.str_evra(installed_inst))) tmp_entry = Bcfg2.Client.XML.SubElement(extra_entry, 'Instance', \ version = installed_inst.get('version'), \ release = installed_inst.get('release')) if installed_inst.get('epoch', None) != None: tmp_entry.set('epoch', str(installed_inst.get('epoch'))) if installed_inst.get('arch', None) != None: tmp_entry.set('arch', installed_inst.get('arch')) else: # Normal package, only check arch. for installed_inst in installed_entry: not_found = True for inst in instances: if installed_inst.get('arch', None) == inst.get('arch', None) or\ inst.tag == 'Package': not_found = False break if not_found: self.logger.info("Extra Normal Package Instance %s %s" % \ (name, self.str_evra(installed_inst))) tmp_entry = Bcfg2.Client.XML.SubElement(extra_entry, 'Instance', \ version = installed_inst.get('version'), \ release = installed_inst.get('release')) if installed_inst.get('epoch', None) != None: tmp_entry.set('epoch', str(installed_inst.get('epoch'))) if installed_inst.get('arch', None) != None: tmp_entry.set('arch', installed_inst.get('arch')) if len(extra_entry) == 0: extra_entry = None return extra_entry def str_evra(self, instance): """Convert evra dict entries to a string.""" if instance.get('epoch', '*') in ['*', None]: return '%s-%s.%s' % (instance.get('version', '*'), instance.get('release', '*'), instance.get('arch', '*')) else: return '%s:%s-%s.%s' % (instance.get('epoch', '*'), instance.get('version', '*'), instance.get('release', '*'), instance.get('arch', '*')) def pkg_vr_equal(self, config_entry, installed_entry): ''' Compare old style entry to installed entry. Which means ignore the epoch and arch. ''' if (config_entry.tag == 'Package' and \ config_entry.get('version') == installed_entry.get('version') and \ config_entry.get('release') == installed_entry.get('release')): return True else: return False def inst_evra_equal(self, config_entry, installed_entry): """Compare new style instance to installed entry.""" if config_entry.get('epoch', None) != None: epoch = int(config_entry.get('epoch')) else: epoch = None if (config_entry.tag == 'Instance' and \ (epoch == installed_entry.get('epoch', 0) or \ (epoch == 0 and installed_entry.get('epoch', 0) == None) or \ (epoch == None and installed_entry.get('epoch', 0) == 0)) and \ config_entry.get('version') == installed_entry.get('version') and \ config_entry.get('release') == installed_entry.get('release') and \ config_entry.get('arch', None) == installed_entry.get('arch', None)): return True else: return False def getinstalledgpg(self): """ Create a list of installed GPG key IDs. The pgp-pubkey package version is the least significant 4 bytes (big-endian) of the key ID which is good enough for our purposes. """ init_ts = rpmtools.rpmtransactionset() init_ts.setVSFlags(rpm._RPMVSF_NODIGESTS|rpm._RPMVSF_NOSIGNATURES) gpg_hdrs = rpmtools.getheadersbykeyword(init_ts, **{'name':'gpg-pubkey'}) keyids = [ header[rpm.RPMTAG_VERSION] for header in gpg_hdrs] keyids.append('None') init_ts.closeDB() del init_ts return keyids def VerifyPath(self, entry, _): """ We don't do anything here since all Paths are processed in __init__ """ return True bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/RPMng.py000066400000000000000000000003251223671746500206510ustar00rootroot00000000000000""" RPM driver called 'RPMng' for backwards compat """ from Bcfg2.Client.Tools.RPM import RPM class RPMng(RPM): """ RPM driver called 'RPMng' for backwards compat """ deprecated = True name = "RPM" bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/RcUpdate.py000066400000000000000000000112701223671746500213760ustar00rootroot00000000000000"""This is rc-update support.""" import os import Bcfg2.Client.Tools import Bcfg2.Client.XML class RcUpdate(Bcfg2.Client.Tools.SvcTool): """RcUpdate support for Bcfg2.""" name = 'RcUpdate' __execs__ = ['/sbin/rc-update', '/bin/rc-status'] __handles__ = [('Service', 'rc-update')] __req__ = {'Service': ['name', 'status']} def get_enabled_svcs(self): """ Return a list of all enabled services. """ return [line.split()[0] for line in self.cmd.run(['/bin/rc-status', '-s']).stdout.splitlines() if 'started' in line] def get_default_svcs(self): """Return a list of services in the 'default' runlevel.""" return [line.split()[0] for line in self.cmd.run(['/sbin/rc-update', 'show']).stdout.splitlines() if 'default' in line] def verify_bootstatus(self, entry, bootstatus): """Verify bootstatus for entry.""" # get a list of all started services allsrv = self.get_default_svcs() # set current_bootstatus attribute if entry.get('name') in allsrv: entry.set('current_bootstatus', 'on') else: entry.set('current_bootstatus', 'off') if bootstatus == 'on': return entry.get('name') in allsrv else: return entry.get('name') not in allsrv def VerifyService(self, entry, _): """ Verify Service status for entry. Assumes we run in the "default" runlevel. """ entry.set('target_status', entry.get('status')) # for reporting bootstatus = self.get_bootstatus(entry) if bootstatus is None: return True current_bootstatus = self.verify_bootstatus(entry, bootstatus) # check if init script exists try: os.stat('/etc/init.d/%s' % entry.get('name')) except OSError: self.logger.debug('Init script for service %s does not exist' % entry.get('name')) return False if entry.get('status') == 'ignore': # 'ignore' should verify current_svcstatus = True svcstatus = True else: svcstatus = self.check_service(entry) if entry.get('status') == 'on': if svcstatus: current_svcstatus = True else: current_svcstatus = False elif entry.get('status') == 'off': if svcstatus: current_svcstatus = False else: current_svcstatus = True if svcstatus: entry.set('current_status', 'on') else: entry.set('current_status', 'off') return current_bootstatus and current_svcstatus def InstallService(self, entry): """Install Service entry.""" self.logger.info('Installing Service %s' % entry.get('name')) bootstatus = self.get_bootstatus(entry) if bootstatus is not None: if bootstatus == 'on': # make sure service is enabled on boot bootcmd = '/sbin/rc-update add %s default' elif bootstatus == 'off': # make sure service is disabled on boot bootcmd = '/sbin/rc-update del %s default' bootcmdrv = self.cmd.run(bootcmd % entry.get('name')).success if self.setup['servicemode'] == 'disabled': # 'disabled' means we don't attempt to modify running svcs return bootcmdrv buildmode = self.setup['servicemode'] == 'build' if (entry.get('status') == 'on' and not buildmode) and \ entry.get('current_status') == 'off': svccmdrv = self.start_service(entry) elif (entry.get('status') == 'off' or buildmode) and \ entry.get('current_status') == 'on': svccmdrv = self.stop_service(entry) else: svccmdrv = True # ignore status attribute return bootcmdrv and svccmdrv else: # when bootstatus is 'None', status == 'ignore' return True def FindExtra(self): """Locate extra rc-update services.""" allsrv = self.get_enabled_svcs() self.logger.debug('Found active services:') self.logger.debug(allsrv) specified = [srv.get('name') for srv in self.getSupportedEntries()] return [Bcfg2.Client.XML.Element('Service', type='rc-update', name=name) for name in allsrv if name not in specified] bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/SELinux.py000066400000000000000000001041241223671746500212170ustar00rootroot00000000000000""" Classes for SELinux entry support """ import os import re import sys import copy import glob import struct import socket import selinux import seobject import Bcfg2.Client.XML import Bcfg2.Client.Tools from Bcfg2.Client.Tools.POSIX.File import POSIXFile from Bcfg2.Compat import long # pylint: disable=W0622 def pack128(int_val): """ pack a 128-bit integer in big-endian format """ max_word_size = 2 ** 32 - 1 if int_val <= max_word_size: return struct.pack('>L', int_val) words = [] for i in range(4): # pylint: disable=W0612 word = int_val & max_word_size words.append(int(word)) int_val >>= 32 words.reverse() return struct.pack('>4I', *words) def netmask_itoa(netmask, proto="ipv4"): """ convert an integer netmask (e.g., /16) to dotted-quad notation (255.255.0.0) or IPv6 prefix notation (ffff::) """ if proto == "ipv4": size = 32 family = socket.AF_INET else: # ipv6 size = 128 family = socket.AF_INET6 try: netmask = int(netmask) except ValueError: return netmask if netmask > size: raise ValueError("Netmask too large: %s" % netmask) res = long(0) for i in range(netmask): res |= 1 << (size - i - 1) netmask = socket.inet_ntop(family, pack128(res)) return netmask class SELinux(Bcfg2.Client.Tools.Tool): """ SELinux entry support """ name = 'SELinux' __handles__ = [('SEBoolean', None), ('SEFcontext', None), ('SEInterface', None), ('SELogin', None), ('SEModule', None), ('SENode', None), ('SEPermissive', None), ('SEPort', None), ('SEUser', None)] __req__ = dict(SEBoolean=['name', 'value'], SEFcontext=['name', 'selinuxtype'], SEInterface=['name', 'selinuxtype'], SELogin=['name', 'selinuxuser'], SEModule=['name'], SENode=['name', 'selinuxtype', 'proto'], SEPermissive=['name'], SEPort=['name', 'selinuxtype'], SEUser=['name', 'roles', 'prefix']) def __init__(self, logger, setup, config): Bcfg2.Client.Tools.Tool.__init__(self, logger, setup, config) self.handlers = {} for handler in self.__handles__: etype = handler[0] self.handlers[etype] = \ globals()["SELinux%sHandler" % etype.title()](self, logger, setup, config) self.txn = False self.post_txn_queue = [] def __getattr__(self, attr): if attr.startswith("VerifySE"): return self.GenericSEVerify elif attr.startswith("InstallSE"): return self.GenericSEInstall # there's no need for an else here, because python checks for # an attribute in the "normal" ways first. i.e., if self.txn # is used, __getattr__() is never called because txn exists as # a "normal" attribute of this object. See # http://docs.python.org/2/reference/datamodel.html#object.__getattr__ # for details def BundleUpdated(self, _, states): for handler in self.handlers.values(): handler.BundleUpdated(states) def FindExtra(self): extra = [] for handler in self.handlers.values(): extra.extend(handler.FindExtra()) return extra def canInstall(self, entry): return (Bcfg2.Client.Tools.Tool.canInstall(self, entry) and self.handlers[entry.tag].canInstall(entry)) def primarykey(self, entry): """ return a string that should be unique amongst all entries in the specification """ return self.handlers[entry.tag].primarykey(entry) def Install(self, entries, states): # start a transaction semanage = seobject.semanageRecords("") if hasattr(semanage, "start"): self.logger.debug("Starting SELinux transaction") semanage.start() self.txn = True else: self.logger.debug("SELinux transactions not supported; this may " "slow things down considerably") Bcfg2.Client.Tools.Tool.Install(self, entries, states) if hasattr(semanage, "finish"): self.logger.debug("Committing SELinux transaction") semanage.finish() self.txn = False for func, arg, kwargs in self.post_txn_queue: states[arg] = func(*arg, **kwargs) def GenericSEInstall(self, entry): """Dispatch install to the proper method according to entry tag""" return self.handlers[entry.tag].Install(entry) def GenericSEVerify(self, entry, _): """Dispatch verify to the proper method according to entry tag""" rv = self.handlers[entry.tag].Verify(entry) if entry.get('qtext') and self.setup['interactive']: entry.set('qtext', '%s\nInstall %s: (y/N) ' % (entry.get('qtext'), self.handlers[entry.tag].tostring(entry))) return rv def Remove(self, entries): """Dispatch verify to the proper removal method according to entry tag""" # sort by type types = list() for entry in entries: if entry.tag not in types: types.append(entry.tag) for etype in types: self.handlers[etype].Remove([e for e in entries if e.tag == etype]) class SELinuxEntryHandler(object): """ Generic handler for all SELinux entries """ etype = None key_format = ("name",) value_format = () str_format = '%(name)s' custom_re = re.compile(r' (?P\S+)$') custom_format = None def __init__(self, tool, logger, setup, config): self.tool = tool self.logger = logger self.setup = setup self.config = config self._records = None self._all = None if not self.custom_format: self.custom_format = self.key_format @property def records(self): """ return the records object for this entry type """ if self._records is None: self._records = getattr(seobject, "%sRecords" % self.etype)("") return self._records @property def all_records(self): """ get a dict of all defined records for this entry type """ if self._all is None: self._all = self.records.get_all() return self._all @property def custom_records(self): """ try to get a dict of all customized records for this entry type, if the records object supports the customized() method """ if hasattr(self.records, "customized") and self.custom_re: rv = dict() for key in self.custom_keys: if key in self.all_records: rv[key] = self.all_records[key] else: self.logger.warning("SELinux %s %s customized, but no " "record found. This may indicate an " "error in your SELinux policy." % (self.etype, key)) return rv else: # ValueError is really a pretty dumb exception to raise, # but that's what the seobject customized() method raises # if it's defined but not implemented. yeah, i know, wtf. raise ValueError("custom_records") @property def custom_keys(self): """ get a list of keys for selinux records of this entry type that have been customized """ keys = [] for cmd in self.records.customized(): match = self.custom_re.search(cmd) if match: if (len(self.custom_format) == 1 and self.custom_format[0] == "name"): keys.append(match.group("name")) else: keys.append(tuple([match.group(k) for k in self.custom_format])) return keys def tostring(self, entry): """ transform an XML SELinux entry into a human-readable string """ return self.str_format % entry.attrib def keytostring(self, key): """ transform a SELinux record key into a human-readable string """ return self.str_format % self._key2attrs(key) def _key(self, entry): """ Generate an SELinux record key from an XML SELinux entry """ if len(self.key_format) == 1 and self.key_format[0] == "name": return entry.get("name") else: rv = [] for key in self.key_format: rv.append(entry.get(key)) return tuple(rv) def _key2attrs(self, key): """ Generate an XML attribute dict from an SELinux record key """ if isinstance(key, tuple): rv = dict((self.key_format[i], key[i]) for i in range(len(self.key_format)) if self.key_format[i]) else: rv = dict(name=key) if self.value_format: vals = self.all_records[key] rv.update(dict((self.value_format[i], vals[i]) for i in range(len(self.value_format)) if self.value_format[i])) return rv def key2entry(self, key): """ Generate an XML entry from an SELinux record key """ attrs = self._key2attrs(key) return Bcfg2.Client.XML.Element("SE%s" % self.etype.title(), **attrs) def _args(self, entry, method): """ Get the argument list for invoking _modify or _add, or _delete methods """ if hasattr(self, "_%sargs" % method): return getattr(self, "_%sargs" % method)(entry) elif hasattr(self, "_defaultargs"): # default args return self._defaultargs(entry) # pylint: disable=E1101 else: raise NotImplementedError def _deleteargs(self, entry): """ Get the argument list for invoking delete methods """ return (self._key(entry)) def canInstall(self, entry): """ return True if this entry is complete and can be installed """ return bool(self._key(entry)) def primarykey(self, entry): """ return a string that should be unique amongst all entries in the specification. some entry types are not universally disambiguated by tag:type:name alone """ return ":".join([entry.tag, entry.get("name")]) def exists(self, entry): """ return True if the entry already exists in the record list """ if self._key(entry) not in self.all_records: self.logger.debug("SELinux %s %s does not exist" % (self.etype, self.tostring(entry))) return False return True def Verify(self, entry): """ verify that the entry is correct on the client system """ if not self.exists(entry): entry.set('current_exists', 'false') return False errors = [] current_attrs = self._key2attrs(self._key(entry)) desired_attrs = entry.attrib for attr in self.value_format: if not attr: continue if current_attrs[attr] != desired_attrs[attr]: entry.set('current_%s' % attr, current_attrs[attr]) errors.append("%s %s has wrong %s: %s, should be %s" % (entry.tag, entry.get('name'), attr, current_attrs[attr], desired_attrs[attr])) if errors: for error in errors: self.logger.debug(error) entry.set('qtext', "\n".join([entry.get('qtext', '')] + errors)) return False else: return True def Install(self, entry, method=None): """ install the entry on the client system """ if not method: if self.exists(entry): method = "modify" else: method = "add" self.logger.debug("%s SELinux %s %s" % (method.title(), self.etype, self.tostring(entry))) try: getattr(self.records, method)(*self._args(entry, method)) self._all = None return True except ValueError: err = sys.exc_info()[1] self.logger.info("Failed to %s SELinux %s %s: %s" % (method, self.etype, self.tostring(entry), err)) return False def Remove(self, entries): """ remove the entry from the client system """ for entry in entries: try: self.records.delete(*self._args(entry, "delete")) self._all = None except ValueError: err = sys.exc_info()[1] self.logger.info("Failed to remove SELinux %s %s: %s" % (self.etype, self.tostring(entry), err)) def FindExtra(self): """ find extra entries of this entry type """ specified = [self._key(e) for e in self.tool.getSupportedEntries() if e.tag == "SE%s" % self.etype.title()] try: records = self.custom_records except ValueError: records = self.all_records return [self.key2entry(key) for key in records.keys() if key not in specified] def BundleUpdated(self, states): """ perform any additional magic tasks that need to be run when a bundle is updated """ pass class SELinuxSebooleanHandler(SELinuxEntryHandler): """ handle SELinux boolean entries """ etype = "boolean" value_format = ("value",) @property def all_records(self): # older versions of selinux return a single 0/1 value for each # bool, while newer versions return a list of three 0/1 values # representing various states. we don't care about the latter # two values, but it's easier to coerce the older format into # the newer format as far as interoperation with the rest of # SELinuxEntryHandler goes rv = SELinuxEntryHandler.all_records.fget(self) if rv.values()[0] in [0, 1]: for key, val in rv.items(): rv[key] = [val, val, val] return rv def _key2attrs(self, key): rv = SELinuxEntryHandler._key2attrs(self, key) status = self.all_records[key][0] if status: rv['value'] = "on" else: rv['value'] = "off" return rv def _defaultargs(self, entry): """ argument list for adding, modifying and deleting entries """ # the only values recognized by both new and old versions of # selinux are the strings "0" and "1". old selinux accepts # ints or bools as well, new selinux accepts "on"/"off" if entry.get("value").lower() == "on": value = "1" else: value = "0" return (entry.get("name"), value) def canInstall(self, entry): if entry.get("value").lower() not in ["on", "off"]: self.logger.debug("SELinux %s %s has a bad value: %s" % (self.etype, self.tostring(entry), entry.get("value"))) return False return (self.exists(entry) and SELinuxEntryHandler.canInstall(self, entry)) class SELinuxSeportHandler(SELinuxEntryHandler): """ handle SELinux port entries """ etype = "port" value_format = ('selinuxtype', None) custom_re = re.compile(r'-p (?Ptcp|udp).*? ' r'(?P\d+)(?:-(?P\d+))?$') @property def custom_keys(self): keys = [] for cmd in self.records.customized(): match = self.custom_re.search(cmd) if match: if match.group('end'): keys.append((int(match.group('start')), int(match.group('end')), match.group('proto'))) else: keys.append((int(match.group('start')), int(match.group('start')), match.group('proto'))) return keys @property def all_records(self): if self._all is None: # older versions of selinux use (startport, endport) as # they key for the ports.get_all() dict, and (type, proto, # level) as the value; this is obviously broken, so newer # versions use (startport, endport, proto) as the key, and # (type, level) as the value. abstracting around this # sucks. ports = self.records.get_all() if len(ports.keys()[0]) == 3: self._all = ports else: # uglist list comprehension ever? self._all = dict([((k[0], k[1], v[1]), (v[0], v[2])) for k, v in ports.items()]) return self._all def _key(self, entry): try: (port, proto) = entry.get("name").split("/") except ValueError: self.logger.error("Invalid SELinux node %s: no protocol specified" % entry.get("name")) return if "-" in port: start, end = port.split("-") else: start = port end = port return (int(start), int(end), proto) def _key2attrs(self, key): if key[0] == key[1]: port = str(key[0]) else: port = "%s-%s" % (key[0], key[1]) vals = self.all_records[key] return dict(name="%s/%s" % (port, key[2]), selinuxtype=vals[0]) def _defaultargs(self, entry): """ argument list for adding and modifying entries """ (port, proto) = entry.get("name").split("/") return (port, proto, entry.get("mlsrange", ""), entry.get("selinuxtype")) def _deleteargs(self, entry): return tuple(entry.get("name").split("/")) class SELinuxSefcontextHandler(SELinuxEntryHandler): """ handle SELinux file context entries """ etype = "fcontext" key_format = ("name", "filetype") value_format = (None, None, "selinuxtype", None) filetypeargs = dict(all="", regular="--", directory="-d", symlink="-l", pipe="-p", socket="-s", block="-b", char="-c", door="-D") filetypenames = dict(all="all files", regular="regular file", directory="directory", symlink="symbolic link", pipe="named pipe", socket="socket", block="block device", char="character device", door="door") filetypeattrs = dict([v, k] for k, v in filetypenames.iteritems()) custom_re = re.compile(r'-f \'(?P[a-z ]+)\'.*? \'(?P.*)\'') @property def all_records(self): if self._all is None: # on older selinux, fcontextRecords.get_all() returns a # list of tuples of (filespec, filetype, seuser, serole, # setype, level); on newer selinux, get_all() returns a # dict of (filespec, filetype) => (seuser, serole, setype, # level). fcontexts = self.records.get_all() if isinstance(fcontexts, dict): self._all = fcontexts else: self._all = dict([(f[0:2], f[2:]) for f in fcontexts]) return self._all def _key(self, entry): ftype = entry.get("filetype", "all") return (entry.get("name"), self.filetypenames.get(ftype, ftype)) def _key2attrs(self, key): rv = dict(name=key[0], filetype=self.filetypeattrs[key[1]]) vals = self.all_records[key] # in older versions of selinux, an fcontext with no selinux # type is the single value None; in newer versions, it's a # tuple whose 0th (and only) value is None. if vals and vals[0]: rv["selinuxtype"] = vals[2] else: rv["selinuxtype"] = "<>" return rv def canInstall(self, entry): return (entry.get("filetype", "all") in self.filetypeargs and SELinuxEntryHandler.canInstall(self, entry)) def _defaultargs(self, entry): """ argument list for adding, modifying, and deleting entries """ return (entry.get("name"), entry.get("selinuxtype"), self.filetypeargs[entry.get("filetype", "all")], entry.get("mlsrange", ""), '') def primarykey(self, entry): return ":".join([entry.tag, entry.get("name"), entry.get("filetype", "all")]) class SELinuxSenodeHandler(SELinuxEntryHandler): """ handle SELinux node entries """ etype = "node" value_format = (None, None, "selinuxtype", None) str_format = '%(name)s (%(proto)s)' custom_re = re.compile(r'-M (?P\S+).*?' r'-p (?Pipv\d).*? (?P\S+)$') custom_format = ('addr', 'netmask', 'proto') def _key(self, entry): try: (addr, netmask) = entry.get("name").split("/") except ValueError: self.logger.error("Invalid SELinux node %s: no netmask specified" % entry.get("name")) return netmask = netmask_itoa(netmask, proto=entry.get("proto")) return (addr, netmask, entry.get("proto")) def _key2attrs(self, key): vals = self.all_records[key] return dict(name="%s/%s" % (key[0], key[1]), proto=key[2], selinuxtype=vals[2]) def _defaultargs(self, entry): """ argument list for adding, modifying, and deleting entries """ (addr, netmask) = entry.get("name").split("/") return (addr, netmask, entry.get("proto"), entry.get("mlsrange", ""), entry.get("selinuxtype")) class SELinuxSeloginHandler(SELinuxEntryHandler): """ handle SELinux login entries """ etype = "login" value_format = ("selinuxuser", None) def _defaultargs(self, entry): """ argument list for adding, modifying, and deleting entries """ return (entry.get("name"), entry.get("selinuxuser"), entry.get("mlsrange", "")) class SELinuxSeuserHandler(SELinuxEntryHandler): """ handle SELinux user entries """ etype = "user" value_format = ("prefix", None, None, "roles") def __init__(self, tool, logger, setup, config): SELinuxEntryHandler.__init__(self, tool, logger, setup, config) self.needs_prefix = False @property def records(self): if self._records is None: self._records = seobject.seluserRecords() return self._records def Install(self, entry, method=None): # in older versions of selinux, modify() is broken if you # provide a prefix _at all_, so we try to avoid giving the # prefix. however, in newer versions, prefix is _required_, # so we a) try without a prefix; b) catch TypeError, which # indicates that we had the wrong number of args (ValueError # is thrown by the bug in older versions of selinux); and c) # try with prefix. try: SELinuxEntryHandler.Install(self, entry, method=method) except TypeError: self.needs_prefix = True SELinuxEntryHandler.Install(self, entry, method=method) def _defaultargs(self, entry): """ argument list for adding, modifying, and deleting entries """ # in older versions of selinux, modify() is broken if you # provide a prefix _at all_, so we try to avoid giving the # prefix. see the comment in Install() above for more # details. rv = [entry.get("name"), entry.get("roles", "").replace(" ", ",").split(","), '', entry.get("mlsrange", "")] if self.needs_prefix: rv.append(entry.get("prefix")) else: key = self._key(entry) if key in self.all_records: attrs = self._key2attrs(key) if attrs['prefix'] != entry.get("prefix"): rv.append(entry.get("prefix")) return tuple(rv) class SELinuxSeinterfaceHandler(SELinuxEntryHandler): """ handle SELinux interface entries """ etype = "interface" value_format = (None, None, "selinuxtype", None) def _defaultargs(self, entry): """ argument list for adding, modifying, and deleting entries """ return (entry.get("name"), entry.get("mlsrange", ""), entry.get("selinuxtype")) class SELinuxSepermissiveHandler(SELinuxEntryHandler): """ handle SELinux permissive domain entries """ etype = "permissive" @property def records(self): try: return SELinuxEntryHandler.records.fget(self) except AttributeError: self.logger.info("Permissive domains not supported by this " "version of SELinux") self._records = None return self._records @property def all_records(self): if self._all is None: if self.records is None: self._all = dict() else: # permissiveRecords.get_all() returns a list, so we just # make it into a dict so that the rest of # SELinuxEntryHandler works self._all = dict([(d, d) for d in self.records.get_all()]) return self._all def _defaultargs(self, entry): """ argument list for adding, modifying, and deleting entries """ return (entry.get("name"),) class SELinuxSemoduleHandler(SELinuxEntryHandler): """ handle SELinux module entries """ etype = "module" value_format = (None, "disabled") def __init__(self, tool, logger, setup, config): SELinuxEntryHandler.__init__(self, tool, logger, setup, config) self.filetool = POSIXFile(logger, setup, config) try: self.setype = selinux.selinux_getpolicytype()[1] except IndexError: self.logger.error("Unable to determine SELinux policy type") self.setype = None @property def all_records(self): if self._all is None: try: # we get a list of tuples back; coerce it into a dict self._all = dict([(m[0], (m[1], m[2])) for m in self.records.get_all()]) except AttributeError: # early versions of seobject don't have moduleRecords, # so we parse the output of `semodule` >_< self._all = dict() self.logger.debug("SELinux: Getting modules from semodule") try: rv = self.tool.cmd.run(['semodule', '-l']) except OSError: # semanage failed; probably not in $PATH. try to # get the list of modules from the filesystem err = sys.exc_info()[1] self.logger.debug("SELinux: Failed to run semodule: %s" % err) self._all.update(self._all_records_from_filesystem()) else: if rv.success: # ran semodule successfully for line in rv.stdout.splitlines(): mod, version = line.split() self._all[mod] = (version, 1) # get other (disabled) modules from the filesystem for mod in self._all_records_from_filesystem().keys(): if mod not in self._all: self._all[mod] = ('', 0) else: self.logger.error("SELinux: Failed to run semodule: %s" % rv.error) self._all.update(self._all_records_from_filesystem()) return self._all def _all_records_from_filesystem(self): """ the seobject API doesn't support modules and semodule is broken or missing, so just list modules on the filesystem. this is terrible. """ self.logger.debug("SELinux: Getting modules from filesystem") rv = dict() for mod in glob.glob(os.path.join("/usr/share/selinux", self.setype, "*.pp")): rv[os.path.basename(mod)[:-3]] = ('', 1) return rv def _key(self, entry): name = entry.get("name").lstrip("/") if name.endswith(".pp"): return name[:-3] else: return name def _key2attrs(self, key): rv = SELinuxEntryHandler._key2attrs(self, key) status = self.all_records[key][1] if status: rv['disabled'] = "false" else: rv['disabled'] = "true" return rv def _filepath(self, entry): """ get the path to the .pp module file for this module entry """ return os.path.join("/usr/share/selinux", self.setype, entry.get("name") + '.pp') def _pathentry(self, entry): """ Get an XML Path entry based on this SELinux module entry, suitable for installing the module .pp file itself to the filesystem """ pathentry = copy.deepcopy(entry) pathentry.set("name", self._filepath(pathentry)) pathentry.set("mode", "0644") pathentry.set("owner", "root") pathentry.set("group", "root") pathentry.set("secontext", "__default__") return pathentry def Verify(self, entry): if not entry.get("disabled"): entry.set("disabled", "false") return (SELinuxEntryHandler.Verify(self, entry) and self.filetool.verify(self._pathentry(entry), [])) def canInstall(self, entry): return (entry.text and self.setype and SELinuxEntryHandler.canInstall(self, entry)) def Install(self, entry, _=None): if not self.filetool.install(self._pathentry(entry)): return False if hasattr(seobject, 'moduleRecords'): # if seobject has the moduleRecords attribute, install the # module using the seobject library return self._install_seobject(entry) else: # seobject doesn't have the moduleRecords attribute, so # install the module using `semodule` self.logger.debug("Installing %s using semodule" % entry.get("name")) self._all = None return self._install_semodule(entry) def _install_seobject(self, entry): """ Install an SELinux module using the seobject library """ try: if not SELinuxEntryHandler.Install(self, entry): return False except NameError: # some versions of selinux have a bug in seobject that # makes modify() calls fail. add() seems to have the same # effect as modify, but without the bug if self.exists(entry): if not SELinuxEntryHandler.Install(self, entry, method="add"): return False if entry.get("disabled", "false").lower() == "true": method = "disable" else: method = "enable" return SELinuxEntryHandler.Install(self, entry, method=method) def _install_semodule(self, entry, fromqueue=False): """ Install an SELinux module using the semodule command """ if fromqueue: self.logger.debug("Installing SELinux module %s from " "post-transaction queue" % entry.get("name")) elif self.tool.txn: # we've started a transaction, so if we run semodule -i # then it'll fail with lock errors. so we add this # installation to a queue to be run after the transaction # is closed. self.logger.debug("Waiting to install SELinux module %s until " "SELinux transaction is finished" % entry.get('name')) self.tool.post_txn_queue.append((self._install_semodule, (entry,), dict(fromqueue=True))) return False self.logger.debug("Install SELinux module %s with semodule -i %s" % (entry.get('name'), self._filepath(entry))) try: rv = self.tool.cmd.run(['semodule', '-i', self._filepath(entry)]) except OSError: err = sys.exc_info()[1] self.logger.error("Failed to install SELinux module %s with " "semodule: %s" % (entry.get("name"), err)) return False if rv.success: if entry.get("disabled", "false").lower() == "true": self.logger.warning("SELinux: Cannot disable modules with " "semodule") return False else: return True else: self.logger.error("Failed to install SELinux module %s with " "semodule: %s" % (entry.get("name"), rv.error)) return False def _addargs(self, entry): """ argument list for adding entries """ return (self._filepath(entry),) def _defaultargs(self, entry): """ argument list for modifying and deleting entries """ return (entry.get("name"),) def FindExtra(self): specified = [self._key(e) for e in self.tool.getSupportedEntries()] rv = [] for module in self._all_records_from_filesystem().keys(): if module not in specified: rv.append(self.key2entry(module)) return rv bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/SMF.py000066400000000000000000000125561223671746500203240ustar00rootroot00000000000000"""SMF support for Bcfg2""" import glob import os import Bcfg2.Client.Tools class SMF(Bcfg2.Client.Tools.SvcTool): """Support for Solaris SMF Services.""" __handles__ = [('Service', 'smf')] __execs__ = ['/usr/sbin/svcadm', '/usr/bin/svcs'] __req__ = {'Service': ['name', 'status', 'FMRI']} def get_svc_command(self, service, action): if service.get('type') == 'lrc': return Bcfg2.Client.Tools.SvcTool.get_svc_command(self, service, action) if action == 'stop': return "/usr/sbin/svcadm disable %s" % (service.get('FMRI')) elif action == 'restart': return "/usr/sbin/svcadm restart %s" % (service.get('FMRI')) elif action == 'start': return "/usr/sbin/svcadm enable %s" % (service.get('FMRI')) def GetFMRI(self, entry): """Perform FMRI resolution for service.""" if not 'FMRI' in entry.attrib: rv = self.cmd.run(["/usr/bin/svcs", "-H", "-o", "FMRI", entry.get('name')]) if rv.success: entry.set('FMRI', rv.stdout.splitlines()[0]) else: self.logger.info('Failed to locate FMRI for service %s' % entry.get('name')) return rv.success return True def VerifyService(self, entry, _): """Verify SMF Service entry.""" if not self.GetFMRI(entry): self.logger.error("smf service %s doesn't have FMRI set" % entry.get('name')) return False if entry.get('FMRI').startswith('lrc'): filename = entry.get('FMRI').split('/')[-1] # this is a legacy service gname = "/etc/rc*.d/%s" % filename files = glob.glob(gname.replace('_', '.')) if files: self.logger.debug("Matched %s with %s" % (entry.get("FMRI"), ":".join(files))) return entry.get('status') == 'on' else: self.logger.debug("No service matching %s" % entry.get("FMRI")) return entry.get('status') == 'off' try: srvdata = \ self.cmd.run("/usr/bin/svcs -H -o STA %s" % entry.get('FMRI')).stdout.splitlines()[0].split() except IndexError: # Occurs when no lines are returned (service not installed) return False entry.set('current_status', srvdata[0]) if entry.get('status') == 'on': return srvdata[0] == 'ON' else: return srvdata[0] in ['OFF', 'UN', 'MNT', 'DIS', 'DGD'] def InstallService(self, entry): """Install SMF Service entry.""" self.logger.info("Installing Service %s" % (entry.get('name'))) if entry.get('status') == 'off': if entry.get("FMRI").startswith('lrc'): try: loc = entry.get("FMRI")[4:].replace('_', '.') self.logger.debug("Renaming file %s to %s" % (loc, loc.replace('/S', '/DISABLED.S'))) os.rename(loc, loc.replace('/S', '/DISABLED.S')) return True except OSError: self.logger.error("Failed to rename init script %s" % loc) return False else: return self.cmd.run("/usr/sbin/svcadm disable %s" % entry.get('FMRI')).success elif entry.get('FMRI').startswith('lrc'): loc = entry.get("FMRI")[4:].replace('_', '.') try: os.stat(loc.replace('/S', '/Disabled.')) self.logger.debug("Renaming file %s to %s" % (loc.replace('/S', '/DISABLED.S'), loc)) os.rename(loc.replace('/S', '/DISABLED.S'), loc) return True except OSError: self.logger.debug("Failed to rename %s to %s" % (loc.replace('/S', '/DISABLED.S'), loc)) return False else: srvdata = \ self.cmd.run("/usr/bin/svcs -H -o STA %s" % entry.get('FMRI'))[1].splitlines()[0].split() if srvdata[0] == 'MNT': cmdarg = 'clear' else: cmdarg = 'enable' return self.cmd.run("/usr/sbin/svcadm %s -r %s" % (cmdarg, entry.get('FMRI'))).success def Remove(self, svcs): """Remove Extra SMF entries.""" # Extra service entry removal is nonsensical # Extra service entries should be reflected in config, even if disabled pass def FindExtra(self): """Find Extra SMF Services.""" allsrv = [] for srvc in self.cmd.run(["/usr/bin/svcs", "-a", "-H", "-o", "FMRI,STATE"]).stdout.splitlines(): name, version = srvc.split() if version != 'disabled': allsrv.append(name) for svc in self.getSupportedEntries(): if svc.get("FMRI") in allsrv: allsrv.remove(svc.get('FMRI')) return [Bcfg2.Client.XML.Element("Service", type='smf', name=name) for name in allsrv] bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/SYSV.py000066400000000000000000000101201223671746500204640ustar00rootroot00000000000000"""This provides bcfg2 support for Solaris SYSV packages.""" import tempfile from Bcfg2.Compat import any # pylint: disable=W0622 import Bcfg2.Client.Tools import Bcfg2.Client.XML # pylint: disable=C0103 noask = ''' mail= instance=overwrite partial=nocheck runlevel=nocheck idepend=nocheck rdepend=nocheck space=ask setuid=nocheck conflict=nocheck action=nocheck basedir=default ''' # pylint: enable=C0103 class SYSV(Bcfg2.Client.Tools.PkgTool): """Solaris SYSV package support.""" __execs__ = ["/usr/sbin/pkgadd", "/usr/bin/pkginfo"] __handles__ = [('Package', 'sysv')] __req__ = {'Package': ['name', 'version']} __ireq__ = {'Package': ['name', 'url', 'version']} name = 'SYSV' pkgtype = 'sysv' pkgtool = ("/usr/sbin/pkgadd %s -n -d %%s", (('%s %s', ['url', 'name']))) def __init__(self, logger, setup, config): Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config) # noaskfile needs to live beyond __init__ otherwise file is removed self.noaskfile = tempfile.NamedTemporaryFile() self.noaskname = self.noaskfile.name try: self.noaskfile.write(noask) # flush admin file contents to disk self.noaskfile.flush() self.pkgtool = (self.pkgtool[0] % ("-a %s" % (self.noaskname)), self.pkgtool[1]) except: # pylint: disable=W0702 self.pkgtool = (self.pkgtool[0] % "", self.pkgtool[1]) def RefreshPackages(self): """Refresh memory hashes of packages.""" self.installed = {} # Build list of packages lines = self.cmd.run("/usr/bin/pkginfo -x").stdout.splitlines() while lines: # Splitting on whitespace means that packages with spaces in # their version numbers don't work right. Found this with # IBM TSM software with package versions like # "Version 6 Release 1 Level 0.0" # Should probably be done with a regex but this works. version = lines.pop().split(') ')[1] pkg = lines.pop().split()[0] self.installed[pkg] = version def VerifyPackage(self, entry, modlist): """Verify Package status for entry.""" desired_version = entry.get('version') if desired_version == 'any': desired_version = self.installed.get(entry.get('name'), desired_version) if not self.cmd.run(["/usr/bin/pkginfo", "-q", "-v", desired_version, entry.get('name')]): if entry.get('name') in self.installed: self.logger.debug("Package %s version incorrect: " "have %s want %s" % (entry.get('name'), self.installed[entry.get('name')], desired_version)) else: self.logger.debug("Package %s not installed" % entry.get("name")) else: if (self.setup['quick'] or entry.attrib.get('verify', 'true') == 'false'): return True rv = self.cmd.run("/usr/sbin/pkgchk -n %s" % entry.get('name')) if rv.success: return True else: output = [line for line in rv.stdout.splitlines() if line[:5] == 'ERROR'] if any(name for name in output if name.split()[-1] not in modlist): self.logger.debug("Package %s content verification failed" % entry.get('name')) else: return True return False def Remove(self, packages): """Remove specified Sysv packages.""" names = [pkg.get('name') for pkg in packages] self.logger.info("Removing packages: %s" % (names)) self.cmd.run("/usr/sbin/pkgrm -a %s -n %s" % (self.noaskname, names)) self.RefreshPackages() self.extra = self.FindExtra() bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/Systemd.py000066400000000000000000000030571223671746500213230ustar00rootroot00000000000000# This is the bcfg2 support for systemd """This is systemd support.""" import Bcfg2.Client.Tools import Bcfg2.Client.XML class Systemd(Bcfg2.Client.Tools.SvcTool): """Systemd support for Bcfg2.""" name = 'Systemd' __execs__ = ['/bin/systemctl'] __handles__ = [('Service', 'systemd')] __req__ = {'Service': ['name', 'status']} conflicts = ['Chkconfig'] def get_svc_command(self, service, action): return "/bin/systemctl %s %s.service" % (action, service.get('name')) def VerifyService(self, entry, _): """Verify Service status for entry.""" if entry.get('status') == 'ignore': return True cmd = "/bin/systemctl status %s.service " % (entry.get('name')) rv = self.cmd.run(cmd) if 'Loaded: error' in rv.stdout: entry.set('current_status', 'off') return False elif 'Active: active' in rv.stdout: entry.set('current_status', 'on') return entry.get('status') == 'on' else: entry.set('current_status', 'off') return entry.get('status') == 'off' def InstallService(self, entry): """Install Service entry.""" if entry.get('status') == 'on': rv = self.cmd.run(self.get_svc_command(entry, 'enable')).success rv &= self.cmd.run(self.get_svc_command(entry, 'start')).success else: rv = self.cmd.run(self.get_svc_command(entry, 'stop')).success rv &= self.cmd.run(self.get_svc_command(entry, 'disable')).success return rv bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/Upstart.py000066400000000000000000000057321223671746500213370ustar00rootroot00000000000000"""Upstart support for Bcfg2.""" import glob import re import Bcfg2.Client.Tools import Bcfg2.Client.XML class Upstart(Bcfg2.Client.Tools.SvcTool): """Upstart service support for Bcfg2.""" name = 'Upstart' __execs__ = ['/lib/init/upstart-job', '/sbin/initctl', '/usr/sbin/service'] __handles__ = [('Service', 'upstart')] __req__ = {'Service': ['name', 'status']} svcre = re.compile("/etc/init/(?P.*).conf") def get_svc_command(self, service, action): return "/usr/sbin/service %s %s" % (service.get('name'), action) def VerifyService(self, entry, _): """Verify Service status for entry Verifying whether or not the service is enabled can be done at the file level with upstart using the contents of /etc/init/servicename.conf. All we need to do is make sure the service is running when it should be. """ if entry.get('status') == 'ignore': return True if entry.get('parameters'): params = entry.get('parameters') else: params = '' try: output = self.cmd.run('/usr/sbin/service %s status %s' % (entry.get('name'), params)).stdout.splitlines()[0] except IndexError: self.logger.error("Service %s not an Upstart service" % entry.get('name')) return False match = re.compile(r'%s( \(.*\))? (start|stop)/(running|waiting)' % entry.get('name')).match(output) if match is None: # service does not exist entry.set('current_status', 'off') status = False elif match.group(3) == 'running': # service is running entry.set('current_status', 'on') if entry.get('status') == 'off': status = False else: status = True else: # service is not running entry.set('current_status', 'off') if entry.get('status') == 'on': status = False else: status = True return status def InstallService(self, entry): """Install Service for entry.""" if entry.get('status') == 'on': cmd = "start" elif entry.get('status') == 'off': cmd = "stop" return self.cmd.run(self.get_svc_command(entry, cmd)).success def FindExtra(self): """Locate extra Upstart services.""" specified = [entry.get('name') for entry in self.getSupportedEntries()] extra = [] for fname in glob.glob("/etc/init/*.conf"): if self.svcre.match(fname).group('name') not in specified: extra.append(self.svcre.match(fname).group('name')) return [Bcfg2.Client.XML.Element('Service', type='upstart', name=name) for name in extra] bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/VCS.py000066400000000000000000000152711223671746500203270ustar00rootroot00000000000000"""VCS support.""" # TODO: # * add svn support # * integrate properly with reports missing = [] import errno import os import shutil import sys import stat # python-dulwich git imports try: import dulwich import dulwich.index from dulwich.errors import NotGitRepository except ImportError: missing.append('git') # subversion import try: import pysvn except ImportError: missing.append('svn') import Bcfg2.Client.Tools def cleanup_mode(mode): """Cleanup a mode value. This will return a mode that can be stored in a tree object. :param mode: Mode to clean up. """ if stat.S_ISLNK(mode): return stat.S_IFLNK elif stat.S_ISDIR(mode): return stat.S_IFDIR elif dulwich.index.S_ISGITLINK(mode): return dulwich.index.S_IFGITLINK ret = stat.S_IFREG | int('644', 8) ret |= (mode & int('111', 8)) return ret def index_entry_from_stat(stat_val, hex_sha, flags, mode=None): """Create a new index entry from a stat value. :param stat_val: POSIX stat_result instance :param hex_sha: Hex sha of the object :param flags: Index flags """ if mode is None: mode = cleanup_mode(stat_val.st_mode) return (stat_val.st_ctime, stat_val.st_mtime, stat_val.st_dev, stat_val.st_ino, mode, stat_val.st_uid, stat_val.st_gid, stat_val.st_size, hex_sha, flags) class VCS(Bcfg2.Client.Tools.Tool): """VCS support.""" __handles__ = [('Path', 'vcs')] __req__ = {'Path': ['name', 'type', 'vcstype', 'sourceurl', 'revision']} def git_write_index(self, entry): """Write the git index""" pass def Verifygit(self, entry, _): """Verify git repositories""" try: repo = dulwich.repo.Repo(entry.get('name')) except NotGitRepository: self.logger.info("Repository %s does not exist" % entry.get('name')) return False try: expected_rev = entry.get('revision') cur_rev = repo.head() except: return False try: client, path = dulwich.client.get_transport_and_path(entry.get('sourceurl')) remote_refs = client.fetch_pack(path, (lambda x: None), None, None, None) if expected_rev in remote_refs: expected_rev = remote_refs[expected_rev] except: pass if cur_rev != expected_rev: self.logger.info("At revision %s need to go to revision %s" % (cur_rev.strip(), expected_rev.strip())) return False return True def Installgit(self, entry): """Checkout contents from a git repository""" destname = entry.get('name') if os.path.lexists(destname): # remove incorrect contents try: if os.path.isdir(destname): shutil.rmtree(destname) else: os.remove(destname) except OSError: self.logger.info('Failed to remove %s' % destname) return False dulwich.file.ensure_dir_exists(destname) destr = dulwich.repo.Repo.init(destname) cl, host_path = dulwich.client.get_transport_and_path(entry.get('sourceurl')) remote_refs = cl.fetch(host_path, destr, determine_wants=destr.object_store.determine_wants_all, progress=sys.stdout.write) if entry.get('revision') in remote_refs: destr.refs['HEAD'] = remote_refs[entry.get('revision')] else: destr.refs['HEAD'] = entry.get('revision') dtree = destr['HEAD'].tree index = dulwich.index.Index(destr.index_path()) for fname, mode, sha in destr.object_store.iter_tree_contents(dtree): full_path = os.path.join(destname, fname) dulwich.file.ensure_dir_exists(os.path.dirname(full_path)) if stat.S_ISLNK(mode): src_path = destr[sha].as_raw_string() try: os.symlink(src_path, full_path) except OSError: e = sys.exc_info()[1] if e.errno == errno.EEXIST: os.unlink(full_path) os.symlink(src_path, full_path) else: raise else: file = open(full_path, 'wb') file.write(destr[sha].as_raw_string()) file.close() os.chmod(full_path, mode) st = os.lstat(full_path) index[fname] = index_entry_from_stat(st, sha, 0) index.write() return True def Verifysvn(self, entry, _): """Verify svn repositories""" headrev = pysvn.Revision( pysvn.opt_revision_kind.head ) client = pysvn.Client() try: cur_rev = str(client.info(entry.get('name')).revision.number) server = client.info2(entry.get('sourceurl'), headrev, recurse=False) if server: server_rev = str(server[0][1].rev.number) except: self.logger.info("Repository %s does not exist" % entry.get('name')) return False if entry.get('revision') == 'latest' and cur_rev == server_rev: return True if cur_rev != entry.get('revision'): self.logger.info("At revision %s need to go to revision %s" % (cur_rev, entry.get('revision'))) return False return True def Installsvn(self, entry): """Checkout contents from a svn repository""" # pylint: disable=E1101 client = pysvn.Client() try: client.update(entry.get('name'), recurse=True) except pysvn.ClientError: self.logger.error("Failed to update repository", exc_info=1) return False return True # pylint: enable=E1101 def VerifyPath(self, entry, _): vcs = entry.get('vcstype') if vcs in missing: self.logger.error("Missing %s python libraries. Cannot verify" % vcs) return False ret = getattr(self, 'Verify%s' % vcs) return ret(entry, _) def InstallPath(self, entry): vcs = entry.get('vcstype') if vcs in missing: self.logger.error("Missing %s python libraries. " "Unable to install" % vcs) return False ret = getattr(self, 'Install%s' % vcs) return ret(entry) bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/YUM.py000066400000000000000000001307061223671746500203470ustar00rootroot00000000000000"""This provides bcfg2 support for yum.""" import copy import os.path import sys import yum import yum.packages import yum.rpmtrans import yum.callbacks import yum.Errors import yum.misc import rpmUtils.arch import Bcfg2.Client.XML import Bcfg2.Client.Tools def build_yname(pkgname, inst): """Build yum appropriate package name.""" rv = {} if isinstance(inst, yum.packages.PackageObject): for i in ['name', 'epoch', 'version', 'release', 'arch']: rv[i] = getattr(inst, i) else: rv['name'] = pkgname if inst.get('version') != 'any': rv['version'] = inst.get('version') if inst.get('epoch', False): rv['epoch'] = inst.get('epoch') if inst.get('release', False) and inst.get('release') != 'any': rv['release'] = inst.get('release') if inst.get('arch', False) and inst.get('arch') != 'any': rv['arch'] = inst.get('arch') return rv def short_yname(nevra): """ given a nevra dict, get a dict of options to pass to functions like yum.YumBase.rpmdb.searchNevra(), which expect short names (e.g., "rel" instead of "release") """ rv = nevra.copy() if 'version' in rv: rv['ver'] = rv['version'] del rv['version'] if 'release' in rv: rv['rel'] = rv['release'] del rv['release'] return rv def nevra2string(pkg): """ convert a yum package object or nevra dict to a friendly human-readable string """ if isinstance(pkg, yum.packages.PackageObject): return str(pkg) else: ret = [] for attr, fmt in [('epoch', '%s:'), ('name', '%s'), ('version', '-%s'), ('release', '-%s'), ('arch', '.%s')]: if attr in pkg: ret.append(fmt % pkg[attr]) return "".join(ret) class RPMDisplay(yum.rpmtrans.RPMBaseCallback): """We subclass the default RPM transaction callback so that we can control Yum's verbosity and pipe it through the right logger.""" def __init__(self, logger): yum.rpmtrans.RPMBaseCallback.__init__(self) # we want to log events to *both* the Bcfg2 logger (which goes # to stderr or syslog or wherever the user wants it to go) # *and* the yum file logger, which will go to yum.log (ticket # #1103) self.bcfg2_logger = logger self.state = None self.package = None def event(self, package, action, te_current, te_total, ts_current, ts_total): """ @param package: A yum package object or simple string of a package name @param action: A yum.constant transaction set state or in the obscure rpm repackage case it could be the string 'repackaging' @param te_current: Current number of bytes processed in the transaction element being processed @param te_total: Total number of bytes in the transaction element being processed @param ts_current: number of processes completed in whole transaction @param ts_total: total number of processes in the transaction. """ if self.package != str(package) or action != self.state: self.bcfg2_logger.info("%s: %s" % (self.action[action], package)) self.state = action self.package = str(package) def scriptout(self, package, msgs): """Handle output from package scripts.""" if msgs: msg = "%s: %s" % (package, msgs) self.bcfg2_logger.debug(msg) def errorlog(self, msg): """Deal with error reporting.""" self.bcfg2_logger.error(msg) class YumDisplay(yum.callbacks.ProcessTransBaseCallback): """Class to handle display of what step we are in the Yum transaction such as downloading packages, etc.""" def __init__(self, logger): yum.callbacks.ProcessTransBaseCallback.__init__(self) self.logger = logger class YUM(Bcfg2.Client.Tools.PkgTool): """Support for Yum packages.""" pkgtype = 'yum' __execs__ = [] __handles__ = [('Package', 'yum'), ('Package', 'rpm'), ('Path', 'ignore')] __req__ = {'Package': ['type'], 'Path': ['type']} conflicts = ['YUM24', 'RPM', 'RPMng', 'YUMng'] def __init__(self, logger, setup, config): self.yumbase = self._loadYumBase(setup=setup, logger=logger) Bcfg2.Client.Tools.PkgTool.__init__(self, logger, setup, config) self.ignores = [] for struct in config: self.ignores.extend([entry.get('name') for entry in struct if (entry.tag == 'Path' and entry.get('type') == 'ignore')]) self.instance_status = {} self.extra_instances = [] self.modlists = {} for struct in config: self.__important__.extend( [entry.get('name') for entry in struct if (entry.tag == 'Path' and (entry.get('name').startswith('/etc/yum.d') or entry.get('name').startswith('/etc/yum.repos.d')) or entry.get('name') == '/etc/yum.conf')]) self.yum_avail = dict() self.yum_installed = dict() self.verify_cache = dict() yup = self.yumbase.doPackageLists(pkgnarrow='updates') if hasattr(self.yumbase.rpmdb, 'pkglist'): yinst = self.yumbase.rpmdb.pkglist else: yinst = self.yumbase.rpmdb.getPkgList() for dest, source in [(self.yum_avail, yup.updates), (self.yum_installed, yinst)]: for pkg in source: if dest is self.yum_avail: pname = pkg.name data = [(pkg.arch, (pkg.epoch, pkg.version, pkg.release))] else: pname = pkg[0] data = [(pkg[1], (pkg[2], pkg[3], pkg[4]))] if pname in dest: dest[pname].update(data) else: dest[pname] = dict(data) # Process the Yum section from the config file. These are all # boolean flags, either we do stuff or we don't self.pkg_checks = self.setup["yum_pkg_checks"] self.pkg_verify = self.setup["yum_pkg_verify"] self.do_install = self.setup["yum_installed_action"] == "install" self.do_upgrade = self.setup["yum_version_fail_action"] == "upgrade" self.do_reinst = self.setup["yum_verify_fail_action"] == "reinstall" self.verify_flags = self.setup["yum_verify_flags"] self.installonlypkgs = self.yumbase.conf.installonlypkgs if 'gpg-pubkey' not in self.installonlypkgs: self.installonlypkgs.append('gpg-pubkey') self.logger.debug("Yum: Install missing: %s" % self.do_install) self.logger.debug("Yum: pkg_checks: %s" % self.pkg_checks) self.logger.debug("Yum: pkg_verify: %s" % self.pkg_verify) self.logger.debug("Yum: Upgrade on version fail: %s" % self.do_upgrade) self.logger.debug("Yum: Reinstall on verify fail: %s" % self.do_reinst) self.logger.debug("Yum: installonlypkgs: %s" % self.installonlypkgs) self.logger.debug("Yum: verify_flags: %s" % self.verify_flags) def _loadYumBase(self, setup=None, logger=None): ''' this may be called before PkgTool.__init__() is called on this object (when the YUM object is first instantiated; PkgTool.__init__() calls RefreshPackages(), which requires a YumBase object already exist), or after __init__() has completed, when we reload the yum config before installing packages. Consequently, we support both methods by allowing setup and logger, the only object properties we use in this function, to be passed as keyword arguments or to be omitted and drawn from the object itself.''' rv = yum.YumBase() # pylint: disable=C0103 if setup is None: setup = self.setup if logger is None: logger = self.logger if setup['debug']: debuglevel = 3 elif setup['verbose']: debuglevel = 2 else: debuglevel = 0 # pylint: disable=E1121,W0212 try: rv.preconf.debuglevel = debuglevel rv._getConfig() except AttributeError: rv._getConfig(self.yumbase.conf.config_file_path, debuglevel=debuglevel) # pylint: enable=E1121,W0212 try: rv.doConfigSetup() rv.doTsSetup() rv.doRpmDBSetup() except yum.Errors.RepoError: logger.error("YUM Repository error: %s" % sys.exc_info()[1]) raise Bcfg2.Client.Tools.ToolInstantiationError except Exception: logger.error("Yum error: %s" % sys.exc_info()[1]) raise Bcfg2.Client.Tools.ToolInstantiationError return rv def _fixAutoVersion(self, entry): """ handle entries with version="auto" by setting the version to the newest available """ # old style entry; synthesize Instances from current installed if (entry.get('name') not in self.yum_installed and entry.get('name') not in self.yum_avail): # new entry; fall back to default entry.set('version', 'any') else: data = copy.copy(self.yum_installed[entry.get('name')]) if entry.get('name') in self.yum_avail: # installed but out of date data.update(self.yum_avail[entry.get('name')]) for (arch, (epoch, vers, rel)) in list(data.items()): inst = Bcfg2.Client.XML.SubElement(entry, "Instance", name=entry.get('name'), version=vers, arch=arch, release=rel, epoch=epoch) if 'verify_flags' in entry.attrib: inst.set('verify_flags', entry.get('verify_flags')) if 'verify' in entry.attrib: inst.set('verify', entry.get('verify')) def _buildInstances(self, entry): """ get a list of all instances of the package from the given entry. converts from a Package entry without any Instance tags as necessary """ instances = [inst for inst in entry if inst.tag == 'Instance' or inst.tag == 'Package'] # Uniquify instances. Cases where duplicates are returned. # However, the elements aren't comparable. if instances == []: # We have an old style no Instance entry. Convert it to new style. instance = Bcfg2.Client.XML.SubElement(entry, 'Package') for attrib in list(entry.attrib.keys()): instance.attrib[attrib] = entry.attrib[attrib] instances = [instance] return instances def _getGPGKeysAsPackages(self): """Return a list of the GPG RPM signing keys installed on the system as a list of Package Objects.""" # GPG keys existing in the RPMDB have numbered days # and newer Yum versions will not return information about them if hasattr(self.yumbase.rpmdb, 'returnGPGPubkeyPackages'): return self.yumbase.rpmdb.returnGPGPubkeyPackages() return self.yumbase.rpmdb.searchNevra(name='gpg-pubkey') def missing_attrs(self, entry): """ Implementing from superclass to check for existence of either name or group attribute for Package entry in the case of a YUM group. """ missing = Bcfg2.Client.Tools.PkgTool.missing_attrs(self, entry) if (entry.get('name', None) is None and entry.get('group', None) is None): missing += ['name', 'group'] return missing def _verifyHelper(self, pkg_obj): """ _verifyHelper primarly deals with a yum bug where the pkg_obj.verify() method does not properly take into count multilib sharing of files. Neither does RPM proper, really....it just ignores the problem. """ def verify(pkg): """ helper to perform the verify according to the best options for whatever version of the API we're using. Disabling file checksums is a new feature yum 3.2.17-ish """ try: return pkg.verify(fast=self.setup.get('quick', False)) except TypeError: # Older Yum API return pkg.verify() key = (pkg_obj.name, pkg_obj.epoch, pkg_obj.version, pkg_obj.release, pkg_obj.arch) if key in self.verify_cache: results = self.verify_cache[key] else: results = verify(pkg_obj) self.verify_cache[key] = results if not rpmUtils.arch.isMultiLibArch(): return results # Okay deal with a buggy yum multilib and verify. first find # all arches of pkg packages = self.yumbase.rpmdb.searchNevra(name=pkg_obj.name, epoch=pkg_obj.epoch, ver=pkg_obj.version, rel=pkg_obj.release) if len(packages) == 1: return results # No mathcing multilib packages # Will be the list of common fnames files = set(pkg_obj.returnFileEntries()) common = {} for pkg in packages: if pkg != pkg_obj: files = files & set(pkg.returnFileEntries()) for pkg in packages: key = (pkg.name, pkg.epoch, pkg.version, pkg.release, pkg.arch) self.logger.debug("Multilib Verify: comparing %s to %s" % (pkg_obj, pkg)) if key not in self.verify_cache: self.verify_cache[key] = verify(pkg) for fname in list(self.verify_cache[key].keys()): # file problems must exist in ALL multilib packages to be real if fname in files: common[fname] = common.get(fname, 0) + 1 flag = len(packages) - 1 for fname, i in list(common.items()): if i == flag: # this fname had verify problems in all but one of the multilib # packages. That means its correct in the package that's # "on top." Therefore, this is a fake verify problem. if fname in results: del results[fname] return results def RefreshPackages(self): """ Creates self.installed{} which is a dict of installed packages. The dict items are lists of nevra dicts. This loosely matches the config from the server and what rpmtools uses to specify pacakges. e.g. self.installed['foo'] = [ {'name':'foo', 'epoch':None, 'version':'1', 'release':2, 'arch':'i386'}, {'name':'foo', 'epoch':None, 'version':'1', 'release':2, 'arch':'x86_64'} ] """ self.installed = {} packages = self._getGPGKeysAsPackages() + \ self.yumbase.rpmdb.returnPackages() for pkg in packages: pattrs = {} for i in ['name', 'epoch', 'version', 'release', 'arch']: if i == 'arch' and getattr(pkg, i) is None: pattrs[i] = 'noarch' elif i == 'epoch' and getattr(pkg, i) is None: pattrs[i] = '0' else: pattrs[i] = getattr(pkg, i) self.installed.setdefault(pkg.name, []).append(pattrs) # pylint: disable=R0914,R0912,R0915 def VerifyPackage(self, entry, modlist): """ Verify Package status for entry. Performs the following: * Checks for the presence of required Package Instances. * Compares the evra 'version' info against self.installed{}. * RPM level package verify (rpm --verify). * Checks for the presence of unrequired package instances. Produces the following dict and list for Yum.Install() to use: * For installs/upgrades/fixes of required instances:: instance_status = { : { 'installed': True|False, 'version_fail': True|False, 'verify_fail': True|False, 'pkg': , 'modlist': [ , ... ], 'verify' : [ ] }, ...... } * For deletions of unrequired instances:: extra_instances = [ , ..... ] Constructs the text prompts for interactive mode. """ if entry.get('version', False) == 'auto': self._fixAutoVersion(entry) if entry.get('group'): self.logger.debug("Verifying packages for group %s" % entry.get('group')) else: self.logger.debug("Verifying package instances for %s" % entry.get('name')) self.verify_cache = dict() # Used for checking multilib packages self.modlists[entry] = modlist instances = self._buildInstances(entry) pkg_cache = [] package_fail = False qtext_versions = [] virt_pkg = False pkg_checks = (self.pkg_checks and entry.get('pkg_checks', 'true').lower() == 'true') pkg_verify = (self.pkg_verify and entry.get('pkg_verify', 'true').lower() == 'true') yum_group = False if entry.get('name') == 'gpg-pubkey': all_pkg_objs = self._getGPGKeysAsPackages() pkg_verify = False # No files here to verify elif entry.get('group'): entry.set('name', 'group:%s' % entry.get('group')) yum_group = True all_pkg_objs = [] instances = [] if self.yumbase.comps.has_group(entry.get('group')): group = self.yumbase.comps.return_group(entry.get('group')) group_packages = [p for p, d in group.mandatory_packages.items() if d] group_type = entry.get('choose', 'default') if group_type in ['default', 'optional', 'all']: group_packages += [ p for p, d in group.default_packages.items() if d] if group_type in ['optional', 'all']: group_packages += [ p for p, d in group.optional_packages.items() if d] if len(group_packages) == 0: self.logger.error("No packages found for group %s" % entry.get("group")) for pkg in group_packages: # create package instances for each package in yum group instance = Bcfg2.Client.XML.SubElement(entry, 'Package') instance.attrib['name'] = pkg instance.attrib['type'] = 'yum' try: newest = \ self.yumbase.pkgSack.returnNewestByName(pkg)[0] instance.attrib['version'] = newest['version'] instance.attrib['epoch'] = newest['epoch'] instance.attrib['release'] = newest['release'] except: # pylint: disable=W0702 self.logger.info("Error finding newest package " "for %s" % pkg) instance.attrib['version'] = 'any' instances.append(instance) else: self.logger.error("Group not found: %s" % entry.get("group")) else: all_pkg_objs = \ self.yumbase.rpmdb.searchNevra(name=entry.get('name')) if len(all_pkg_objs) == 0 and yum_group is not True: # Some sort of virtual capability? Try to resolve it all_pkg_objs = self.yumbase.rpmdb.searchProvides(entry.get('name')) if len(all_pkg_objs) > 0: virt_pkg = True self.logger.info("%s appears to be provided by:" % entry.get('name')) for pkg in all_pkg_objs: self.logger.info(" %s" % pkg) for inst in instances: if yum_group: # the entry is not the name of the package nevra = build_yname(inst.get('name'), inst) all_pkg_objs = \ self.yumbase.rpmdb.searchNevra(name=inst.get('name')) else: nevra = build_yname(entry.get('name'), inst) if nevra in pkg_cache: continue # Ignore duplicate instances else: pkg_cache.append(nevra) self.logger.debug("Verifying: %s" % nevra2string(nevra)) # Set some defaults here stat = self.instance_status.setdefault(inst, {}) stat['installed'] = True stat['version_fail'] = False stat['verify'] = {} stat['verify_fail'] = False if yum_group: stat['pkg'] = inst else: stat['pkg'] = entry stat['modlist'] = modlist if inst.get('verify_flags'): # this splits on either space or comma verify_flags = \ inst.get('verify_flags').lower().replace(' ', ',').split(',') else: verify_flags = self.verify_flags if 'arch' in nevra: # If arch is specified use it to select the package pkg_objs = [p for p in all_pkg_objs if p.arch == nevra['arch']] else: pkg_objs = all_pkg_objs if len(pkg_objs) == 0: # Package (name, arch) not installed entry.set('current_exists', 'false') self.logger.debug(" %s is not installed" % nevra2string(nevra)) stat['installed'] = False package_fail = True qtext_versions.append("I(%s)" % nevra) continue if not pkg_checks: continue # Check EVR if virt_pkg: # we need to make sure that the version of the symbol # provided matches the one required in the # configuration vlist = [] for attr in ["epoch", "version", "release"]: vlist.append(nevra.get(attr)) if tuple(vlist) == (None, None, None): # we just require the package name, no particular # version, so just make a copy of all_pkg_objs since every # package that provides this symbol satisfies the # requirement pkg_objs = [po for po in all_pkg_objs] else: pkg_objs = [po for po in all_pkg_objs if po.checkPrco('provides', (nevra["name"], 'EQ', tuple(vlist)))] elif entry.get('name') == 'gpg-pubkey': if 'version' not in nevra: self.logger.warning("Skipping verify: gpg-pubkey without " "an RPM version") continue if 'release' not in nevra: self.logger.warning("Skipping verify: gpg-pubkey without " "an RPM release") continue pkg_objs = [p for p in all_pkg_objs if (p.version == nevra['version'] and p.release == nevra['release'])] else: pkg_objs = self.yumbase.rpmdb.searchNevra(**short_yname(nevra)) if len(pkg_objs) == 0: package_fail = True stat['version_fail'] = True # Just chose the first pkg for the error message if virt_pkg: provides = \ [p for p in all_pkg_objs[0].provides if p[0] == entry.get("name")][0] entry.set('current_version', "%s:%s-%s" % provides[2]) self.logger.info( " %s: Wrong version installed. " "Want %s, but %s provides %s" % (entry.get("name"), nevra2string(nevra), nevra2string(all_pkg_objs[0]), yum.misc.prco_tuple_to_string(provides))) else: entry.set('current_version', "%s:%s-%s.%s" % (all_pkg_objs[0].epoch, all_pkg_objs[0].version, all_pkg_objs[0].release, all_pkg_objs[0].arch)) self.logger.info(" %s: Wrong version installed. " "Want %s, but have %s" % (entry.get("name"), nevra2string(nevra), nevra2string(all_pkg_objs[0]))) entry.set('version', "%s:%s-%s.%s" % (nevra.get('epoch', 'any'), nevra.get('version', 'any'), nevra.get('release', 'any'), nevra.get('arch', 'any'))) qtext_versions.append("U(%s)" % str(all_pkg_objs[0])) continue if self.setup.get('quick', False): # Passed -q on the command line continue if not (pkg_verify and inst.get('pkg_verify', 'true').lower() == 'true'): continue # XXX: We ignore GPG sig checking the package as it # has nothing to do with the individual file hash/size/etc. # GPG checking the package only eaxmines some header/rpmdb # wacky-ness, and will not properly detect a compromised rpmdb. # Yum's verify routine does not support it for that reaosn. if len(pkg_objs) > 1: self.logger.debug(" Verify Instance found many packages:") for pkg in pkg_objs: self.logger.debug(" %s" % str(pkg)) try: vrfy_result = self._verifyHelper(pkg_objs[0]) except: # pylint: disable=W0702 err = sys.exc_info()[1] # Unknown Yum exception self.logger.warning(" Verify Exception: %s" % err) package_fail = True continue # Now take out the Yum specific objects / modlists / unproblems ignores = [ig.get('name') for ig in entry.findall('Ignore')] + \ [ig.get('name') for ig in inst.findall('Ignore')] + \ self.ignores for fname, probs in list(vrfy_result.items()): if fname in modlist: self.logger.debug(" %s in modlist, skipping" % fname) continue if fname in ignores: self.logger.debug(" %s in ignore list, skipping" % fname) continue tmp = [] for prob in probs: if prob.type == 'missing' and os.path.islink(fname): continue elif 'no' + prob.type in verify_flags: continue if prob.type not in ['missingok', 'ghost']: tmp.append((prob.type, prob.message)) if tmp != []: stat['verify'][fname] = tmp if stat['verify'] != {}: stat['verify_fail'] = True package_fail = True self.logger.info("It is suggested that you either manage " "these files, revert the changes, or ignore " "false failures:") self.logger.info(" Verify Problems: %s" % stat['pkg'].get('name')) for fname, probs in list(stat['verify'].items()): if len(probs) > 1: self.logger.info(" %s" % fname) for prob in probs: self.logger.info(" %s" % prob[1]) else: self.logger.info(" %s: %s" % (fname, probs[0])) if len(all_pkg_objs) > 0: # Is this an install only package? We just look at the first one provides = set([p[0] for p in all_pkg_objs[0].provides] + [all_pkg_objs[0].name]) install_only = len(set(self.installonlypkgs) & provides) > 0 else: install_only = False if virt_pkg or \ (install_only and not self.setup['kevlar']) or \ yum_group: # virtual capability supplied, we are probably dealing # with multiple packages of different names. This check # doesn't make a lot of since in this case. # install_only: Yum may clean some of these up itself. # Otherwise having multiple instances of install only packages # is considered correct self.extra_instances = None else: self.extra_instances = self.FindExtraInstances(entry, all_pkg_objs) if self.extra_instances is not None: package_fail = True return not package_fail # pylint: enable=R0914,R0912,R0915 def FindExtraInstances(self, entry, all_pkg_objs): """ Check for installed instances that are not in the config. Return a Package Entry with Instances to remove, or None if there are no Instances to remove. """ if len(all_pkg_objs) == 0: return None name = entry.get('name') extra_entry = Bcfg2.Client.XML.Element('Package', name=name, type=self.pkgtype) instances = self._buildInstances(entry) pkg_objs = [p for p in all_pkg_objs] # Shallow copy # Algorythm is sensitive to duplicates, check for them checked = [] for inst in instances: nevra = build_yname(name, inst) pkgs = self.yumbase.rpmdb.searchNevra(**short_yname(nevra)) if len(pkgs) > 0: if pkgs[0] in checked: continue # We've already taken care of this Instance else: checked.append(pkgs[0]) pkg_objs.remove(pkgs[0]) for pkg in pkg_objs: self.logger.debug(" Extra Instance Found: %s" % str(pkg)) Bcfg2.Client.XML.SubElement(extra_entry, 'Instance', epoch=pkg.epoch, name=pkg.name, version=pkg.version, release=pkg.release, arch=pkg.arch) if pkg_objs == []: return None else: return extra_entry def FindExtra(self): """Find extra packages.""" packages = [e.get('name') for e in self.getSupportedEntries()] extras = [] for pkg in list(self.installed.keys()): if pkg not in packages: entry = Bcfg2.Client.XML.Element('Package', name=pkg, type=self.pkgtype) for i in self.installed[pkg]: Bcfg2.Client.XML.SubElement(entry, 'Instance', epoch=i['epoch'], version=i['version'], release=i['release'], arch=i['arch']) extras.append(entry) return extras def _installGPGKey(self, inst, key_file): """Examine the GPG keys carefully before installation. Avoid installing duplicate keys. Returns True on successful install.""" # RPM Transaction Set tset = self.yumbase.rpmdb.readOnlyTS() if not os.path.exists(key_file): self.logger.debug("GPG Key file %s not installed" % key_file) return False rawkey = open(key_file).read() gpg = yum.misc.getgpgkeyinfo(rawkey) ver = yum.misc.keyIdToRPMVer(gpg['keyid']) rel = yum.misc.keyIdToRPMVer(gpg['timestamp']) if not (ver == inst.get('version') and rel == inst.get('release')): self.logger.info("GPG key file %s does not match gpg-pubkey-%s-%s" % (key_file, inst.get('version'), inst.get('release'))) return False if not yum.misc.keyInstalled(tset, gpg['keyid'], gpg['timestamp']) == 0: result = tset.pgpImportPubkey(yum.misc.procgpgkey(rawkey)) else: self.logger.debug("gpg-pubkey-%s-%s already installed" % (inst.get('version'), inst.get('release'))) return True if result != 0: self.logger.debug( "Unable to install %s-%s" % (self.instance_status[inst].get('pkg').get('name'), nevra2string(inst))) return False else: self.logger.debug( "Installed %s-%s-%s" % (self.instance_status[inst].get('pkg').get('name'), inst.get('version'), inst.get('release'))) return True def _runYumTransaction(self): """ run the yum transaction that has already been set up """ def cleanup(): """ clean up open stuff when we hit an error """ self.yumbase.closeRpmDB() self.RefreshPackages() rpm_display = RPMDisplay(self.logger) yum_display = YumDisplay(self.logger) # Run the Yum Transaction try: rescode, restring = self.yumbase.buildTransaction() except yum.Errors.YumBaseError: err = sys.exc_info()[1] self.logger.error("Error building Yum transaction: %s" % err) cleanup() return self.logger.debug("Initial Yum buildTransaction() run said:") self.logger.debug(" resultcode: %s, msgs: %s" % (rescode, restring)) if rescode != 1: # Transaction built successfully, run it try: self.yumbase.processTransaction(callback=yum_display, rpmDisplay=rpm_display) self.logger.info("Single Pass for Install Succeeded") except yum.Errors.YumBaseError: err = sys.exc_info()[1] self.logger.error("Error processing Yum transaction: %s" % err) cleanup() return else: # The yum command failed. No packages installed. # Try installing instances individually. self.logger.error("Single Pass Install of Packages Failed") skip_broken = self.yumbase.conf.skip_broken self.yumbase.conf.skip_broken = True try: rescode, restring = self.yumbase.buildTransaction() if rescode != 1: self.yumbase.processTransaction(callback=yum_display, rpmDisplay=rpm_display) self.logger.debug( "Second pass install did not install all packages") else: self.logger.error("Second pass yum install failed.") self.logger.debug(" %s" % restring) except yum.Errors.YumBaseError: err = sys.exc_info()[1] self.logger.error("Error rerunning Yum transaction: %s" % err) self.yumbase.conf.skip_broken = skip_broken cleanup() def Install(self, packages, states): # pylint: disable=R0912,R0914 """ Try and fix everything that Yum.VerifyPackages() found wrong for each Package Entry. This can result in individual RPMs being installed (for the first time), deleted, downgraded or upgraded. packages is a list of Package Elements that has states[] == False The following effects occur: - states{} is conditionally updated for each package. - self.installed{} is rebuilt, possibly multiple times. - self.instance_status{} is conditionally updated for each instance of a package. - Each package will be added to self.modified[] if its states{} entry is set to True. """ self.logger.debug('Running Yum.Install()') install_pkgs = [] gpg_keys = [] upgrade_pkgs = [] reinstall_pkgs = [] def queue_pkg(pkg, inst, queue): """ add a package to the appropriate work queue -- packages to install, packages to upgrade, etc. """ if pkg.get('name') == 'gpg-pubkey': gpg_keys.append(inst) else: queue.append(inst) # Remove extra instances. # Can not reverify because we don't have a package entry. if self.extra_instances is not None and len(self.extra_instances) > 0: if (self.setup.get('remove') == 'all' or self.setup.get('remove') == 'packages'): self.Remove(self.extra_instances) else: self.logger.info("The following extra package instances will " "be removed by the '-r' option:") for pkg in self.extra_instances: for inst in pkg: self.logger.info(" %s %s" % ((pkg.get('name'), nevra2string(inst)))) # Figure out which instances of the packages actually need something # doing to them and place in the appropriate work 'queue'. for pkg in packages: insts = [pinst for pinst in pkg if pinst.tag in ['Instance', 'Package']] if insts: for inst in insts: if inst not in self.instance_status: self.logger.warning( " Asked to install/update package never " "verified: %s" % nevra2string(build_yname(pkg.get('name'), inst))) continue status = self.instance_status[inst] if not status.get('installed', False) and self.do_install: queue_pkg(pkg, inst, install_pkgs) elif status.get('version_fail', False) and self.do_upgrade: queue_pkg(pkg, inst, upgrade_pkgs) elif status.get('verify_fail', False) and self.do_reinst: queue_pkg(pkg, inst, reinstall_pkgs) else: # Either there was no Install/Version/Verify # task to be done or the user disabled the actions # in the configuration. XXX Logging for the latter? pass else: msg = "Yum: Package tag found where Instance expected: %s" self.logger.warning(msg % pkg.get('name')) queue_pkg(pkg, pkg, install_pkgs) # Install GPG keys. # Alternatively specify the required keys using 'gpgkey' in the # repository definition in yum.conf. YUM will install the keys # automatically. if len(gpg_keys) > 0: self.logger.info("Installing GPG keys.") for inst in gpg_keys: if inst.get('simplefile') is None: self.logger.error("GPG key has no simplefile attribute") continue key_file = os.path.join( self.instance_status[inst].get('pkg').get('uri'), inst.get('simplefile')) self._installGPGKey(inst, key_file) self.RefreshPackages() pkg = self.instance_status[gpg_keys[0]].get('pkg') states[pkg] = self.VerifyPackage(pkg, []) # We want to reload all Yum configuration in case we've # deployed new .repo files we should consider self._loadYumBase() # Install packages. if len(install_pkgs) > 0: self.logger.info("Attempting to install packages") for inst in install_pkgs: pkg_arg = self.instance_status[inst].get('pkg').get('name') self.logger.debug("Installing %s" % pkg_arg) try: self.yumbase.install(**build_yname(pkg_arg, inst)) except yum.Errors.YumBaseError: yume = sys.exc_info()[1] self.logger.error("Error installing package %s: %s" % (pkg_arg, yume)) if len(upgrade_pkgs) > 0: self.logger.info("Attempting to upgrade packages") for inst in upgrade_pkgs: pkg_arg = self.instance_status[inst].get('pkg').get('name') self.logger.debug("Upgrading %s" % pkg_arg) try: self.yumbase.update(**build_yname(pkg_arg, inst)) except yum.Errors.YumBaseError: yume = sys.exc_info()[1] self.logger.error("Error upgrading package %s: %s" % (pkg_arg, yume)) if len(reinstall_pkgs) > 0: self.logger.info("Attempting to reinstall packages") for inst in reinstall_pkgs: pkg_arg = self.instance_status[inst].get('pkg').get('name') self.logger.debug("Reinstalling %s" % pkg_arg) try: self.yumbase.reinstall(**build_yname(pkg_arg, inst)) except yum.Errors.YumBaseError: yume = sys.exc_info()[1] self.logger.error("Error reinstalling package %s: %s" % (pkg_arg, yume)) self._runYumTransaction() if not self.setup['kevlar']: for pkg_entry in [p for p in packages if self.canVerify(p)]: self.logger.debug("Reverifying Failed Package %s" % pkg_entry.get('name')) states[pkg_entry] = \ self.VerifyPackage(pkg_entry, self.modlists.get(pkg_entry, [])) for entry in [ent for ent in packages if states[ent]]: self.modified.append(entry) def Remove(self, packages): """ Remove specified entries. packages is a list of Package Entries with Instances generated by FindExtra(). """ self.logger.debug('Running Yum.Remove()') for pkg in packages: for inst in pkg: nevra = build_yname(pkg.get('name'), inst) if pkg.get('name') != 'gpg-pubkey': self.yumbase.remove(**nevra) self.modified.append(pkg) else: self.logger.info("WARNING: gpg-pubkey package not in " "configuration %s %s-%s" % (nevra['name'], nevra['version'], nevra['release'])) self._runYumTransaction() self.extra = self.FindExtra() def VerifyPath(self, entry, _): # pylint: disable=W0613 """Do nothing here since we only verify Path type=ignore""" return True bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/YUM24.py000066400000000000000000000437021223671746500205140ustar00rootroot00000000000000"""This provides bcfg2 support for yum.""" import copy import os.path import sys import yum import Bcfg2.Client.XML from Bcfg2.Client.Tools.RPM import RPM def build_yname(pkgname, inst): """Build yum appropriate package name.""" ypname = pkgname if inst.get('version') != 'any': ypname += '-' if inst.get('epoch', False): ypname += "%s:" % inst.get('epoch') if inst.get('version', False) and inst.get('version') != 'any': ypname += "%s" % (inst.get('version')) if inst.get('release', False) and inst.get('release') != 'any': ypname += "-%s" % (inst.get('release')) if inst.get('arch', False) and inst.get('arch') != 'any': ypname += ".%s" % (inst.get('arch')) return ypname class YUM24(RPM): """Support for Yum packages.""" pkgtype = 'yum' deprecated = True __execs__ = ['/usr/bin/yum', '/var/lib/rpm'] __handles__ = [('Package', 'yum'), ('Package', 'rpm'), ('Path', 'ignore')] __req__ = {'Package': ['name', 'version']} __ireq__ = {'Package': ['name']} #__ireq__ = {'Package': ['name', 'version']} __new_req__ = {'Package': ['name'], 'Instance': ['version', 'release', 'arch']} __new_ireq__ = {'Package': ['name'], \ 'Instance': []} #__new_ireq__ = {'Package': ['name', 'uri'], \ # 'Instance': ['simplefile', 'version', 'release', 'arch']} __gpg_req__ = {'Package': ['name', 'version']} __gpg_ireq__ = {'Package': ['name', 'version']} __new_gpg_req__ = {'Package': ['name'], 'Instance': ['version', 'release']} __new_gpg_ireq__ = {'Package': ['name'], 'Instance': ['version', 'release']} def __init__(self, logger, setup, config): RPM.__init__(self, logger, setup, config) self.__important__ = self.__important__ + \ [entry.get('name') for struct in config \ for entry in struct \ if entry.tag in ['Path', 'ConfigFile'] and \ (entry.get('name').startswith('/etc/yum.d') \ or entry.get('name').startswith('/etc/yum.repos.d')) \ or entry.get('name') == '/etc/yum.conf'] self.autodep = setup.get("yum24_autodep") self.yum_avail = dict() self.yum_installed = dict() self.yb = yum.YumBase() self.yb.doConfigSetup() self.yb.doTsSetup() self.yb.doRpmDBSetup() yup = self.yb.doPackageLists(pkgnarrow='updates') if hasattr(self.yb.rpmdb, 'pkglist'): yinst = self.yb.rpmdb.pkglist else: yinst = self.yb.rpmdb.getPkgList() for dest, source in [(self.yum_avail, yup.updates), (self.yum_installed, yinst)]: for pkg in source: if dest is self.yum_avail: pname = pkg.name data = {pkg.arch: (pkg.epoch, pkg.version, pkg.release)} else: pname = pkg[0] if pkg[1] is None: a = 'noarch' else: a = pkg[1] if pkg[2] is None: e = '0' else: e = pkg[2] data = {a: (e, pkg[3], pkg[4])} if pname in dest: dest[pname].update(data) else: dest[pname] = dict(data) def VerifyPackage(self, entry, modlist): pinned_version = None if entry.get('version', False) == 'auto': # old style entry; synthesize Instances from current installed if entry.get('name') not in self.yum_installed and \ entry.get('name') not in self.yum_avail: # new entry; fall back to default entry.set('version', 'any') else: data = copy.copy(self.yum_installed[entry.get('name')]) if entry.get('name') in self.yum_avail: # installed but out of date data.update(self.yum_avail[entry.get('name')]) for (arch, (epoch, vers, rel)) in list(data.items()): x = Bcfg2.Client.XML.SubElement(entry, "Instance", name=entry.get('name'), version=vers, arch=arch, release=rel, epoch=epoch) if 'verify_flags' in entry.attrib: x.set('verify_flags', entry.get('verify_flags')) if 'verify' in entry.attrib: x.set('verify', entry.get('verify')) if entry.get('type', False) == 'yum': # Check for virtual provides or packages. If we don't have # this package use Yum to resolve it to a real package name knownPkgs = list(self.yum_installed.keys()) + list(self.yum_avail.keys()) if entry.get('name') not in knownPkgs: # If the package name matches something installed # or available the that's the correct package. try: pkgDict = dict([(i.name, i) for i in \ self.yb.returnPackagesByDep(entry.get('name'))]) except yum.Errors.YumBaseError: e = sys.exc_info()[1] self.logger.error('Yum Error Depsolving for %s: %s' % \ (entry.get('name'), str(e))) pkgDict = {} if len(pkgDict) > 1: # What do we do with multiple packages? s = "YUM24: returnPackagesByDep(%s) returned many packages" self.logger.info(s % entry.get('name')) s = "YUM24: matching packages: %s" self.logger.info(s % str(list(pkgDict.keys()))) pkgs = set(pkgDict.keys()) & set(self.yum_installed.keys()) if len(pkgs) > 0: # Virtual packages matches an installed real package pkg = pkgDict[pkgs.pop()] s = "YUM24: chosing: %s" % pkg.name self.logger.info(s) else: # What's the right package? This will fail verify # and Yum should Do The Right Thing on package install pkg = None elif len(pkgDict) == 1: pkg = list(pkgDict.values())[0] else: # len(pkgDict) == 0 s = "YUM24: returnPackagesByDep(%s) returned no results" self.logger.info(s % entry.get('name')) pkg = None if pkg is not None: s = "YUM24: remapping virtual package %s to %s" self.logger.info(s % (entry.get('name'), pkg.name)) entry.set('name', pkg.name) return RPM.VerifyPackage(self, entry, modlist) def Install(self, packages, states): """ Try and fix everything that YUM24.VerifyPackages() found wrong for each Package Entry. This can result in individual RPMs being installed (for the first time), deleted, downgraded or upgraded. NOTE: YUM can not reinstall a package that it thinks is already installed. packages is a list of Package Elements that has states[] == False The following effects occur: - states{} is conditionally updated for each package. - self.installed{} is rebuilt, possibly multiple times. - self.instance_status{} is conditionally updated for each instance of a package. - Each package will be added to self.modified[] if its states{} entry is set to True. """ self.logger.info('Running YUM24.Install()') install_pkgs = [] gpg_keys = [] upgrade_pkgs = [] # Remove extra instances. # Can not reverify because we don't have a package entry. if len(self.extra_instances) > 0: if (self.setup.get('remove') == 'all' or \ self.setup.get('remove') == 'packages'): self.Remove(self.extra_instances) else: self.logger.info("The following extra package instances will be removed by the '-r' option:") for pkg in self.extra_instances: for inst in pkg: self.logger.info(" %s %s" % \ ((pkg.get('name'), self.str_evra(inst)))) # Figure out which instances of the packages actually need something # doing to them and place in the appropriate work 'queue'. for pkg in packages: insts = [pinst for pinst in pkg \ if pinst.tag in ['Instance', 'Package']] if insts: for inst in insts: if self.FixInstance(inst, self.instance_status[inst]): if self.instance_status[inst].get('installed', False) \ == False: if pkg.get('name') == 'gpg-pubkey': gpg_keys.append(inst) else: install_pkgs.append(inst) elif self.instance_status[inst].get('version_fail', \ False) == True: upgrade_pkgs.append(inst) else: install_pkgs.append(pkg) # Install GPG keys. # Alternatively specify the required keys using 'gpgkey' in the # repository definition in yum.conf. YUM will install the keys # automatically. if len(gpg_keys) > 0: for inst in gpg_keys: self.logger.info("Installing GPG keys.") if inst.get('simplefile') is None: self.logger.error("GPG key has no simplefile attribute") continue key_arg = os.path.join(self.instance_status[inst].get('pkg').get('uri'), \ inst.get('simplefile')) if self.cmd.run("rpm --import %s" % key_arg).success: self.logger.debug("Unable to install %s-%s" % \ (self.instance_status[inst].get('pkg').get('name'), \ self.str_evra(inst))) else: self.logger.debug("Installed %s-%s-%s" % \ (self.instance_status[inst].get('pkg').get('name'), \ inst.get('version'), inst.get('release'))) self.RefreshPackages() self.gpg_keyids = self.getinstalledgpg() pkg = self.instance_status[gpg_keys[0]].get('pkg') states[pkg] = self.VerifyPackage(pkg, []) # Install packages. if len(install_pkgs) > 0: self.logger.info("Attempting to install packages") if self.autodep: pkgtool = "/usr/bin/yum -d0 -y install %s" else: pkgtool = "/usr/bin/yum -d0 install %s" install_args = [] for inst in install_pkgs: pkg_arg = self.instance_status[inst].get('pkg').get('name') install_args.append(build_yname(pkg_arg, inst)) if self.cmd.run(pkgtool % " ".join(install_args)).success: # The yum command succeeded. All packages installed. self.logger.info("Single Pass for Install Succeeded") self.RefreshPackages() else: # The yum command failed. No packages installed. # Try installing instances individually. self.logger.error("Single Pass Install of Packages Failed") installed_instances = [] for inst in install_pkgs: pkg_arg = build_yname(self.instance_status[inst].get('pkg').get('name'), inst) if self.cmd.run(pkgtool % pkg_arg).success: installed_instances.append(inst) else: self.logger.debug("%s %s would not install." % (self.instance_status[inst].get('pkg').get('name'), self.str_evra(inst))) self.RefreshPackages() # Fix upgradeable packages. if len(upgrade_pkgs) > 0: self.logger.info("Attempting to upgrade packages") if self.autodep: pkgtool = "/usr/bin/yum -d0 -y update %s" else: pkgtool = "/usr/bin/yum -d0 update %s" upgrade_args = [] for inst in upgrade_pkgs: pkg_arg = build_yname(self.instance_status[inst].get('pkg').get('name'), inst) upgrade_args.append(pkg_arg) if self.cmd.run(pkgtool % " ".join(upgrade_args)).success: # The yum command succeeded. All packages installed. self.logger.info("Single Pass for Install Succeeded") self.RefreshPackages() else: # The yum command failed. No packages installed. # Try installing instances individually. self.logger.error("Single Pass Install of Packages Failed") installed_instances = [] for inst in upgrade_pkgs: pkg_arg = build_yname(self.instance_status[inst].get('pkg').get('name'), inst) if self.cmd.run(pkgtool % pkg_arg).success: installed_instances.append(inst) else: self.logger.debug("%s %s would not install." % \ (self.instance_status[inst].get('pkg').get('name'), \ self.str_evra(inst))) self.RefreshPackages() if not self.setup['kevlar']: for pkg_entry in [p for p in packages if self.canVerify(p)]: self.logger.debug("Reverifying Failed Package %s" % (pkg_entry.get('name'))) states[pkg_entry] = self.VerifyPackage(pkg_entry, \ self.modlists.get(pkg_entry, [])) for entry in [ent for ent in packages if states[ent]]: self.modified.append(entry) def Remove(self, packages): """ Remove specified entries. packages is a list of Package Entries with Instances generated by FindExtra(). """ self.logger.debug('Running YUM24.Remove()') if self.autodep: pkgtool = "/usr/bin/yum -d0 -y erase %s" else: pkgtool = "/usr/bin/yum -d0 erase %s" erase_args = [] for pkg in packages: for inst in pkg: if pkg.get('name') != 'gpg-pubkey': pkg_arg = pkg.get('name') + '-' if inst.get('epoch', False): pkg_arg = pkg_arg + inst.get('epoch') + ':' pkg_arg = pkg_arg + inst.get('version') + '-' + inst.get('release') if inst.get('arch', False): pkg_arg = pkg_arg + '.' + inst.get('arch') erase_args.append(pkg_arg) else: pkgspec = {'name': pkg.get('name'), 'version': inst.get('version'), 'release': inst.get('release')} self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\ % (pkgspec.get('name'), self.str_evra(pkgspec))) self.logger.info(" This package will be deleted in a future version of the YUM24 driver.") rv = self.cmd.run(pkgtool % " ".join(erase_args)) if rv.success: self.modified += packages for pkg in erase_args: self.logger.info("Deleted %s" % (pkg)) else: self.logger.info("Bulk erase failed with errors:") self.logger.debug("Erase results: %s" % rv.error) self.logger.info("Attempting individual erase for each package.") for pkg in packages: pkg_modified = False for inst in pkg: if pkg.get('name') != 'gpg-pubkey': pkg_arg = pkg.get('name') + '-' if 'epoch' in inst.attrib: pkg_arg = pkg_arg + inst.get('epoch') + ':' pkg_arg = pkg_arg + inst.get('version') + '-' + inst.get('release') if 'arch' in inst.attrib: pkg_arg = pkg_arg + '.' + inst.get('arch') else: self.logger.info("WARNING: gpg-pubkey package not in configuration %s %s"\ % (pkg.get('name'), self.str_evra(pkg))) self.logger.info(" This package will be deleted in a future version of the YUM24 driver.") continue rv = self.cmd.run(self.pkgtool % pkg_arg) if rv.success: pkg_modified = True self.logger.info("Deleted %s" % pkg_arg) else: self.logger.error("Unable to delete %s" % pkg_arg) self.logger.debug("Failure: %s" % rv.error) if pkg_modified == True: self.modified.append(pkg) self.RefreshPackages() self.extra = self.FindExtra() bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/YUMng.py000066400000000000000000000003561223671746500206710ustar00rootroot00000000000000""" YUM driver called 'YUMng' for backwards compat """ from Bcfg2.Client.Tools.YUM import YUM class YUMng(YUM): """ YUM driver called 'YUMng' for backwards compat """ deprecated = True conflicts = ['YUM24', 'RPM', 'RPMng'] bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/__init__.py000066400000000000000000000621501223671746500214310ustar00rootroot00000000000000"""This contains all Bcfg2 Tool modules""" import os import sys import stat import Bcfg2.Client import Bcfg2.Client.XML from Bcfg2.Utils import Executor, ClassName from Bcfg2.Compat import walk_packages # pylint: disable=W0622 __all__ = [m[1] for m in walk_packages(path=__path__)] # pylint: disable=C0103 #: All available tools drivers = [item for item in __all__ if item not in ['rpmtools']] #: The default set of tools that will be used if "drivers" is not set #: in bcfg2.conf default = drivers[:] # pylint: enable=C0103 class ToolInstantiationError(Exception): """ This error is raised if the toolset cannot be instantiated. """ pass class Tool(object): """ The base tool class. All tools subclass this. .. private-include: _entry_is_complete .. autoattribute:: Bcfg2.Client.Tools.Tool.__execs__ .. autoattribute:: Bcfg2.Client.Tools.Tool.__handles__ .. autoattribute:: Bcfg2.Client.Tools.Tool.__req__ .. autoattribute:: Bcfg2.Client.Tools.Tool.__important__ """ #: The name of the tool. By default this uses #: :class:`Bcfg2.Client.Tools.ClassName` to ensure that it is the #: same as the name of the class. name = ClassName() #: Full paths to all executables the tool uses. When the tool is #: instantiated it will check to ensure that all of these files #: exist and are executable. __execs__ = [] #: A list of 2-tuples of entries handled by this tool. Each #: 2-tuple should contain ``(, )``, where ```` is #: the ``type`` attribute of the entry. If this tool handles #: entries with no ``type`` attribute, specify None. __handles__ = [] #: A dict that describes the required attributes for entries #: handled by this tool. The keys are the names of tags. The #: values may either be lists of attribute names (if the same #: attributes are required by all tags of that name), or dicts #: whose keys are the ``type`` attribute and whose values are #: lists of attributes required by tags with that ``type`` #: attribute. In that case, the ``type`` attribute will also be #: required. __req__ = {} #: A list of entry names that will be treated as important and #: installed before other entries. __important__ = [] #: This tool is deprecated, and a warning will be produced if it #: is used. deprecated = False #: This tool is experimental, and a warning will be produced if it #: is used. experimental = False #: List of other tools (by name) that this tool conflicts with. #: If any of the listed tools are loaded, they will be removed at #: runtime with a warning. conflicts = [] def __init__(self, logger, setup, config): """ :param logger: Logger that will be used for logging by this tool :type logger: logging.Logger :param setup: The option set Bcfg2 was invoked with :type setup: Bcfg2.Options.OptionParser :param config: The XML configuration for this client :type config: lxml.etree._Element :raises: :exc:`Bcfg2.Client.Tools.ToolInstantiationError` """ #: A :class:`Bcfg2.Options.OptionParser` object describing the #: option set Bcfg2 was invoked with self.setup = setup #: A :class:`logging.Logger` object that will be used by this #: tool for logging self.logger = logger #: The XML configuration for this client self.config = config #: An :class:`Bcfg2.Utils.Executor` object for #: running external commands. self.cmd = Executor(timeout=self.setup['command_timeout']) #: A list of entries that have been modified by this tool self.modified = [] #: A list of extra entries that are not listed in the #: configuration self.extra = [] #: A list of all entries handled by this tool self.handled = [] self._analyze_config() self._check_execs() def _analyze_config(self): """ Analyze the config at tool initialization-time for important and handled entries """ for struct in self.config: for entry in struct: if (entry.tag == 'Path' and entry.get('important', 'false').lower() == 'true'): self.__important__.append(entry.get('name')) self.handled = self.getSupportedEntries() def _check_execs(self): """ Check all executables used by this tool to ensure that they exist and are executable """ for filename in self.__execs__: try: mode = stat.S_IMODE(os.stat(filename)[stat.ST_MODE]) except OSError: raise ToolInstantiationError(sys.exc_info()[1]) except: raise ToolInstantiationError("%s: Failed to stat %s" % (self.name, filename)) if not mode & stat.S_IEXEC: raise ToolInstantiationError("%s: %s not executable" % (self.name, filename)) def BundleUpdated(self, bundle, states): # pylint: disable=W0613 """ Callback that is invoked when a bundle has been updated. :param bundle: The bundle that has been updated :type bundle: lxml.etree._Element :param states: The :attr:`Bcfg2.Client.Frame.Frame.states` dict :type states: dict :returns: None """ return def BundleNotUpdated(self, bundle, states): # pylint: disable=W0613 """ Callback that is invoked when a bundle has been updated. :param bundle: The bundle that has been updated :type bundle: lxml.etree._Element :param states: The :attr:`Bcfg2.Client.Frame.Frame.states` dict :type states: dict :returns: None """ return def Inventory(self, states, structures=None): """ Take an inventory of the system as it exists. This involves two steps: * Call the appropriate entry-specific Verify method for each entry this tool verifies; * Call :func:`Bcfg2.Client.Tools.Tool.FindExtra` to populate :attr:`Bcfg2.Client.Tools.Tool.extra` with extra entries. This implementation of :func:`Bcfg2.Client.Tools.Tool.Inventory` calls a ``Verify`` method to verify each entry, where ```` is the entry tag. E.g., a Path entry would be verified by calling :func:`VerifyPath`. :param states: The :attr:`Bcfg2.Client.Frame.Frame.states` dict :type states: dict :param structures: The list of structures (i.e., bundles) to get entries from. If this is not given, all children of :attr:`Bcfg2.Client.Tools.Tool.config` will be used. :type structures: list of lxml.etree._Element :returns: None """ if not structures: structures = self.config.getchildren() mods = self.buildModlist() for struct in structures: for entry in struct.getchildren(): if self.canVerify(entry): try: func = getattr(self, "Verify%s" % entry.tag) except AttributeError: self.logger.error("%s: Cannot verify %s entries" % (self.name, entry.tag)) continue try: states[entry] = func(entry, mods) except: # pylint: disable=W0702 self.logger.error("%s: Unexpected failure verifying %s" % (self.name, self.primarykey(entry)), exc_info=1) self.extra = self.FindExtra() def Install(self, entries, states): """ Install entries. 'Install' in this sense means either initially install, or update as necessary to match the specification. This implementation of :func:`Bcfg2.Client.Tools.Tool.Install` calls a ``Install`` method to install each entry, where ```` is the entry tag. E.g., a Path entry would be installed by calling :func:`InstallPath`. :param entries: The entries to install :type entries: list of lxml.etree._Element :param states: The :attr:`Bcfg2.Client.Frame.Frame.states` dict :type states: dict :returns: None """ for entry in entries: try: func = getattr(self, "Install%s" % entry.tag) except AttributeError: self.logger.error("%s: Cannot install %s entries" % (self.name, entry.tag)) continue try: states[entry] = func(entry) if states[entry]: self.modified.append(entry) except: # pylint: disable=W0702 self.logger.error("%s: Unexpected failure installing %s" % (self.name, self.primarykey(entry)), exc_info=1) def Remove(self, entries): """ Remove specified extra entries. :param entries: The entries to remove :type entries: list of lxml.etree._Element :returns: None """ pass def getSupportedEntries(self): """ Get all entries that are handled by this tool. :returns: list of lxml.etree._Element """ rv = [] for struct in self.config.getchildren(): rv.extend([entry for entry in struct.getchildren() if self.handlesEntry(entry)]) return rv def handlesEntry(self, entry): """ Return True if the entry is handled by this tool. :param entry: Determine if this entry is handled. :type entry: lxml.etree._Element :returns: bool """ return (entry.tag, entry.get('type')) in self.__handles__ def buildModlist(self): """ Build a list of all Path entries in the configuration. (This can be used to determine which paths might be modified from their original state, useful for verifying packages) :returns: list of lxml.etree._Element """ rv = [] for struct in self.config.getchildren(): rv.extend([entry.get('name') for entry in struct.getchildren() if entry.tag == 'Path']) return rv def missing_attrs(self, entry): """ Return a list of attributes that were expected on an entry (from :attr:`Bcfg2.Client.Tools.Tool.__req__`), but not found. :param entry: The entry to find missing attributes on :type entry: lxml.etree._Element :returns: list of strings """ required = self.__req__[entry.tag] if isinstance(required, dict): required = ["type"] try: required.extend(self.__req__[entry.tag][entry.get("type")]) except KeyError: pass return [attr for attr in required if attr not in entry.attrib or not entry.attrib[attr]] def canVerify(self, entry): """ Test if entry can be verified by calling :func:`Bcfg2.Client.Tools.Tool._entry_is_complete`. :param entry: The entry to evaluate :type entry: lxml.etree._Element :returns: bool - True if the entry can be verified, False otherwise. """ return self._entry_is_complete(entry, action="verify") def FindExtra(self): """ Return a list of extra entries, i.e., entries that exist on the client but are not in the configuration. :returns: list of lxml.etree._Element """ return [] def primarykey(self, entry): """ Return a string that describes the entry uniquely amongst all entries in the configuration. :param entry: The entry to describe :type entry: lxml.etree._Element :returns: string """ return "%s:%s" % (entry.tag, entry.get("name")) def canInstall(self, entry): """ Test if entry can be installed by calling :func:`Bcfg2.Client.Tools.Tool._entry_is_complete`. :param entry: The entry to evaluate :type entry: lxml.etree._Element :returns: bool - True if the entry can be installed, False otherwise. """ return self._entry_is_complete(entry, action="install") def _entry_is_complete(self, entry, action=None): """ Test if the entry is complete. This involves three things: * The entry is handled by this tool (as reported by :func:`Bcfg2.Client.Tools.Tool.handlesEntry`; * The entry does not report a bind failure; * The entry is not missing any attributes (as reported by :func:`Bcfg2.Client.Tools.Tool.missing_attrs`). :param entry: The entry to evaluate :type entry: lxml.etree._Element :param action: The action being performed on the entry (e.g., "install", "verify"). This is used to produce error messages; if not provided, generic error messages will be used. :type action: string :returns: bool - True if the entry can be verified, False otherwise. """ if not self.handlesEntry(entry): return False if 'failure' in entry.attrib: if action is None: msg = "%s: %s reports bind failure" else: msg = "%%s: Cannot %s entry %%s with bind failure" % action self.logger.error(msg % (self.name, self.primarykey(entry))) return False missing = self.missing_attrs(entry) if missing: if action is None: desc = "%s is" % self.primarykey(entry) else: desc = "Cannot %s %s due to" % (action, self.primarykey(entry)) self.logger.error("%s: %s missing required attribute(s): %s" % (self.name, desc, ", ".join(missing))) return False return True class PkgTool(Tool): """ PkgTool provides a one-pass install with fallback for use with packaging systems. PkgTool makes a number of assumptions that may need to be overridden by a subclass. For instance, it assumes that packages are installed by a shell command; that only one version of a given package can be installed; etc. Nonetheless, it offers a strong base for writing simple package tools. """ #: A tuple describing the format of the command to run to install #: a single package. The first element of the tuple is a string #: giving the format of the command, with a single '%s' for the #: name of the package or packages to be installed. The second #: element is a tuple whose first element is the format of the #: name of the package, and whose second element is a list whose #: members are the names of attributes that will be used when #: formatting the package name format string. pkgtool = ('echo %s', ('%s', ['name'])) #: The ``type`` attribute of Packages handled by this tool. pkgtype = 'echo' def __init__(self, logger, setup, config): Tool.__init__(self, logger, setup, config) #: A dict of installed packages; the keys should be package #: names and the values should be simple strings giving the #: installed version. self.installed = {} self.RefreshPackages() def VerifyPackage(self, entry, modlist): """ Verify the given Package entry. :param entry: The Package entry to verify :type entry: lxml.etree._Element :param modlist: A list of all Path entries in the configuration, which may be considered when verifying a package. For instance, a package should verify successfully if paths in ``modlist`` have been modified outside the package. :type modlist: list of strings :returns: bool - True if the package verifies, false otherwise. """ raise NotImplementedError def _get_package_command(self, packages): """ Get the command to install the given list of packages. :param packages: The Package entries to install :type packages: list of lxml.etree._Element :returns: string - the command to run """ pkgargs = " ".join(self.pkgtool[1][0] % tuple(pkg.get(field) for field in self.pkgtool[1][1]) for pkg in packages) return self.pkgtool[0] % pkgargs def Install(self, packages, states): """ Run a one-pass install where all required packages are installed with a single command, followed by single package installs in case of failure. :param entries: The entries to install :type entries: list of lxml.etree._Element :param states: The :attr:`Bcfg2.Client.Frame.Frame.states` dict :type states: dict :returns: None """ self.logger.info("Trying single pass package install for pkgtype %s" % self.pkgtype) pkgcmd = self._get_package_command(packages) self.logger.debug("Running command: %s" % pkgcmd) if self.cmd.run(pkgcmd): self.logger.info("Single Pass Succeded") # set all package states to true and flush workqueues pkgnames = [pkg.get('name') for pkg in packages] for entry in list(states.keys()): if (entry.tag == 'Package' and entry.get('type') == self.pkgtype and entry.get('name') in pkgnames): self.logger.debug('Setting state to true for pkg %s' % entry.get('name')) states[entry] = True self.RefreshPackages() else: self.logger.error("Single Pass Failed") # do single pass installs self.RefreshPackages() for pkg in packages: # handle state tracking updates if self.VerifyPackage(pkg, []): self.logger.info("Forcing state to true for pkg %s" % (pkg.get('name'))) states[pkg] = True else: self.logger.info("Installing pkg %s version %s" % (pkg.get('name'), pkg.get('version'))) if self.cmd.run(self._get_package_command([pkg])): states[pkg] = True else: self.logger.error("Failed to install package %s" % pkg.get('name')) self.RefreshPackages() self.modified.extend(entry for entry in packages if states[entry]) def RefreshPackages(self): """ Refresh the internal representation of the package database (:attr:`Bcfg2.Client.Tools.PkgTool.installed`). :returns: None""" raise NotImplementedError def FindExtra(self): packages = [entry.get('name') for entry in self.getSupportedEntries()] extras = [data for data in list(self.installed.items()) if data[0] not in packages] return [Bcfg2.Client.XML.Element('Package', name=name, type=self.pkgtype, version=version) for (name, version) in extras] FindExtra.__doc__ = Tool.FindExtra.__doc__ class SvcTool(Tool): """ Base class for tools that handle Service entries """ def __init__(self, logger, setup, config): Tool.__init__(self, logger, setup, config) #: List of services that have been restarted self.restarted = [] __init__.__doc__ = Tool.__init__.__doc__ def get_svc_command(self, service, action): """ Return a command that can be run to start or stop a service. :param service: The service entry to modify :type service: lxml.etree._Element :param action: The action to take (e.g., "stop", "start") :type action: string :returns: string - The command to run """ return '/etc/init.d/%s %s' % (service.get('name'), action) def get_bootstatus(self, service): """ Return the bootstatus attribute if it exists. :param service: The service entry :type service: lxml.etree._Element :returns: string or None - Value of bootstatus if it exists. If bootstatus is unspecified and status is not *ignore*, return value of status. If bootstatus is unspecified and status is *ignore*, return None. """ if service.get('bootstatus') is not None: return service.get('bootstatus') elif service.get('status') != 'ignore': return service.get('status') return None def start_service(self, service): """ Start a service. :param service: The service entry to modify :type service: lxml.etree._Element :returns: Bcfg2.Utils.ExecutorResult - The return value from :class:`Bcfg2.Utils.Executor.run` """ self.logger.debug('Starting service %s' % service.get('name')) return self.cmd.run(self.get_svc_command(service, 'start')) def stop_service(self, service): """ Stop a service. :param service: The service entry to modify :type service: lxml.etree._Element :returns: Bcfg2.Utils.ExecutorResult - The return value from :class:`Bcfg2.Utils.Executor.run` """ self.logger.debug('Stopping service %s' % service.get('name')) return self.cmd.run(self.get_svc_command(service, 'stop')) def restart_service(self, service): """ Restart a service. :param service: The service entry to modify :type service: lxml.etree._Element :returns: Bcfg2.Utils.ExecutorResult - The return value from :class:`Bcfg2.Utils.Executor.run` """ self.logger.debug('Restarting service %s' % service.get('name')) restart_target = service.get('target', 'restart') return self.cmd.run(self.get_svc_command(service, restart_target)) def check_service(self, service): """ Check the status a service. :param service: The service entry to modify :type service: lxml.etree._Element :returns: bool - True if the status command returned 0, False otherwise """ return bool(self.cmd.run(self.get_svc_command(service, 'status'))) def Remove(self, services): if self.setup['servicemode'] != 'disabled': for entry in services: entry.set("status", "off") self.InstallService(entry) Remove.__doc__ = Tool.Remove.__doc__ def BundleUpdated(self, bundle, states): if self.setup['servicemode'] == 'disabled': return for entry in bundle: if not self.handlesEntry(entry): continue estatus = entry.get('status') restart = entry.get("restart", "true").lower() if (restart == "false" or estatus == 'ignore' or (restart == "interactive" and not self.setup['interactive'])): continue success = False if estatus == 'on': if self.setup['servicemode'] == 'build': success = self.stop_service(entry) elif entry.get('name') not in self.restarted: if self.setup['interactive']: if not Bcfg2.Client.prompt('Restart service %s? (y/N) ' % entry.get('name')): continue success = self.restart_service(entry) if success: self.restarted.append(entry.get('name')) else: success = self.stop_service(entry) if not success: self.logger.error("Failed to manipulate service %s" % (entry.get('name'))) BundleUpdated.__doc__ = Tool.BundleUpdated.__doc__ def Install(self, entries, states): install_entries = [] for entry in entries: if entry.get('install', 'true').lower() == 'false': self.logger.info("Installation is false for %s:%s, skipping" % (entry.tag, entry.get('name'))) else: install_entries.append(entry) return Tool.Install(self, install_entries, states) Install.__doc__ = Tool.Install.__doc__ def InstallService(self, entry): """ Install a single service entry. See :func:`Bcfg2.Client.Tools.Tool.Install`. :param entry: The Service entry to install :type entry: lxml.etree._Element :returns: bool - True if installation was successful, False otherwise """ raise NotImplementedError bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/launchd.py000066400000000000000000000136201223671746500213060ustar00rootroot00000000000000"""launchd support for Bcfg2.""" import os import Bcfg2.Client.Tools class launchd(Bcfg2.Client.Tools.Tool): # pylint: disable=C0103 """Support for Mac OS X launchd services. Currently requires the path to the plist to load/unload, and Name is acually a reverse-fqdn (or the label).""" __handles__ = [('Service', 'launchd')] __execs__ = ['/bin/launchctl', '/usr/bin/defaults'] __req__ = {'Service': ['name', 'status']} def __init__(self, logger, setup, config): Bcfg2.Client.Tools.Tool.__init__(self, logger, setup, config) # Locate plist file that provides given reverse-fqdn name: # # * ``/Library/LaunchAgents``: Per-user agents provided by the # administrator. # * ``/Library/LaunchDaemons``: System-wide daemons provided # by the administrator. # * ``/System/Library/LaunchAgents``: Mac OS X per-user # agents. # * ``/System/Library/LaunchDaemons``: Mac OS X system-wide # daemons. plist_locations = ["/Library/LaunchDaemons", "/System/Library/LaunchDaemons"] self.plist_mapping = {} for directory in plist_locations: for daemon in os.listdir(directory): if daemon.endswith(".plist"): daemon = daemon[:-6] dpath = os.path.join(directory, daemon) rv = self.cmd.run(['defaults', 'read', dpath, 'Label']) if rv.success: label = rv.stdout.splitlines()[0] self.plist_mapping[label] = dpath else: self.logger.warning("Could not get label from %s" % dpath) def FindPlist(self, entry): """ Find the location of the plist file for the given entry """ return self.plist_mapping.get(entry.get('name'), None) def os_version(self): """ Determine the OS version """ rv = self.cmd.run('sw_vers') if rv: for line in rv.stdout.splitlines(): if line.startswith("ProductVersion"): return line.split()[-1] else: return '' def VerifyService(self, entry, _): """Verify launchd service entry.""" if entry.get('status') == 'ignore': return True try: services = self.cmd.run("/bin/launchctl list").stdout.splitlines() except IndexError: # happens when no services are running (should be never) services = [] # launchctl output changed in 10.5 # It is now three columns, with the last # column being the name of the # service if int(self.os_version().split('.')[1]) >= 5: services = [s.split()[-1] for s in services] if entry.get('name') in services: # doesn't check if non-spawning services are Started return entry.get('status') == 'on' else: self.logger.debug("Launchd: Didn't find service Loaded " "(launchd running under same user as bcfg)") return entry.get('status') == 'off' try: # Perhaps add the "-w" flag to load and # unload to modify the file itself! self.cmd.run("/bin/launchctl load -w %s" % self.FindPlist(entry)) except IndexError: return 'on' return False def InstallService(self, entry): """Enable or disable launchd item.""" name = entry.get('name') if entry.get('status') == 'on': self.logger.error("Installing service %s" % name) self.cmd.run("/bin/launchctl load -w %s" % self.FindPlist(entry)) return self.cmd.run("/bin/launchctl start %s" % name).success else: self.logger.error("Uninstalling service %s" % name) self.cmd.run("/bin/launchctl stop %s" % name) return self.cmd.run("/bin/launchctl unload -w %s" % self.FindPlist(entry)).success def Remove(self, svcs): """Remove Extra launchd entries.""" pass def FindExtra(self): """Find Extra launchd services.""" try: allsrv = self.cmd.run("/bin/launchctl list").stdout.splitlines() except IndexError: allsrv = [] for entry in self.getSupportedEntries(): svc = entry.get("name") if svc in allsrv: allsrv.remove(svc) return [Bcfg2.Client.XML.Element("Service", type='launchd', name=name, status='on') for name in allsrv] def BundleUpdated(self, bundle, states): """Reload launchd plist.""" for entry in [entry for entry in bundle if self.handlesEntry(entry)]: if not self.canInstall(entry): self.logger.error("Insufficient information to restart " "service %s" % entry.get('name')) else: name = entry.get('name') if entry.get('status') == 'on' and self.FindPlist(entry): self.logger.info("Reloading launchd service %s" % name) # stop? self.cmd.run("/bin/launchctl stop %s" % name) # what if it disappeared? how do we stop services # that are currently running but the plist disappeared?! self.cmd.run("/bin/launchctl unload -w %s" % (self.FindPlist(entry))) self.cmd.run("/bin/launchctl load -w %s" % (self.FindPlist(entry))) self.cmd.run("/bin/launchctl start %s" % name) else: # only if necessary.... self.cmd.run("/bin/launchctl stop %s" % name) self.cmd.run("/bin/launchctl unload -w %s" % (self.FindPlist(entry))) bcfg2-1.3.3/src/lib/Bcfg2/Client/Tools/rpmtools.py000077500000000000000000001100561223671746500215530ustar00rootroot00000000000000#!/usr/bin/env python """ Module that uses rpm-python to implement the following rpm functionality for the bcfg2 RPM and YUM client drivers: rpm -qa rpm --verify rpm --erase The code closely follows the rpm C code. The code was written to be used in the bcfg2 RPM/YUM drivers. Some command line options have been provided to assist with testing and development, but the output isn't pretty and looks nothing like rpm output. Run 'rpmtools' -h for the options. """ import grp import optparse import os import pwd import rpm import stat import sys if sys.version_info >= (2, 5): import hashlib py24compat = False else: # FIXME: Remove when client python dep is 2.5 or greater py24compat = True import md5 # Determine what prelink tools we have available. # The isprelink module is a python extension that examines the ELF headers # to see if the file has been prelinked. If it is not present a lot of files # are unnecessarily run through the prelink command. try: from isprelink import * isprelink_imported = True except ImportError: isprelink_imported = False # If the prelink command is installed on the system then we need to do # prelink -y on files. if os.access('/usr/sbin/prelink', os.X_OK): prelink_exists = True else: prelink_exists = False # If we don't have isprelink then we will use the prelink configuration file to # filter what we have to put through prelink -y. import re blacklist = [] whitelist = [] try: f = open('/etc/prelink.conf', mode='r') for line in f: if line.startswith('#'): continue option, pattern = line.split() if pattern.startswith('*.'): pattern = pattern.replace('*.', '\.') pattern += '$' elif pattern.startswith('/'): pattern = '^' + pattern if option == '-b': blacklist.append(pattern) elif option == '-l': whitelist.append(pattern) f.close() except IOError: pass blacklist_re = re.compile('|'.join(blacklist)) whitelist_re = re.compile('|'.join(whitelist)) # Flags that are not defined in rpm-python. # They are defined in lib/rpmcli.h # Bit(s) for verifyFile() attributes. # RPMVERIFY_NONE = 0 # /*!< */ RPMVERIFY_MD5 = 1 # 1 << 0 # /*!< from %verify(md5) */ RPMVERIFY_FILESIZE = 2 # 1 << 1 # /*!< from %verify(size) */ RPMVERIFY_LINKTO = 4 # 1 << 2 # /*!< from %verify(link) */ RPMVERIFY_USER = 8 # 1 << 3 # /*!< from %verify(user) */ RPMVERIFY_GROUP = 16 # 1 << 4 # /*!< from %verify(group) */ RPMVERIFY_MTIME = 32 # 1 << 5 # /*!< from %verify(mtime) */ RPMVERIFY_MODE = 64 # 1 << 6 # /*!< from %verify(mode) */ RPMVERIFY_RDEV = 128 # 1 << 7 # /*!< from %verify(rdev) */ RPMVERIFY_CONTEXTS = 32768 # (1 << 15) # /*!< from --nocontexts */ RPMVERIFY_READLINKFAIL = 268435456 # (1 << 28) # /*!< readlink failed */ RPMVERIFY_READFAIL = 536870912 # (1 << 29) # /*!< file read failed */ RPMVERIFY_LSTATFAIL = 1073741824 # (1 << 30) # /*!< lstat failed */ RPMVERIFY_LGETFILECONFAIL = 2147483648 # (1 << 31) # /*!< lgetfilecon failed */ RPMVERIFY_FAILURES = \ (RPMVERIFY_LSTATFAIL|RPMVERIFY_READFAIL|RPMVERIFY_READLINKFAIL| \ RPMVERIFY_LGETFILECONFAIL) # Bit(s) to control rpm_verify() operation. # VERIFY_DEFAULT = 0, # /*!< */ VERIFY_MD5 = 1 << 0 # /*!< from --nomd5 */ VERIFY_SIZE = 1 << 1 # /*!< from --nosize */ VERIFY_LINKTO = 1 << 2 # /*!< from --nolinkto */ VERIFY_USER = 1 << 3 # /*!< from --nouser */ VERIFY_GROUP = 1 << 4 # /*!< from --nogroup */ VERIFY_MTIME = 1 << 5 # /*!< from --nomtime */ VERIFY_MODE = 1 << 6 # /*!< from --nomode */ VERIFY_RDEV = 1 << 7 # /*!< from --nodev */ # /* bits 8-14 unused, reserved for rpmVerifyAttrs */ VERIFY_CONTEXTS = 1 << 15 # /*!< verify: from --nocontexts */ VERIFY_FILES = 1 << 16 # /*!< verify: from --nofiles */ VERIFY_DEPS = 1 << 17 # /*!< verify: from --nodeps */ VERIFY_SCRIPT = 1 << 18 # /*!< verify: from --noscripts */ VERIFY_DIGEST = 1 << 19 # /*!< verify: from --nodigest */ VERIFY_SIGNATURE = 1 << 20 # /*!< verify: from --nosignature */ VERIFY_PATCHES = 1 << 21 # /*!< verify: from --nopatches */ VERIFY_HDRCHK = 1 << 22 # /*!< verify: from --nohdrchk */ VERIFY_FOR_LIST = 1 << 23 # /*!< query: from --list */ VERIFY_FOR_STATE = 1 << 24 # /*!< query: from --state */ VERIFY_FOR_DOCS = 1 << 25 # /*!< query: from --docfiles */ VERIFY_FOR_CONFIG = 1 << 26 # /*!< query: from --configfiles */ VERIFY_FOR_DUMPFILES = 1 << 27 # /*!< query: from --dump */ # /* bits 28-31 used in rpmVerifyAttrs */ # Comes from C cource. lib/rpmcli.h VERIFY_ATTRS = \ (VERIFY_MD5 | VERIFY_SIZE | VERIFY_LINKTO | VERIFY_USER | VERIFY_GROUP | \ VERIFY_MTIME | VERIFY_MODE | VERIFY_RDEV | VERIFY_CONTEXTS) VERIFY_ALL = \ (VERIFY_ATTRS | VERIFY_FILES | VERIFY_DEPS | VERIFY_SCRIPT | VERIFY_DIGEST |\ VERIFY_SIGNATURE | VERIFY_HDRCHK) # Some masks for what checks to NOT do on these file types. # The C code actiually resets these up for every file. DIR_FLAGS = ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | \ RPMVERIFY_LINKTO) # These file types all have the same mask, but hopefully this will make the # code more readable. FIFO_FLAGS = CHR_FLAGS = BLK_FLAGS = GHOST_FLAGS = DIR_FLAGS LINK_FLAGS = ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | \ RPMVERIFY_MODE | RPMVERIFY_USER | RPMVERIFY_GROUP) REG_FLAGS = ~(RPMVERIFY_LINKTO) def s_isdev(mode): """ Check to see if a file is a device. """ return stat.S_ISBLK(mode) | stat.S_ISCHR(mode) def rpmpackagelist(rts): """ Equivalent of rpm -qa. Intended for RefreshPackages() in the RPM Driver. Requires rpmtransactionset() to be run first to get a ts. Returns a list of pkgspec dicts. e.g. [ {'name':'foo', 'epoch':'20', 'version':'1.2', 'release':'5', 'arch':'x86_64' }, {'name':'bar', 'epoch':'10', 'version':'5.2', 'release':'2', 'arch':'x86_64' } ] """ return [{'name':header[rpm.RPMTAG_NAME], 'epoch':header[rpm.RPMTAG_EPOCH], 'version':header[rpm.RPMTAG_VERSION], 'release':header[rpm.RPMTAG_RELEASE], 'arch':header[rpm.RPMTAG_ARCH], 'gpgkeyid':header.sprintf("%|SIGGPG?{%{SIGGPG:pgpsig}}:{None}|").split()[-1]} for header in rts.dbMatch()] def getindexbykeyword(index_ts, **kwargs): """ Return list of indexs from the rpmdb matching keywords ex: getHeadersByKeyword(name='foo', version='1', release='1') Can be passed any structure that can be indexed by the pkgspec keyswords as other keys are filtered out. """ lst = [] name = kwargs.get('name') if name: index_mi = index_ts.dbMatch(rpm.RPMTAG_NAME, name) else: index_mi = index_ts.dbMatch() if 'epoch' in kwargs: if kwargs['epoch'] != None and kwargs['epoch'] != 'None': kwargs['epoch'] = int(kwargs['epoch']) else: del(kwargs['epoch']) keywords = [key for key in list(kwargs.keys()) \ if key in ('name', 'epoch', 'version', 'release', 'arch')] keywords_len = len(keywords) for hdr in index_mi: match = 0 for keyword in keywords: if hdr[keyword] == kwargs[keyword]: match += 1 if match == keywords_len: lst.append(index_mi.instance()) del index_mi return lst def getheadersbykeyword(header_ts, **kwargs): """ Borrowed parts of this from from Yum. Need to fix it though. Epoch is not handled right. Return list of headers from the rpmdb matching keywords ex: getHeadersByKeyword(name='foo', version='1', release='1') Can be passed any structure that can be indexed by the pkgspec keyswords as other keys are filtered out. """ lst = [] name = kwargs.get('name') if name: header_mi = header_ts.dbMatch(rpm.RPMTAG_NAME, name) else: header_mi = header_ts.dbMatch() if 'epoch' in kwargs: if kwargs['epoch'] != None and kwargs['epoch'] != 'None': kwargs['epoch'] = int(kwargs['epoch']) else: del(kwargs['epoch']) keywords = [key for key in list(kwargs.keys()) \ if key in ('name', 'epoch', 'version', 'release', 'arch')] keywords_len = len(keywords) for hdr in header_mi: match = 0 for keyword in keywords: if hdr[keyword] == kwargs[keyword]: match += 1 if match == keywords_len: lst.append(hdr) del header_mi return lst def prelink_md5_check(filename): """ Checks if a file is prelinked. If it is run it through prelink -y to get the unprelinked md5 and file size. Return 0 if the file was not prelinked, otherwise return the file size. Always return the md5. """ prelink = False try: plf = open(filename, "rb") except IOError: return False, 0 if prelink_exists: if isprelink_imported: plfd = plf.fileno() if isprelink(plfd): plf.close() cmd = '/usr/sbin/prelink -y %s 2> /dev/null' \ % (re.escape(filename)) plf = os.popen(cmd, 'rb') prelink = True elif whitelist_re.search(filename) and not blacklist_re.search(filename): plf.close() cmd = '/usr/sbin/prelink -y %s 2> /dev/null' \ % (re.escape(filename)) plf = os.popen(cmd, 'rb') prelink = True fsize = 0 if py24compat: chksum = md5.new() else: chksum = hashlib.md5() while 1: data = plf.read() if not data: break fsize += len(data) chksum.update(data) plf.close() file_md5 = chksum.hexdigest() if prelink: return file_md5, fsize else: return file_md5, 0 def prelink_size_check(filename): """ This check is only done if the prelink_md5_check() is not done first. Checks if a file is prelinked. If it is run it through prelink -y to get the unprelinked file size. Return 0 if the file was not prelinked, otherwise return the file size. """ fsize = 0 try: plf = open(filename, "rb") except IOError: return False if prelink_exists: if isprelink_imported: plfd = plf.fileno() if isprelink(plfd): plf.close() cmd = '/usr/sbin/prelink -y %s 2> /dev/null' \ % (re.escape(filename)) plf = os.popen(cmd, 'rb') while 1: data = plf.read() if not data: break fsize += len(data) elif whitelist_re.search(filename) and not blacklist_re.search(filename): plf.close() cmd = '/usr/sbin/prelink -y %s 2> /dev/null' \ % (re.escape(filename)) plf = os.popen(cmd, 'rb') while 1: data = plf.read() if not data: break fsize += len(data) plf.close() return fsize def debug_verify_flags(vflags): """ Decodes the verify flags bits. """ if vflags & RPMVERIFY_MD5: print('RPMVERIFY_MD5') if vflags & RPMVERIFY_FILESIZE: print('RPMVERIFY_FILESIZE') if vflags & RPMVERIFY_LINKTO: print('RPMVERIFY_LINKTO') if vflags & RPMVERIFY_USER: print('RPMVERIFY_USER') if vflags & RPMVERIFY_GROUP: print('RPMVERIFY_GROUP') if vflags & RPMVERIFY_MTIME: print('RPMVERIFY_MTIME') if vflags & RPMVERIFY_MODE: print('RPMVERIFY_MODE') if vflags & RPMVERIFY_RDEV: print('RPMVERIFY_RDEV') if vflags & RPMVERIFY_CONTEXTS: print('RPMVERIFY_CONTEXTS') if vflags & RPMVERIFY_READLINKFAIL: print('RPMVERIFY_READLINKFAIL') if vflags & RPMVERIFY_READFAIL: print('RPMVERIFY_READFAIL') if vflags & RPMVERIFY_LSTATFAIL: print('RPMVERIFY_LSTATFAIL') if vflags & RPMVERIFY_LGETFILECONFAIL: print('RPMVERIFY_LGETFILECONFAIL') def debug_file_flags(fflags): """ Decodes the file flags bits. """ if fflags & rpm.RPMFILE_CONFIG: print('rpm.RPMFILE_CONFIG') if fflags & rpm.RPMFILE_DOC: print('rpm.RPMFILE_DOC') if fflags & rpm.RPMFILE_ICON: print('rpm.RPMFILE_ICON') if fflags & rpm.RPMFILE_MISSINGOK: print('rpm.RPMFILE_MISSINGOK') if fflags & rpm.RPMFILE_NOREPLACE: print('rpm.RPMFILE_NOREPLACE') if fflags & rpm.RPMFILE_GHOST: print('rpm.RPMFILE_GHOST') if fflags & rpm.RPMFILE_LICENSE: print('rpm.RPMFILE_LICENSE') if fflags & rpm.RPMFILE_README: print('rpm.RPMFILE_README') if fflags & rpm.RPMFILE_EXCLUDE: print('rpm.RPMFILE_EXLUDE') if fflags & rpm.RPMFILE_UNPATCHED: print('rpm.RPMFILE_UNPATCHED') if fflags & rpm.RPMFILE_PUBKEY: print('rpm.RPMFILE_PUBKEY') def rpm_verify_file(fileinfo, rpmlinktos, omitmask): """ Verify all the files in a package. Returns a list of error flags, the file type and file name. The list entries are strings that are the same as the labels for the bitwise flags used in the C code. """ (fname, fsize, fmode, fmtime, fflags, frdev, finode, fnlink, fstate, \ vflags, fuser, fgroup, fmd5) = fileinfo # 1. rpmtsRootDir stuff. What does it do and where to I get it from? file_results = [] flags = vflags # Check to see if the file was installed - if not pretend all is ok. # This is what the rpm C code does! if fstate != rpm.RPMFILE_STATE_NORMAL: return file_results # Get the installed files stats try: lstat = os.lstat(fname) except OSError: if not (fflags & (rpm.RPMFILE_MISSINGOK|rpm.RPMFILE_GHOST)): file_results.append('RPMVERIFY_LSTATFAIL') #file_results.append(fname) return file_results # 5. Contexts? SELinux stuff? # Setup what checks to do. This is straight out of the C code. if stat.S_ISDIR(lstat.st_mode): flags &= DIR_FLAGS elif stat.S_ISLNK(lstat.st_mode): flags &= LINK_FLAGS elif stat.S_ISFIFO(lstat.st_mode): flags &= FIFO_FLAGS elif stat.S_ISCHR(lstat.st_mode): flags &= CHR_FLAGS elif stat.S_ISBLK(lstat.st_mode): flags &= BLK_FLAGS else: flags &= REG_FLAGS if (fflags & rpm.RPMFILE_GHOST): flags &= GHOST_FLAGS flags &= ~(omitmask | RPMVERIFY_FAILURES) # 8. SELinux stuff. prelink_size = 0 if flags & RPMVERIFY_MD5: prelink_md5, prelink_size = prelink_md5_check(fname) if prelink_md5 == False: file_results.append('RPMVERIFY_MD5') file_results.append('RPMVERIFY_READFAIL') elif prelink_md5 != fmd5: file_results.append('RPMVERIFY_MD5') if flags & RPMVERIFY_LINKTO: linkto = os.readlink(fname) if not linkto: file_results.append('RPMVERIFY_READLINKFAIL') file_results.append('RPMVERIFY_LINKTO') else: if len(rpmlinktos) == 0 or linkto != rpmlinktos: file_results.append('RPMVERIFY_LINKTO') if flags & RPMVERIFY_FILESIZE: if not (flags & RPMVERIFY_MD5): # prelink check hasn't been done. prelink_size = prelink_size_check(fname) if (prelink_size != 0): # This is a prelinked file. if (prelink_size != fsize): file_results.append('RPMVERIFY_FILESIZE') elif lstat.st_size != fsize: # It wasn't a prelinked file. file_results.append('RPMVERIFY_FILESIZE') if flags & RPMVERIFY_MODE: metamode = fmode filemode = lstat.st_mode # Comparing the type of %ghost files is meaningless, but perms are ok. if fflags & rpm.RPMFILE_GHOST: metamode &= ~0xf000 filemode &= ~0xf000 if (stat.S_IFMT(metamode) != stat.S_IFMT(filemode)) or \ (stat.S_IMODE(metamode) != stat.S_IMODE(filemode)): file_results.append('RPMVERIFY_MODE') if flags & RPMVERIFY_RDEV: if (stat.S_ISCHR(fmode) != stat.S_ISCHR(lstat.st_mode) or stat.S_ISBLK(fmode) != stat.S_ISBLK(lstat.st_mode)): file_results.append('RPMVERIFY_RDEV') elif (s_isdev(fmode) & s_isdev(lstat.st_mode)): st_rdev = lstat.st_rdev if frdev != st_rdev: file_results.append('RPMVERIFY_RDEV') if flags & RPMVERIFY_MTIME: if lstat.st_mtime != fmtime: file_results.append('RPMVERIFY_MTIME') if flags & RPMVERIFY_USER: try: user = pwd.getpwuid(lstat.st_uid)[0] except KeyError: user = None if not user or not fuser or (user != fuser): file_results.append('RPMVERIFY_USER') if flags & RPMVERIFY_GROUP: try: group = grp.getgrgid(lstat.st_gid)[0] except KeyError: group = None if not group or not fgroup or (group != fgroup): file_results.append('RPMVERIFY_GROUP') return file_results def rpm_verify_dependencies(header): """ Check package dependencies. Header is an rpm.hdr. Don't like opening another ts to do this, but it was the only way I could find of clearing the ts out. Have asked on the rpm-maint list on how to do this the right way (28 Feb 2007). ts.check() returns: ((name, version, release), (reqname, reqversion), \ flags, suggest, sense) """ _ts1 = rpmtransactionset() _ts1.addInstall(header, 'Dep Check', 'i') dep_errors = _ts1.check() _ts1.closeDB() return dep_errors def rpm_verify_package(vp_ts, header, verify_options): """ Verify a single package specified by header. Header is an rpm.hdr. If errors are found it returns a dictionary of errors. """ # Set some transaction level flags. vsflags = 0 if 'nodigest' in verify_options: vsflags |= rpm._RPMVSF_NODIGESTS if 'nosignature' in verify_options: vsflags |= rpm._RPMVSF_NOSIGNATURES ovsflags = vp_ts.setVSFlags(vsflags) # Map from the Python options to the rpm bitwise flags. omitmask = 0 if 'nolinkto' in verify_options: omitmask |= VERIFY_LINKTO if 'nomd5' in verify_options: omitmask |= VERIFY_MD5 if 'nosize' in verify_options: omitmask |= VERIFY_SIZE if 'nouser' in verify_options: omitmask |= VERIFY_USER if 'nogroup' in verify_options: omitmask |= VERIFY_GROUP if 'nomtime' in verify_options: omitmask |= VERIFY_MTIME if 'nomode' in verify_options: omitmask |= VERIFY_MODE if 'nordev' in verify_options: omitmask |= VERIFY_RDEV omitmask = ((~omitmask & VERIFY_ATTRS) ^ VERIFY_ATTRS) package_results = {} # Check Signatures and Digests. # No idea what this might return. Need to break something to see. # Setting the vsflags above determines what gets checked in the header. hdr_stat = vp_ts.hdrCheck(header.unload()) if hdr_stat: package_results['hdr'] = hdr_stat # Check Package Depencies. if 'nodeps' not in verify_options: dep_stat = rpm_verify_dependencies(header) if dep_stat: package_results['deps'] = dep_stat # Check all the package files. if 'nofiles' not in verify_options: vp_fi = header.fiFromHeader() for fileinfo in vp_fi: # Do not bother doing anything with ghost files. # This is what RPM does. if fileinfo[4] & rpm.RPMFILE_GHOST: continue # This is only needed because of an inconsistency in the # rpm.fi interface. linktos = vp_fi.FLink() file_stat = rpm_verify_file(fileinfo, linktos, omitmask) #if len(file_stat) > 0 or options.verbose: if len(file_stat) > 0: fflags = fileinfo[4] if fflags & rpm.RPMFILE_CONFIG: file_stat.append('c') elif fflags & rpm.RPMFILE_DOC: file_stat.append('d') elif fflags & rpm.RPMFILE_GHOST: file_stat.append('g') elif fflags & rpm.RPMFILE_LICENSE: file_stat.append('l') elif fflags & rpm.RPMFILE_PUBKEY: file_stat.append('P') elif fflags & rpm.RPMFILE_README: file_stat.append('r') else: file_stat.append(' ') file_stat.append(fileinfo[0]) # The filename. package_results.setdefault('files', []).append(file_stat) # Run the verify script if there is one. # Do we want this? #if 'noscripts' not in verify_options: # script_stat = rpmVerifyscript() # if script_stat: # package_results['script'] = script_stat # If there have been any errors, add the package nevra to the result. if len(package_results) > 0: package_results.setdefault('nevra', (header[rpm.RPMTAG_NAME], \ header[rpm.RPMTAG_EPOCH], \ header[rpm.RPMTAG_VERSION], \ header[rpm.RPMTAG_RELEASE], \ header[rpm.RPMTAG_ARCH])) else: package_results = None # Put things back the way we found them. vsflags = vp_ts.setVSFlags(ovsflags) return package_results def rpm_verify(verify_ts, verify_pkgspec, verify_options=[]): """ Requires rpmtransactionset() to be run first to get a ts. pkgspec is a dict specifying the package e.g.: For a single package { name='foo', epoch='20', version='1', release='1', arch='x86_64'} For all packages {} Or any combination of keywords to select one or more packages to verify. options is a list of 'rpm --verify' options. Default is to check everything. e.g.: [ 'nodeps', 'nodigest', 'nofiles', 'noscripts', 'nosignature', 'nolinkto' 'nomd5', 'nosize', 'nouser', 'nogroup', 'nomtime', 'nomode', 'nordev' ] Returns a list. One list entry per package. Each list entry is a dictionary. Dict keys are 'files', 'deps', 'nevra' and 'hdr'. Entries only get added for the failures. If nothing failed, None is returned. Its all a bit messy and probably needs reviewing. [ { 'hdr': [???], 'deps: [((name, version, release), (reqname, reqversion), flags, suggest, sense), .... ] 'files': [ ['filename1', 'RPMVERIFY_GROUP', 'RPMVERIFY_USER' ], ['filename2', 'RPMVERFIY_LSTATFAIL']] 'nevra': ['name1', 'epoch1', 'version1', 'release1', 'arch1'] } { 'hdr': [???], 'deps: [((name, version, release), (reqname, reqversion), flags, suggest, sense), .... ] 'files': [ ['filename', 'RPMVERIFY_GROUP', 'RPMVERIFY_USER" ], ['filename2', 'RPMVERFIY_LSTATFAIL']] 'nevra': ['name2', 'epoch2', 'version2', 'release2', 'arch2'] } ] """ verify_results = [] headers = getheadersbykeyword(verify_ts, **verify_pkgspec) for header in headers: result = rpm_verify_package(verify_ts, header, verify_options) if result: verify_results.append(result) return verify_results def rpmtransactionset(): """ A simple wrapper for rpm.TransactionSet() to keep everthiing together. Might use it to set some ts level flags later. """ ts = rpm.TransactionSet() return ts class Rpmtscallback(object): """ Callback for ts.run(). Used for adding, upgrading and removing packages. Starting with all possible reasons codes, but bcfg2 will probably only make use of a few of them. Mostly just printing stuff at the moment to understand how the callback is used. """ def __init__(self): self.fdnos = {} def callback(self, reason, amount, total, key, client_data): """ Generic rpmts call back. """ if reason == rpm.RPMCALLBACK_INST_OPEN_FILE: pass elif reason == rpm.RPMCALLBACK_INST_CLOSE_FILE: pass elif reason == rpm.RPMCALLBACK_INST_START: pass elif reason == rpm.RPMCALLBACK_TRANS_PROGRESS or \ reason == rpm.RPMCALLBACK_INST_PROGRESS: pass # rpm.RPMCALLBACK_INST_PROGRESS' elif reason == rpm.RPMCALLBACK_TRANS_START: pass elif reason == rpm.RPMCALLBACK_TRANS_STOP: pass elif reason == rpm.RPMCALLBACK_REPACKAGE_START: pass elif reason == rpm.RPMCALLBACK_REPACKAGE_PROGRESS: pass elif reason == rpm.RPMCALLBACK_REPACKAGE_STOP: pass elif reason == rpm.RPMCALLBACK_UNINST_PROGRESS: pass elif reason == rpm.RPMCALLBACK_UNINST_START: pass elif reason == rpm.RPMCALLBACK_UNINST_STOP: pass # How do we get at this? # RPM.modified += key elif reason == rpm.RPMCALLBACK_UNPACK_ERROR: pass elif reason == rpm.RPMCALLBACK_CPIO_ERROR: pass elif reason == rpm.RPMCALLBACK_UNKNOWN: pass else: print('ERROR - Fell through callBack') def rpm_erase(erase_pkgspecs, erase_flags): """ pkgspecs is a list of pkgspec dicts specifying packages e.g.: For a single package { name='foo', epoch='20', version='1', release='1', arch='x86_64'} """ erase_ts_flags = 0 if 'noscripts' in erase_flags: erase_ts_flags |= rpm.RPMTRANS_FLAG_NOSCRIPTS if 'notriggers' in erase_flags: erase_ts_flags |= rpm.RPMTRANS_FLAG_NOTRIGGERS if 'repackage' in erase_flags: erase_ts_flags |= rpm.RPMTRANS_FLAG_REPACKAGE erase_ts = rpmtransactionset() erase_ts.setFlags(erase_ts_flags) for pkgspec in erase_pkgspecs: idx_list = getindexbykeyword(erase_ts, **pkgspec) if len(idx_list) > 1 and not 'allmatches' in erase_flags: #pass print('ERROR - Multiple package match for erase', pkgspec) else: for idx in idx_list: erase_ts.addErase(idx) #for te in erase_ts: erase_problems = [] if 'nodeps' not in erase_flags: erase_problems = erase_ts.check() if erase_problems == []: erase_ts.order() erase_callback = Rpmtscallback() erase_ts.run(erase_callback.callback, 'Erase') #else: erase_ts.closeDB() del erase_ts return erase_problems def display_verify_file(file_results): ''' Display file results similar to rpm --verify. ''' filename = file_results[-1] filetype = file_results[-2] result_string = '' if 'RPMVERIFY_LSTATFAIL' in file_results: result_string = 'missing ' else: if 'RPMVERIFY_FILESIZE' in file_results: result_string = result_string + 'S' else: result_string = result_string + '.' if 'RPMVERIFY_MODE' in file_results: result_string = result_string + 'M' else: result_string = result_string + '.' if 'RPMVERIFY_MD5' in file_results: if 'RPMVERIFY_READFAIL' in file_results: result_string = result_string + '?' else: result_string = result_string + '5' else: result_string = result_string + '.' if 'RPMVERIFY_RDEV' in file_results: result_string = result_string + 'D' else: result_string = result_string + '.' if 'RPMVERIFY_LINKTO' in file_results: if 'RPMVERIFY_READLINKFAIL' in file_results: result_string = result_string + '?' else: result_string = result_string + 'L' else: result_string = result_string + '.' if 'RPMVERIFY_USER' in file_results: result_string = result_string + 'U' else: result_string = result_string + '.' if 'RPMVERIFY_GROUP' in file_results: result_string = result_string + 'G' else: result_string = result_string + '.' if 'RPMVERIFY_MTIME' in file_results: result_string = result_string + 'T' else: result_string = result_string + '.' print(result_string + ' ' + filetype + ' ' + filename) sys.stdout.flush() #=============================================================================== # Some options and output to assist with development and testing. # These are not intended for normal use. if __name__ == "__main__": p = optparse.OptionParser() p.add_option('--name', action='store', \ default=None, \ help='''Package name to verify. ****************************************** NOT SPECIFYING A NAME MEANS 'ALL' PACKAGES. ****************************************** The specified operation will be carried out on all instances of packages that match the package specification (name, epoch, version, release, arch).''') p.add_option('--epoch', action='store', \ default=None, \ help='''Package epoch.''') p.add_option('--version', action='store', \ default=None, \ help='''Package version.''') p.add_option('--release', action='store', \ default=None, \ help='''Package release.''') p.add_option('--arch', action='store', \ default=None, \ help='''Package arch.''') p.add_option('--erase', '-e', action='store_true', \ default=None, \ help='''**************************************************** REMOVE PACKAGES. THERE ARE NO WARNINGS. MULTIPLE PACKAGES WILL BE REMOVED IF A FULL PACKAGE SPEC IS NOT GIVEN. E.G. IF JUST A NAME IS GIVEN ALL INSTALLED INSTANCES OF THAT PACKAGE WILL BE REMOVED PROVIDED DEPENDENCY CHECKS PASS. IF JUST AN EPOCH IS GIVEN ALL PACKAGE INSTANCES WITH THAT EPOCH WILL BE REMOVED. ****************************************************''') p.add_option('--list', '-l', action='store_true', \ help='''List package identity info. rpm -qa ish equivalent intended for use in RefreshPackages().''') p.add_option('--verify', action='store_true', \ help='''Verify Package(s). Output is only produced after all packages has been verified. Be patient.''') p.add_option('--verbose', '-v', action='store_true', \ help='''Verbose output for --verify option. Output is the same as rpm -v --verify.''') p.add_option('--nodeps', action='store_true', \ default=False, \ help='Do not do dependency testing.') p.add_option('--nodigest', action='store_true', \ help='Do not check package digests.') p.add_option('--nofiles', action='store_true', \ help='Do not do file checks.') p.add_option('--noscripts', action='store_true', \ help='Do not run verification scripts.') p.add_option('--nosignature', action='store_true', \ help='Do not do package signature verification.') p.add_option('--nolinkto', action='store_true', \ help='Do not do symlink tests.') p.add_option('--nomd5', action='store_true', \ help='''Do not do MD5 checksums on files. Note that this does not work for prelink files yet.''') p.add_option('--nosize', action='store_true', \ help='''Do not do file size tests. Note that this does not work for prelink files yet.''') p.add_option('--nouser', action='store_true', \ help='Do not check file user ownership.') p.add_option('--nogroup', action='store_true', \ help='Do not check file group ownership.') p.add_option('--nomtime', action='store_true', \ help='Do not check file modification times.') p.add_option('--nomode', action='store_true', \ help='Do not check file modes (permissions).') p.add_option('--nordev', action='store_true', \ help='Do not check device node.') p.add_option('--notriggers', action='store_true', \ help='Do not do not generate triggers on erase.') p.add_option('--repackage', action='store_true', \ help='''Do repackage on erase.i Packages are put in /var/spool/repackage.''') p.add_option('--allmatches', action='store_true', \ help='''Remove all package instances that match the pkgspec. *************************************************** NO WARNINGS ARE GIVEN. IF THERE IS NO PACKAGE SPEC THAT MEANS ALL PACKAGES!!!! ***************************************************''') options, arguments = p.parse_args() pkgspec = {} rpm_options = [] if options.nodeps: rpm_options.append('nodeps') if options.nodigest: rpm_options.append('nodigest') if options.nofiles: rpm_options.append('nofiles') if options.noscripts: rpm_options.append('noscripts') if options.nosignature: rpm_options.append('nosignature') if options.nolinkto: rpm_options.append('nolinkto') if options.nomd5: rpm_options.append('nomd5') if options.nosize: rpm_options.append('nosize') if options.nouser: rpm_options.append('nouser') if options.nogroup: rpm_options.append('nogroup') if options.nomtime: rpm_options.append('nomtime') if options.nomode: rpm_options.append('nomode') if options.nordev: rpm_options.append('nordev') if options.repackage: rpm_options.append('repackage') if options.allmatches: rpm_options.append('allmatches') main_ts = rpmtransactionset() cmdline_pkgspec = {} if options.name != 'all': if options.name: cmdline_pkgspec['name'] = str(options.name) if options.epoch: cmdline_pkgspec['epoch'] = str(options.epoch) if options.version: cmdline_pkgspec['version'] = str(options.version) if options.release: cmdline_pkgspec['release'] = str(options.release) if options.arch: cmdline_pkgspec['arch'] = str(options.arch) if options.verify: results = rpm_verify(main_ts, cmdline_pkgspec, rpm_options) for r in results: files = r.get('files', '') for f in files: display_verify_file(f) elif options.list: for p in rpmpackagelist(main_ts): print(p) elif options.erase: if options.name: rpm_erase([cmdline_pkgspec], rpm_options) else: print('You must specify the "--name" option') bcfg2-1.3.3/src/lib/Bcfg2/Client/XML.py000066400000000000000000000030501223671746500172240ustar00rootroot00000000000000'''XML lib compatibility layer for the Bcfg2 client''' # library will use lxml, then builtin xml.etree, then ElementTree # pylint: disable=E0611,W0611,W0613,C0103 try: from lxml.etree import Element, SubElement, XML, tostring from lxml.etree import XMLSyntaxError as ParseError driver = 'lxml' except ImportError: # lxml not available from xml.parsers.expat import ExpatError as ParseError try: import xml.etree.ElementTree Element = xml.etree.ElementTree.Element SubElement = xml.etree.ElementTree.SubElement XML = xml.etree.ElementTree.XML def tostring(el, encoding=None, xml_declaration=None): """ tostring implementation compatible with lxml """ return xml.etree.ElementTree.tostring(el, encoding=encoding) driver = 'etree-py' except ImportError: try: from elementtree.ElementTree import Element, SubElement, XML, \ tostring driver = 'etree' import elementtree.ElementTree Element = elementtree.ElementTree.Element SubElement = elementtree.ElementTree.SubElement XML = elementtree.ElementTree.XML def tostring(el, encoding=None, xml_declaration=None): """ tostring implementation compatible with lxml """ return elementtree.ElementTree.tostring(el) except ImportError: print("Failed to load lxml, xml.etree or elementtree.ElementTree") print("Cannot continue") raise SystemExit(1) bcfg2-1.3.3/src/lib/Bcfg2/Client/__init__.py000066400000000000000000000017121223671746500203260ustar00rootroot00000000000000"""This contains all Bcfg2 Client modules""" import os import sys import select from Bcfg2.Compat import input # pylint: disable=W0622 def prompt(msg): """ Helper to give a yes/no prompt to the user. Flushes input buffers, handles exceptions, etc. Returns True if the user answers in the affirmative, False otherwise. :param msg: The message to show to the user. The message is not altered in any way for display; i.e., it should contain "[y/N]" if desired, etc. :type msg: string :returns: bool - True if yes, False if no """ while len(select.select([sys.stdin.fileno()], [], [], 0.0)[0]) > 0: os.read(sys.stdin.fileno(), 4096) try: ans = input(msg) return ans in ['y', 'Y'] except UnicodeEncodeError: ans = input(msg.encode('utf-8')) return ans in ['y', 'Y'] except EOFError: # handle ^C on rhel-based platforms raise SystemExit(1) bcfg2-1.3.3/src/lib/Bcfg2/Compat.py000066400000000000000000000210121223671746500165670ustar00rootroot00000000000000""" Compatibility imports, mostly for Py3k support, but also for Python 2.4 and such-like """ ################################################### # # # IF YOU ADD SOMETHING TO THIS FILE, YOU MUST # # DOCUMENT IT IN docs/development/compat.txt # # # ################################################### import sys # pylint: disable=E0601,E0602,E0611,W0611,W0622,C0103 try: from email.Utils import formatdate except ImportError: from email.utils import formatdate # urllib imports try: from urllib import quote_plus from urlparse import urljoin, urlparse from urllib2 import HTTPBasicAuthHandler, \ HTTPPasswordMgrWithDefaultRealm, build_opener, install_opener, \ urlopen, HTTPError, URLError except ImportError: from urllib.parse import urljoin, urlparse, quote_plus from urllib.request import HTTPBasicAuthHandler, \ HTTPPasswordMgrWithDefaultRealm, build_opener, install_opener, urlopen from urllib.error import HTTPError, URLError try: from cStringIO import StringIO except ImportError: from io import StringIO try: import ConfigParser except ImportError: import configparser as ConfigParser try: import cPickle except ImportError: import pickle as cPickle try: from Queue import Queue, Empty, Full except ImportError: from queue import Queue, Empty, Full # xmlrpc imports try: import xmlrpclib import SimpleXMLRPCServer except ImportError: import xmlrpc.client as xmlrpclib import xmlrpc.server as SimpleXMLRPCServer # socketserver import try: import SocketServer except ImportError: import socketserver as SocketServer # httplib imports try: import httplib except ImportError: import http.client as httplib try: unicode = unicode except NameError: unicode = str def u_str(string, encoding=None): """ print to file compatibility """ if sys.hexversion >= 0x03000000: return string else: if encoding is not None: return unicode(string, encoding) else: return unicode(string) try: from functools import wraps except ImportError: def wraps(wrapped): # pylint: disable=W0613 """ implementation of functools.wraps() for python 2.4 """ return lambda f: f # base64 compat if sys.hexversion >= 0x03000000: from base64 import b64encode as _b64encode, b64decode as _b64decode @wraps(_b64encode) def b64encode(val, **kwargs): # pylint: disable=C0111 try: return _b64encode(val, **kwargs) except TypeError: return _b64encode(val.encode('UTF-8'), **kwargs).decode('UTF-8') @wraps(_b64decode) def b64decode(val, **kwargs): # pylint: disable=C0111 return _b64decode(val.encode('UTF-8'), **kwargs).decode('UTF-8') else: from base64 import b64encode, b64decode try: input = raw_input except NameError: input = input try: reduce = reduce except NameError: from functools import reduce try: from collections import MutableMapping except ImportError: from UserDict import DictMixin as MutableMapping class CmpMixin(object): """ In Py3K, :meth:`object.__cmp__` is no longer magical, so this mixin can be used to define the rich comparison operators from ``__cmp__`` -- i.e., it makes ``__cmp__`` magical again. """ def __lt__(self, other): return self.__cmp__(other) < 0 def __gt__(self, other): return self.__cmp__(other) > 0 def __eq__(self, other): return self.__cmp__(other) == 0 def __ne__(self, other): return not self.__eq__(other) def __ge__(self, other): return self.__gt__(other) or self.__eq__(other) def __le__(self, other): return self.__lt__(other) or self.__eq__(other) try: from pkgutil import walk_packages except ImportError: try: from pkgutil import iter_modules # iter_modules was added in python 2.5; use it to get an exact # re-implementation of walk_packages if possible def walk_packages(path=None, prefix='', onerror=None): """ Implementation of walk_packages for python 2.5 """ def seen(path, seenpaths={}): # pylint: disable=W0102 """ detect if a path has been 'seen' (i.e., considered for inclusion in the generator). tracks what has been seen through the magic of python default arguments """ if path in seenpaths: return True seenpaths[path] = True for importer, name, ispkg in iter_modules(path, prefix): yield importer, name, ispkg if ispkg: try: __import__(name) except ImportError: if onerror is not None: onerror(name) except Exception: if onerror is not None: onerror(name) else: raise else: path = getattr(sys.modules[name], '__path__', []) # don't traverse path items we've seen before path = [p for p in path if not seen(p)] for item in walk_packages(path, name + '.', onerror): yield item except ImportError: import os def walk_packages(path=None, prefix='', onerror=None): """ Imperfect, incomplete implementation of :func:`pkgutil.walk_packages` for python 2.4. Differences: * Requires a full path, not a path relative to something in sys.path. Anywhere we care about that shouldn't be an issue. * The first element of each tuple is None instead of an importer object. """ if path is None: path = sys.path for mpath in path: for fname in os.listdir(mpath): fpath = os.path.join(mpath, fname) if (os.path.isfile(fpath) and fname.endswith(".py") and fname != '__init__.py'): yield None, prefix + fname[:-3], False elif os.path.isdir(fpath): mname = prefix + fname if os.path.exists(os.path.join(fpath, "__init__.py")): yield None, mname, True try: __import__(mname) except ImportError: if onerror is not None: onerror(mname) except Exception: if onerror is not None: onerror(mname) else: raise else: for item in walk_packages([fpath], prefix=mname + '.', onerror=onerror): yield item try: all = all any = any except NameError: def all(iterable): """ implementation of builtin all() for python 2.4 """ for element in iterable: if not element: return False return True def any(iterable): """ implementation of builtin any() for python 2.4 """ for element in iterable: if element: return True return False try: from hashlib import md5 except ImportError: from md5 import md5 def oct_mode(mode): """ Convert a decimal number describing a POSIX permissions mode to a string giving the octal mode. In Python 2, this is a synonym for :func:`oct`, but in Python 3 the octal format has changed to ``0o000``, which cannot be used as an octal permissions mode, so we need to strip the 'o' from the output. I.e., this function acts like the Python 2 :func:`oct` regardless of what version of Python is in use. :param mode: The decimal mode to convert to octal :type mode: int :returns: string """ return oct(mode).replace('o', '') try: long = long except NameError: # longs are just ints in py3k long = int try: cmp = cmp except NameError: def cmp(a, b): """ Py3k implementation of cmp() """ return (a > b) - (a < b) bcfg2-1.3.3/src/lib/Bcfg2/Encryption.py000077500000000000000000000167051223671746500175160ustar00rootroot00000000000000""" Bcfg2.Encryption provides a number of convenience methods for handling encryption in Bcfg2. See :ref:`server-encryption` for more details. """ import os from M2Crypto import Rand from M2Crypto.EVP import Cipher, EVPError from Bcfg2.Compat import StringIO, md5, b64encode, b64decode #: Constant representing the encryption operation for #: :class:`M2Crypto.EVP.Cipher`, which uses a simple integer. This #: makes our code more readable. ENCRYPT = 1 #: Constant representing the decryption operation for #: :class:`M2Crypto.EVP.Cipher`, which uses a simple integer. This #: makes our code more readable. DECRYPT = 0 #: Default cipher algorithm. To get a full list of valid algorithms, #: you can run:: #: #: openssl list-cipher-algorithms | grep -v ' => ' | \ #: tr 'A-Z-' 'a-z_' | sort -u ALGORITHM = "aes_256_cbc" #: Default initialization vector. For best security, you should use a #: unique IV for each message. :func:`ssl_encrypt` does this in an #: automated fashion. IV = r'\0' * 16 #: The config file section encryption options and passphrases are #: stored in CFG_SECTION = "encryption" #: The config option used to store the algorithm CFG_ALGORITHM = "algorithm" #: The config option used to store the decryption strictness CFG_DECRYPT = "decrypt" Rand.rand_seed(os.urandom(1024)) def _cipher_filter(cipher, instr): """ M2Crypto reads and writes file-like objects, so this uses StringIO to pass data through it """ inbuf = StringIO(instr) outbuf = StringIO() while 1: buf = inbuf.read() if not buf: break outbuf.write(cipher.update(buf)) outbuf.write(cipher.final()) rv = outbuf.getvalue() inbuf.close() outbuf.close() return rv def str_encrypt(plaintext, key, iv=IV, algorithm=ALGORITHM, salt=None): """ Encrypt a string with a key. For a higher-level encryption interface, see :func:`ssl_encrypt`. :param plaintext: The plaintext data to encrypt :type plaintext: string :param key: The key to encrypt the data with :type key: string :param iv: The initialization vector :type iv: string :param algorithm: The cipher algorithm to use :type algorithm: string :param salt: The salt to use :type salt: string :returns: string - The decrypted data """ cipher = Cipher(alg=algorithm, key=key, iv=iv, op=ENCRYPT, salt=salt) return _cipher_filter(cipher, plaintext) def str_decrypt(crypted, key, iv=IV, algorithm=ALGORITHM): """ Decrypt a string with a key. For a higher-level decryption interface, see :func:`ssl_decrypt`. :param crypted: The raw binary encrypted data :type crypted: string :param key: The encryption key to decrypt with :type key: string :param iv: The initialization vector :type iv: string :param algorithm: The cipher algorithm to use :type algorithm: string :returns: string - The decrypted data """ cipher = Cipher(alg=algorithm, key=key, iv=iv, op=DECRYPT) return _cipher_filter(cipher, crypted) def ssl_decrypt(data, passwd, algorithm=ALGORITHM): """ Decrypt openssl-encrypted data. This can decrypt data encrypted by :func:`ssl_encrypt`, or ``openssl enc``. It performs a base64 decode first if the data is base64 encoded, and automatically determines the salt and initialization vector (both of which are embedded in the encrypted data). :param data: The encrypted data (either base64-encoded or raw binary) to decrypt :type data: string :param passwd: The password to use to decrypt the data :type passwd: string :param algorithm: The cipher algorithm to use :type algorithm: string :returns: string - The decrypted data """ # base64-decode the data data = b64decode(data) salt = data[8:16] # pylint: disable=E1101 hashes = [md5(passwd + salt).digest()] for i in range(1, 3): hashes.append(md5(hashes[i - 1] + passwd + salt).digest()) # pylint: enable=E1101 key = hashes[0] + hashes[1] iv = hashes[2] return str_decrypt(data[16:], key=key, iv=iv, algorithm=algorithm) def ssl_encrypt(plaintext, passwd, algorithm=ALGORITHM, salt=None): """ Encrypt data in a format that is openssl compatible. :param plaintext: The plaintext data to encrypt :type plaintext: string :param passwd: The password to use to encrypt the data :type passwd: string :param algorithm: The cipher algorithm to use :type algorithm: string :param salt: The salt to use. If none is provided, one will be randomly generated. :type salt: bytes :returns: string - The base64-encoded, salted, encrypted string. The string includes a trailing newline to make it fully compatible with openssl command-line tools. """ if salt is None: salt = Rand.rand_bytes(8) # pylint: disable=E1101 hashes = [md5(passwd + salt).digest()] for i in range(1, 3): hashes.append(md5(hashes[i - 1] + passwd + salt).digest()) # pylint: enable=E1101 key = hashes[0] + hashes[1] iv = hashes[2] crypted = str_encrypt(plaintext, key=key, salt=salt, iv=iv, algorithm=algorithm) return b64encode("Salted__" + salt + crypted) + "\n" def get_algorithm(setup): """ Get the cipher algorithm from the config file. This is used in case someone uses the OpenSSL algorithm name (e.g., "AES-256-CBC") instead of the M2Crypto name (e.g., "aes_256_cbc"), and to handle errors in a sensible way and deduplicate this code. :param setup: The Bcfg2 option set to extract passphrases from :type setup: Bcfg2.Options.OptionParser :returns: dict - a dict of ````: ```` """ return setup.cfp.get(CFG_SECTION, CFG_ALGORITHM, default=ALGORITHM).lower().replace("-", "_") def get_passphrases(setup): """ Get all candidate encryption passphrases from the config file. :param setup: The Bcfg2 option set to extract passphrases from :type setup: Bcfg2.Options.OptionParser :returns: dict - a dict of ````: ```` """ section = CFG_SECTION if setup.cfp.has_section(section): return dict([(o, setup.cfp.get(section, o)) for o in setup.cfp.options(section) if o not in [CFG_ALGORITHM, CFG_DECRYPT]]) else: return dict() def bruteforce_decrypt(crypted, passphrases=None, setup=None, algorithm=ALGORITHM): """ Convenience method to decrypt the given encrypted string by trying the given passphrases or all passphrases (as returned by :func:`get_passphrases`) sequentially until one is found that works. Either ``passphrases`` or ``setup`` must be provided. :param crypted: The data to decrypt :type crypted: string :param passphrases: The passphrases to try. :type passphrases: list :param setup: A Bcfg2 option set to extract passphrases from :type setup: Bcfg2.Options.OptionParser :param algorithm: The cipher algorithm to use :type algorithm: string :returns: string - The decrypted data :raises: :class:`M2Crypto.EVP.EVPError`, if the data cannot be decrypted """ if passphrases is None: passphrases = get_passphrases(setup).values() for passwd in passphrases: try: return ssl_decrypt(crypted, passwd, algorithm=algorithm) except EVPError: pass raise EVPError("Failed to decrypt") bcfg2-1.3.3/src/lib/Bcfg2/Logger.py000066400000000000000000000201331223671746500165660ustar00rootroot00000000000000"""Bcfg2 logging support""" import copy import fcntl import logging import logging.handlers import math import socket import struct import sys import termios logging.raiseExceptions = 0 class TermiosFormatter(logging.Formatter): """The termios formatter displays output in a terminal-sensitive fashion. """ def __init__(self, fmt=None, datefmt=None): logging.Formatter.__init__(self, fmt, datefmt) if sys.stdout.isatty(): # now get termios info try: self.width = struct.unpack('hhhh', fcntl.ioctl(0, termios.TIOCGWINSZ, "\000" * 8))[1] if self.width == 0: self.width = 80 except: # pylint: disable=W0702 self.width = 80 else: # output to a pipe self.width = 32768 def format(self, record): '''format a record for display''' returns = [] line_len = self.width if isinstance(record.msg, str): for line in record.msg.split('\n'): if len(line) <= line_len: returns.append(line) else: inner_lines = \ int(math.floor(float(len(line)) / line_len)) + 1 for msgline in range(inner_lines): returns.append( line[msgline * line_len:(msgline + 1) * line_len]) elif isinstance(record.msg, list): if not record.msg: return '' record.msg.sort() msgwidth = self.width col_width = max([len(item) for item in record.msg]) columns = int(math.floor(float(msgwidth) / (col_width + 2))) lines = int(math.ceil(float(len(record.msg)) / columns)) for lineno in range(lines): indices = [idx for idx in [(colNum * lines) + lineno for colNum in range(columns)] if idx < len(record.msg)] retformat = (len(indices) * (" %%-%ds " % col_width)) returns.append(retformat % tuple([record.msg[idx] for idx in indices])) else: returns.append(str(record.msg)) if record.exc_info: returns.append(self.formatException(record.exc_info)) return '\n'.join(returns) class FragmentingSysLogHandler(logging.handlers.SysLogHandler): """ This handler fragments messages into chunks smaller than 250 characters """ def __init__(self, procname, path, facility): self.procname = procname self.unixsocket = False logging.handlers.SysLogHandler.__init__(self, path, facility) def emit(self, record): """Chunk and deliver records.""" record.name = self.procname if isinstance(record.msg, str): msgs = [] error = record.exc_info record.exc_info = None msgdata = record.msg if len(msgdata) == 0: return while msgdata: newrec = copy.copy(record) newrec.msg = msgdata[:250] msgs.append(newrec) msgdata = msgdata[250:] msgs[0].exc_info = error else: msgs = [record] for newrec in msgs: msg = '<%d>%s\000' % \ (self.encodePriority(self.facility, newrec.levelname.lower()), self.format(newrec)) try: try: encoded = msg.encode('utf-8') except UnicodeDecodeError: encoded = msg self.socket.send(encoded) except socket.error: for i in range(10): # pylint: disable=W0612 try: if isinstance(self.address, tuple): self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.connect(self.address) else: self._connect_unixsocket(self.address) break except socket.error: continue try: reconn = copy.copy(record) reconn.msg = 'Reconnected to syslog' self.socket.send('<%d>%s\000' % (self.encodePriority(self.facility, logging.WARNING), self.format(reconn))) self.socket.send(msg) except: # pylint: disable=W0702 # If we still fail then drop it. Running # bcfg2-server as non-root can trigger permission # denied exceptions. pass def add_console_handler(level=logging.DEBUG): """ Add a logging handler that logs at a level to sys.stderr """ console = logging.StreamHandler(sys.stderr) console.setLevel(level) # tell the handler to use this format console.setFormatter(TermiosFormatter()) try: console.set_name("console") # pylint: disable=E1101 except AttributeError: console.name = "console" # pylint: disable=W0201 logging.root.addHandler(console) def add_syslog_handler(procname, syslog_facility, level=logging.DEBUG): """Add a logging handler that logs as procname to syslog_facility.""" try: try: syslog = FragmentingSysLogHandler(procname, '/dev/log', syslog_facility) except socket.error: syslog = FragmentingSysLogHandler(procname, ('localhost', 514), syslog_facility) try: syslog.set_name("syslog") # pylint: disable=E1101 except AttributeError: syslog.name = "syslog" # pylint: disable=W0201 syslog.setLevel(level) syslog.setFormatter( logging.Formatter('%(name)s[%(process)d]: %(message)s')) logging.root.addHandler(syslog) except socket.error: logging.root.error("Failed to activate syslogging") except: print("Failed to activate syslogging") def add_file_handler(to_file, level=logging.DEBUG): """Add a logging handler that logs to to_file.""" filelog = logging.FileHandler(to_file) try: filelog.set_name("file") # pylint: disable=E1101 except AttributeError: filelog.name = "file" # pylint: disable=W0201 filelog.setLevel(level) filelog.setFormatter( logging.Formatter('%(asctime)s %(name)s[%(process)d]: %(message)s')) logging.root.addHandler(filelog) def setup_logging(procname, to_console=True, to_syslog=True, syslog_facility='daemon', level=0, to_file=None): """Setup logging for Bcfg2 software.""" if hasattr(logging, 'already_setup'): return params = [] if to_console: if to_console is True: to_console = logging.WARNING if level == 0: clvl = to_console else: clvl = min(to_console, level) params.append("%s to console" % logging.getLevelName(clvl)) add_console_handler(clvl) if to_syslog: if level == 0: slvl = logging.INFO else: slvl = min(level, logging.INFO) params.append("%s to syslog" % logging.getLevelName(slvl)) add_syslog_handler(procname, syslog_facility, level=slvl) if to_file is not None: params.append("%s to %s" % (logging.getLevelName(level), to_file)) add_file_handler(to_file, level=level) logging.root.setLevel(logging.DEBUG) logging.root.debug("Configured logging: %s" % "; ".join(params)) logging.already_setup = True bcfg2-1.3.3/src/lib/Bcfg2/Options.py000066400000000000000000001336611223671746500170150ustar00rootroot00000000000000"""Option parsing library for utilities.""" import copy import getopt import inspect import os import re import shlex import sys import grp import pwd import Bcfg2.Client.Tools from Bcfg2.Compat import ConfigParser from Bcfg2.version import __version__ class OptionFailure(Exception): """ raised when malformed Option objects are instantiated """ pass DEFAULT_CONFIG_LOCATION = '/etc/bcfg2.conf' DEFAULT_INSTALL_PREFIX = '/usr' class DefaultConfigParser(ConfigParser.ConfigParser): """ A config parser that can be used to query options with default values in the event that the option is not found """ def __init__(self, *args, **kwargs): """Make configuration options case sensitive""" ConfigParser.ConfigParser.__init__(self, *args, **kwargs) self.optionxform = str def get(self, section, option, **kwargs): """ convenience method for getting config items """ default = None if 'default' in kwargs: default = kwargs['default'] del kwargs['default'] try: return ConfigParser.ConfigParser.get(self, section, option, **kwargs) except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): if default is not None: return default else: raise def getboolean(self, section, option, **kwargs): """ convenience method for getting boolean config items """ default = None if 'default' in kwargs: default = kwargs['default'] del kwargs['default'] try: return ConfigParser.ConfigParser.getboolean(self, section, option, **kwargs) except (ConfigParser.NoSectionError, ConfigParser.NoOptionError, ValueError): if default is not None: return default else: raise class Option(object): """ a single option, which might be read from the command line, environment, or config file """ # pylint: disable=C0103,R0913 def __init__(self, desc, default, cmd=None, odesc=False, env=False, cf=False, cook=False, long_arg=False, deprecated_cf=None): self.desc = desc self.default = default self.cmd = cmd self.long = long_arg if not self.long: if cmd and (cmd[0] != '-' or len(cmd) != 2): raise OptionFailure("Poorly formed command %s" % cmd) elif cmd and not cmd.startswith('--'): raise OptionFailure("Poorly formed command %s" % cmd) self.odesc = odesc self.env = env self.cf = cf self.deprecated_cf = deprecated_cf self.boolean = False if not odesc and not cook and isinstance(self.default, bool): self.boolean = True self.cook = cook self.value = None # pylint: enable=C0103,R0913 def get_cooked_value(self, value): """ get the value of this option after performing any option munging specified in the 'cook' keyword argument to the constructor """ if self.boolean: return True if self.cook: return self.cook(value) else: return value def __str__(self): rv = ["%s: " % self.__class__.__name__, self.desc] if self.cmd or self.cf: rv.append(" (") if self.cmd: if self.odesc: if self.long: rv.append("%s=%s" % (self.cmd, self.odesc)) else: rv.append("%s %s" % (self.cmd, self.odesc)) else: rv.append("%s" % self.cmd) if self.cf: if self.cmd: rv.append("; ") rv.append("[%s].%s" % self.cf) if self.cmd or self.cf: rv.append(")") if hasattr(self, "value"): rv.append(": %s" % self.value) return "".join(rv) def buildHelpMessage(self): """ build the help message for this option """ vals = [] if not self.cmd: return '' if self.odesc: if self.long: vals.append("%s=%s" % (self.cmd, self.odesc)) else: vals.append("%s %s" % (self.cmd, self.odesc)) else: vals.append(self.cmd) vals.append(self.desc) return " %-28s %s\n" % tuple(vals) def buildGetopt(self): """ build a string suitable for describing this short option to getopt """ gstr = '' if self.long: return gstr if self.cmd: gstr = self.cmd[1] if self.odesc: gstr += ':' return gstr def buildLongGetopt(self): """ build a string suitable for describing this long option to getopt """ if self.odesc: return self.cmd[2:] + '=' else: return self.cmd[2:] def parse(self, opts, rawopts, configparser=None): """ parse a single option. try parsing the data out of opts (the results of getopt), rawopts (the raw option string), the environment, and finally the config parser. either opts or rawopts should be provided, but not both """ if self.cmd and opts: # Processing getopted data optinfo = [opt[1] for opt in opts if opt[0] == self.cmd] if optinfo: if optinfo[0]: self.value = self.get_cooked_value(optinfo[0]) else: self.value = True return if self.cmd and self.cmd in rawopts: if self.odesc: data = rawopts[rawopts.index(self.cmd) + 1] else: data = True self.value = self.get_cooked_value(data) return # No command line option found if self.env and self.env in os.environ: self.value = self.get_cooked_value(os.environ[self.env]) return if self.cf and configparser: try: self.value = self.get_cooked_value(configparser.get(*self.cf)) return except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): pass if self.deprecated_cf: try: self.value = self.get_cooked_value( configparser.get(*self.deprecated_cf)) print("Warning: [%s] %s is deprecated, use [%s] %s instead" % (self.deprecated_cf[0], self.deprecated_cf[1], self.cf[0], self.cf[1])) return except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): pass # Default value not cooked self.value = self.default class OptionSet(dict): """ a set of Option objects that interfaces with getopt and DefaultConfigParser to populate a dict of