pax_global_header00006660000000000000000000000064132341113520014505gustar00rootroot0000000000000052 comment=c95dd990cc2518f6be97a5fc99f086cb3bbf5fe0 patroni-1.4.2/000077500000000000000000000000001323411135200131655ustar00rootroot00000000000000patroni-1.4.2/.gitignore000066400000000000000000000010011323411135200151450ustar00rootroot00000000000000*.py[cod] # vi(m) swap files: *.sw? # C extensions *.so # Packages .cache/ *.egg *.eggs *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml coverage.xml htmlcov junit.xml features/output dummy # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject pgpass scm-source.json # Sphinx-generated documentation docs/build/ docs/source/_static/ docs/source/_templates/ patroni-1.4.2/.travis.yml000066400000000000000000000136051323411135200153030ustar00rootroot00000000000000sudo: true dist: trusty language: python env: global: - ETCDVERSION=3.0.17 ZKVERSION=3.4.11 CONSULVERSION=0.7.4 - PYVERSIONS="2.7 3.5 3.6" - EXCLUDE_BEHAVE="3.5" - BOTO_CONFIG=/doesnotexist matrix: include: - python: "3.5" env: TEST_SUITE="python setup.py" - python: "3.6" env: DCS="etcd" TEST_SUITE="behave" - python: "3.6" env: DCS="exhibitor" TEST_SUITE="behave" - python: "3.6" env: DCS="consul" TEST_SUITE="behave" - python: "3.6" env: DCS="kubernetes" TEST_SUITE="behave" branches: only: - master - /^v\d+\.\d+(\.\d+)?$/ cache: directories: - $HOME/mycache before_cache: - | rm -fr $HOME/mycache/python* for pv in $PYVERSIONS; do if [[ $TEST_SUITE != "behave" || $pv != $EXCLUDE_BEHAVE ]]; then fpv=$(basename $(readlink $HOME/virtualenv/python${pv})) mv $HOME/virtualenv/${fpv} $HOME/mycache/${fpv} fi done install: - | set -e if [[ $TEST_SUITE == "behave" ]]; then function get_consul() { CC=~/mycache/consul_${CONSULVERSION} if [[ ! -x $CC ]]; then curl -L https://releases.hashicorp.com/consul/${CONSULVERSION}/consul_${CONSULVERSION}_linux_amd64.zip \ | gunzip > $CC [[ ${PIPESTATUS[0]} == 0 ]] || return 1 chmod +x $CC fi ln -s $CC consul } function get_etcd() { EC=~/mycache/etcd_${ETCDVERSION} if [[ ! -x $EC ]]; then curl -L https://github.com/coreos/etcd/releases/download/v${ETCDVERSION}/etcd-v${ETCDVERSION}-linux-amd64.tar.gz \ | tar xz -C . --strip=1 --wildcards --no-anchored etcd [[ ${PIPESTATUS[0]} == 0 ]] || return 1 mv etcd $EC fi ln -s $EC etcd } function get_kubernetes() { wget -O localkube "https://storage.googleapis.com/minikube/k8sReleases/v1.7.0/localkube-linux-amd64" chmod +x localkube sudo nohup ./localkube --logtostderr=true --enable-dns=false > localkube.log 2>&1 & echo "Waiting for localkube to start..." if ! timeout 120 sh -c "while ! curl -ks http://127.0.0.1:8080/ >/dev/null; do sleep 1; done"; then sudo cat localkube.log echo "localkube did not start" exit 1 fi echo "Check certificate permissions" sudo chmod 644 /var/lib/localkube/certs/* sudo ls -altr /var/lib/localkube/certs/ echo "Set up .kube/config" mkdir ~/.kube echo -e "apiVersion: v1\nclusters:\n- cluster:\n certificate-authority: /var/lib/localkube/certs/ca.crt\n server: https://127.0.0.1:8443\n name: local\ncontexts:\n- context:\n cluster: local\n user: myself\n name: local\ncurrent-context: local\nkind: Config\npreferences: {}\nusers:\n- name: myself\n user:\n client-certificate: /var/lib/localkube/certs/apiserver.crt\n client-key: /var/lib/localkube/certs/apiserver.key\n" > ~/.kube/config } function get_exhibitor() { ZC=~/mycache/zookeeper-${ZKVERSION} if [[ ! -d $ZC ]]; then curl -L http://www.apache.org/dist/zookeeper/zookeeper-${ZKVERSION}/zookeeper-${ZKVERSION}.tar.gz | tar xz [[ ${PIPESTATUS[0]} == 0 ]] || return 1 mv zookeeper-${ZKVERSION}/conf/zoo_sample.cfg zookeeper-${ZKVERSION}/conf/zoo.cfg mv zookeeper-${ZKVERSION} $ZC fi $ZC/bin/zkServer.sh start # following lines are 'emulating' exhibitor REST API while true; do echo -e 'HTTP/1.0 200 OK\nContent-Type: application/json\n\n{"servers":["127.0.0.1"],"port":2181}' \ | nc -l 8181 &> /dev/null done& } attempt_num=1 until get_${DCS}; do [[ $attempt_num -ge 3 ]] && exit 1 echo "Attempt $attempt_num failed! Trying again in $attempt_num seconds..." sleep $(( attempt_num++ )) done fi for pv in $PYVERSIONS; do if [[ $TEST_SUITE != "behave" || $pv != $EXCLUDE_BEHAVE ]]; then fpv=$(basename $(readlink $HOME/virtualenv/python$pv)) if [[ -d ~/mycache/${fpv} ]]; then mv ~/virtualenv/${fpv} ~/virtualenv/${fpv}.bckp mv ~/mycache/${fpv} ~/virtualenv/${fpv} fi source ~/virtualenv/python${pv}/bin/activate # explicitly install all needed python modules to cache them for p in '-r requirements.txt' 'behave codacy-coverage coverage coveralls flake8 mock pytest-cov pytest setuptools'; do pip install $p --upgrade done fi done script: - | for pv in $PYVERSIONS; do if [[ $TEST_SUITE == "behave" && $pv == $EXCLUDE_BEHAVE ]]; then continue fi source ~/virtualenv/python${pv}/bin/activate if [[ $TEST_SUITE != "behave" ]]; then echo Running unit tests using python${pv} $TEST_SUITE test $TEST_SUITE flake8 elif [[ $pv != $EXCLUDE_BEHAVE ]]; then echo Running acceptance tests using python${pv} if ! PATH=.:/usr/lib/postgresql/9.6/bin:$PATH $TEST_SUITE; then # output all log files when tests are failing grep . features/output/*/*postgres?.* exit 1 fi fi done set +e after_success: # before_cache is executed earlier than after_success, so we need to restore one of virtualenv directories - fpv=$(basename $(readlink $HOME/virtualenv/python3.6)) && mv $HOME/mycache/${fpv} $HOME/virtualenv/${fpv} - coveralls - if [[ $TEST_SUITE != "behave" ]]; then python-codacy-coverage -r coverage.xml; fi - if [[ $DCS == "exhibitor" ]]; then ~/mycache/zookeeper-${ZKVERSION}/bin/zkServer.sh stop; fi - sudo kill $(jobs -p) patroni-1.4.2/.zappr.yaml000066400000000000000000000006071323411135200152660ustar00rootroot00000000000000# for github.com approvals: groups: zalando: minimum: 2 from: orgs: - "zalando" # team should be valid team id in team service https://teams.auth.zalando.com/api/teams/:id X-Zalando-Team: "acid" # type should be one of [code, doc, config, tools, secrets] # code will be the default value, if X-Zalando-Type is not found in .zappr.yml X-Zalando-Type: code patroni-1.4.2/Dockerfile000066400000000000000000000041271323411135200151630ustar00rootroot00000000000000## This Dockerfile is meant to aid in the building and debugging patroni whilst developing on your local machine ## It has all the necessary components to play/debug with a single node appliance, running etcd FROM postgres:9.6 MAINTAINER Alexander Kukushkin RUN export DEBIAN_FRONTEND=noninteractive \ && echo 'APT::Install-Recommends "0";\nAPT::Install-Suggests "0";' > /etc/apt/apt.conf.d/01norecommend \ && apt-get update -y \ && apt-get upgrade -y \ && apt-get install -y curl jq haproxy python-psycopg2 python-yaml python-requests python-six python-pysocks \ python-dateutil python-pip python-setuptools python-prettytable python-wheel python-psutil python locales \ ## Make sure we have a en_US.UTF-8 locale available && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 \ && pip install 'python-etcd>=0.4.3,<0.5' click tzlocal cdiff \ && mkdir -p /home/postgres \ && chown postgres:postgres /home/postgres \ # Clean up && apt-get remove -y python-pip python-setuptools \ && apt-get autoremove -y \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* /root/.cache ENV ETCDVERSION 3.2.3 RUN curl -L https://github.com/coreos/etcd/releases/download/v${ETCDVERSION}/etcd-v${ETCDVERSION}-linux-amd64.tar.gz \ | tar xz -C /usr/local/bin --strip=1 --wildcards --no-anchored etcd etcdctl ENV CONFDVERSION 0.11.0 RUN curl -L https://github.com/kelseyhightower/confd/releases/download/v${CONFDVERSION}/confd-${CONFDVERSION}-linux-amd64 > /usr/local/bin/confd \ && chmod +x /usr/local/bin/confd ADD patronictl.py patroni.py docker/entrypoint.sh / ADD patroni /patroni/ ADD extras/confd /etc/confd RUN ln -s /patronictl.py /usr/local/bin/patronictl ### Setting up a simple script that will serve as an entrypoint RUN mkdir /data/ && touch /pgpass /patroni.yml \ && chown postgres:postgres -R /patroni/ /data/ /pgpass /patroni.yml /etc/haproxy /var/run/ /var/lib/ /var/log/ EXPOSE 2379 5432 8008 ENV LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 ENTRYPOINT ["/bin/bash", "/entrypoint.sh"] USER postgres patroni-1.4.2/LICENSE000066400000000000000000000020761323411135200141770ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 Compose, Zalando SE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. patroni-1.4.2/MAINTAINERS000066400000000000000000000002231323411135200146570ustar00rootroot00000000000000Alexander Kukushkin Feike Steenbergen Oleksii Kliukin patroni-1.4.2/MANIFEST.in000066400000000000000000000001031323411135200147150ustar00rootroot00000000000000include requirements* include *.rst recursive-include patroni *.py patroni-1.4.2/README.rst000066400000000000000000000150121323411135200146530ustar00rootroot00000000000000|Build Status| |Coverage Status| Patroni: A Template for PostgreSQL HA with ZooKeeper, etcd or Consul -------------------------------------------------------------------- You can find a version of this documentation that is searchable and also easier to navigate at `patroni.readthedocs.io `__. There are many ways to run high availability with PostgreSQL; for a list, see the `PostgreSQL Documentation `__. Patroni is a template for you to create your own customized, high-availability solution using Python and - for maximum accessibility - a distributed configuration store like `ZooKeeper `__, `etcd `__, `Consul `__ or `Kubernetes `__. Database engineers, DBAs, DevOps engineers, and SREs who are looking to quickly deploy HA PostgreSQL in the datacenter-or anywhere else-will hopefully find it useful. We call Patroni a "template" because it is far from being a one-size-fits-all or plug-and-play replication system. It will have its own caveats. Use wisely. **Note to Kubernetes users**: Patroni can run natively on top of Kubernetes. Take a look at the `Kubernetes `__ chapter of the Patroni documentation. .. contents:: :local: :depth: 1 :backlinks: none ================= How Patroni Works ================= Patroni originated as a fork of `Governor `__, the project from Compose. It includes plenty of new features. For an example of a Docker-based deployment with Patroni, see `Spilo `__, currently in use at Zalando. For additional background info, see: * `Elephants on Automatic: HA Clustered PostgreSQL with Helm `_, talk by Josh Berkus and Oleksii Kliukin at KubeCon Berlin 2017 * `PostgreSQL HA with Kubernetes and Patroni `__, talk by Josh Berkus at KubeCon 2016 (video) * `Feb. 2016 Zalando Tech blog post `__ ================== Development Status ================== Patroni is in active development and accepts contributions. See our `Contributing `__ section below for more details. We report new releases information `here `__. =================================== Technical Requirements/Installation =================================== **Pre-requirements for Mac OS** To install requirements on a Mac, run the following: :: brew install postgresql etcd haproxy libyaml python **General installation for pip** Patroni can be installed with pip: :: pip install patroni[dependencies] where dependencies can be either empty, or consist of one or more of the following: etcd `python-etcd` module in order to use Etcd as DCS consul `python-consul` module in order to use Consul as DCS zookeeper `kazoo` module in order to use Zookeeper as DCS exhibitor `kazoo` module in order to use Exhibitor as DCS (same dependencies as for Zookeeper) kubernetes `kubernetes` module in order to use Kubernetes as DCS in Patroni aws `boto` in order to use AWS callbacks For example, the command in order to install Patroni together with dependencies for Etcd as a DCS and AWS callbacks is: :: pip install patroni[etcd,aws] Note that external tools to call in the replica creation or custom bootstap scripts (i.e. WAL-E) should be installed independently of Patroni. ======================= Running and Configuring ======================= To get started, do the following from different terminals: :: > etcd --data-dir=data/etcd > ./patroni.py postgres0.yml > ./patroni.py postgres1.yml You will then see a high-availability cluster start up. Test different settings in the YAML files to see how the cluster's behavior changes. Kill some of the components to see how the system behaves. Add more ``postgres*.yml`` files to create an even larger cluster. Patroni provides an `HAProxy `__ configuration, which will give your application a single endpoint for connecting to the cluster's leader. To configure, run: :: > haproxy -f haproxy.cfg :: > psql --host 127.0.0.1 --port 5000 postgres ================== YAML Configuration ================== Go `here `__ for comprehensive information about settings for etcd, consul, and ZooKeeper. And for an example, see `postgres0.yml `__. ========================= Environment Configuration ========================= Go `here `__ for comprehensive information about configuring(overriding) settings via environment variables. =================== Replication Choices =================== Patroni uses Postgres' streaming replication, which is asynchronous by default. Patroni's asynchronous replication configuration allows for ``maximum_lag_on_failover`` settings. This setting ensures failover will not occur if a follower is more than a certain number of bytes behind the leader. This setting should be increased or decreased based on business requirements. It's also possible to use synchronous replication for better durability guarantees. See `replication modes documentation `__ for details. ====================================== Applications Should Not Use Superusers ====================================== When connecting from an application, always use a non-superuser. Patroni requires access to the database to function properly. By using a superuser from an application, you can potentially use the entire connection pool, including the connections reserved for superusers, with the ``superuser_reserved_connections`` setting. If Patroni cannot access the Primary because the connection pool is full, behavior will be undesirable. .. |Build Status| image:: https://travis-ci.org/zalando/patroni.svg?branch=master :target: https://travis-ci.org/zalando/patroni .. |Coverage Status| image:: https://coveralls.io/repos/zalando/patroni/badge.svg?branch=master :target: https://coveralls.io/r/zalando/patroni?branch=master patroni-1.4.2/TODO.md000066400000000000000000000010351323411135200142530ustar00rootroot00000000000000Failover ======== - When determining who should become master, include the minor version of PostgreSQL in the decision. Configuration ============== - Provide a way to change pg_hba.conf of a running cluster on the Patroni level, without changing individual nodes. - Provide hooks to store and retrieve cluster-wide passwords without exposing them in a plain-text form to unauthorized users. Documentation ============== - Document how to run cascading replication and possibly initialize the cluster without an access to the master node. patroni-1.4.2/docker-compose.yml000066400000000000000000000025011323411135200166200ustar00rootroot00000000000000# docker compose file for running a 3-node PostgreSQL cluster # with etcd as the SIS patroni_etcd: container_name: patroni_etcd image: patroni command: --etcd dbnode1: image: patroni hostname: dbnode1 links: - patroni_etcd:patroni_etcd volumes: - ./patroni:/patroni env_file: docker/patroni-secrets.env environment: PATRONI_ETCD_HOST: patroni_etcd:2379 PATRONI_NAME: dbnode1 PATRONI_SCOPE: testcluster dbnode2: image: patroni hostname: dbnode2 links: - patroni_etcd:patroni_etcd volumes: - ./patroni:/patroni env_file: docker/patroni-secrets.env environment: PATRONI_ETCD_HOST: patroni_etcd:2379 PATRONI_NAME: dbnode2 PATRONI_SCOPE: testcluster dbnode3: image: patroni hostname: dbnode3 links: - patroni_etcd:patroni_etcd volumes: - ./patroni:/patroni env_file: docker/patroni-secrets.env environment: PATRONI_ETCD_HOST: patroni_etcd:2379 PATRONI_NAME: dbnode3 PATRONI_SCOPE: testcluster haproxy: image: patroni links: - patroni_etcd:patroni_etcd ports: - "5000:5000" - "5001:5001" environment: PATRONI_ETCD_HOST: patroni_etcd:2379 PATRONI_SCOPE: testcluster command: --confd patroni-1.4.2/docker/000077500000000000000000000000001323411135200144345ustar00rootroot00000000000000patroni-1.4.2/docker/README.md000066400000000000000000000044241323411135200157170ustar00rootroot00000000000000# Patroni Dockerfile You can run Patroni in a docker container using this Dockerfile, or by using one of the Docker image at https://registry.opensource.zalan.do/v1/repositories/acid/patroni/tags This Dockerfile is meant in aiding development of Patroni and quick testing of features. It is not a production-worthy Dockerfile # Examples ## Standalone Patroni docker run -d registry.opensource.zalan.do/acid/patroni:1.0-SNAPSHOT ## Multiple Patroni's communicating with a standalone etcd inside Docker Basically what you would do would be: * Run 1 container which provides etcd docker run -d --etcd-only * Run n containers running Patroni, passing the `--etcd` option to the `docker run` command docker run -d --etcd= To automate this you can run the following script: dev_patroni_cluster.sh [OPTIONS] Options: --image IMAGE The Docker image to use for the cluster --members INT The number of members for the cluster --name NAME The name of the new cluster Example session: $ ./dev_patroni_cluster.sh --image registry.opensource.zalan.do/acid/patroni:1.0-SNAPSHOT --members=2 --name=bravo The etcd container is 6be871a11cb373406ca5ea1c6b39e1.0-SNAPSHOTfdde9fb1d6177212d6ad0c0d1bd9b563, ip=172.17.1.24 Started Patroni container 67e611f2eca7c40f9e6e0e24a4a8f2cba7e3e56d22a420e15ab9240a37a9d7a4, ip=172.17.1.25 Started Patroni container 47dd12ae635ab83b039f5889e250048b606ed5e48e3650b69e365e7e1d4acbcf, ip=172.17.1.26 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 47dd12ae635a registry.opensource.zalan.do/acid/patroni:1.0-SNAPSHOT "/bin/bash /entrypoi 10 seconds ago Up 8 seconds 4001/tcp, 5432/tcp, 2380/tcp bravo_OR64g8bx 67e611f2eca7 registry.opensource.zalan.do/acid/patroni:1.0-SNAPSHOT "/bin/bash /entrypoi 11 seconds ago Up 10 seconds 2380/tcp, 4001/tcp, 5432/tcp bravo_si9no8iz 6be871a11cb3 registry.opensource.zalan.do/acid/patroni:1.0-SNAPSHOT "/bin/bash /entrypoi 12 seconds ago Up 10 seconds 4001/tcp, 5432/tcp, 2380/tcp bravo_etcd patroni-1.4.2/docker/dev_patroni_cluster.sh000077500000000000000000000055241323411135200210540ustar00rootroot00000000000000#!/bin/bash DOCKER_IMAGE="registry.opensource.zalan.do/acid/patroni:1.0-SNAPSHOT" MEMBERS=3 function usage() { cat <<__EOF__ Usage: $0 Options: --image IMAGE The Docker image to use for the cluster --members INT The number of members for the cluster --name NAME The name of the new cluster Examples: $0 --image ${DOCKER_IMAGE} $0 $0 --image ${DOCKER_IMAGE} --members=2 __EOF__ } optspec=":-:" while getopts "$optspec" optchar; do case "${optchar}" in -) case "${OPTARG}" in help) usage exit 0 ;; name) PATRONI_SCOPE="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 )) ;; name=*) PATRONI_SCOPE="${OPTARG#*=}" ;; image) DOCKER_IMAGE="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 )) ;; image=*) DOCKER_IMAGE="${OPTARG#*=}" ;; members) MEMBERS="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 )) ;; members=*) MEMBERS="${OPTARG#*=}" ;; *) if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then echo "Unknown option --${OPTARG}" >&2 fi ;; esac;; *) if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then echo "Non-option argument: '-${OPTARG}'" >&2 usage exit 1 fi ;; esac done if [ -z ${PATRONI_SCOPE} ]; then PATRONI_SCOPE=$(cat /dev/urandom | LC_ALL=C tr -dc 'a-z0-9' | head -c 8) fi function docker_run() { local name=$1 shift container=$(docker run -d --name=$name $*) container_ip=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' ${container}) echo "Started container ${name}, ip=${container_ip}" } ETCD_CONTAINER="${PATRONI_SCOPE}_etcd" docker_run ${ETCD_CONTAINER} ${DOCKER_IMAGE} --etcd DOCKER_ARGS="--link=${ETCD_CONTAINER}:${ETCD_CONTAINER} -e PATRONI_SCOPE=${PATRONI_SCOPE} -e PATRONI_ETCD_HOST=${ETCD_CONTAINER}:2379" PATRONI_ENV=$(sed 's/#.*//g' docker/patroni-secrets.env | sed -n 's/^PATRONI_.*$/-e &/p' | tr '\n' ' ') PATRONI_VOLUME="-v $(dirname $(dirname $(realpath $0)))/patroni:/patroni" for i in $(seq 1 "${MEMBERS}"); do container_name=postgres${i} docker_run "${PATRONI_SCOPE}_${container_name}" \ $PATRONI_VOLUME \ $DOCKER_ARGS \ $PATRONI_ENV \ -e PATRONI_NAME=${container_name} \ ${DOCKER_IMAGE} done docker_run "${PATRONI_SCOPE}_haproxy" \ -p=5000 -p=5001 \ $DOCKER_ARGS \ ${DOCKER_IMAGE} --confd patroni-1.4.2/docker/entrypoint.sh000077500000000000000000000073751323411135200172220ustar00rootroot00000000000000#!/bin/bash function usage() { cat <<__EOF__ Usage: $0 Options: --etcd Do not run Patroni, run a standalone etcd --confd Do not run Patroni, run a standalone confd --zookeeper Do not run Patroni, run a standalone zookeeper Examples: $0 --etcd $0 --confd $0 --zookeeper $0 __EOF__ } DOCKER_IP=$(hostname --ip-address) PATRONI_SCOPE=${PATRONI_SCOPE:-batman} ETCD_ARGS="--data-dir /tmp/etcd.data -advertise-client-urls=http://${DOCKER_IP}:2379 -listen-client-urls=http://0.0.0.0:2379" optspec=":vh-:" while getopts "$optspec" optchar; do case "${optchar}" in -) case "${OPTARG}" in confd) haproxy -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -D CONFD="confd -prefix=${PATRONI_NAMESPACE:-/service}/$PATRONI_SCOPE -interval=10 -backend" if [ ! -z ${PATRONI_ZOOKEEPER_HOSTS} ]; then while ! /usr/share/zookeeper/bin/zkCli.sh -server ${PATRONI_ZOOKEEPER_HOSTS} ls /; do sleep 1 done exec $CONFD zookeeper -node ${PATRONI_ZOOKEEPER_HOSTS} else while ! curl -s ${PATRONI_ETCD_HOST}/v2/members | jq -r '.members[0].clientURLs[0]' | grep -q http; do sleep 1 done exec $CONFD etcd -node $PATRONI_ETCD_HOST fi ;; etcd) exec etcd $ETCD_ARGS ;; zookeeper) exec /usr/share/zookeeper/bin/zkServer.sh start-foreground ;; cheat) CHEAT=1 ;; help) usage exit 0 ;; *) if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then echo "Unknown option --${OPTARG}" >&2 fi ;; esac;; *) if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then echo "Non-option argument: '-${OPTARG}'" >&2 usage exit 1 fi ;; esac done ## We start an etcd if [[ -z ${PATRONI_ETCD_HOST} && -z ${PATRONI_ZOOKEEPER_HOSTS} ]]; then etcd $ETCD_ARGS > /var/log/etcd.log 2> /var/log/etcd.err & export PATRONI_ETCD_HOST="127.0.0.1:2379" fi export PATRONI_SCOPE export PATRONI_NAME="${PATRONI_NAME:-${HOSTNAME}}" export PATRONI_RESTAPI_CONNECT_ADDRESS="${DOCKER_IP}:8008" export PATRONI_RESTAPI_LISTEN="0.0.0.0:8008" export PATRONI_admin_PASSWORD="${PATRONI_admin_PASSWORD:=admin}" export PATRONI_admin_OPTIONS="${PATRONI_admin_OPTIONS:-createdb, createrole}" export PATRONI_POSTGRESQL_CONNECT_ADDRESS="${DOCKER_IP}:5432" export PATRONI_POSTGRESQL_LISTEN="0.0.0.0:5432" export PATRONI_POSTGRESQL_DATA_DIR="data/${PATRONI_SCOPE}" export PATRONI_REPLICATION_USERNAME="${PATRONI_REPLICATION_USERNAME:-replicator}" export PATRONI_REPLICATION_PASSWORD="${PATRONI_REPLICATION_PASSWORD:-abcd}" export PATRONI_SUPERUSER_USERNAME="${PATRONI_SUPERUSER_USERNAME:-postgres}" export PATRONI_SUPERUSER_PASSWORD="${PATRONI_SUPERUSER_PASSWORD:-postgres}" export PATRONI_POSTGRESQL_PGPASS="$HOME/.pgpass" cat > /patroni.yml <<__EOF__ bootstrap: dcs: postgresql: use_pg_rewind: true pg_hba: - host all all 0.0.0.0/0 md5 - host replication replicator ${DOCKER_IP}/16 md5 __EOF__ mkdir -p "$HOME/.config/patroni" [ -h "$HOME/.config/patroni/patronictl.yaml" ] || ln -s /patroni.yml "$HOME/.config/patroni/patronictl.yaml" [ -z $CHEAT ] && exec python /patroni.py /patroni.yml while true; do sleep 60 done patroni-1.4.2/docker/patroni-secrets.env000066400000000000000000000004341323411135200202710ustar00rootroot00000000000000PATRONI_RESTAPI_USERNAME=admin PATRONI_RESTAPI_PASSWORD=admin PATRONI_SUPERUSER_USERNAME=postgres PATRONI_SUPERUSER_PASSWORD=postgres PATRONI_REPLICATION_USERNAME=replicator PATRONI_REPLICATION_PASSWORD=replicate PATRONI_admin_PASSWORD=admin PATRONI_admin_OPTIONS=createdb,createrole patroni-1.4.2/docs/000077500000000000000000000000001323411135200141155ustar00rootroot00000000000000patroni-1.4.2/docs/CONTRIBUTING.rst000066400000000000000000000013461323411135200165620ustar00rootroot00000000000000.. _contributing: Contributing guidelines ======================= Wanna contribute to Patroni? Yay - here is how! Reporting issues ---------------- If you have a question about patroni or have a problem using it, please read the :ref:`README ` before filing an issue. Also double check with the current issues on our `Issues Tracker `__. Contributing a pull request --------------------------- 1) Submit a comment to the relevant issue or create a new issue describing your proposed change. 2) Do a fork, develop and test your code changes. 3) Include documentation 4) Submit a pull request. You'll get feedback about your pull request as soon as possible. Happy Patroni hacking ;-) patroni-1.4.2/docs/ENVIRONMENT.rst000066400000000000000000000202101323411135200164460ustar00rootroot00000000000000.. _environment: Environment Configuration Settings ================================== It is possible to override some of the configuration parameters defined in the Patroni configuration file using the system environment variables. This document lists all environment variables handled by Patroni. The values set via those variables always take precedence over the ones set in the Patroni configuration file. Global/Universal ---------------- - **PATRONI\_CONFIGURATION**: it is possible to set the entire configuration for the Patroni via ``PATRONI_CONFIGURATION`` environment variable. In this case any other environment variables will not be considered! - **PATRONI\_NAME**: name of the node where the current instance of Patroni is running. Must be unique for the cluster. - **PATRONI\_NAMESPACE**: path within the configuration store where Patroni will keep information about the cluster. Default value: "/service" - **PATRONI\_SCOPE**: cluster name Bootstrap configuration ----------------------- It is possible to create new database users right after the successful initialization of a new cluster. This process is defined by the following variables: - **PATRONI\_\_PASSWORD=''** - **PATRONI\_\_OPTIONS='list,of,options'** Example: defining ``PATRONI_admin_PASSWORD=strongpasswd`` and ``PATRONI_admin_OPTIONS='createrole,createdb'`` will cause creation of the user **admin** with the password **strongpasswd** that is allowed to create other users and databases. Consul ------ - **PATRONI\_CONSUL\_HOST**: the host:port for the Consul endpoint. - **PATRONI\_CONSUL\_URL**: url for the Consul, in format: http(s)://host:port - **PATRONI\_CONSUL\_PORT**: (optional) Consul port - **PATRONI\_CONSUL\_SCHEME**: (optional) **http** or **https**, defaults to **http** - **PATRONI\_CONSUL\_TOKEN**: (optional) ACL token - **PATRONI\_CONSUL\_VERIFY**: (optional) whether to verify the SSL certificate for HTTPS requests - **PATRONI\_CONSUL\_CACERT**: (optional) The ca certificate. If pressent it will enable validation. - **PATRONI\_CONSUL\_CERT**: (optional) File with the client certificate - **PATRONI\_CONSUL\_KEY**: (optional) File with the client key. Can be empty if the key is part of certificate. - **PATRONI\_CONSUL\_DC**: (optional) Datacenter to communicate with. By default the datacenter of the host is used. - **PATRONI\_CONSUL\_CHECKS**: (optional) list of Consul health checks used for the session. If not specified Consul will use "serfHealth" in additional to the TTL based check created by Patroni. Additional checks, in particular the "serfHealth", may cause the leader lock to expire faster than in `ttl` seconds when the leader instance becomes unavailable. Etcd ---- - **PATRONI\_ETCD\_HOST**: the host:port for the etcd endpoint. - **PATRONI\_ETCD\_HOSTS**: list of etcd endpoints in format host1:port1,host2:port2,etc... - **PATRONI\_ETCD\_URL**: url for the etcd, in format: http(s)://(username:password@)host:port - **PATRONI\_ETCD\_PROXY**: proxy url for the etcd. If you are connecting to the etcd using proxy, use this parameter instead of **PATRONI\_ETCD\_URL** - **PATRONI\_ETCD\_SRV**: Domain to search the SRV record(s) for cluster autodiscovery. - **PATRONI\_ETCD\_CACERT**: The ca certificate. If pressent it will enable validation. - **PATRONI\_ETCD\_CERT**: File with the client certificate - **PATRONI\_ETCD\_KEY**: File with the client key. Can be empty if the key is part of certificate. Exhibitor --------- - **PATRONI\_EXHIBITOR\_HOSTS**: initial list of Exhibitor (ZooKeeper) nodes in format: 'host1,host2,etc...'. This list updates automatically whenever the Exhibitor (ZooKeeper) cluster topology changes. - **PATRONI\_EXHIBITOR\_PORT**: Exhibitor port. .. _kubernetes_environment: Kubernetes ---------- - **PATRONI\_KUBERNETES\_NAMESPACE**: (optional) Kubernetes namespace where the Patroni pod is running. Default value is `default`. - **PATRONI\_KUBERNETES\_LABELS**: Labels in format ``{label1: value1, label2: value2}``. These labels will be used to find existing objects (Pods and either Endpoints or ConfigMaps) associated with the current cluster. Also Patroni will set them on every object (Endpoint or ConfigMap) it creates. - **PATRONI\_KUBERNETES\_SCOPE\_LABEL**: (optional) name of the label containing cluster name. Default value is `cluster-name`. - **PATRONI\_KUBERNETES\_ROLE\_LABEL**: (optional) name of the label containing Postgres role (`master` or `replica`). Patroni will set this label on the pod it is running in. Default value is `role`. - **PATRONI\_KUBERNETES\_USE\_ENDPOINTS**: (optional) if set to true, Patroni will use Endpoints instead of ConfigMaps to run leader elections and keep cluster state. - **PATRONI\_KUBERNETES\_POD\_IP**: (optional) IP address of the pod Patroni is running in. This value is required when `PATRONI_KUBERNETES_USE_ENDPOINTS` is enabled and is used to populate the leader endpoint subsets when the pod's PostgreSQL is promoted. - **PATRONI\_KUBERNETES\_PORTS**: (optional) if the Service object has the name for the port, the same name must appear in the Endpoint object, otherwise service wont work. For example, if your service is defined as ``{Kind: Service, spec: {ports: [{name: postgresql, port: 5432, targetPort: 5432}]}}``, then you have to set ``PATRONI_KUBERNETES_PORTS='{[{"name": "postgresql", "port": 5432}]}'`` and Patroni will use it for updating subsets of the leader Endpoint. This parameter is used only if `PATRONI_KUBERNETES_USE_ENDPOINTS` is set. PostgreSQL ---------- - **PATRONI\_POSTGRESQL\_LISTEN**: IP address + port that Postgres listens to. Multiple comma-separated addresses are permitted, as long as the port component is appended after to the last one with a colon, i.e. ``listen: 127.0.0.1,127.0.0.2:5432``. Patroni will use the first address from this list to establish local connections to the PostgreSQL node. - **PATRONI\_POSTGRESQL\_CONNECT\_ADDRESS**: IP address + port through which Postgres is accessible from other nodes and applications. - **PATRONI\_POSTGRESQL\_DATA\_DIR**: The location of the Postgres data directory, either existing or to be initialized by Patroni. - **PATRONI\_POSTGRESQL\_CONFIG\_DIR**: The location of the Postgres configuration directory, defaults to the data directory. Must be writable by Patroni. - **PATRONI\_POSTGRESQL\_BIN_DIR**: Path to PostgreSQL binaries. (pg_ctl, pg_rewind, pg_basebackup, postgres) The default value is an empty string meaning that PATH environment variable will be used to find the executables. - **PATRONI\_POSTGRESQL\_PGPASS**: path to the `.pgpass `__ password file. Patroni creates this file before executing pg\_basebackup and under some other circumstances. The location must be writable by Patroni. - **PATRONI\_REPLICATION\_USERNAME**: replication username; the user will be created during initialization. Replicas will use this user to access master via streaming replication - **PATRONI\_REPLICATION\_PASSWORD**: replication password; the user will be created during initialization. - **PATRONI\_SUPERUSER\_USERNAME**: name for the superuser, set during initialization (initdb) and later used by Patroni to connect to the postgres. Also this user is used by pg_rewind. - **PATRONI\_SUPERUSER\_PASSWORD**: password for the superuser, set during initialization (initdb). REST API -------- - **PATRONI\_RESTAPI\_CONNECT\_ADDRESS**: IP address and port to access the REST API. - **PATRONI\_RESTAPI\_LISTEN**: IP address and port that Patroni will listen to, to provide health-check information for HAProxy. - **PATRONI\_RESTAPI\_USERNAME**: Basic-auth username to protect unsafe REST API endpoints. - **PATRONI\_RESTAPI\_PASSWORD**: Basic-auth password to protect unsafe REST API endpoints. - **PATRONI\_RESTAPI\_CERTFILE**: Specifies the file with the certificate in the PEM format. If the certfile is not specified or is left empty, the API server will work without SSL. - **PATRONI\_RESTAPI\_KEYFILE**: Specifies the file with the secret key in the PEM format. ZooKeeper --------- - **PATRONI\_ZOOKEEPER\_HOSTS**: comma separated list of ZooKeeper cluster members: "'host1:port1','host2:port2','etc...'". It is important to quote every single entity! patroni-1.4.2/docs/Makefile000066400000000000000000000011341323411135200155540ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXPROJ = Patroni SOURCEDIR = . BUILDDIR = build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) patroni-1.4.2/docs/README.rst000066400000000000000000000114651323411135200156130ustar00rootroot00000000000000.. _readme: ============ Introduction ============ Patroni originated as a fork of `Governor `__, the project from Compose. It includes plenty of new features. For an example of a Docker-based deployment with Patroni, see `Spilo `__, currently in use at Zalando. For additional background info, see: * `PostgreSQL HA with Kubernetes and Patroni `__, talk by Josh Berkus at KubeCon 2016 (video) * `Feb. 2016 Zalando Tech blog post `__ Development Status ------------------ Patroni is in active development and accepts contributions. See our :ref:`Contributing ` section below for more details. We report new releases information :ref:`here `. Technical Requirements/Installation ----------------------------------- **Pre-requirements for Mac OS** To install requirements on a Mac, run the following: :: brew install postgresql etcd haproxy libyaml python **General installation for pip** Patroni can be installed with pip: :: pip install patroni[dependencies] where dependencies can be either empty, or consist of one or more of the following: etcd `python-etcd` module in order to use Etcd as DCS consul `python-consul` module in order to use Consul as DCS zookeeper `kazoo` module in order to use Zookeeper as DCS exhibitor `kazoo` module in order to use Exhibitor as DCS (same dependencies as for Zookeeper) kubernetes `kubernetes` module in order to use Kubernetes as DCS in Patroni aws `boto` in order to use AWS callbacks For example, the command in order to install Patroni together with dependencies for Etcd as a DCS and AWS callbacks is: :: pip install patroni[etcd,aws] Note that external tools to call in the replica creation or custom bootstap scripts (i.e. WAL-E) should be installed independently of Patroni. Running and Configuring ----------------------- The following section assumes Patroni repository as being cloned from https://github.com/zalando/patroni. Namely, you will need example configuration files `postgres0.yml` and `postgres1.yml`. If you installed Patroni with pip, you can obtain those files from the git repository and replace `./patroni.py` below with `patroni` command. To get started, do the following from different terminals: :: > etcd --data-dir=data/etcd > ./patroni.py postgres0.yml > ./patroni.py postgres1.yml You will then see a high-availability cluster start up. Test different settings in the YAML files to see how the cluster's behavior changes. Kill some of the components to see how the system behaves. Add more ``postgres*.yml`` files to create an even larger cluster. Patroni provides an `HAProxy `__ configuration, which will give your application a single endpoint for connecting to the cluster's leader. To configure, run: :: > haproxy -f haproxy.cfg :: > psql --host 127.0.0.1 --port 5000 postgres YAML Configuration ------------------ Go :ref:`here ` for comprehensive information about settings for etcd, consul, and ZooKeeper. And for an example, see `postgres0.yml `__. Environment Configuration ------------------------- Go :ref:`here ` for comprehensive information about configuring(overriding) settings via environment variables. Replication Choices ------------------- Patroni uses Postgres' streaming replication, which is asynchronous by default. Patroni's asynchronous replication configuration allows for ``maximum_lag_on_failover`` settings. This setting ensures failover will not occur if a follower is more than a certain number of bytes behind the leader. This setting should be increased or decreased based on business requirements. It's also possible to use synchronous replication for better durability guarantees. See :ref:`replication modes documentation ` for details. Applications Should Not Use Superusers -------------------------------------- When connecting from an application, always use a non-superuser. Patroni requires access to the database to function properly. By using a superuser from an application, you can potentially use the entire connection pool, including the connections reserved for superusers, with the ``superuser_reserved_connections`` setting. If Patroni cannot access the Primary because the connection pool is full, behavior will be undesirable. .. |Build Status| image:: https://travis-ci.org/zalando/patroni.svg?branch=master :target: https://travis-ci.org/zalando/patroni .. |Coverage Status| image:: https://coveralls.io/repos/zalando/patroni/badge.svg?branch=master :target: https://coveralls.io/r/zalando/patroni?branch=master patroni-1.4.2/docs/SETTINGS.rst000066400000000000000000000351271323411135200161170ustar00rootroot00000000000000.. _settings: =========================== YAML Configuration Settings =========================== Global/Universal ---------------- - **name**: the name of the host. Must be unique for the cluster. - **namespace**: path within the configuration store where Patroni will keep information about the cluster. Default value: "/service" - **scope**: cluster name Bootstrap configuration ----------------------- - **dcs**: This section will be written into `///config` of a given configuration store after initializing of new cluster. This is the global configuration for the cluster. If you want to change some parameters for all cluster nodes - just do it in DCS (or via Patroni API) and all nodes will apply this configuration. - **loop\_wait**: the number of seconds the loop will sleep. Default value: 10 - **ttl**: the TTL to acquire the leader lock. Think of it as the length of time before initiation of the automatic failover process. Default value: 30 - **retry\_timeout**: timeout for DCS and PostgreSQL operation retries. DCS or network issues shorter than this will not cause Patroni to demote the leader. Default value: 10 - **maximum\_lag\_on\_failover**: the maximum bytes a follower may lag to be able to participate in leader election. - **master\_start\_timeout**: the amount of time a master is allowed to recover from failures before failover is triggered. Default is 300 seconds. When set to 0 failover is done immediately after a crash is detected if possible. When using asynchronous replication a failover can cause lost transactions. Best worst case failover time for master failure is: loop\_wait + master\_start\_timeout + loop\_wait, unless master\_start\_timeout is zero, in which case it's just loop\_wait. Set the value according to your durability/availability tradeoff. - **synchronous\_mode**: turns on synchronous replication mode. In this mode a replica will be chosen as synchronous and only the latest leader and synchronous replica are able to participate in leader election. Synchronous mode makes sure that succesfully committed transactions will not be lost at failover, at the cost of losing availability for writes when Patroni cannot ensure transaction durability. See `replication modes documentation `__ for details. - **postgresql**: - **use\_pg\_rewind**:whether or not to use pg_rewind - **use\_slots**: whether or not to use replication_slots. Must be False for PostgreSQL 9.3. You should comment out max_replication_slots before it becomes ineligible for leader status. - **recovery\_conf**: additional configuration settings written to recovery.conf when configuring follower. - **parameters**: list of configuration settings for Postgres. Many of these are required for replication to work. - **method**: custom script to use for bootstrpapping this cluster. See :ref:`custom bootstrap methods documentation ` for details. When ``initdb`` is specified revert to the default ``initdb`` command. ``initdb`` is also triggered when no ``method`` parameter is present in the configuration file. - **initdb**: List options to be passed on to initdb. - **- data-checksums**: Must be enabled when pg_rewind is needed on 9.3. - **- encoding: UTF8**: default encoding for new databases. - **- locale: UTF8**: default locale for new databases. - **pg\_hba**: list of lines that you should add to pg\_hba.conf. - **- host all all 0.0.0.0/0 md5**. - **- host replication replicator 127.0.0.1/32 md5**: A line like this is required for replication. - **users**: Some additional users users which needs to be created after initializing new cluster - **admin**: the name of user - **password: zalando**: - **options**: list of options for CREATE USER statement - **- createrole** - **- createdb** - **post\_bootstrap** or **post\_init**: An additional script that will be executed after initializing the cluster. The script receives a connection string URL (with the cluster superuser as a user name). The PGPASSFILE variable is set to the location of pgpass file. .. _consul_settings: Consul ------ Most of the parameters are optional, but you have to specify one of the **host** or **url** - **host**: the host:port for the Consul endpoint, in format: http(s)://host:port - **url**: url for the Consul endpoint - **port**: (optional) Consul port - **scheme**: (optional) **http** or **https**, defaults to **http** - **token**: (optional) ACL token - **verify** (optional) whether to verify the SSL certificate for HTTPS requests - **cacert**: (optional) The ca certificate. If pressent it will enable validation. - **cert**: (optional) file with the client certificate - **key**: (optional) file with the client key. Can be empty if the key is part of **cert**. - **dc**: (optional) Datacenter to communicate with. By default the datacenter of the host is used. - **checks**: (optional) list of Consul health checks used for the session. If not specified Consul will use "serfHealth" in additional to the TTL based check created by Patroni. Additional checks, in particular the "serfHealth", may cause the leader lock to expire faster than in `ttl` seconds when the leader instance becomes unavailable Etcd ---- Most of the parameters are optional, but you have to specify one of the **host**, **hosts**, **url**, **proxy** or **srv** - **host**: the host:port for the etcd endpoint. - **hosts**: list of etcd endpoint in format host1:port1,host2:port2,etc... Could be a comma separated string or an actual yaml list. - **url**: url for the etcd - **proxy**: proxy url for the etcd. If you are connecting to the etcd using proxy, use this parameter instead of **url** - **srv**: Domain to search the SRV record(s) for cluster autodiscovery. - **protocol**: (optional) http or https, if not specified http is used. If the **url** or **proxy** is specified - will take protocol from them. - **username**: (optional) username for etcd authentication - **password**: (optional) password for etcd authentication. - **cacert**: (optional) The ca certificate. If pressent it will enable validation. - **cert**: (optional) file with the client certificate - **key**: (optional) file with the client key. Can be empty if the key is part of **cert**. Exhibitor --------- - **hosts**: initial list of Exhibitor (ZooKeeper) nodes in format: 'host1,host2,etc...'. This list updates automatically whenever the Exhibitor (ZooKeeper) cluster topology changes. - **poll\_interval**: how often the list of ZooKeeper and Exhibitor nodes should be updated from Exhibitor - **port**: Exhibitor port. .. _kubernetes_settings: Kubernetes ---------- - **namespace**: (optional) Kubernetes namespace where Patroni pod is running. Default value is `default`. - **labels**: Labels in format ``{label1: value1, label2: value2}``. These labels will be used to find existing objects (Pods and either Endpoints or ConfigMaps) associated with the current cluster. Also Patroni will set them on every object (Endpoint or ConfigMap) it creates. - **scope\_label**: (optional) name of the label containing cluster name. Default value is `cluster-name`. - **role\_label**: (optional) name of the label containing role (master or replica). Patroni will set this label on the pod it runs in. Default value is ``role``. - **use\_endpoints**: (optional) if set to true, Patroni will use Endpoints instead of ConfigMaps to run leader elections and keep cluster state. - **pod\_ip**: (optional) IP address of the pod Patroni is running in. This value is required when `use_endpoints` is enabled and is used to populate the leader endpoint subsets when the pod's PostgreSQL is promoted. - **ports**: (optional) if the Service object has the name for the port, the same name must appear in the Endpoint object, otherwise service wont work. For example, if your service is defined as ``{Kind: Service, spec: {ports: [{name: postgresql, port: 5432, targetPort: 5432}]}}``, then you have to set ``kubernetes.ports: {[{"name": "postgresql", "port": 5432}]}`` and Patroni will use it for updating subsets of the leader Endpoint. This parameter is used only if `kubernetes.use_endpoints` is set. .. _postgresql_settings: PostgreSQL ---------- - **authentication**: - **superuser**: - **username**: name for the superuser, set during initialization (initdb) and later used by Patroni to connect to the postgres. - **password**: password for the superuser, set during initialization (initdb). - **replication**: - **username**: replication username; the user will be created during initialization. Replicas will use this user to access master via streaming replication - **password**: replication password; the user will be created during initialization. - **callbacks**: callback scripts to run on certain actions. Patroni will pass the action, role and cluster name. (See scripts/aws.py as an example of how to write them.) - **on\_reload**: run this script when configuration reload is triggered. - **on\_restart**: run this script when the cluster restarts. - **on\_role\_change**: run this script when the cluster is being promoted or demoted. - **on\_start**: run this script when the cluster starts. - **on\_stop**: run this script when the cluster stops. - **connect\_address**: IP address + port through which Postgres is accessible from other nodes and applications. - **create\_replica\_method**: an ordered list of the create methods for turning a Patroni node into a new replica. "basebackup" is the default method; other methods are assumed to refer to scripts, each of which is configured as its own config item. See :ref:`custom replica creation methods documentation ` for further explanation. - **data\_dir**: The location of the Postgres data directory, either existing or to be initialized by Patroni. - **config\_dir**: The location of the Postgres configuration directory, defaults to the data directory. Must be writable by Patroni. - **bin\_dir**: Path to PostgreSQL binaries. (pg_ctl, pg_rewind, pg_basebackup, postgres) The default value is an empty string meaning that PATH environment variable will be used to find the executables. - **listen**: IP address + port that Postgres listens to; must be accessible from other nodes in the cluster, if you're using streaming replication. Multiple comma-separated addresses are permitted, as long as the port component is appended after to the last one with a colon, i.e. ``listen: 127.0.0.1,127.0.0.2:5432``. Patroni will use the first address from this list to establish local connections to the PostgreSQL node. - **use\_unix\_socket**: specifies that Patroni should prefer to use unix sockets to connect to the cluster. Default value is ``false``. If ``unix_socket_directories`` is definded, Patroni will use first suitable value from it to connect to the cluster and fallback to tcp if nothing is suitable. If ``unix_socket_directories`` is not specified in ``postgresql.parameters``, Patroni will assume that default value should be used and omit ``host`` from connection parameters. - **pgpass**: path to the `.pgpass `__ password file. Patroni creates this file before executing pg\_basebackup, the post_init script and under some other circumstances. The location must be writable by Patroni. - **recovery\_conf**: additional configuration settings written to recovery.conf when configuring follower. - **custom\_conf** : path to an optional custom ``postgresql.conf`` file, that will be used in place of ``postgresql.base.conf``. The file must exist on all cluster nodes, be readable by PostgreSQL and will be included from its location on the real ``postgresql.conf``. Note that Patroni will not monitor this file for changes, nor backup it. However, its settings can still be overriden by Patroni's own configuration facilities - see `dynamic configuration `__ for details. - **parameters**: list of configuration settings for Postgres. Many of these are required for replication to work. - **pg\_hba**: list of lines that Patroni will use to generate ``pg_hba.conf``. This parameter has higher priority than ``bootstrap.pg_hba``. Together with :ref:`dynamic configuration ` it simplifies management of ``pg_hba.conf``. - **- host all all 0.0.0.0/0 md5**. - **- host replication replicator 127.0.0.1/32 md5**: A line like this is required for replication. - **pg\_ctl\_timeout**: How long should pg_ctl wait when doing ``start``, ``stop`` or ``restart``. Default value is 60 seconds. - **use\_pg\_rewind**: try to use pg\_rewind on the former leader when it joins cluster as a replica. - **remove\_data\_directory\_on\_rewind\_failure**: If this option is enabled, Patroni will remove postgres data directory and recreate replica. Otherwise it will try to follow the new leader. Default value is **false**. - **replica\_method** for each create_replica_method other than basebackup, you would add a configuration section of the same name. At a minimum, this should include "command" with a full path to the actual script to be executed. Other configuration parameters will be passed along to the script in the form "parameter=value". REST API -------- - **connect\_address**: IP address and port to access the REST API. - **listen**: IP address and port that Patroni will listen to, to provide health-check information for HAProxy. - **Optional**: - **authentication**: - **username**: Basic-auth username to protect unsafe REST API endpoints. - **password**: Basic-auth password to protect unsafe REST API endpoints. - **certfile**: Specifies the file with the certificate in the PEM format. If the certfile is not specified or is left empty, the API server will work without SSL. - **keyfile**: Specifies the file with the secret key in the PEM format. ZooKeeper ---------- - **hosts**: list of ZooKeeper cluster members in format: ['host1:port1', 'host2:port2', 'etc...']. Watchdog -------- - **mode**: ``off``, ``automatic`` or ``required``. When ``off`` watchdog is disabled. When ``automatic`` watchdog will be used if available, but ignored if it is not. When ``required`` the node will not become a leader unless watchdog can be succesfully enabled. - **device**: Path to watchdog device. Defaults to ``/dev/watchdog``. - **safety_margin**: Number of seconds of safety margin between watchdog triggering and leader key expiration. patroni-1.4.2/docs/_static/000077500000000000000000000000001323411135200155435ustar00rootroot00000000000000patroni-1.4.2/docs/_static/custom.css000066400000000000000000000000371323411135200175670ustar00rootroot00000000000000li { margin-bottom: 0.5em } patroni-1.4.2/docs/conf.py000066400000000000000000000135261323411135200154230ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Patroni documentation build configuration file, created by # sphinx-quickstart on Mon Dec 19 16:54:09 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath('..')) from patroni.version import __version__ # -- 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.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.mathjax', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Patroni' copyright = '2015 Compose, Zalando SE' author = 'Zalando SE' # 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 = __version__[:__version__.rfind('.')] # The full version, including alpha/beta/rc tags. release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'Patronidoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'Patroni.tex', 'Patroni Documentation', 'Zalando SE', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'patroni', 'Patroni Documentation', [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'Patroni', 'Patroni Documentation', author, 'Patroni', 'One line description of project.', 'Miscellaneous'), ] # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # The unique identifier of the text. This can be a ISBN number # or the project homepage. # # epub_identifier = '' # A unique identification for the text. # # epub_uid = '' # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'https://docs.python.org/': None} # A possibility to have an own stylesheet, to add new rules or override existing ones # For the latter case, the CSS specificity of the rules should be higher than the default ones def setup(app): app.add_stylesheet("custom.css") patroni-1.4.2/docs/dynamic_configuration.rst000066400000000000000000000225671323411135200212360ustar00rootroot00000000000000.. _dynamic_configuration: Patroni configuration ===================== Patroni configuration is stored in the DCS (Distributed Configuration Store). There are 3 types of configuration: - Dynamic configuration. These options can be set in DCS at any time. If the options changed are not part of the startup configuration, they are applied asynchronously (upon the next wake up cycle) to every node, which gets subsequently reloaded. If the node requires a restart to apply the configuration (for options with context postmaster, if their values have changed), a special flag, ``pending_restart`` indicating this, is set in the members.data JSON. Additionally, the node status also indicates this, by showing ``"restart_pending": true``. - Local :ref:`configuration ` (patroni.yml). These options are defined in the configuration file and take precedence over dynamic configuration. patroni.yml could be changed and reload in runtime (without restart of Patroni) by sending SIGHUP to the Patroni process or by performing ``POST /reload`` REST-API request. - Environment :ref:`configuration ` . It is possible to set/override some of the "Local" configuration parameters with environment variables. Environment configuration is very useful when you are running in a dynamic environment and you don't know some of the parameters in advance (for example it's not possible to know you external IP address when you are running inside ``docker``). Some of the PostgreSQL parameters must hold the same values on the master and the replicas. For those, values set either in the local patroni configuration files or via the environment variables take no effect. To alter or set their values one must change the shared configuration in the DCS. Below is the actual list of such parameters together with the default values: - max_connections: 100 - max_locks_per_transaction: 64 - max_worker_processes: 8 - max_prepared_transactions: 0 - wal_level: hot_standby - wal_log_hints: on - track_commit_timestamp: off For the parameters below, PostgreSQL does not require equal values among the master and all the replicas. However, considering the possibility of a replica to become the master at any time, it doesn't really make sense to set them differently; therefore, Patroni restricts setting their values to the Dynamic configuration - max_wal_senders: 5 - max_replication_slots: 5 - wal_keep_segments: 8 These parameters are validated to ensure they are sane, or meet a minimum value. There are some other Postgres parameters controlled by Patroni: - listen_addresses - is set either from ``postgresql.listen`` or from ``PATRONI_POSTGRESQL_LISTEN`` environment variable - port - is set either from ``postgresql.listen`` or from ``PATRONI_POSTGRESQL_LISTEN`` environment variable - cluster_name - is set either from ``scope`` or from ``PATRRONI_SCOPE`` environment variable - hot_standby: on To be on the safe side parameters from the above lists are not written into ``postgresql.conf``, but passed as a list of arguments to the ``pg_ctl start`` which gives them the highest precedence, even above `ALTER SYSTEM `__ When applying the local or dynamic configuration options, the following actions are taken: - The node first checks if there is a postgresql.base.conf or if the ``custom_conf`` parameter is set. - If the `custom_conf` parameter is set, it will take the file specified on it as a base configuration, ignoring `postgresql.base.conf` and `postgresql.conf`. - If the `custom_conf` parameter is not set and `postgresql.base.conf` exists, it contains the renamed "original" configuration and it will be used as a base configuration. - If there is no `custom_conf` nor `postgresql.base.conf`, the original postgresql.conf is taken and renamed to postgresql.base.conf. - The dynamic options (with the exceptions above) are dumped into the postgresql.conf and an include is set in postgresql.conf to the used base configuration (either postgresql.base.conf or what is on ``custom_conf``). Therefore, we would be able to apply new options without re-reading the configuration file to check if the include is present not. - Some parameters that are essential for Patroni to manage the cluster are overridden using the command line. - If some of the options that require restart are changed (we should look at the context in pg_settings and at the actual values of those options), a pending_restart flag of a given node is set. This flag is reset on any restart. The parameters would be applied in the following order (run-time are given the highest priority): 1. load parameters from file `postgresql.base.conf` (or from a `custom_conf` file, if set) 2. load parameters from file `postgresql.conf` 3. load parameters from file `postgresql.auto.conf` 4. run-time parameter using `-o --name=value` This allows configuration for all the nodes (2), configuration for a specific node using `ALTER SYSTEM` (3) and ensures that parameters essential to the running of Patroni are enforced (4), as well as leaves room for configuration tools that manage `postgresql.conf` directly without involving Patroni (1). Also, the following Patroni configuration options can be changed only dynamically: - ttl: 30 - loop_wait: 10 - retry_timeouts: 10 - maximum_lag_on_failover: 1048576 - postgresql.use_slots: true Upon changing these options, Patroni will read the relevant section of the configuration stored in DCS and change its run-time values. Patroni nodes are dumping the state of the DCS options to disk upon for every change of the configuration into the file ``patroni.dynamic.json`` located in the Postgres data directory. Only the master is allowed to restore these options from the on-disk dump if these are completely absent from the DCS or if they are invalid. REST API ======== We provide a REST API endpoint for working with dynamic configuration. GET /config ----------- Get current version of dynamic configuration. .. code-block:: bash $ curl -s localhost:8008/config | jq . { "ttl": 30, "loop_wait": 10, "retry_timeout": 10, "maximum_lag_on_failover": 1048576, "postgresql": { "use_slots": true, "use_pg_rewind": true, "parameters": { "hot_standby": "on", "wal_log_hints": "on", "wal_keep_segments": 8, "wal_level": "hot_standby", "max_wal_senders": 5, "max_replication_slots": 5, "max_connections": "100" } } } PATCH /config ------------- Change existing configuration. .. code-block:: bash $ curl -s -XPATCH -d \ '{"loop_wait":5,"ttl":20,"postgresql":{"parameters":{"max_connections":"101"}}}' \ http://localhost:8008/config | jq . { "ttl": 20, "loop_wait": 5, "maximum_lag_on_failover": 1048576, "retry_timeout": 10, "postgresql": { "use_slots": true, "use_pg_rewind": true, "parameters": { "hot_standby": "on", "wal_log_hints": "on", "wal_keep_segments": 8, "wal_level": "hot_standby", "max_wal_senders": 5, "max_replication_slots": 5, "max_connections": "101" } } } The above REST API call patches the existing configuration and returns the new configuration. Let's check that the node processed this configuration. First of all it should start printing log lines every 5 seconds (loop_wait=5). The change of "max_connections" requires a restart, so the "restart_pending" flag should be exposed: .. code-block:: bash $ curl -s http://localhost:8008/patroni | jq . { "pending_restart": true, "database_system_identifier": "6287881213849985952", "postmaster_start_time": "2016-06-13 13:13:05.211 CEST", "xlog": { "location": 2197818976 }, "patroni": { "scope": "batman", "version": "1.0" }, "state": "running", "role": "master", "server_version": 90503 } Removing parameters: If you want to remove (reset) some setting just patch it with ``null``: .. code-block:: bash $ curl -s -XPATCH -d \ '{"postgresql":{"parameters":{"max_connections":null}}}' \ http://localhost:8008/config | jq . { "ttl": 20, "loop_wait": 5, "retry_timeout": 10, "maximum_lag_on_failover": 1048576, "postgresql": { "use_slots": true, "use_pg_rewind": true, "parameters": { "hot_standby": "on", "unix_socket_directories": ".", "wal_keep_segments": 8, "wal_level": "hot_standby", "wal_log_hints": "on", "max_wal_senders": 5, "max_replication_slots": 5 } } } Above call removes ``postgresql.parameters.max_connections`` from the dynamic configuration. PUT /config ----------- It's also possible to perform the full rewrite of an existing dynamic configuration unconditionally: .. code-block:: bash $ curl -s -XPUT -d \ '{"maximum_lag_on_failover":1048576,"retry_timeout":10,"postgresql":{"use_slots":true,"use_pg_rewind":true,"parameters":{"hot_standby":"on","wal_log_hints":"on","wal_keep_segments":8,"wal_level":"hot_standby","unix_socket_directories":".","max_wal_senders":5}},"loop_wait":3,"ttl":20}' \ http://localhost:8008/config | jq . { "ttl": 20, "maximum_lag_on_failover": 1048576, "retry_timeout": 10, "postgresql": { "use_slots": true, "parameters": { "hot_standby": "on", "unix_socket_directories": ".", "wal_keep_segments": 8, "wal_level": "hot_standby", "wal_log_hints": "on", "max_wal_senders": 5 }, "use_pg_rewind": true }, "loop_wait": 3 } patroni-1.4.2/docs/ha_loop_diagram.dot000066400000000000000000000177341323411135200177460ustar00rootroot00000000000000// Graphviz source for ha_loop_diagram.png // recompile with: // dot -Tpng ha_loop_diagram.dot -o ha_loop_diagram.png digraph G { rankdir=TB; fontname="sans-serif"; penwidth="0.3"; layout="dot"; newrank=true; edge [fontname="sans-serif", fontsize=12, color=black, fontcolor=black]; node [fontname=serif, fontsize=12, fillcolor=white, color=black, fontcolor=black, style=filled]; "start" [label=Start, shape="rectangle", fillcolor="green"] "start" -> "load_cluster_from_dcs"; "update_member" [label="Persist node state in DCS"] "update_member" -> "start" subgraph cluster_run_cycle { label="run_cycle" "load_cluster_from_dcs" [label="Load cluster from DCS"]; "touch_member" [label="Persist node in DCS"]; "cluster.has_member" [shape="diamond", label="Is node registered on DCS?"] "cluster.has_member" -> "touch_member" [label="no" color="red"] "long_action_in_progress?" [shape="diamond" label="Is the PostgreSQL currently being\nstopping/starting/restarting/reinitializing?"] "load_cluster_from_dcs" -> "cluster.has_member"; "touch_member" -> "long_action_in_progress?"; "cluster.has_member" -> "long_action_in_progress?" [label="yes" color="green"]; "long_action_in_progress?" -> "recovering?" [label="no" color="red"] "recovering?" [label="Was cluster recovering and failed?", shape="diamond"]; "recovering?" -> "post_recover" [label="yes" color="green"]; "recovering?" -> "data_directory_empty" [label="no" color="red"]; "post_recover" [label="Remove leader key (if I was the leader)"]; "data_directory_empty" [label="Is data folder empty?", shape="diamond"]; "data_directory_empty" -> "cluster_initialize" [label="no" color="red"]; "data_belongs_to_cluster" [label="Does data dir belong to cluster?", shape="diamond"]; "data_belongs_to_cluster" -> "exit" [label="no" color="red"]; "data_belongs_to_cluster" -> "is_healthy" [label="yes" color="green"] "exit" [label="Fail and exit", fillcolor=red]; "cluster_initialize" [label="Is cluster initialized on DCS?" shape="diamond"] "cluster_initialize" -> "cluster.has_leader" [label="no" color="red"] "cluster.has_leader" [label="Does the cluster has leader?", shape="diamond"] "cluster.has_leader" -> "dcs.initialize" [label="no", color="red"] "cluster.has_leader" -> "is_healthy" [label="yes", color="green"] "cluster_initialize" -> "data_belongs_to_cluster" [label="yes" color="green"] "dcs.initialize" [label="Initialize new cluster"]; "dcs.initialize" -> "is_healthy" "is_healthy" [label="Is node healthy?\n(running Postgres)", shape="diamond"]; "recover" [label="Start as read-only\nand set Recover flag"] "is_healthy" -> "recover" [label="no" color="red"]; "is_healthy" -> "cluster.is_unlocked" [label="yes" color="green"]; "cluster.is_unlocked" [label="Does the cluster has a leader?", shape="diamond"] } "post_recover" -> "update_member" "recover" -> "update_member" "long_action_in_progress?" -> "async_has_lock?" [label="yes" color="green"]; "cluster.is_unlocked" -> "unhealthy_is_healthiest" [label="no" color="red"] "cluster.is_unlocked" -> "healthy_has_lock" [label="yes" color="green"] "data_directory_empty" -> "bootstrap.is_unlocked" [label="yes" color="green"] subgraph cluster_async { label = "Long action in progress\n(Start/Stop/Restart/Reinitialize)" "async_has_lock?" [label="Do I have the leader lock?", shape="diamond"] "async_update_lock" [label="Renew leader lock"] "async_has_lock?" -> "async_update_lock" [label="yes" color="green"] } "async_update_lock" -> "update_member" "async_has_lock?" -> "update_member" [label="no" color="red"] subgraph cluster_bootstrap { label = "Node bootstrap"; "bootstrap.is_unlocked" [label="Does the cluster has a leader?", shape="diamond"] "bootstrap.is_initialized" [label="Does the cluster has an initialize key?", shape="diamond"] "bootstrap.is_unlocked" -> "bootstrap.is_initialized" [label="no" color="red"] "bootstrap.is_unlocked" -> "bootstrap.select_node" [label="yes" color="green"] "bootstrap.select_node" [label="Select a node to take a backup from"] "bootstrap.do_bootstrap" [label="Run pg_basebackup\n(async)"] "bootstrap.select_node" -> "bootstrap.do_bootstrap" "bootstrap.is_initialized" -> "bootstrap.initialization_race" [label="no" color="red"] "bootstrap.is_initialized" -> "bootstrap.wait_for_leader" [label="yes" color="green"] "bootstrap.initialization_race" [label="Race for initialize key"] "bootstrap.initialization_race" -> "bootstrap.won_initialize_race?" "bootstrap.won_initialize_race?" [label="Do I won initialize race?", shape="diamond"] "bootstrap.won_initialize_race?" -> "bootstrap.initdb_and_start" [label="yes" color="green"] "bootstrap.won_initialize_race?" -> "bootstrap.wait_for_leader" [label="no" color="red"] "bootstrap.wait_for_leader" [label="Need to wait for leader key"] "bootstrap.initdb_and_start" [label="Run initdb, start postgres and create roles"] "bootstrap.initdb_and_start" -> "bootstrap.success?" "bootstrap.success?" [label="Success", shape="diamond"] "bootstrap.success?" -> "bootstrap.take_leader_key" [label="yes" color="green"] "bootstrap.success?" -> "bootstrap.clean" [label="no" color="red"] "bootstrap.clean" [label="Remove initialize key from DCS\nand data directory from filesystem"] "bootstrap.take_leader_key" [label="Take a leader key in DCS"] } "bootstrap.do_bootstrap" -> "update_member" "bootstrap.wait_for_leader" -> "update_member" "bootstrap.clean" -> "update_member" "bootstrap.take_leader_key" -> "update_member" subgraph cluster_process_healthy_cluster { label = "process_healthy_cluster" "healthy_has_lock" [label="Am I the owner of the leader lock?", shape=diamond] "healthy_is_leader" [label="Is Postgres running as master?", shape=diamond] "healthy_no_lock" [label="Follow the leader (async,\ncreate/update recovery.conf and restart if necessary)"] "healthy_has_lock" -> "healthy_no_lock" [label="no" color="red"] "healthy_has_lock" -> "healthy_update_leader_lock" [label="yes" color="green"] "healthy_update_leader_lock" [label="Try to update leader lock"] "healthy_update_leader_lock" -> "healthy_update_success" "healthy_update_success" [label="Success?", shape=diamond] "healthy_update_success" -> "healthy_is_leader" [label="yes" color="green"] "healthy_update_success" -> "healthy_demote" [label="no" color="red"] "healthy_demote" [label="Demote (async,\nrestart in read-only)"] "healthy_failover" [label="Promote Postgres to master"] "healthy_is_leader" -> "healthy_failover" [label="no" color="red"] } "healthy_demote" -> "update_member" "healthy_is_leader" -> "update_member" [label="yes" color="green"] "healthy_failover" -> "update_member" "healthy_no_lock" -> "update_member" subgraph cluster_process_unhealthy_cluster { label = "process_unhealthy_cluster" "unhealthy_is_healthiest" [label="Am I the healthiest node?", shape="diamond"] "unhealthy_is_healthiest" -> "unhealthy_leader_race" [label="yes", color="green"] "unhealthy_leader_race" [label="Try to create leader key"] "unhealthy_leader_race" -> "unhealthy_acquire_lock" "unhealthy_acquire_lock" [label="Was I able to get the lock?", shape="diamond"] "unhealthy_is_leader" [label="Is Postgres running as master?", shape=diamond] "unhealthy_acquire_lock" -> "unhealthy_is_leader" [label="yes" color="green"] "unhealthy_is_leader" -> "unhealthy_promote" [label="no" color="red"] "unhealthy_promote" [label="Promote to master"] "unhealthy_is_healthiest" -> "unhealthy_follow" [label="no" color="red"] "unhealthy_follow" [label="try to follow somebody else()"] "unhealthy_acquire_lock" -> "unhealthy_follow" [label="no" color="red"] } "unhealthy_follow" -> "update_member" "unhealthy_promote" -> "update_member" "unhealthy_is_leader" -> "update_member" [label="yes" color="green"] } patroni-1.4.2/docs/ha_loop_diagram.png000066400000000000000000017655351323411135200177560ustar00rootroot00000000000000‰PNG  IHDR „ :î&¶bKGDÿÿÿ ½§“ IDATxœìÝ|Ýu}/ðW’¦I÷Ð4M›¤iùÕŠ(„9ƒGÃ[ü ^™ëîÜ.^眛w»»ÎéÃm¢ó±‰:':çd¨üÚ¼Ì!RP˜eŒß’´MÓpú‹¦i›äþç¬! h{šöù|<Î#ŸÏ9Ÿó=ïwmSÂywÕÐÐÐP7ª+]/Œ@À8#0ÎL¨tDzÎÎÎüøÇ?®tG´3Î8#---•.Ž(UCCCC•.àXõ¶·½-ßøÆ7*]Æí­o}kþáþ¡ÒeÀ¥ºÒË’•I†ÜF½­üé¯0Œ@À8#0΄Œ3!ãŒ@À8#0΄Œ3!ãŒ@À8#0΄Œ3!ãŒ@À8#0΄ŒwCI®NrV’YI&%9-Éï'ùÏ眭: õŽ×€cœ@Àx÷ÇIþ1ÉUIÖ'ÙäÓIþ%É©¬ 8d&Tº^¢¿Nr’¹?Ý×'yM’¿O²¬RE‡’ !ãÝ®$3F¹i’¡ýöUû}-ÝÞ³ßã×'i˳’–$ïO²ã9×,=ï‰$&)ìwß¼pP„Œw+’ü^’âÏ87´ß×Òí‹û=þ«IÞ•äé$wýôëÿã¿•äŠ$=Iþù¼pPL¨t¼D_Lò‘$K’œ™ä‚<îh|×ÙšÈä$Ÿþé5GóûI~á§ëóŸó\à3!`¼›œä/“t$¹$ÏN÷xE’w$Ùö®[ȳSBFóó/ẇжmÛrÍ5×ä7ó7óÐCUº8dL8ZÌHò֟ޞɳáË’|åž»9ɇ“|7Io’Ÿq~ò‹/ó`{ì±Çrà 7äúë¯ÏwÜ‘ÁÁÁ ¥­­-§œrJ¥Ë€CB àh4%ÏN 9õÏ_œä¤$w$Yÿz—iÕÁ/í¥ÈêÕ«sã7æÚk¯Íã?žššš epp°|nÚ´i¬-€ñ®*É“IZžsÿ„$SG9;š;“|#ÉÌýîÛõê9Èžzê©ÜvÛm¹þúëóï|';wîÌĉ³gÏž$φDžkêÔç6G€£ÁÅI>™äeIj’<”äCI~ó9瓬NòsIþ5ɯ'Y›ä’ün’ßO2?Iw’?y‘µŒõ/F1¹åÞ[2gΜTUU¥ºº:ûöíK’rd,!ÍBÆ»ÕIþ6É%y6ÈQ“ää$—&ùíçœý‹$ïH²>IS’Ïýôþ«“\‘äÌ$[“œ˜äzU’¡Ÿž«zÎ×Òýò/Æ`Òßߟ¡¡¡ÔÔԔà â5¯yMŽ;î¸Lš4)õõõ) Ãnrÿœ9sR[[û€C£jhhh´oÃ]tQ®É5É7+]Éê¢deVæ²Ë.˧?ýé|ç;ßIUUÕC>ùÉO¦¶¶6Û·oÏŽ;²}ûö‹Åòú¹÷eúôéåÛ´iÓ2}úôÌœ93Ó§OÏŒ3FJfΜ9l?qâăù+IB¹lذ!]]]Y»vmùÖÕÕ•»ï¾;ÏÙ(2–ŸB¾ùÍgz{{ó•¯|%õW•uëÖe„ c†C¶lÙ’Y³fðKí)­‹ÅâˆðÈöíÛ³uëÖlß¾=Û¶mK±X,ßöîÝ;âºS¦LyÞÀÈóJêëë_ܯG=€—hË–-ÃBk׮ͺuëÒÝÝîîîlذ¡Z¨©©ICCCš››³páÂüä'?É#/D d,Ï „” æ¶ÛnË_ÿõ_çºë®KMMMöìÙ3ìLÿaŸÎ±sçÎlݺuXHd´ÛhgúûûG\oÒ¤I##Çw\fÍš•Y³feîܹ™={vy_Z×ÔÔÖ¾8üBžGÖ¯_Ÿžžžlذ!éèè(ï{ì±lß¾½|¾P(¤¡¡!óçÏOkkë°ukkkššš2a„òù‹.º(×䱌Ù_www¾øÅ/æóŸÿ|6oÞœªªªTUU99äHµk×®ç Œ”nO?ýtžzê©lÙ²%›6möû¯ä¹‘Ò×9sædöìÙ£†Hªªª*Ð5/–@pL+‹#Bûï{{{Sz»e]]]G„š;v”Ï …a“=ÚÛۇ훚š2a‚·Vrì( ) |¾¯¯¯<}¤··7›6mʆ ²iÓ¦lܸ1ÝÝݹçž{²qãÆ<ýôÓÞ;eʔ̟??sçÎÍܹsËëã?>óæÍËܹsÓÐÐã?>“&M:Ø­•ü«8âìÝ»7›7oòØÊGooo†††’<;© ìhhhȲe˲jÕªràãÄOÌ´iÓ*ÜŒo“&MÊÂ… ³páŸy¶¿¿¿ügxãÆÙ´iSzzz²yóælܸ1÷ß9X²uëÖaÏ:uê°ÀHCCCæÌ™“yóæÔ×תvŽx!ÀaW,G y”ö]]]H’ÔÖÖföìÙåÀG[[[y] €444¤ªªªÂ]%uuuY°`A,Xð3Ïö÷÷ç©§žJ±X̆ ÒÓÓ3lýðÃç¶ÛnK±XKþ+ V í?õ§´^¸pajkke»!T»wïNOOϘ“=º»»³sçÎòùB¡0ìMÜíííÃöÍÍÍ©©©©`GÀ¡TWW—ùóçgþüùY¶lÙóžÝ½{wyÚȦM›²nݺôööfíÚµéííÍC=” 6ä©§ž*?§ºº:Ç|ù5J·ÆÆÆÌ›7/MMMillÌqÇw¨[8¨B€¶gÏžlÙ²eÌÉ¥uIéÓûKŸÖ¿|ùòa“=š››3uêÔ vŒ'õõõijjJSSÓóž+M)MÙÿëúõësÏ=÷ä‰'žÈÖ­[ËÏ©««Kccã¨SFL#ŽD!@Y±X3äÑÑÑ‘®®® $I&Nœ˜Y³f9Ù£´8ÜöŸ:²|ùò1ÏíØ±#k׮ͺuë²nݺ¬]»6k×®Íúõësã7fíÚµÙ¾}{ùü”)SÒÔÔ” ¤±±qغ¹¹YÈ 8¬BàÑ××7jÈ£´ïêêÊ3Ï_(ÊÁŽÑÍÍÍ©©©©`G/Í´iÓ²téÒ,]ºtÌ3cýÝÙÓÓ“ûï¿Ĥ‘B¡0ìïÊý§"ÍŸ??‹-2e8(Bà(°gÏžlÙ²eDÈcÿ}±X,Ÿ¯¯¯6ÉcùòåÃÞ´ÜÒÒ’)S¦T°#€#äI“Ê?ŽeÛ¶méîîNWWW:;;ÓÝÝîîîüÇüGn¸á†lذ¡|vòäÉiii)Oijj*¯[ZZÒÐÐêêêÃÑ0Î „À8P,Çœì±aÆtvvfpp0I2qâÄÌš5«øxîdÖÖÖ … wpô˜1cFN=õÔœzê©£>¾gÏž¬[·nÄßá?þxn¿ýötvvf×®]I’ÚÚÚ,\¸pØt‘ýoþþJB ÂúúúÆœìÑÓÓ3ìÂIR(Ê“<–-[–+V”÷óçÏOsssjjj*Øû›8qâóNLOOOyÂHGGGž|òÉ<òÈ#ùîw¿›uëÖ•C¥ï‹-öµµµ5MMM™8qâál ¨ 8„úûû³~ýú!Òú‰'žÈÖ­[Ëçëëë‡Mòhkk+ï²hÑ¢Lž<¹‚p°UWWgÁ‚Y°`AÚÚÚF<¾gÏžtvvæÉ'Ÿ,‡E:::rË-·¤£££ü}¤¦¦& ,(‡D–,Y’N8!K–,É’%K2uêÔÃÝp „ÀKP,G„<ößwvv–?Õ}âĉY°`Ay’G{{{.¾øâràcñâÅ™9sf…;àH3qâÄœxâ‰9ñÄG}üé§ŸéèèÈ¿þ뿦»»;I’yóæ•"ûE–,Y’iÓ¦Ζ€ƒ@ ÆÐ××7jÈ£´_»vmöîÝ[>_(Ê“<–-[–+V”÷óçÏOKKKª««+ØG£ãŽ;.Çw\–/_>â±½{÷fíÚµåï]¥Û×¾öµ<òÈ#å°Hé{Xé¶téÒ,[¶,K–,ÉŒ3wKÀà˜Ôßߟõë×9ÙãñÇ϶mÛÊçëëëË“5B8¢ô÷÷gýúõ#B¥ýc=–íÛ·—Ï …QC¥HSSS&L˜PÁŽàذwïÞ¬]»6yàòàƒ¦££#÷ß6nܘäÙïÛK—.ͲeËÊ__ö²—eÞ¼y®Æ«b±8"ä±ÿ¾··7¥·¶ÕÕÕ¥±±qDÈ£´?á„2}úô wü,½½½åÈþçæÈ<­[·&I²lÙ²òíe/{Y–.]š3fT¸r8r „pЋÅ1'{ttt¤»»;ûöí+Ÿ/ #BûïRUUUÁŽ€CiݺuyðÁsÿý÷—Ã">ø`vîÜ™$ijjÊÒ¥KË‘—½ìeY¶lY&Ož\áÊ¡òB8 »wïNOOψGiýè£fÇŽåó…BaÌÉ­­­ijjÊ„ *Øp¤êééɃ>Xž*òÀdÍš5yæ™g’<;QdùòååÛ²eËÒÚÚZáªáð {÷îÍæÍ›G„<öŸòÑÛÛ›Ò[ÎêëëËÁŽýC¥õ‰'ž˜iÓ¦U¸+àh200'Ÿ|2kÖ¬É}÷ÝW¾uww'IæÌ™“ÓN;-¯xÅ+rÚi§å´ÓNË)§œ"€ÊQK àP,G y”ö]]]H’ÔÖÖföìÙ£>Jû†††TUUU¸+€dÛ¶m¹ÿþûsï½÷–§‰üû¿ÿ{úúúR[[›N8¡¹;mmm9ãŒ3RWWWér9ʄǜ¾¾¾1'{ôôô¤³³3»ví*Ÿ/ cNö˜?~ZZZR]]]ÁŽ8ìܹ3kÖ¬)‡DV¯^§žz*“'OÎ+_ùÊrHäÜsÏÍܹs+].ãœ@pTéïïÏúõëG„ûìœsÎ99ùä“+].ãŒ@0®‹Å!ý÷L’ÔÕÕ¥±±qDÈ£´^¼xqfΜYáŽ8–lݺ5«W¯Î]wݕիWgõêÕÙ¹sgæÍ›—sÎ9'çž{nÎ=÷Ü,]º4UUU•.—#˜@pÄèëë5äQÚ¯]»6{÷î-Ÿ/ #Bûï[ZZR]]]ÁŽàù dÍš5¹ãŽ;òÃþ0·ÜrKŠÅbæÌ™“3Ï<3gŸ}vÚÛÛóÊW¾Ò¿mF 8,úûû³~ýú1'{<þøãÙ¶m[ù|}}ý¨!Ò~áÂ…©­­­`Gpð äá‡.‡Cn»í¶<õÔS™6mZÎ<óÌ´··§­­-gžy¦ãB€ƒ¢X,Ž:Ù£´~òÉ'Sz»R]]]Çœì±dɒ̘1£Â@å æ¾ûîËí·ßžïÿûùÁ~§žz*3gÎÌÙgŸ×¼æ5iooÏ©§žšªªªJ—Ëa$üLÅbqÔG)ÒÝÝ}ûö•Ï …Q'{”Ö---©®®®`G0> æÈ÷¿ÿýrHdË–-ihhÈyç—×½îu9ï¼ó2wîÜJ—Ê!&Ǹþþþ¬_¿~DÈ£´ì±Ç²}ûöòùB¡0jÈ£ijjÊ„ *Ø[xàÜxã¹å–[òƒü ýýýimmÍ\+Väì³ÏN}}}¥Ëä €£\±XòØßÛÛ›ÒÛˆêêêÒØØ8"äQÚŸp ™>}z…;Ʋk×®Üyç¹å–[rË-·äÞ{ïͤI“ÒÖÖ–ööö´··çôÓOOUUU¥Kå%€q¬X,Ž9Ù£££#ÝÝÝÙ·o_ù|¡PòØßÐÐàapéêêÊÍ7ßœ›o¾9·ÞzkŠÅb,X×½îuyÝë^—öööÌš5«Òeò"„Àj÷îÝéééò(­}ôÑìØ±£|¾P(Œ9Ù£µµ5MMM™0aB;*i`` kÖ¬)O¹ýöÛ300W¼â¹à‚ ò–·¼%§œrJ¥Ëä „@ìÝ»7›7oòØÊGoooJo﩯¯/;öy”Ö'žxb¦M›VᮀñdÛ¶m¹ùæ›sýõ×çŸÿùŸóôÓOgéÒ¥yãߘ7¾ñ9óÌ3S]]]é2ƒ@ÅbqÔGißÕÕ•$ImmmfÏž=jࣴohhHUUU…»ŽVY½zun¼ñÆ\wÝuyøá‡3{öìœþùY±bE^ÿú× ¡aBàÚ½{wzzzÆœìÑÝÝ;w–Ï …QC¥}sssjjj*ØÀp¹á†rã7æûßÿ~&L˜³Ï>;\pAV®\™ùóçWºÄcž@ìgÏž=Ù²e˘“=Jë’úúúçìÑÜÜœ©S§V°#€—¦··77Þxc®¿þúÜrË-éïïÏ™gž™7½éMyË[Þ’æææJ—xLà˜R,Ç yttt¤««+I’‰'fÖ¬YcNö(­Ž»víÊ÷¾÷½\ýõ¹îºëòôÓO笳ÎÊ[ßúÖ¬\¹2 •.ñ˜!ÀQ£¯¯oÔGißÕÕ•gžy¦|¾P(ŒòhmmMsssjjj*ØÀ‘k`` «W¯ÎW¿úÕ|ãßÈÎ;óªW½*+W®Ì[ßúÖüñ•.ñ¨&À¸°gÏžlÙ²eDÈ£´â‰'²uëÖòùúúúQC¥}KKK¦L™RÁŽŽýýý¹ùæ›sÍ5×äÛßþvúúúrÖYgå’K.É[Þò–̘1£Ò%uB8"‹Å1'{lذ!L’Lœ81³fÍò(í/^œ™3gV¸#€cS___n¹å–|õ«_Íõ×_Ÿ$9ï¼ó²råÊ\xá…™:uj…+<:„pÈõõõ9Ù£§§'ÙµkWù|¡Ps²ÇüùóÓÒÒ’êêê vÀ(‹ùö·¿üÇÌ­·ÞšI“&åWõWsÉ%—¤½½ÝÛ½!¼$ýýýY¿~ýˆGiýøãgÛ¶måóõõõcNöhhhÈ¢E‹2yòä vÀ¡°iÓ¦üÓ?ýSþþïÿ>wÞyg.\˜K.¹$ïz×»r 'Tº¼qG €çU,G„<ößwvvfpp0IRWW—ÆÆÆ!ÒzñâÅ™9sf…; Òyä‘üÝßý]¾úÕ¯¦§§'mmmùïÿý¿ç¢‹.Ê´iÓ*]Þ¸ q¾ô¥/e`` Òe/QoooV®\™SN9¥Ò¥ð<úúúF y”ök×®ÍÞ½{Ëç …ÂˆÇþû–––TWWW°#Æ“ÁÁÁÜvÛm¹úê«síµ×fpp0+V¬ÈûÞ÷¾´µµUº¼#š@pĹꪫ²jÕªJ—¼D÷Þ{o’dùòå®àØÕßߟõë×9ÙãñÇ϶mÛÊçëëëG y”ö .Lmmm;àhV,sõÕW窫®Êƒ>˜Ÿû¹ŸËªU«ò¶·½-S§N­tyGàˆ#G€C¯X,Ž:Ù£´~òÉ'SzkH]]]Çœì±dɒ̘1£ÂÀ³~ðƒäóŸÿ|®½öÚLœ81ïxÇ;òÛ¿ýÛY¶lY¥K;bL¨tŒT,G y” ÝÝÝÙ·o_ù|¡P(‡<–/_^}”‹-JUUU;€÷êW¿:¯~õ«så•Wæ+_ùJ>ÿùÏçoþæoòK¿ôK¹ì²ËrÁ¤ºººÒeV”@Àa¶{÷îôôôŒy”ö=öX¶oß^>_(†Möhkk6壩©)&xGŸY³fåòË/ÏûßÿþÜzë­¹òÊ+ó¦7½)‹-ʪU«ò¿ñ9î¸ã*]fET •æ‚!®ºêª¬ZµªÒe/ѽ÷Þ›$Y¾|y…+8üŠÅâˆÇþûÞÞÞ”Þ²QWW—ÆÆÆaý'{œp ™>}z…;€#Ç#<’Ï~ö³ùÊW¾’$¹ôÒKsùå—§¹¹¹Â•^!ÀG Ž)@j¥ IDAT!ÀѪX,Ž9Ù£££#ÝÝÝÙ·o_ù|¡PòØßÐЪªª vãÓ¶mÛò¥/})Ÿþô§³aƼõ­oÍ?øÁ¼üå/¯ti‡…Y¡À1£ô+Ÿ“Œe÷îÝéééò(­}ôÑìØ±£|¾P( y´··Û755eÂoÏ€CaÆŒ¹üòËó?ÿçÿÌw¾ó|âŸÈi§–¶¶¶|èCÊŠ+*]â!å'À1aïÞ½Ù¼yóˆÇþS>z{{Ë!òúúúr°£¡¡!Ë–-˪U«ÊO<1Ó¦M«pW@mmmV®\™•+WæŽ;îÈ'>ñ‰¼ñoÌ«^õª|øÃ>jƒ!!ÀQ¡X,Žò(í»ºº200äÙ7ŠÌž=»øhkk+¯K†††ò¤A`|8ûì³söÙggõêÕù“?ù“r0ä£ýh^ÿú×Wº¼ƒJ 8âíÞ½;===cNöèîîÎÎ;Ëç …By’GkkkÚÛۇ훛›SSSSÁŽ€CéU¯zUþùŸÿ9wß}wþÏÿù?9ÿüósæ™gæÿïÿóÏ?¿Òå!p”)}zí®]»ò»¿û»ùæ7¿™§žz*ûöíöøÐÐИÏÝÿ±Ò}ûöíËÇ?þñ|ñ‹_̆ ÒÚÚšßýÝßÍ»ßýîUç¾}ûò¹Ï}._ýêWóðÃgpp0?ÿó?Ÿ|à™2eJ^ûÚ×fÞ¼yéèèȤI“†=·¿¿?‹/ÎúõësË-·äµ¯}íó^ïg€þÚ×¾–/~ñ‹Y³fMúúú²páÂ\xá…ùÈG>’3f¼¨þ€·gÏžlÙ²eÌÉ¥uI}}}9ØÑÐÐåË—›ìÑÜÜœ©S§V°#àHqæ™g榛nÊ}÷Ý—}ìcù•_ù•œyæ™ùÄ'>‘sÎ9§Òå½$UC£ý__€ ºêª«²jÕªJ—ãV)Àñæ7¿9×^{mùþÒ_l äÒK/ÍßþíߎxÎ7¿ùͬ\¹òÕ¸wïÞ\pÁ¹ùæ›G}|hh(mmm¹óÎ;ó©O}*øÀ†=þÙÏ~6—]vYÎ8ãŒüèG?: ëÖßÐÐPÞùÎwæë_ÿú¨Ï[¶lYî¼óÎLŸ>ýõ<ëÞ{ïM’,_¾¼Â••V,Ç yttt¤««+I’‰'fÖ¬YåÀG)ä±dþüùî¯îºë®|èCÊí·ßž_ûµ_ËŸþéŸæä“O®tY/Š !p”ºë®»rÓM7åœsÎ9(ŸŒû½ï}/7ÜpCÎ9çœlÛ¶-¿ó;¿“믿>ŸùÌg^p ä3ŸùLn¾ùæL:5ûØÇrá…fΜ9ù÷ÿ÷|ò“ŸL’üáþaÎ?ÿü|ò“ŸÌÿøÿ#S¦LIòìt?û³?K’|øÃ>àëæK_úR¾þõ¯§±±1ù—™×¼æ5™2eJÖ¬Y“÷½ï}ùñœ?ýÓ?-¿0R__ߨ!Ò¾««+Ï<óLù|¡P(;Z[[ÓÞÞ>,ðÑÜÜœššš vÍÎ:ë¬üÛ¿ý[nºé¦|øÃΩ§žšw¿ûÝù£?ú£q÷!&„GBà¥)MÁøÖ·¾•_ûµ_óñ:!äæ›oÎyçW¾ÿÉ'ŸLkkk¦M›–íÛ·¿ _ñŠWä¾ûîËUW]•ßøßóÜgœ‘{î¹'ög–}èCIþk:È)§œ’x UUU|½çöwÖYgåî»ïÎí·ßžW¿úÕÃÎvttdñâÅ9á„ò裾 þ€g™ãßž={²eË–!Òþ‰'žÈÖ­[ËçëëëGLòØÊGKKK9ì Piƒƒƒ¹öÚkó{¿÷{éííÍ?øÁüÞïý^êëë+]ÚŽ8!ðÒ”BO?ýt …˜¿Ð@H__ß°| ¥ººzÌk=ŸÉ“'§¯¯/›6mÊœ9sÆŸþô§sÒI'Uº¼1 „À1æøãOooo:;;³hÑ¢òý?øÁ[ 'tRÖ¬Y“ë®».ïyÏ{ÆþÜë}ík_ËÅ_œ$ù¿ÿ÷ÿæ#ùÈ‹ºÞhõ^vÙeùìg?û¼õú1*¼8÷Þ{o’dùò宎,}}}£†ò‘d„ #ž{(!ɳ!Ž+¯¼2_ýêWó裦¦¦&gœqF.¿üò¬X±bØÙ»ï¾;guV¦M›–îîîÌœ9óE]o¬zo½õÖüÍßüMV¯^M›6e„ immÍ/ÿò/ç’K.Éi§ö‚ûB86õ÷÷gýúõcNöxì±Ç²}ûöòùúúúQC¥ýÂ… S[[[ÁŽH’k®¹&ï}ï{3uêÔ\}õÕyõ«_]é’B€#@ð\—^zi¾üå/çø@>õ©OUºà „p4*‹£Nö(­Ÿ|òÉr¹®®.cNöX²dIf̘QáŽ8P½½½ùõ_ÿõü¿ÿ÷ÿòû¿ÿûùèG?ZþнJ¨Ü+€ÎÎÎÜtÓMIž †À¡R,G y” ÝÝÝÙ·o_ù|¡P(‡<–/_^}”‹-*O©`ü›7o^nºé¦\}õÕyï{ß›ï~÷»ùÚ×¾–O<±"õ„žÑí… -ÞÿšoxòtéÒ\$ÉîÝ»ÓÓÓ3"äQÚ?öØcÙ¾}{ù|¡P6Ù£­­mØ”¦¦¦Š~ú#•sÉ%—äçþçóÎw¾3Ë—/ÏUW]•·½ím‡½?ŽhõõõùÅ_üÅ|á _¨t)ÁŠÅâˆÇþûÞÞÞr8¹¾¾>óçÏ/>ÚÛÛ³jÕªòþ„NÈôéÓ+ÜG²“O>9«W¯Î‡>ô¡¼ãïÈ]wÝ•O}êS©­­=l5„Å ™üQÉk0þ‹Å1'{ttt¤»»;ûöí+Ÿ/ åIÏìÑÐІ††žpc©­­Í_üÅ_äÜsÏÍ»Þõ®Üyçù§ú§477–× bvïÞžžž!ÒúÑGÍŽ;Êç …By’GkkkÚÛۇ훚š2a‚ÿÀáó«¿ú«¹óÎ;óæ7¿9gžyf¾õ­oå~áùëú)‡ÄÞ½{³yóæ!çNù(©¯¯/;²lÙ²¬Zµªø8ñÄ3mÚ´ v£[ºti~ô£åï|gÚÛÛsõÕWç¿ý·ÿvH_S €¥X,ŽòèèèHWWW’$µµµ™={v9ðÑÖÖV^— ©ªªªpWðâL›6-ßúÖ·rùå—碋.ÊG?úÑüÑýÑ!{=Fؽ{wzzzÆœìÑÝÝ;w–Ï …ò$ÖÖÖ´··Û777§¦¦¦‚À¡WSS“+¯¼2---¹âŠ+²eË–|æ3ŸIuuõA-€cÌž={²eË–1'{”Ö%õõõå`GCCC–/_>l²Gsss¦NZÁŽàÈòþ÷¿?ÍÍÍyÇ;Þ‘íÛ·çË_þòAÿÀ€£L±X3äÑÑÑ‘®®® $I&Nœ˜Y³f9Ù£´^˜ /¼0ßýîwsÁ¤¿¿?ÿ÷Ÿ ^ŒC `éëë5äQÚwuuå™gž)Ÿ/ å`ÇhæææƒþÉ„À³Î=÷ÜÜtÓMù•_ù•¼ímoË׿þõÔÖÖ”k „!öìÙ“-[¶Œy”öO<ñD¶nÝZ>___?l’ÇòåËÓÚÚZÞ·´´dÊ”)ì8çœsrà 7ä‚ .ÈÅ_œ¯ýë©®®~É×8LŠÅ☓=6lØÎÎÎ &I&Nœ˜Y³f•ííí¹øâ‹ËûÅ‹gæÌ™î8¿ø‹¿˜ë¯¿>çŸ~®¸âŠüÅ_üÅK¾¦@ÀAÐ××7ædžžžtvvf×®]åó…B¡<ÉcÙ²eY±bEy?þü´´´”O Ž ¿ôK¿”«¯¾:oûÛ³pá¼ÿýïI×øúûû³~ýú!ÒúñÇ϶mÛÊçëëëË““&MJuuuf̘‘$™:ujjkk3eÊ”Lž<9Ó¦MËôéÓ3yòäLž<9…B!“'OΔ)SÊÕÔÔ¼€_U8pÅbqÔG)ÒÝÝ}ûö•Ï …ò$åË——'{”‹-*ÿ™^÷º×åòË/ÏW\‘7¼á ihh8àçV •>.àqÕUWeÕªU•.†††²yóæ<ýôÓ)‹)‹ÃÖÏÝ—Ö;vìÈÎ;Ÿ÷ÚÓ¦M+#JÁ‹R8¢´¨ªªÊÌ™3“üWТ¶¶6S§N-_§®®.“'Oóuž/pQ ™ŒfïÞ½ÃzسgOžyæ™$ϾÉ>I9¸²oß¾ìØ±#ɳ!˜ÁÁÁìܹ3»víÊÎ;Ë÷¥®®.Ó¦MK¡PH¡PÈqÇW^µ?î¸ã2gΜLœ8qÌërtÛ½{wn¾ùælÞ¼9Ó§O1åãÑG-ÿ¾Lž {”‚û‡óæÍËœ9s2wîÜòúøãOccc&MšTÎy±ŠÅâˆÇþûÞÞÞ”>›®¾¾>óçÏ/‡<ÚÛÛ³jÕªòþÄO,O¸8ÖLž<9_øÂòÚ×¾6ßùÎwò¦7½é€žgBpÄ1!Ž^ƒƒƒéííÍÚµk³nݺ¬[·.ÝÝÝåuWWWz{{‡MŘ4iRæÎ›†††Ì™3'sæÌ)¯K÷—&R …L™2¥‚ÛöìÙS‡<õÔSÙ¼ysz{{³iÓ¦òzÿOi’IÉìÙ³ÓØØ˜¦¦¦,\¸0 ,È‚ ÒÔÔT^×ÕÕU¨»cK±X5äQÚwww c …ò$ý'{ô÷÷göìÙùå_þåTUUU°#€#ß%—\’Ûo¿=<òÈý<\ 8â„Àø¶sçÎ<ñÄ£ÞÖ­[—½{÷&Iªªª2oÞ¼òÿ.\8,Pš&1uêÔ wÄ¡²gÏžlÚ´)7nLOOϰpPi½~ýúô÷÷—Ÿ3oÞ¼,Z´(‹/q;þøã+ØÍóûÞ÷¾—üãyûÛßž÷¼ç=ÿŸ½;«²Îÿÿÿ}tçw*&&FwÞy§œUYY©U«VéÅ_Tnn®êêêÔœ¯ÞåííÝèÊõÛ×!ðó :TNNNÚ¸qc£}_;´kÇŽÓ¦M›´k×.íÞ½['Ož”‡‡‡¢££5}út 8ð²+×???ùùù)66ÖÜväÈ%''k÷îÝz÷ÝwõÏþS...ºé¦›tøða•••]ñqvìØ¡;ï¼³%K\c¦Nª|PÇW`` Õ>B¸FÔÖÖê›o¾ÑúõëµaÃ8p@8p fÍš¥Êd2ÉÁ¯€æ SXX˜¦N*é§•v’““µvíZýðÃæ~ƒAvvvª©©irƒAÇ¿ª5Ú¿¸¸8yxxhíÚµzúé§­öáÚ±ššmÛ¶M|ð6nܨ'N($$D±±±š?¾ $'''[— \3BCCªI“&I’8 „„}ôÑGÊÌÌ”Á`P]]$ÉÑÑQÕÕÕæ×õmB—ãä䤘˜mݺµÑ@ˆ]+×Z@FF†æÌ™£ >\ÐSO=¥ýû÷ëèÑ£úÏþ£!C†®²Þ½{ë_ÿú—ÒÓÓ•ŸŸ¯%K–èþûï—“““êêêÔµkWuèÐA’äàà ªª*!€f6l˜víÚ¥ .X}ŸBh'*++µfͽõÖ[JNN–¯¯¯yäMš4Iaaa¶.¸îùøøhòäÉš]_ýµ8 ±cÇꫯ¾Ò† ´sçNeddغD@çïï¯=zhïÞ½Vß'@U]]­7ÞxC={öÔK/½¤qãÆ)##C~ø¡î¿ÿ~ÙÛÛÛºDÍЫW/-X°@ÙÙÙZ¸p¡vìØ¡ððp͘1C'Nœ°uy€6,22RiiiVß#@´ÿ~õïß_O=õ”F¥üQóçÏW@@@«ß`0˜mEKÖÓZs³ÕùKMMÕàÁƒåááÑæ®ã¥.þ¬ ¹¸¸( @±±±Z¾|¹*++›‘‘¡iÓ¦)$$D:t§§§ú÷ï¯^xAßÿ½¹_]]tÇwÈËËK...ºå–[ôüóÏëàÁƒ­1MI’³³³üq>|Xï¾û®>ùäÝxãŠW]]]«Õh?"##•ššjõ=!´1óçÏWTT”ÜÜÜ”ššª7ß|S>>>­ZCýÍémé&õ–¬¥-ÍëjøÍo~£éÓ§ëôéÓÚ¶m›­ËiÒÅŸµºº:9sF{öìÑ„ ôÞ{ïÉd2éÇl0nëÖ­úÍo~£_ýêWúþûïuöìY}ûí·z衇´xñbÝrË-æ¾/½ô’V¯^­øøxåææ*??_ÿþ÷¿µyófõîÝ»ÕæZÏÎÎNcÇŽUjjª¦L™¢'žxB£FÒÙ³g[½@Û¡#GŽXý%Cݵþ€v'>>^Ó¦M³u@««­­Õ”)S” üãúýïoÓ• C› N´dM­1?[CGGG•——ËÞÞ¾Õýs4užâããõòË/ë»ï¾S§N$I999=z´vîÜ)z^I[ IDATc’’’4dÈó>}||tàÀuíÚÕ¢_ZZš"##mþ9ÿâ‹/4vìXuìØQIIIò÷÷·i=-)%%E’d2™l\ ´O_}õ•î¸ã;vLÁÁÁï±BmÄŒ3´råJmذA³fͲiäJgÆ kpÎêÇeddèW¿ú•ŒFc£u^<¦ººZ2 úÃþÐì}5ç:Ö9tèî¸ã¹ººê®»îRzzºRSSÕ¯_?¹»»ëž{î¹¢kÖ˜iÓ¦ièСzå•WÌm ,ÐôéÓ­†A$)&&Æ"äQVVf“\,""ÂæaI0`€öíÛ';;;ÅÄĨ¨¨ÈÖ%ÚˆŽ;J’JJJ¼G €6àÃ?Ô›o¾©•+Wjøðá¶.犤§§køðáúŸÿùedd˜Æ SzzºE߸¸8=úè£:s挾üòK9sFO>ù¤EŸŒŒ óþ233•‘‘¡_ÿú×zàšUÏ‘#G4~üxÍž=[§NÒ—_~©óçÏkذaMŽk,`­ýÑG•§§§öîÝ«³gÏê“O>Qzzºúõë×`\]]ù±téRI?³!C†(66V™™™:vì˜&L˜ ¸¸8åää4ØÇôéÓ5kÖ,åååé“O>iÖ<êùü£Yûjîu¬ßÏܹsµlÙ2åççë–[nÑ„ ô§?ýIË—/Wnn®ÂÃÃõûßÿ¾ÉZ›kêÔ©úøãͯ·lÙ¢Áƒ7{|ll¬þð‡?´é …¯¯¯¶oß®sçÎiæÌ™¶.ÐFÔÿà‘µ@ˆ¡®-üì\$>>^Ó¦M³u@«©««SDD„úõë§åË—Ûº3ƒÁЬÕ&L˜ Ûn»MO?ý´Eû‚ ôí·ß*11±Ñ±EEEêÑ£‡ Ím'NTTTTƒý-_¾\“&MºlM=ô†®‰'šÛ>¬›nºÉb¬µù56çKÛ=<<”••¥Î;›ÛŽ=ªÐÐÐËCúéœÝ|óÍš={¶EûÛo¿­ýû÷káÂ…ûعs§î¾ûî&çÝÜy4¶¯+¹ŽƒA»víÒ Aƒ$Iyyyò÷÷·hËÉÉ‘ÉdÒ‰'~v½õΟ?/•––J’\]]U\\,''§Ëî[úi…^xA êׯŸFŽ©¸¸8ùûû7k|kÚ°aƒF¥¨W¯^¶.çKII‘$™L&WíSii©<<<´iÓ&1Ââ=VÀÆ8 ~øAO=õ”­KùY’’’4zôèícÆŒQRRR“cF£Îœ9cѶmÛ6«û»Ü õvìØ¡{ï½×¢íÆolV¸¥¹ú÷ï¯Q£FiË–-ª®®–$Ýpà Í>FRR’Æß ýþûï×¶mÛ´ß~ûí¿¬àfìëJ¯cß¾}ÍÛݺukÐæçç§S§NýÒr%Y_¥åJ®§«««^}õUeffê‘GÑ—_~©[o½U?ü°Îž=Û"5¶”‘#G*44Tï¿ÿ¾­K´nnn’~úñ¤KÀÆŽ;&IŠŒŒ´m!?ÓéÓ§Í€‹ùúúêôéÓæ×§NÒ”)S   †fïÏZ[cõx{{_Á ®Üû￯Ûo¿]3fÌPçÎ5pà@-\¸PUUUÍ_XX¨ó9¨tëÖÍüy¸˜««k‹ÕÞØ¾š{ëyxx˜·íì쬶µT'--MÝ»w7¿ Rnnîï§S§Nzà” cÇŽ©¼¼¼Í± ƒ"##uôèQ[—hê¿K±ö7w!ؘ———$éäÉ“6®äçñööV~~~ƒöüü|‹`ÆÄ‰åææ¦Ï>ûLåå媫«³úå…··· ´6«///«†æ0 ª¨¨°h»té§`Á‚ ”‘‘¡ÌÌL͘1C«V­Òƒ>جãx{{ëÌ™3æspñ£¬¬ìgÕþK5÷:Ú›o¾i±zɰa쮤r%ÜÜÜôꫯjݺu¿´¼wâÄ óÿ 4†@6Ö§OyzzjÕªU¶.¥Ù.^Ù#&&Fk×®mÐç£>RLLŒùõ_|¡—^zI!!!rpp$«á‡¡C‡Z½Iûöíͪíî»ïVRR’EÛþýûqٱݺuSvv¶EÛîÝ»ô3 ÊÉÉ‘ôSbüøñÚ´i“>ýôÓý¬¹ï¾û´k×®í{öìQß¾}/[çÕÐÜëØÚ^ýumß¾]¿ûÝïÌm¿ûÝï´xñb[³víZ <ØüÚ`0X]yÅÁÁAîîî-^ó/‘žž®}ûöYÔ€5¶.€ë«««žxâ ýíoÓ¸qãl뒮ȟþô'ÝsÏ=êØ±£F%ƒÁ õë×kÁ‚¡‡höìÙzþùçåçç§ììlýå/i°¿¹sçêî»ï–‡‡‡bcceoo¯¤¤$ýýïoV=/¾ø¢ÆŒ#???õïß_zì±Çôä“O^vì!CôüóÏëÕW_•§§§>ÿüs½ñÆVûN™2EóçÏW=T\\¬… 6NøûûkïÞ½ŠŠŠÒÎ;5yòd?~\sçÎÕ¨Q£TSS£{î¹GNNNJNNÖoû[-^¼¸YóliͽŽW[yy¹ ôå—_jéÒ¥*((жmÛÔ©S'sŸ   ýýïWLLŒæÌ™£AƒÉÅÅEÇ×êÕ«õúë¯kóæÍû8q¢æÍ›§^½zÉÞÞ^‡ÒsÏ=§Ç¼Õæv9µµµúío«›nºI#FŒ°u9€6ŽBhæÌ™£àà` >\'Ož´u9æ•- ƒÕÇÅzöì©Í›7ëÃ?Thh¨n¸á}ðÁÚ¼y³zôèaî— òòrõë×O3fŒî¿ÿ~‹ãIRhh¨6oÞ¬>ø@7Üpƒ‚ƒƒõÖ[oé½÷ÞkÐך޽{+11QsçΕ···FŽ©±cÇjúôéVçw±ùóçËÉÉI}ûöU·nÝ´xñb-Y²¤Aߤ¤$¹¸¸hРAêØ±£ú÷ï¯òòr½óÎ;û[°`~øa¹»»kÆŒæ°GHHˆ>üðC­ZµJÝ»w—¯¯¯þüç?kÑ¢EŠ‹‹kô:4Gcc.·¯æ^Gk箹mÍ©×ÓÓSÑÑÑZ±b…~øa¥¤¤¨gÏž Æ9R Z³fn¼ñFuêÔIC† QVV–’““Õ»wosß½{÷*<<\<òˆ¼½½åíí­©S§jôèÑš3gN“õµ–ºº:MŸ>]Ÿ}ö™doooë’mœ¡®®®ÎÖEÀÅâãã5mÚ4[—´º'Nèî»ïVUU•>úè#‹Ú\»JKKõØciݺuúàƒ4jÔ([—ÔbRRR$I&“ÉÆ•@ûe0´zõj7΢Bh#|||´k×.ùûû«ÿþZ¸p¡jkkm]€«è³Ï>STT”víÚ¥O?ýôš ƒ®.!´!>>>JJJÒ¬Y³4{ölÝqÇÚ½{·­ËÐÂrss5eÊ 4HÝ»w×ÿýßÿéî»ï¶uY€v„@mŒ£££æÎ«””yzzjРA9r¤¾øâ [—àÊËËÓ¬Y³¦¤¤$­X±B›6mR@@€­K´3Bh£zõê¥O?ýT[·nUQQ‘¢££5hÐ ­ZµJååå¶.Àøâ‹/ôØc)44T+W®Ô_ÿúW>|X>ø ­K´SBhㆪÏ?ÿ\ÉÉÉ2š8q¢üüü4cÆ íÛ·ÏÖåhD~~¾æÍ›§ððpEGGkß¾}Z´h‘233õÌ3ϨC‡¶.ÐŽ9غÐ<ÔÀUPP ÄÄD-[¶L¯½öšz÷î­ &(..N7Þx£­Ë®k………úä“O´fÍmÙ²EzðÁ•˜˜¨¨¨([—¸†°BíL·nÝôì³Ï*--MŸþ¹úõë§yóæé¦›nRXX˜fÍš¥äädÕÔÔØºTàºpäȽòÊ+4h|||4eÊUWW+11QyyyZ¼x1a@‹c…Ú±hÀ€zã7ôÅ_hÆ Z¿~½æÏŸ¯Î;køð኉‰ÑÀjërkBqq±öìÙ£]»viÓ¦M:|ø°:wî¬#FhæÌ™ºï¾ûÔ±cG[— ¸Æà`oo¯»îºKwÝu—æÍ›§#GŽhýúõúä“O4}út•——+00Pƒ Ò Aƒ4pà@………Ùºl ](,,4@vïÞ­ýû÷«®®NŠÕ›o¾©;ï¼Sööö¶.p!À5(,,L³fÍÒ¬Y³TQQ¡¯¾úJÉÉÉJNNÖÓO?­²²2ùúú*::Z·Ýv›¢¢¢Ô·o_yzzÚºtÀ¦ªªªtàÀíÛ·OûöíÓÞ½{•šš*ƒÁ Þ½{kРAzñÅ5pà@y{{Ûº\ÀuŒ@׸:hàÀ8p æÌ™£ªª*}ýõ×Ú½{·öîÝ«… *//OƒA=zôPTT”¢¢¢d2™Ô·o_yxxØz ÀUQ]]­´´4sø#%%Eû÷ïWEE…ÜÝÝÕ§OÝwß}úÛßþ¦»îºKF£ÑÖ%`F €ëŒ£££¢££mnËËËSJJŠùñ¯ýK’$___EFF*""Âü|ë­·ÊÝÝÝVS®HMM²²²”ššª´´4óó¡C‡TVV&GGGõìÙS&“I=ôî¼óNÝzë­²···ué4Š@ŸŸŸüüükn;~ü¸RRR”––¦ƒj÷îÝzóÍ7UQQ!ƒÁ EFF*22R={öT=Ô½{wùûûË`0Øp6¸^)##CJOOWjjª:¤C‡5øÜÆÄÄè©§žÒ­·Þªˆˆ98ðµ }á/ÛÀªÀÀ@jôèÑæ¶ššeddèàÁƒ:tè<¨-[¶hÑ¢E*++“$9;;+44Ô¹ø¤:ØjJhçjkkUPP`}Ô?ê·Ïœ9#IrppPPPÂÃÃ5tèPýïÿþ¯"##.777Ï€–A 4›½½½ÂÂÂÖར¢"effZ<8 ?þX™™™æ~F£Q¾¾¾òóóShhhƒíàà`¹»»·æ´ÐF)//OùùùÊÌÌl°}ìØ1sðÈÉÉI UŸ>}ôë_ÿZ¡¡¡ UDD„\\\l<®.! EF™L&™L¦ï+##CÇWvv¶rrr”››«´´4mݺUyyyªªª2÷ïÖ­›ºté"___uíÚU]»vU·nÝäããcÑÞ¥K9::¶æ4q…NŸ>­“'OêÔ©SÊËËÓ©S§tòäIåçç›ÛóóóUPP ÊÊJó¸nݺÉßß_ ×!Cäïï¯àà`Ýpà ò÷÷·á¬°=!àªóôôl4,"Iuuu*((ÐñãÇ•““£ãÇ›ƒ'OžÔ?þhÞ.//·ëíí-///Fó£sçί/}ÏÝÝ]:uj©_ÊÊÊTZZª¢¢"óãÌ™3¯/}ïÌ™3:uê”EÐÇÞÞ^]ºt1|ºvíª°°0uëÖM¾¾¾ R@@€üýýÕ¡C΀¶@°9ƒÁ ___ùúúêöÛo·Ú'55UK–,QBB‚JJJ4`ÀM™2E¥¥¥æpBýsNNŽE@áüùóV÷éîî.WWWs@ÄÕÕU®®®òôô”›››\]]åáᡎ;ÊÞÞ^nnnrrr’ƒƒƒ<<<$I:u’\\\äìì,;;»a£ÑØèÜëÇ[sîÜ9UWW[}¯´´Ô"lqá•——«¦¦F%%%ã+**TVV¦ºº:K’JJJTVV¦²²2™·KJJtîÜ9?Þüž5ŽŽŽVƒ7æàÍ¥«¹tíÚUƒ¡Ñsš@h³ÊË˵aÃÅÇÇkûöíò÷÷×´iÓôÄO(((¨Ùû©¬¬´Œ”––êìÙ³*--5¯~qöìYsâìÙ³:qâ„Ο?¯óçÏ«¸¸Xuuu í…£££ÜÝÝ%ý¿Š‡‡‡\]]åææ&OOOyyy)((H;v´xÏh4šC3?ê÷lƒ@hsRSS•˜˜¨%K–¨´´Tqqqúøã5bÄÙÛÛ_ñþœœœäãã#Ÿ¯µ~óçÏ«²²RÕÕÕ:wîœùýªª*•––Z{ñjÖ8;;ËÅÅÅê{:t««kƒ×ƒAžžž’d^Ñ\{„€6¡¤¤D«V­R||¼RRR¦Ù³gkÒ¤IêÚµ«­Ëk”Ñh´xh B€M¥¤¤(>>^ï½÷žªªª4jÔ(ýãÿн÷Þ+ƒÁ`ëòÚ$! ÕkÍš5zíµ×´ÿ~…‡‡ëÅ_Ô”)SäååeëòÚ<! ÕÔ¯²bÅ ÕÕÕiäÈ‘zå•WcëÒÚ!àª:qâ„V­Z¥%K–(55U&“I¯¾úª|ðAyxxغ<€v‰@hqµµµÚ±c‡âããµnÝ:¹ººjüøñJLLTŸ>}l]@»G ´˜¼¼<%&&êÍ7ßÔÑ£Ge2™ôßÿþW?ü°ÜÜÜl]À5ƒ@øE.^ ä£>’‡‡‡ÆŽ«™3gªwïÞ¶.àšD ü,999z÷ÝwõÚk¯)''Gýû÷×âÅ‹5qâD¹¸¸Øº<€kÐl•••úøã• Í›7«K—.zôÑG5uêTuïÞÝÖå\7„€Ë:räˆÞ~ûm-[¶L§OŸÖàÁƒµråJ=ZŽŽŽ¶.àºC XUQQ¡õë×+>>^Û·o—ŸŸŸ&Mš¤ßþö· ±uy×5!À¡C‡ôÎ;ïhéÒ¥*..Ö=÷Ü£Õ«Wk̘1rpà«€¶€om€.\¸ 7*>>^IIIêÙ³§fΜ©É“'+00ÐÖåàB¸Ž¥¤¤(>>^+W®Tee¥F¥mÛ¶éÞ{ï•Á`°uyh®3gÏžÕêÕ«õÆoèÛo¿ÕM7ݤ^xA=ö˜ºtébëòÐ B¸NÔ¯òî»ïª¦¦F±±±š7o«´CB¸†kÍš5Z¼x±¾ÿþ{EDDhΜ9š:uª:wîlëòð3àSWW§Ï?ÿ\‰‰‰JLL”£££xà-^¼XwÞy§­Ë@ À5¢  @ï¼óŽ–,Y¢ŒŒ ™L&ýûßÿÖC=$www[—€D €v¬¶¶V;vìP||¼Ö­['WWW?^|ðn½õV[—€«„@íPnn®V¬X¡7ÞxCÇŽ“ÉdÒÿû_M˜0A®®®¶.WÚ‰ššíܹSñññúè£äáᡱcÇêÉ'ŸT¯^½l]ZÚ¸ôôt­X±Bo¿ý¶rss5xð`½÷Þ{Š‹‹““““­Ë€   ª¨¨Ðúõë¯íÛ·«[·nzä‘G4mÚ4…††Úº<ØÚÇkÙ²ezûí·UXX¨ÁƒkõêÕ3fŒø³>~Â7GØXyy¹6lØ`^ Äßß_=ö˜¦OŸ®àà`[—€6ˆ@6’––¦„„-]ºTçÎÓСCµzõjýêW¿’½½½­Ë@F €VtîÜ9­\¹R úüóϦgŸ}V“&MR×®]m]Ú !´‚””ÅÇÇë½÷ÞSUU•F¥mÛ¶éÞ{ï•Á`°uyhg„p•œ={V«W¯Ö믿®ï¾ûNááázñÅ5yòdy{{Ûº<´cBhaõ«¬X±BµµµŠÕ¿þõ/ÅÄÄØº4\#„ÐŠŠŠôþûïkÑ¢E:xð L&“þþ÷¿ë‘G‘Ñh´uy¸Æàgª­­ÕŽ;¯?þXÎÎÎzàôÎ;ï¨oß¾¶.×0!\¡üü|%$$(>>^™™™2™LZ´h‘~øa¹¹¹Ùº<\„Ð ¯²nÝ:¹¹¹iܸqš1c†n¾ùf[—€ë š››«+Vèõ×_WVV–L&“þûßÿjâĉrqq±uy¸Nà•••Úºu«µvíZuéÒE>ú¨¦L™¢=zغ<€@õ~üñG½õÖ[Z¶l™NŸ>­ÁƒkåÊ•=z´m]`F p]«¨¨Ðúõë¯íÛ·Ë××W“&MÒã?®n¸ÁÖåV\—~øá-_¾\o½õ–ŠŠŠtÏ=÷hõêÕ3fŒøó9Ú6¾Ñ\7ÊË˵aÃÅÇÇ+))Iš¬šš‹÷ªªªôŸÿüGþóŸÕ¯_?ýç?ÿÑøñãåîîn£j€ÖE Ú ªª*ååå)''G999ÊÍ͵|deeéĉª®®6éÖ­›üýý|èqY IDATåçç§ììlmܸQyyyª¬¬4÷ñöö–ŸŸŸ‚‚‚Ì‘   ùùù) @m"Tñý÷ßë¾ûîSaa¡ªªª¬ö)..Ö¢E‹4sæÌV®°=!ÐÊ***TXX¨üü|eff*//¯ÁvVV–ŪF£Q¡¡¡òõõU¯^½4dÈùùù™ÛBBBäææfõxEEEV‘‘‘¡={ö(''G%%%æþÎÎÎòó󓯯¯Å1.Þöõõ•Á`¸*ççÓO?Õ˜1cTYYix¹”ƒƒƒ¶lÙB ×%!ЂÊËË•——g5€Qÿ|ìØ1ÕÖÖJ’Í«vøúú*22R±±±Œ   98üü?çFFEFF6ÚçÂ… T’’’”‘‘¡ââbsÿ¦B#õÏ!!!²³³»¢Z—.]ªÇ\’Ìç¨1ÕÕÕÚ²e‹rssåïïEÇÚ;!ÐLÖB—†' TWW'Irrr’———90a2™„'‚ƒƒeoooã™I... Uhhh£}ššRRÒeço-ìQ$0™Læm???uïÞ]žžž6œÑµ¯C‡òóó“ŸŸŸL&“Õ>UUU:uê”Õkš››«={ö(;;[ÕÕÕæ1F£Ñ|/¾¦õÛêØ±ckM¸ª„h“š Ôo_.M  rtt¼lhDj:ôÙgŸ]6té6 ´B´ºŠŠ Z yÔogee©¦¦Æ<Æh4šoÜ·ö ‘››› g[02™L— 4*JIIQNNŽJJJÌýëC#M­6âëë+ƒÁÐS¬" E•——+//Ïê øõÏÇŽSmm­¤ŸV‚ðöö6߀©ØØX‹ðƒ‚‚äàÀŸ3ñóFFEFF6ÚçÂ… ”’’’”‘‘¡ââbsÿ¦B#õÏ!!!²³³k)à:Ä7¨šÍÚMó—Þ<_PP ºº:I’“““¼¼¼Ì7Ì›L¦7ÏËÞÞÞÆ3ÃõÎÅÅE¡¡¡ m´OýçßZØ)))鲟ká>ÿø¹„Ôô ùùù—]!!&&†pMkNh¤©rRRR´qãÆ&Wȱa…XÃ7HÀu ¨¨Èê êõÛ999*))1÷¯{Ôߘ©‰'Z´ùúúÊ`0ØpV@Ûãìì|ÙÐHee¥NŸ>mõßcjjª’’’”••¥ššó£Ñh¹4<"77·Ö˜"Ú!@;WTTdqSù¥a¬¬,?ÞÜÿÒ°‡Éd²¸Ñ¼{÷îòôô´áŒ€k›“““üüüäçç'“ÉdµOUU•N:eõßtff¦’’’”­êêjó£ÑØè*#¾¾¾ TÇŽ[kš¸Ê„mTS7„×o_î†ðèèhnÚ!GGLjF¤¦aŸ}öÙea—nh?„6PQQ¡ÂÂB«!úí¬¬,ÕÔÔ˜ÇFóÛÖÂ!!!rss³á¬ðK 2DÛ¶m³uhGŒF£L&ÓeC#…ÊRRR”““£’’sÿúÐHS«øúúÊ`0´ÆÐ!@ +//W^^žÕ°ëŸ;¦ÚÚZI?­àíím¾;22R±±±7`ÉÁáêÿ9ïâ¼ëêê®úñZJ}Ýí©fkê?WÊ`0´é¹·µësiÁÙÙY^^^êÓ§~ýë_롇’“““Õ±úç?ÿ©O?ýTùùùrqqQxx¸¬ñãÇëæ›o–ôÓ\õÚk¯éÇTYY™ÂÂÂtÿý÷롇R¯^½$I;vìТE‹”œœ¬ššõìÙS3gÎÔ£>Úb £Ñ(£Ñ¨ÈÈÈFû\¸p¡Ñ€ZRR’222T\\lqÎ Ô?‡„„ÈÎήE怆 umå8øÿÅÇÇkÚ´i¶.«¬Ý4}éÍÓæß;tè Î;7ú+û~~~ –½½½göÿ´õpAcÚkÝ-¡=̽­Õxq=.\PAA¾þúk½õÖ[ÊÏÏ×ÚµkÕ³gO‹1[·nÕ_ÿúW½ð 0`€œœœ”ŸŸ¯7jΜ9:{ö¬yŸsçÎÕ7ß|£—_~Yaaa*//×·ß~«ßýîwúî»ïÌý ƒn¾ùf-]ºT½zõÒ?þ¨3fhèСš3gN랔˨ÿÿ¯±ÕF.ýÿÏÉÉI^^^íêÿ?-/%%E’š\ÉÐ4ƒÁ Õ«Wkܸq–íB´5B¶ÒÔ/äççç_7¿ßÖnÜo®öZwKhsok56UO||¼^~ùe}÷ÝwêÔ©“$)''G£GÖÎ;åááÑ`LRR’† bÞ§8 ®]»ZôKKKSdd¤E dÿþýæ•E¤ŸV!‰ŽŽVAAA‹Ìµ5ýÒ’¬…Gåèèhã™ø¹„À/×X ÄÁFõ­ª¨¨¨É_µÏÉÉQII‰¹}Ø£þÆäÈÈHMœ8Ñ¢Í××WƒÁ†³jgÏžÕܹsõñÇ+//OF£Q÷ÝwŸžxâ Ý~û펫?7ÙÙÙš9s¦vìØ!WWW 2D .”———EÿƒjöìÙÚ³g$é®»îÒ¼yóÔ«W/‹~©©©zöÙgµ{÷nÙÛÛkðàÁZ¸p¡Õ<¨çž{N»wï–$EGGë•W^i°ÏÆjOOO׳Ï>«;wšÃ@õ7ò7wß×kgg§þýûëÕW_Udd¤ÅþêyqH¡9ç¾~ÜÅŸÅÉ“'kéҥͮójÍ÷r×Çšæ|®ô³u¥¦M›¦””½òÊ+úË_þ"IZ°`¦OŸn5 "I111×®¬¬Ì&¹XDD„E?k¡///UTTü¢9ØŠ³³³BCCÚhŸÊÊJ>}ÚêÿÇ©©©JJJRVV–jjjÌcŒF£ÕÐ]}[HHˆÜÜÜZcŠm´{EEEV…¾¾-++KçÏŸ7÷¿4ìa2™,n4îÞ½»<==m8£¶åÑGUŸ>}´wï^yzz*--MO>ù¤úõë×äŠuuu2 š2eŠžyæ½ûî»***ÒÓO?­Y³fiÙ²eæ¾ééé>|¸^zé%-_¾\’´~ýz 6L»víR=$ý´rÂðáÃ5wî\-_¾\vvvÚ²e‹xàÇOOO×!Cô§?ýÉÜwóæÍŠ‹‹Srr².[ûôéÓ5wî\½ûî»Úµk—FŒqEû¾´^{{{mÛ¶MãÇ7çÒc^鹯gíZ4·Î«5ߦ®5Íý\Égëçš:uª{ì1s dË–-zòÉ'›=>66VøÃôÇ?þQF£ñŠŽý—¿üEÑÑÑW4¦=qrr’ŸŸŸüüü]1 ªªJ§N²úzff¦’’’”­êêjó£ÑØè*#¾¾¾ TÇŽ[kšW¡®©olÀâãã5mÚ4[—hšº!¸~›‚[Þ¥áeee©sçÎæ¶£G*44´É@Hý¾Ö­[§¸¸8sÛáÇ5xð`åææšÛ&L˜ Ûn»MO?ý´Åø èÛo¿Ubb¢$iâÄ‰ŠŠŠjÐoùòåš4i’E=&LÐÍ7߬ٳg[ô}ûí·µÿþË®Za0´sçNÝ}÷Ý ÞkwÕªUzðÁœ¿Ÿ{î „\É9¸šóµv}¬iîç ¾Þæ|¶ÓØ9«wþüyùøø¨´´T’äêêªââb999]vßÒO+„¼ð JHHP¿~ý4räHÅÅÅÉßßßjÿììl%''kÑ¢E:zô¨¾üòKuïÞ½YǺžýÒ@à¥Û–—’’"IÀ—g0´zõj7β@€¶†@\***TXXh5äQ¿••¥ššó£Ñhqãî¥7󆄄ÈÍÍ͆³º6\z£üСCUVV¦_|Q111rphþÂÃAgΜ±X!¡¢¢B...ª­­5·uëÖM_}õ•‚ƒƒ-Æ=zT P~~~“ý þ?öî<¬Ê:ÿÿøë°¯âQFޏ&š–Û¨é@:šk–-j6£VZù­l™šùZ6Ö43YMY?MrfÊ-“Æ\Qs̰rÅrdpA@ÜðüþàËÎð|\×¹Î9÷ù|îûý¹¹…ë’ûÅ[ÁÁÁvuW6öøñã8p j`õ@ýF n\eƒêp“Ðܹs•œœ¬””MŸ>]K—.Õú裫&ç>44T êÑ£‡¶nݪ_ÿú×ÊÈȨµsp½ë­êëãHM¯ƒÚvñâEåäähçÎZ¸p¡rrr´iÓ&ØÆ„‡‡ëõ×_WLLŒ~÷»ßiÀ€òööVFF†–-[¦?üPëÖ­³Ûï„ ôÇ?þQ;w–«««~üñG½ð zôÑGíÆõíÛW&“Iß|óM­±¡ÊË˳ y\ý³"--Mçγ/{”ÿLˆŽŽ¶ûYѦM‡€úÈdåÏŽ¨g,X ©S§]à&r£7_ýº1ß|å_¼/ÿ¯ÃÍ›7ëý÷ß×öíÛUXX¨ÐÐP5J¯¾úªüýý¯i_Umß¿¿žþymß¾]’Ô¯_?ýñT—.]ìö›””¤çž{Nÿú׿d2™Ô§O½ýöÛŠŠŠª°ÏÇë7¿ùâããURR¢N:é7¿ùM…NUÕ~õ>¯ußWÖëââ¢èwÞQûöíí:àÜȹ_±b…^xá;vLááázë­·4bĈ×YWë­îëãHM®ƒk½¶®võz===Õ¼ysuïÞ]÷Üs|ðAyzz:œ{ðàAÍ™3Gñññ*((Ppp°bccõ /¨M›6¶q;wîÔâÅ‹µuëV¥§§ËÕÕU;vÔ¯~õ+MŸ>Ý®†Þ½{ËÅÅE;vì¨òÜ4&UË_n‰‰‰’TiP=“ɤeË–é¾ûî³ßN @}C Pòòò*½©8++K™™™:sæŒm¼£ÐÈÕ7W¸±¨‰¤¤$ 6L©©©F—8Ý¥K—têÔ©J¿ggg+--Í.0e6›«ü~l±XäëëkàªT†@ܸÊ!nÕ8•Ùl–Ùl¶u)päÂ… •Þ |ðàA%''+??ß6¾<4âè/Ò—?[,¹¸¸8c‰¨§L&“ÞyçMœ8Q>>>úñÇõÄOhúôéF—Ôº‹/*++ËaÈ£ü955U—/_–$¹»»+00Ðö}3**JÇ·û¾&wwwƒWPÿþ···"##YéG¡‘òçøøxeee)''Gåy===Õ¬Y³ ‘+_GDDÈÕÕÕYË„“­Y³Fo¿ý¶~ûÛßÊÅÅEíÚµÓ“O>©‡~ØèÒ€kRþý¯ªnKW~ÿóððPóæÍmßó¢££ùþP‹„× &¡‘ªþB~bb¢Ö¬YSå_Èw —›ÿw36l˜† ft@•ªê”]m‡¤˜˜:$8¿Aj™——Wµ¡‘¢¢"åææ:¼;))IñññJKKSii©mŽÙl¶ Š\±X,òõõuÆÜDòòòªìê‘™™©3gÎØÆ; {L˜0ÁîûMpp°L&“«Àžžž QHHˆ¢££Ž)..ÖÉ“'•]áî””ÅÇÇ+==]%%%¶9f³¹Ò.#ÁÁÁ S“&MœµLu,//Ï.äqõ÷Š´´4;wÎ6¾<ìQþ=!::Úî{E›6mÔ´iSW€š"ÔSîîîÕ†F¤ªoÿæ›oª½!üê×ܯª@Xùëêa}ûö%Ѐnrf³YÑÑÑÕ†F*»©<11Q™™™:sæŒm¼£ÐÈÕá‘àà`™L&g,hP.]º¤S§N9ü÷Xþ:--M¥¥¥¶9f³ÙöoÏQØÃb±È×××ÀUÀÙ„€Ùl–ÙlVTTT¥c.\¸Pé êTrr²òóómãËC#ŽºŒ”?[,¹¸¸8c‰@½pñâEeee9 y”?§¦¦êòåË’Ê:ÚþÝDEEiøðávÿ®ÂÃÃ忯çÀ¿A IòööVdd¤"##+ã(4Rþ¯¬¬,åääÈjµJ’<<<Ô¼yó ‘+_GDDÈÕÕÕYË®[ùõ_Y·ê®ÿèèh®Ô!j¬&¡‘ª:$$&&jÍš5UvHp¡CêZUr²³³«íC‡8¿AP«¼¼¼ª )77×á øIIIŠWZZšJKKmsÌf³]PäêðˆÅb‘¯¯¯3–ˆ›L^^^•]=233uæÌÛxGa &Ø]oÁÁÁ2™L® Nçéé©…„„(::Úá˜ââb|¸Ýy —›ÿ \‰ß €$oooEFF*22²Ò1ŽB#åÏñññÊÊÊRNNެV«$)ÊÝ]}}•ЦÃ.#!!!Šˆˆ«««³–yCªZù¶+×ïáá¡æÍ›ÛÖ}S¯¨O„@ Õ$4rñâEeefJù‹"æÏWN@€þÔ§²rr”˜˜¨5kÖTÙ!ãêÀ„³:dTÕ!%;;»Ú)111tHœˆ@Ô"¯œEN*mß.=û¬BgÏÖ;vcŠŠŠ”››ë0€‘””¤øøx¥¥¥©´´Ô6Çl6ÛE®X,ùúú:¬)//ÏaÈ£üuff¦Îœ9óß58{L˜0ÁîxÁÁÁ2™LusT‹@Ô«Uúè#éÙg%‹Eúî;é¶ÛõôôTHHˆBBBípLqq±-¬‘‘‘¡¬¬,¥§§ëرc:pà€¾úê+?~\%%%¶9AA Uh`¨òòò”™™©¬¬,]ºtÉ6¦¼Ixx¸Ú´i£~ýú)""B!!!jÕª•ÂÂÂäççW«§@í#7*=]úõ¯¥¯¿. „¼úªäéyC»twwWDD„"""*SZZªœœeddèØ±cú{‹¿+þÖxuœÖQ:t¨-äªV­ZÉÛÛû†êP?€±b…ôè£Rp°ôí·Òí·;íЮ®® Uhh¨$i‡v(JQúÇ?þá´ÃÅèঔ“#)Ý¿4a‚ôÃN ƒ8’©LµR+CkàBàZ­X!uî,8 mÝ*½û®äéitUÊT¦Âftœ€@ÔÔ‰Ò˜1Ò¸qÒ=÷HûöIýû]•M†2è4nF7…+¤iÓ$??iófià@£+²SªRå(‡@ÐHÐ!ª’—'MœXÖdÌiÿþz‘¤le«D%B€F‚!P™µk¥)S$77iãF)&ÆèŠ*•©LIR˜Â ®€3Ð!®VP =ú¨4l˜Ô·¯´gO½ƒHR†2ä"+ØèR8BàJë×—u))‘ââ¤#Œ®¨F2•©–j)w¹] ' CHÒ™3e]A†•z÷–¸i RY $LaF—ÀIè›6I“'K/JŸ.mtE×Î…A IDAT,S™ U¨Ñep:„h¼ÎŸ—^|Q2DêÙSJJº)à ’tX‡ÕVm.€“Ð8íØ!uë&-X }ü±´|¹htU×툎¨Ú]'! q¹p¡¬+HÿþR»vÒÒ„ FWuCr”£B7£ §Ù¹Sš4IÊÉ‘>üPš:ÕèŠjÅa–$!@#B‡ ßÅ‹e]A~þsÉb)ë Ò@ RY ÄW¾ V°Ñ¥p:„hØöî•~X:zTúàiÊÉd2ºªZuX‡ÕVmeRÃZ€ÊÑ!@ÃT\,½ù¦tûíR‹Òþýe]AXD* „´S;£ËàDB4<û÷K={J³gK¯½&­_/…‡]U!4>B4%%e]Azô||¤={¤^\î…ZeU²’ „Œ›Ñ@­HJ’&M*{ž=[zî¹)—¥,Ó9!@#Óð  a+--ë -¹¹I»w7ø® W:¬Ã’D hdèàæ•œ,=òˆ´k—ôê«ÒÌ™’««ÑU9Õa–¿ü¤ £KàDãOähX¬ViÁ©kWéÌiçβ® , "•Bè4>BÜ\Ž• ’žx¢ì±kWY0¤‘JR’nÑ-F—ÀÉ„¸9”w¹õV)7Wúî;éÜÝ®ÌPt@ÕÙè28õ_Zš+MŸ^öø÷¿¥îÝ®Êp*P†2ÔE]Œ.€“P¿-Y"ué"åäH e]A<<$I&“I¥¥¥úýï/‹Å"OOOµoß^ï¿ÿ~…ÝüóŸÿT¯^½äëë+___õêÕK_}õ•³WS«耬²Ò!h„„¨Ÿrr¤#¤_ýJš6MJL”zô¨0lÚ´iºpá‚âããuúôi}ôÑGzûí·µlÙ2Û˜;wjÒ¤Izê©§”ššª£Gê‰'žÐ„ ´k×.g®ªVí×~ùË_á 7ºNæftPÁŠÒcIf³´u«Ô¯_¥Cƒ‚‚4{ölÛûè½÷ÞÓìÙ³5nÜ8IÒ›o¾©×^{M>ø mÜøñ㕟Ÿ¯?üáúüóÏën-u耨‹ºÈ$“Ñ¥p2:„¨?Ž—F–ƓƎ•öî­2 "I“&Mª°­wïÞúÏþc{ÿÃ?høðáÆ1B‰‰‰7\¶Qè€:«³Ñe0õÊRçÎe!-[¤ùó%_ßj§Y,– ÛÌf³òòòlï?®-ZT¤œœœ*Û(VYµW{ÕU].€„0ÖÉ“eÝ@Ɠƌ‘öí“î¼³ÆÓ]\ªÿoΠ  8q¢Âö'N¨eË–×Rm½‘¬då+_ÑŠ6º À8_}%uí*ýûßÒ¦Me]Aüüjý0·Ýv›þùÏVØþå—_ê¶Ûn«õã9C¢å*WuQ£K`7£ ÐåçK/¼ -X M˜ Í›'ùû×Ùáž{î9Ý}÷ÝjÚ´©bcc%I›6mÒÿþïÿjݺuuvܺ´[»ÕIä#£K`!œkÝ:iÊ©´TúòKiøð:?dŸ>}´xñb½þúëšéÕW¥™3%WW£«j´¶i›Æj¬Ñe¨ø~*²Z¥ ¤=ÊÞïÞ]Ö„0ˆar”£ÿè? F—  ÀÞÑ£ÒÀÒOHO>)}óÔ±£ÑU5z_ëk¹ÉM½ÕÛèRÔB”)ï rë­R^žôÝwÒþ ¹»]$mÓ6E+Zþò7ºõ 1ÈÉ‘,éosüyjª#MŸ^öصKêÞÝ™¢[µUwêN£ËPOºË—¥”ÒӥǗ22þûÙ•]AŽ—vî,ë âáa\½¨ U©:¤C¬ÁF— ž 4tsæHÛ¶•…?.]’&L({-QÖdÚ4)1QŠŽ6ºZ8°Aä+_õQ£KPO¸]€:ô¯I¯¼RÖ%D’Š‹¥íÛË Ÿ}&…„H;vHwÜah™¨ÚmÐ@ ”§<.@=A‡ ¡:yRº÷^Éd²ß~ù²´xqÙg?ü@¤ž+Q‰¶h‹k°Ñ¥¨G„ ‘Õ*Mš$ååI¥¥Ž?ß¿_òðpzi¸6 JP „°C hˆÞ|SZ¿^*.vüyq±´k—4ožsëÂ5Û  ²È¢vjgt)ê!@C³c‡ôòËÒåËU»|Yzî9éðaçÔ…ë²Q5DCŒ.@=C hHrs¥±c%“©êq&“äî.IŸ}æœÚpͲ”¥D%j¨†] €zÆÍèÔ«Uš8±,RZZñsww©¸¸ì¹gOiÈé¿n¿Ýùµ¢F¾Ô—ò’—~¡_] €z†@½¼¼77WEEEöQ–(H˜ÍfùùùÉl6+<<\þþþjÚ´©\]] é¿:®îÒqewOOOùø8îÎPUò®#W+..ÖÙ³g+Œ+ïNRXX¨’’[g’óçÏ«¨¨HgÏžµ ·¤¥¥Ù`Bóòtêòe}+i³¤cÒ¢Erùë_Õ¼ys»G`` š7o®-Z(((HÁÁÁ¶G³fÍjô5CÝ[«µ*V±†j¨Ñ¥¨‡„ N(55Õö8vì˜rrrlÏYYYÊÏÏ·wqqQ‹-ô³ŸýÌ^èØ±£ÃPC`` Ìf³-ØQŸTUS‹-êì¸ãÏS~~¾-8qBÇ· Øxyy)88X!!!vÏááá²X,jݺµ‚ƒƒë¬~üWœâÔOý¨@£KPÀ )..VJJŠ>¬ÔÔT=zÔþ8zô¨]§Œ–-[*$$D!!!jÓ¦úõë§–-[*44ÔöÜ¢E ¹¹ñßV×Ë××W¾¾¾ ­ñœÜÜ\[g–+Ã:ÇŽSbb¢Ž;¦cÇŽ©¸¸XRYhÄb±Ø­[·–ÅbQdd¤:tè ÿºZ^£Q¬b­Ó:ý¯þ×èRÔSüf5’ŸŸ¯ääd¥¤¤())I´½¾xñ¢¤²®‘‘‘ V=tß}÷)22R‘‘‘jß¾=Azª¼ëJçΫ———§””»Ç‘#G´qãF¥§§«¤¤DRÙuЩS'EEE)22ÒöÚb±ÈÅÅÅKºémÓ6å)O#4ÂèRÔSB`§¤¤D‡Òž={´{÷níÙ³GûöíÓÉ“'%IÞÞÞêСƒ:tè aÆiæÌ™jß¾½Ú·o/???ƒ«G]2›ÍŠŽŽVttt…ÏŠ‹‹•ššªŸ~úI‡Ò¡C‡tðàA­ZµJ§N’TÖ½¤cÇŽêÞ½»ºuë¦nݺ©k×®\7|¦ÏÔC=©H£KPOhÄJJJ´{÷n%&&j÷îÝÚ½{·8  .ÈÃÃCQQQêÞ½»î¾ûnÝrË-êСƒ"""d2™Œ.õŒ»»»Úµk§víÚiøðávŸåææê§Ÿ~ÒO?ý¤¤¤$íÙ³G+W®T~~¾\\\Ô¶m[uëÖMÝ»wW÷îÝÕ«W/´ã]Ò%­Öj½¬—.@=F  9s挾ÿþ{}óÍ7JLLÔöíÛUPP Ýzë­êÞ½»zè!EGG«Gòòò2ºd4Í›7Wß¾}Õ·o_»íYYYJLLTbb¢<¨¿ýíoz饗dµZ©¾}ûêç?ÿ¹úöí«N:5š ÒZ­U¾ò5NãŒ.@=F  ;}ú´âããµiÓ&íØ±C?ýô“¬V«:vì¨Þ½{ë­·ÞRŸ>}Ô±cÇFs³=ê…„„Øu9~ü¸´cÇ%$$hùòå***RPPúôé£AƒiðàÁj×®•×­Ïô™ú«¿Bjt)ê1! Hii©víÚ¥õë×kÆ Úµk—$©gÏž9r¤Þ|óMõîÝ[W 8¤Q£FiÔ¨Q’¤¢¢"%&&*!!AÛ·o×oû[=ù䓊ŒŒÔàÁƒ5xð` 4HþþþW^;Î霾ÒWš«¹F— ž#p“+**Ò† ´lÙ2­_¿^§OŸVxx¸¬gŸ}V111jÚ´©Ñe×ÅÓÓS}úôQŸ>}ôì³Ïª¤¤D;wîÔ† ´aÃÍŸ?_®®®êÛ·¯ÆŽ«±cÇ*((È貯ÛúB—tI÷è£KPϹ]®]II‰Ö¯_¯GyD-[¶ÔèÑ£•™™©ßþö·:xð ÒÒÒ´`Á;–0777ýüç?×k¯½¦ï¿ÿ^Ç×'Ÿ|¢½øâ‹ Ull¬.\¨Ó§O]î5ûLŸi°«¹š] €zŽ@ÀM$99YO?ý´‚ƒƒ5tèPýôÓOš5k–ÒÓÓµmÛ6=ýôÓºå–[Œ.pšÀÀ@=ðÀúÇ?þ¡'NhéÒ¥ ÐSO=¥–-[jäȑڴi“¬V«Ñ¥V븎k“6é=`t)nBê9«Õªøøx1BíÛ·×êÕ«õÌ3Ï(%%E úŸÿù…††]&`8ooo;V+W®ÔñãÇõ׿þU………ºë®»Ô¹sgÍŸ?_çÎ3ºÌJ}¢Oä#Ò(£Kp POY­V-_¾\]ºtQll¬ µbÅ 9rD¿ùÍod±XŒ.¨·üýýõÐCiË–-Ú»w¯z÷î­§Ÿ~ZaaaúÝï~§ÂÂB£K¬àc}¬ š ù] €›€zh×®]êׯŸxàÝvÛmÚ³g¶nݪ1cÆÈÕÕÕèòœ"66Öèê=ÎQÍÜzë­Z¸p¡ÒÓÓ5sæLÍ›7OíÛ·×¢E‹tùòe£Ë“$ýKÿÒúQ¿Ò¯Œ.ÀM‚@@=râÄ M˜0A={ö”«««¾ÿþ{-Y²D]»v­³cšL&»‡n¹åM™2EÿùÏêì¸Õ¹ÞõM&S-WR;ÊÏomªí0ÃÕׂ···Zµj¥áÇëã?Ö¥K—*›œœ¬©S§Êb±ÈÓÓSM›6UïÞ½õòË/kß¾}¶qV«UK–,Q¯^½Ô¼ysy{{«k×®z饗tàÀZ]ÏÕõÒK/éðáú÷Þ{õøã+::Z;vì¨ÓãÖÄ"-R´¢Õ]Ý.ÀM‚@@=±eËuëÖMÛ·o׊+´mÛ6EGG×ùq­V«íùòåË:~ü¸>ýôSµjÕJ½zõÒÆë¼G6oÞlÈqëJùy®Mµ}Ž®¼¬V«NŸ>­íÛ·küøñúôÓO­Ã‡W˜·aÃMš4IcƌѾ}ûTPP Ý»wëÁÔ¼yóìM¯¾úª–-[¦ èØ±cÊÎÎÖ;ï¼£uëÖ©K—.µºžÊ4oÞ\ùË_´oß>iÀ€š={¶aÝB T •Z©_ë׆ÀÍÉd­‹ßDÀ X°`¦NjtàTK–,ÑäÉ“5jÔ(}ôÑG pêñM&“ÃÀš5kôÒK/Ùux¨ï*[K}PŸk+WU ,Ðo¼¡={öØ®ÑÌÌL5J[·n•¿¿…9ñññеí3((Hû÷ïW‹-ìÆ>>ÊÏÏ—‡‡Gµû–Ê:„¼üòËZ²d‰zöì©»ï¾[#GŽThhhæ×µ7ÞxC¿ÿýïuøða…„„ÔùñÆj¬Ò”¦]ÚUé˜ââbµnÝZ6lPTT”m{nn®Ú¶m«C‡©E‹š8q¢ºvíªgŸ}Ö6&..N ,°uñ÷÷×±cÇÔ¤I“º[WHLL”$EGG\ ܼL&“–-[¦ûî»Ïn;B räÈmÞ¼Y¯½öš¡ar&“I...jÑ¢…î¿ÿ~edd(!!A±±±’¤øøx7®Â¼aÆiÓ¦M¶ßqÇÓ»wo1BëׯWII‰$©uëÖÕ†8®w^Môïßßî½ÅbQvv¶Ý¶øøx5ªÂÜÑ£G+>>Þö~Ó¦MÇ 2¤Â¶k=§5U“õ\¯ª:“Ô„Þ~ûm¥¤¤hâĉڹs§ºu릇zHµRãxæ™gäíím øÔ¥4¥)Nqš©ª»·¸»»kÚ´iº„,\¸PÇW‹-$Iß~û­FŒa7¦ÿþÚ»w¯í}§N4sæÌ…ƒÔoÆßiÐH%$$ÈÓÓS111F—"I¶!çÏŸ×O?ý¤… ª}ûö¶Ïssse±Xd2™ì-[¶Tjjj…ýùøø8<Ί+tÇwhúôéjÖ¬™ú÷ï¯wß}WÅÅÅUÖw½ójÂl6Û½÷ôô¬r8uê”Z¶lYanpp°N:Uí8GÛ®õœÖTMÖs½<¨6mÚØÞ‡‡‡_W¸ @÷ß¿–,Y¢ÔÔT]¼xQO=õT­Ôx#<==5xð`}ûí·u~¬wõ®‚¤1SíØ©S§jÕªU¶k­´´T|ðžxâ Û˜ŒŒ µoßÞîZjÖ¬™]héÒ¥:yò¤Ú¶m«[n¹E<òˆâââjíúà<B ’ŸŸ¯¦M›ÊÕÕÕèRj$00P§OŸ¶G®|œ?¾Æû Ðܹs•œœ¬””MŸ>]K—.ÕüðCë½÷ÞÓ“O>i÷ùÀW£}yzzªk×®š:uªÖ­[§eË–]SýŒG À ¿øÅ/äåå¥%K–]J¼òÊ+š5k–V®\©ÜÜ\jÍš5zà4kÖ¬kÚ×äÉ“•””¤¢¢"?~\þóŸíB72/44T *..ÖÆvMµUfÖ¬Yš;w®.\¨'NèäÉ“Z´h‘æÎk·þW^yEo½õ–/^¬“'OêôéÓZ¾|¹^ýõ û¬ÍsZ.^¼¨ÔÔT-]ºT111š7ož6mÚ¤€€Û˜ððp½þú늉‰Q\\œòóóUTT¤#GŽhΜ9zê©§ôî»ïÚíw„ JHHPaa¡ÎŸ?¯ÄÄDMœ8Q>ú¨³—XAZZš¶lÙ¢áÇ×Ù1.ë²ÞÓ{zD¨¹š×x^çÎÕ±cGÍš5K?þø£î»ï>»ÏgÍš¥Ù³gë“O>Qnn®Î;§Í›7kذa¶1ýû÷×ßÿþweffª¤¤D999š;w®ú÷ï_këàB âïï¯iÓ¦iöìÙÊÊÊ2¬ŽòŽÕuÖ°X,úüóϵtéRµiÓFÁÁÁš={¶Þ{ï=9Òáþí3>>^ÞÞÞ0`€š4i¢Þ½{ëâÅ‹úä“Oª¬©&ó$iîܹz衇äçç§éÓ§kÞ¼y×¼vGÛÛµk§uëÖéóÏ?Wdd¤Z·n­•+WjݺujÛ¶­m\dd¤Ö­[§•+WªuëÖŠˆˆÐ¢E‹ôé§ŸVØgMÏiMk¿–õT·O“ɤ¦M›ªoß¾úûßÿ®‡zH‰‰‰j×®]…ywß}·–,Y¢åË—«C‡ Pll¬ÒÒÒ´mÛ6uéÒÅ66!!A·Ür‹&Nœ¨ÀÀ@jÊ”)5j”~÷»ßUY_]³Z­š>}ºÚ¶m«Ñ£G×ÙqVj¥Žê¨žÔ“վʌ3ôÆohÊ”)òðð°û,**Jk×®Õ²eËÔºukýìg?Óœ9sì:ºÌž=[«W¯V·nÝäïï¯~ýú©´´TŸ}öÙ ¯ €s™¬V«Õè"àJ ,ÐÔ©S.œâìÙ³êÙ³§¼¼¼´e˻Πœë…^Ð;ï¼£-[¶¨oß¾ur «¬ê¦nê¤NúL׸pႚ5k¦#GŽ(44´* v%&&J’¢££ ®n^&“IË–-Ó}÷Ýg·!òóóÓš5ktüøq 0ÀÐN!@cURR¢'žxBo½õ–-ZTgaIZ¥UÚ¯ýzI/]óÜüü|½ù曚8q"aBŒÖºukíØ±C—.]R÷îݵvíZ£Kôôt 8P‹/ÖòåË5~üø:;–UV½¦×4VcÕE]®i®ÉdRhh¨öíÛ§?ýéOuT!€› €z ""Bß}÷F¥ÿÏÞÇGUÝÿOöu ’@6 ¨ì -*hN‚€`Q·«¥|±ZÔjAm«ð+ ]l¡Öº´ßŸÖ­E…±‚ÒJÄ…Å@2™¬ „ÈžÌï;óc’™,rCòzòÈCï3÷~Ι;É<’ó¾gÚ´iš>}ºrrrŒ. 趪ªª´bÅ 6L¥¥¥úøã5{öì zÎèúB_èQ=Úîç:9sFo¾ù¦ÂÃÃ/@u.6Bºˆ°°0­[·N[·nU^^ž.»ì2-^¼XåååF—t+o¿ý¶†ª'Ÿ|R÷ß¿öîÝ«‘#G^ðóþJ¿Ò,ÍÒ¸àçÐýèb,‹233µjÕ*ýíoÓÀõÈ#¨°°ÐèÒ€‹Vuuµžþy5J×_½&Mš¤ììl-_¾\AAAüü´A™ÊÔ#z䂟 @Ï@   ò÷÷×¢E‹tøða-^¼X/¼ð‚’’’4wî\íܹÓèò€‹Faa¡yä 0@÷Þ{¯FŽ©½{÷ê¹çžS¿~ý:¥†F5j¹–k†f(U©rNÝ€.Ìl6ë±Ç“Ýn׋/¾¨¼¼<?^#GŽÔÓO?­ÜÜ\£KºœŠŠ ýõ¯ÕôéÓ•””¤^xA‹-R^^ž^|ñE1¢SëyQ/ê }¡_èz^Ý€‹@@@€n¾ùfíÞ½[»wïÖUW]¥U«V)99YW\q…Ö¬Y£ÂÂB£Ë SUU¥×^{M7Üpƒ¢££u×]wI’^~ùeÙívýüç?Wtttç×¥*-Ó2e(CÃ4¬ÓÏ û"p‘;v¬þð‡?èÈ‘#z÷Ýwuùå—ë‰'žPBB‚®¸â -[¶L»víRCCƒÑ¥TNNŽž}öYÍœ9S}ûöÕ¼yóTQQ¡ßþö·*))ÑÛo¿­›nºI†Õ¸B+tR'õsýܰtO~F€sãçç§É“'kòäÉúÃþ -[¶èwÞÑK/½¤'žxB‘‘‘²X,š]Ó§O—$Ùl6Y­VY­Výä'?Ñ©S§£ñãÇkܸqJMMÕØ±cåïïopå€wÅÅÅÊÌÌÔÎ;õÑGé“O>Qmm­.¿ürÝpà ²X,ºæšk ]¤%?ÓÏÔ[½õcýØèRtCBº¡äädedd(##C555Úµk—>úè#íÚµKË–-Syy¹ÂÂÂtÅW誫®Òرc5jÔ(ÅÄÄ]:z¨Ó§OëË/¿Ôž={´k×.íܹSòóóÓèÑ£uÕUWiñâÅš0a‚úöíkt¹­úLŸée½¬¿éo V°Ñå膄tsš8q¢&Nœ(IjllÔþýûµsçNíÚµKýë_õøãK’¢££5jÔ(5J£GÖ¨Q£4xð`ùøøÙt3%%%úì³ÏôÙgŸiïÞ½úüóÏuøða566Êl6ëÊ+¯ÔÂ… 5nÜ8}ç;ßQ¯^½Œ.¹]rh‘é;úŽæj®Ñå覄ô0>>>>|¸†®{î¹G’tâÄ ×ýÏ>ûL›6mÒªU«T__¯ 6L—]v™† ¢!C†è²Ë.ÓÀåïïopoЕ(++KYYY:xð ²²²´oß>•””H’âãã5räHÍ™3ÇDJNN6¸êó·Në´û¿ÿL2]€nŠ@Ô»woMš4I“&Mrí«©©Ñ¾}û´wï^íÛ·O_}õ•¶mÛ¦¼¼<9ùùù)99Ù>>ŠUbb¢’’’\A‘ÄÄDÅÆÆªÿþ 5¢[h‡ÚÚZ•””¨°°Pùùù®à‡ó+//OÕÕÕ’$%$$hÈ!š0a‚.\è DEEÜ“ÎsŸîS¤"µLËŒ.@7G íÒ«W/=Z£GnöXii©[` 77Wv»]™™™ÊÍÍUUU•«mHHˆú÷ï¯èèh 0@ÑÑÑ®íþýû+**J}úôQŸ>}äçǯ±:Ryy¹Ž;¦ÒÒR=zT………:zô¨ ܶ¿þúk×süüüÔ¿W°çÊ+¯t ûÄÅÅÉ×××À^o£6ê-½¥-Ú¢…]€nŽ¿¤ ÃDEE)**JcÆŒñøøÑ£G]+N”””¨¨¨ÈõßÇ«¨¨HGU}}½Ûó"##Õ·o_W@¤éWDD„BCC¦°°0™Íf………)44TÁÁÁÑõNÕÐРS§NéäÉ“ª¨¨ÐéÓ§UQQáÚwêÔ)•––êØ±c:~üx³¯¦ã¥~ýú)..NýúõÓ·¾õ-W0ǹ?66–`N Né”~¨j(MiF— à/¸è4ÑÑÑŠŽŽÖÈ‘#½¶illÔ×_­ÒÒRW€¡´´Ômûøñã:|ø°ëÿËËË›…œüüüÜB"Îm???…„„( @½zõR`` ‚‚‚¬€€…„üÿ‚ƒƒäõØMÕÔÔ¨²²²Ù~gé®®N§OŸV}}½***ÔØØ¨òòrIRYY™¤oVóhllÔÉ“']ÁOÇvŠŒŒTxx¸úôéã ÑŒ9²Yˆ¦oß¾ŠŠŠRtt´½m³TKU«Z­Ò*£KÐC@—âãã£~ýú©_¿~íz^UU•***TQQáZ%ù]QQ¡òòrW¸ÂùßŠŠ Õ××ë믿V]]*++USS£êêjUUU¹Žíl×TÓvN¾¾¾ ÷XgDD„|||ÜÚùøø(""B’d6›%I‰‰‰òññq…WÂÃÃ]+ „……)""Bnû¼Ö»zWë´Nÿ«ÿUõ1º=t ÁÁÁ Ö%—\bÈùM&“^}õUÍ™3ÇóÃ_ëkÝ¡;t£nÔMºÉèrô >F£F5j¾æ+D!ú“þdt9zV€s°R+µMÛ´C;®p£ËÐðB´Ó'úDË´LOëiÕX£ËЀv(W¹æj®&j¢–h‰Ñåè¡„@;,ÔBU©J/é%™d2º=”ŸÑÀÅb­Öêu½®wõ®.Ñ%F— c…h«¬z@è)=%‹,F— ‡#­ÈU®æižæj®ÔƒF—B %ªÐ ÍÐ Ðz­7º$ù]tUjÔ|ÍW©Jõ‰>Q/õ2º$D ¼zLi³6ë}½¯þêot9àB к]·k‰–èÝat9Ð 8Ë—úR³5[³4KÿGÿÇèrÀ#!ð_¹ÊÕdMÖhÖ zA>ü @Å_3@R©Ju­®U_õÕ[zK 4º$ðÊÏèÀh•ªÔtMW½êµMÛ¡£K€УթN³5[9ÊÑGúHýÔÏè’ UBôXõª×ͺYëcmÓ6¥(Åè’ M„è‘Ô ùš¯ÍÚ¬MÚ¤ÑmtIÐfBô8 jÐíº]oÿ÷ß5ºÆè’ ]„èQÕ¨;t‡ÞÐz[ok’&]´=F£u§îÔßõw½¡7ô=}Ïè’àœÐ#8äнºWÿWÿWoè MÓ4£K€sF @·× ý@?Ðÿêõ–ÞÒTM5º$8/BtkµªÕ-ºEï轡7ƒè„è¶Î茾¯ïë#}¤ Ú ‹,F—‚@€né¤Njš¦é+}%«¬«±F—†@€n§D%š¢)úZ_k»¶k˜†]t(!º»ìJWº|ä£ÝÚ­xÅ]t8£ €Žò¹>×8S˜Â´C;ƒè¶„èÞÕ»ºZWëR]ªôúª¯Ñ%ÀC ÀEïÏú³fh†fk¶6k³ÂntIpAù]p±ÉÍÍÕž={šíß½{·L&“k»_¿~ºúê«;³´Ç!‡×ãzBOèçú¹–i™L2µþD¸ÈÚiÅŠZ·n]³ýk֬њ5k\Û‘‘‘*++ëÌÒz”ÕèÝ¡7ô†^Ô‹ºU·]t£ .6Ó§Ooµ¿¿¿fÌ˜Ñ ÕôLÇu\iJÓ»zWïé= zVÚ)==]*//÷Ú¦®®N·ÜrK'VÕsìÓ>ÍÔLIÒ.íÒ¥ºÔàŠ ó±BÐNþþþºé¦›àµMdd¤&MšÔ‰Uõ oêM]©+Õ_ýµ[» ƒè±„ç`Þ¼yª­­õøX@@€n½õVùù±@oGqÈ¡Z¡u£nÖÍÚª­ê«¾F—†á/ÒÀ9¸æšk£#GŽ4{¬¶¶VóæÍ3 ªî©BZ Ú¤MZ§uº[w]ŽB€s`2™tË-·(  Ùc±±±ºâŠ+ ¨ªûÉV¶®ÐúHé=½Gþ‹@pŽæÍ›§ÚÚZ·}Z°`L&“AUuïè}[ßV˜Â´W{õ]}×è’ Ë œ£o}ë[4hÛ¾ÚÚZÍ›7Ï Šº‡5ègú™¦kºfi–¶k»âgtYÐ¥ÎÃüùóåïïïÚ4h†n`E]ߟõgÑÑ™fÓ1]«kµZ«µFkô¼žW  ¨º6!Ày˜?¾êëë%IþþþºãŽ; ®¨k; ºO÷éS}ªÅZìöØvm×H”]víÖîfþ?!Ày8p FŒ!Iª¯¯×M7ÝdpE]Wj4WsÕ¨F9äПõg½®×åCkµViJÓXÕôÔH£Ë€.@pžn»í6IRjjª’““ ®¦ëzDè ªNu’$ùè.Ý%‹,z@èI=©7õ¦"ip¥Ðõù]p1¨®®V~~¾Û—ÝnW~~¾rss%I‡Ò•W^©øøx×Wbb¢  øøxõîÝÛà^g«¶jÖÈ!‡k_£U©J}¢O´]Ûu•®2°B¸¸$•––º‚yyyÊËËS~~¾ ”ŸŸ¯’’WÛÐÐP%$$(!!A)))²X,ª©©QDD„ •ŸŸ¯íÛ·+??_Gõø¼³C#ÎíØØXùùu¿_Ù•ªT·èùÈG jp{¬^õ:£3úTŸ€vè~]š¨««Ó±cÇtäÈÙl6Ùl6»¶>¬S§N¹Ú›Íf%''+&&F£FÒìÙ³•œœìÚ#“ÉÔ¦s×ÖÖª°°Ðí|6›MÙÙÙúðÕ——§3gÎ4;÷Ùç‹Urr² ¤ˆˆˆŸ íNÝ©“:Ù, âÔ¨Fݯûõ]}WÃ5¼“«€‹\ôª««U\\ì1ìa³Ù”ŸŸ¯úúzIR@@€ú÷ïï ZX,edd¸Â í°Ú\áoÊÊÊ<ÖmµZU\\¬’’9IRPP+ â)4’ __ß«ÿ|ýQÔFm”CŽÛ9äÐ<ÍÓ§úT è¤êàâE ]ž30á)4áÜv2›Ín ‹Å⶘˜({ÓœÙlVjjªRSS=>ÞRàÅjµ6 ¼ôéÓÇkhdÈ!xiÉAÔÿèZ ƒøÉOjÔWÿý7B#:¥>¸˜€¡jjjTTTä5ìa·ÛUYYéjo6›]!‡¦aAƒ)""ÂÀÞ\AAAm^e¤ihÄjµ*;;[ååå®¶Î1<{ìÎÞNJJ’Éd:¯škT£¹š«F5z|ÜO~ªW½Â¦ôÿþ›¦iŠSÜyz !¸ ÊÊÊš=Î,Øív56~ r (Œ7Îm;>>^~~üJË“ÖV©ªªòø8WÉËËSCCƒ$)00PqqqC#ÉÉÉ0`€üýý[¬ç=¢: }sL_ùÊ!‡Õ¨Á¬Ùš-‹,ú®¾+µ|,@süõ笮®NÇŽóö8tè***\íÏ^Ý#55Õãê¸0‚ƒƒ[\e¤¶¶V………Wi9pà€²²²túôiWû³_˦¡‘‚!ZºF9$IŠÐµºVÓ4M“5Y}Õ·Sú ÝxÕtU‰¦aO«J8ËE®À@RR’zõêepàM@@@‹é›Õ^<]™™™Ú¸q£rssåp8¤ÿ‘|æû(rg¤¤Qu£Û/V•±•ú<ùs%''+!!A¾¾¾ØCè^„ô`Î þž&ùçääèäÉ“®¶f³Ùm%‹Åâ¶:Dbb¢||| ì .4³Ù¬ÔÔT¥¦¦z|¼¦¦FEEEß\Oû‹u¤êˆlµß\Oïo}_ª««“$ùûû+**Ême‘³¯§””………uf÷à¢B  ›r›œß$ìa³ÙZœœï\݃ÉùhÀÀÀ6¯2Òôº´Z­­†š†F!èÉ„\¤œ뛆=œÛ¹¹¹r8’¤   ·IõãÆsÛNHH¯¯¯Á=BOÐÚ*#UUUÍÂKÅÅÅ®ÐH^^ž$}@‰‹‹óIJJR¯^½:³{Ði„tAµµµ*,,ôöÈÊÊÒéÓ§]íÍf³k"|jjªÛäø*22ÒÀÞmÜâ*#uuu:vì˜ÇЈÕjÕ¡C‡TQQájö{£ipĹ #!ð¶ ‚sŸ§Uœ؇ªŒŒ פöÈßßßàÃßß_±±±ŠõºÊHYYY³0•ÍfSff¦6nÜ(»Ý®ÆÆFIÍWÏi‰—Ÿ¿FÐõð—L€  ¬¬ÌkØ#;;[ååå®¶Î bbb”œœ,‹Åâ61=))I&“ÉÀÞ³Ù,³Ù¬¡C‡z|¼¦¦FEEEÍÞ›6›M}ô‘ìv»*++ÝŽç ˆ4 ÞRÐÎjµ6 ÚõéÓÇkh$%%EaaaÙ=- :DMMŠŠŠ¼†=ìv»*++%IþþþŠŠŠòö>^~~üZº‚ÖV©ªªòø½ß¹ÊH^^ž$IŠ‹‹óIJJR¯^½:³{@·ÅÌ  ºº:;vÌkØãСCª¨¨pµwÞ!>99Y©©©ï肃ƒ[\e¤¶¶V………W‡:pà€²²²túôiWû³†4 $''Ël6wV×€‹zowwwîótwwç$]‹Å¢ŒŒ îîð(  ÅÀˆôÍ*SMöØl6effjãÆÊÍÍ•ÃáÔ|•©¦¡‘„„ùúúvV÷€.‹@Ý€s¢­§É¶999:yò¤«­Ùlv›\k±XÜ&Ü&&&ÊÇÇÇÀÞº³Ù¬ÔÔT¥¦¦z|¼¦¦FEEEŽY­V¨®®N’äïﯨ¨¨f+‹8Ž û¬>¬ÊÊJ¥¤¤hÚ´iºùæ›5lØ0·öYYYzê©§´uëV;vL}ûöÕĉõðÃkèСnm;úzé®:ã}p¡ÞSz<\ÁÁÁ-®2RWW§cÇŽy _Z­V:tH®ögÿLnqnF"Àyhz7ò¦w$·Ûí®‰Äλ‘;'’:T®É¥ ¿¿¿Á=‚ôÍdòÎ38Ïs¡Ý‡Ã¡Ó§O+;;[6lÐW\¡W^yEééé¼#=þøãúä“O´~ýz¥¤¤¨ººZ{÷îÕý÷߯§žzÊíµÞ¼y³|ðA-]ºTO?ý´úôé£ÒÒRY­VÝpà zúé§uýõ×»ÚwÖõÒ]uäûàB½§Þÿý?&:Ÿ¿¿¿bccëu•‘²²2+veffjãÆn?×›®ÚÕ44/??~€ Çäà/Õº˜õë×+##Ãè2IßL õöÈÎÎVyy¹«­óNâÞî"ž””Ô)“ÿÑ1:r‚KÇê¬ ·ólܸQ<òˆ¾øâ‹N;§¢££õå—_ê’K.qÛàÀ :ÔUgAAÒÓÓõᇪo߾͎sôèQ]}õÕÚ²e‹]û»R_»ªÎzðZàBª©©QQQQ³ÏÎm»Ý®ÊÊJWû³WiúaРAŠˆˆ0°7Ð9233%ÉkÐ:“ɤW_}UsæÌqÛïcP=®ººZ6›MV«Uëׯ×òå˵páB¥¥¥iàÀò÷÷WïÞ½5fÌÍŸ?_k×®ÕÎ;%I‹E+W®Ô† ´gÏUTTèĉÚ³gÞ~ûm­[·NK—.Õ7Þ¨ñãÇ+99™0ÈE®¼¼\K–,Qrr²‚‚‚£Ûo¿]ÿùÏZ|žóu7™L®¯»ï¾Û­MAAfΜ©°°0EGGkþüù:~üx³cíÛ·OÓ¦MSXX˜ÂÂÂ4eÊíÛ·ï¼úe±X”••åvŽ©S§ºÎ1uêÔfçhËX´Öïýû÷kêÔ© Uxx¸&Ož¬¸Úž}“ɤœœÍž=[f³¹Y›¶ŒKee¥Ç‰×—_~¹[x`ÕªUúÑ~ä1 "},Y´h‘Ö¬YÓ⸶×矮ôôt…„„(<<\×^{­Þ{ï=×ãMûÜÒþÖÆ¬£ÆÔùœ¶\¿my8Ùív·vÙÙÙ:}ú´Û>»ÝÞâxæççkÖ¬YŠˆˆPhh¨¦M›¦ƒ6kמ~žkߥ¶_ïèú•œœ,‹Å¢Ûn»MK—.Õºuë´uëVíß¿_gΜq}øûßÿ®eË–Éb±H’¬V«V¬X¡9sæh̘1ŠŒŒTïÞ½5tèP¥¥¥iáÂ…Z±b…^zé%Y­VÙl6×j$€'¬ Ëa…tçêMïà}ö>§   ·U=šÞÅ;!!A¾¾¾ö­é*×_½F­{î¹G‘‘‘:pà€-Z¤;w¶ºAk+#¤§§kÉ’%?~¼ÊÊÊô“ŸüD!!!úË_þâj—­«¯¾ZË–-Ó 7Ü mÞ¼YË–-ÓöíÛÕ¿ÿsªáwÞÑÃ?¬/¾øBÙÙÙš8q¢üq]wÝu’¤ 6hùòåÚ¶m› Ô®±ðvΜœMœ8QË—/×u×]'___mݺU¿üå/µoß¾fÏ1™LJKKÓòåË5zôhmÛ¶MS§N•Ãáhó¸Ü|óÍŠŽŽÖÏþs™Íf¯ãt饗jãÆ®¾zrèÐ!Íœ9Ó-`p>«R:tH3fÌÐï~÷;]uÕU²Ûíºçž{´cÇŽ6§§ý-Yk·çZótý.^¼Xn×okcÔô±/¿üRwÝu—[ÈÈáp(%%E¯¿þºFŽéuЄ š«­ãRYY©Ÿýìgz饗4vìX]wÝuš9s¦âââܞ׫W/•••)00ÐÛPªººZ½{÷Veee«}m‹›o¾Y×^{­n½õV×¾¬¬,]zé¥çñ6f­=ÞžkÍÓõ›••¥I“&¹]¿-Õïí±o}ë[úË_þâ lÞ¼Y¿þõ¯õþûï{<ÆÙÇúË_þ¢Ûo¿ÝmÿêÕ«õÅ_è…^8§~zã¶ô½½×;PWW§cÇŽ¹}¾9ûóÎáÇuêÔ)W{çgœ¦¡羘˜V¢`(!pþ„¸hôÍê-­ìa·ÛÕØØ(É}uO“!ãããåççgpp±i:<==]•••zôÑGe±XÚuMµ6þĉn«V444ÈßßßuKR¿~ýôïÿ[ nÏ?zô¨&Nœ¨´ZƒSpp°âãã5~üxýô§?UJJJ‹çÈÍÍÕUW]åZU§­cá­ßÞÎsüøqEEEyœxæÌõêÕ«ÍÇò6.åååÚ¼y³6mڤ͛7+==]Ï>û¬+¬“'O¶1›Íªªªjµ¯mѯ_?}úé§Šm±]{!ÞÆ¬µÇÛ3¦ž®ßšš»]¿-Õïí±µk×*++KÏ>û¬$iÚ´iº÷Þ{5mÚ4Ç8ûX%%%ŠŽŽvÛŸ››«qãÆ©¸¸øœúéiŒÛÒ÷ö^ï@[xúœtö6Ÿ“t%BàüpÑ Òýqçk\ šN///×ã?®þóŸ:vì˜F¥n¸A÷Þ{¯üýýÛu¬¶<Öt¿¿¿¿êëë=#88ØmµŠöÖàäçç§3gÎ4 CTWW+,,Luuu’Ú>ÞÎéí<ÞžÓRíç3.gΜÑm·Ý¦ÐÐP½øâ‹’¤!C†èwÞÑ Aƒ¼>ïðáÚ>}º¾úê«6ÕØ???UVV*  Åví „´TOGiGÕäé±cÇŽé²Ë.S~~¾JJJtÝu×iÿþý­~¿÷vž¦×ñùö³­}oïõt„ÚÚZvÈJjTdd¤½p±#çÏ[ „Û~:\UU•×°‡ÍfS^^ž$Iêß¿¿kâ¡ÅbQFF†k2bbb¢BBB î EDDhõêÕZ½zµJKKõþûïë™gžÑŽ;ôúë¯_ðóGEEéÀn«\ˆs9rD‰‰‰nû9¢¨¨(×öùŽETT”JJJš­˜PRRrN5Ÿë¸„„„hÍš5>|¸kßäÉ“õÞ{ïµy÷Ýwe±XÚ}>oúôé£ÒÒÒ6­RSSã,8qâD‡ÕáÔ×Z[ôíÛWãÆÓ+¯¼¢ƒêÇ?þq›Ã'NœPïÞ½Ýö9rD}ûöumwV?;òzÚ* Àî𦬬Ìãç4«Õªââb•””¸Kg¯2â)4’ __ßÎêþ‹@ Ýœ=M"´Ùl*++sµ5›Ín-‹Ûvbb¢||| ì Ð6&“Iêß¿¿¢¢¢4wî\¥¥¥5 Ox{îùšX°`~ùË_ª¢¢BŸ}öY›ŸgµZ›Ýç­·ÞRzzºk»3ÞSRÇ^ï@G2›ÍJMMõzÇþêêj{ äçç»VÙ PŸ>}¼†FRRRÖ™Ýè„ÜÔÔÔ¨¨¨Èãä¿ââbÙívUVVJ’üýýå5ì1xð`…‡‡Ü# ãÜ}÷ÝZµj• ¤“'OjíÚµmZ)"..Nü±ÆŒ£>ø@wÝu— ÚuîåË—kÆŒjhhÐĉ íÛ·ëž{îÑïÿûsí’›eË–iâĉ ׌3d2™´aí^½ZÛ¶mskÛ–±ðÖïåË—k„ ÓôéÓåãã£-[¶èOúS»knϸÜzë­Z¹r¥† &___ú¨fÍš¥ØØX]yå•ÊÉÉÑwÞ©E‹¹µKKKÓ#<¢5kÖ(22R;wîÔÿøÇ«ÃéB]kçò>¸îºëôƒü@÷ÜszõêÕæsýêW¿’$}ï{ßScc£6lØ µk×jûöí®6ñžrž§£®w 3µy•‘¦Aa«Õªììl•——»ÚšÍæf+‹œ½””Ô!J€žÄähéö„`€õë×+##Ãè2º­²²2«z8÷åææºîdäqžs;>>^~~ÜkÝÏÙRï‡÷ß_¿ûÝï´cÇUTT(..N×_½üñVïzþÚk¯iéÒ¥***R||¼V­Zå \4=·óKÒáÇõðÃËjµª¾¾^—_~¹~øáf+´¥?Þ|ùå—úéOª;vH’®¾új­\¹RÇwµiëXxë·$íß¿_>ø >üðCùøøè»ßý®žy楤¤¨¡¡ÁcíÞêo˸ìÞ½[Ï?ÿ¼>øàåççË××W—^z©î¼óNÝwß}ÍΓ••¥•+WÊjµêÈ‘#ª««Ó÷¿ÿ}=ôÐCÍî¦ß– Ì­ûǬ|P™™™êÛ·¯î½÷^=ôÐCnmJKKµxñbmݺU•••š4i’~ÿûß+>>Þí­YGi{¯ßö¾$©¾¾^ƒÖ®]»ӬΦœÇÚ¿¿–,Y¢]»vÉápèšk®ÑªU«tÙe—uH?ÛÛ÷¶^ï@wSUUåõ3§ÍfS^^žë=¨¸¸8¯¡‘¤¤¤vÃt™™™’äuE"@ëL&“^}õUÍ™3Ç}?] sW[[«ÒÒR¯ï:¤ŠŠ W{çš[º[3t–ýû÷kÚ´i²ÛíF—âæäÉ“2dˆ~ó›ßhîܹF—Ó£üýïׯõÒK/]J‡ëª×;ЙjkkUXXèqUº#GŽ(++K§OŸvµoé³krr²Ìf³½à 8Þ!ÜÆ."çr—eçd9‹Å¢ŒŒ î²  K0™Lzæ™gtÛm·©W¯^:xð ~ô£é¾ûî3º´f"##õ׿þUóçÏWCCƒf̘¡ÐÐP£ËêÖL&“þýïkÅŠzî¹çŒ.ç¼]L×;Й\aoÊÊÊš}æµÙlÊÌÌÔÆÛµº]BB‚|}};«{èBœÞ>>ö¼Û¸q£Ö¬Y£G}T>>>|¸fΜéö9<11‘•ü@—artÕYz¬õë×+##Ãè2 q®“Õ<…F˜¬†î¨+B:Û¬Y³¯µk×¶ù9F‡:Û¹ŒQGa¬»® 5n]éõh “ɤ#Fè¹çžÓ°aÃtøðaÝwß}JOO×c=fty@›ÕÔÔ¨¨¨Èãçg›Í¦‚‚ÕÕÕIòº>ûóôàÁƒnp€î'33S’”ššjp%pñ2™LzõÕW5gηý¬]ˆÙlVjjª×‰2UUUÍ&¹9W±Z­ÊËËSCCƒ$)00Pqqq^C#IIIêÕ«Wgv@©­­Õû￯×^{ÍèRº,ƨó0Ö¯—_~Y#FŒ$1B/¼ð‚ÆG ]Š30Ý4ìáÜÎÍÍu…±‚‚‚Ü>÷Ž7Îm;!!A¾¾¾÷ ãp«X¸ˆ+99Y‹Ezúé§õÒK/iëÖ­ÊÉÉQee¥ŠŠŠ´gϽüòËÊÈÈÐСCUUU%«Õªx@iii6l˜BBBÔ»wo3FsæÌÑâÅ‹µbÅ ½öÚkÊÌÌTqq±ÑÝER^^®%K–(99YAAAЉ‰Ñí·ß®ÿüç?®6&“Éõ_ç×Ýwßíz|ß¾}š:uªÂ¦©S§jß¾}nçq>ïÀš2eŠÂÃêiÓ¦éàÁƒçÝÖÓ¾‚‚Íœ9SaaaŠŽŽÖüùóuüøñfc°ÿ~M:U¡¡¡ ×äÉ“uàÀÇÞ±c‡4a„¿ 6hܸq Rbb¢–,Y¢ŠŠ }ÊÉÉÑìÙ³e6›ÝêkéFQkµKß\CÓ¦Ms]CS¦Liv 1Ö7ÖçÛoÏoϸIm{½ýð‡?Ô< ââbmÚ´©Mç0zŒZ«=;;[iiiš>}ºl6›ìv»æÏŸ¯™3gª°°ÐÕŽ±î¼±>ß~´ôü¶Ž›Ô¶×ËoÇ;ûÜ555úö·¿­gžy¦CúÜÞ×®©'Ÿ|RãÆkµo@[UUU¹V²[¿~½zè!ÝvÛmJKKÓÀÕëÿ±wçÑqÕ÷ýÿŸ£]ÞÇ–-k$Kòx,"aq¡È¦)KH9…„°}[ªBOÛ@“†´!IÍ· ô|C¾I€4 ´$$à†m(PL/)lÃXûbawm–ôûÏ ‘F’7i´<÷ptïϽú|îÈ3ƒôyÝ÷Œ,[¶ŒsÎ9‡«¯¾š{h4J0¤ªªŠo}ë[<õÔS¼ñÆìß¿Ÿ;wòë_ÿšû·ãÎ;ïäÖ[oåÿ𩬬$ ¦{¸’$I’$I’$Iã.Ð?Ú_%I’$iœÝsÏ=TWW§»ÓVWWMMMD£Qš››iii!&¶kkk9pàÙÙÙ …‡Ã„ÃaŠŠŠÛ+V¬`Μ9i‘&ƒÙ³gSWWÇüùóû¶mÛF8NšÀRNh¾êª«øà?ÈÍ7ßœ´ÿŽ;îà7¿ù ?þñ“ÎñÀ$&HlûÛßþ–{ï½÷¨Ûî[ à¿øÿøÇû6oÞÌïþîïÒÔÔ”ØwõÕWsúé§éÿ<À§>õ©!ç=餓øüç?Ïg>óàØòj` IDAT¯_*±XŒåË—§¬ºðÌ3Ï$ªA 6Ò÷Hç5­ïW]u'Ÿ|2·ÜrKÒþþð‡¼öÚkÜyç€×z<¯õ±Žc¤ãäº 6Òóu$ß àÆod×®]üë¿þë¨ç‹ç ¾¾žçž{Ž»îº‹mÛ¶ñÒK/±lÙ²®€ô¾X,–øœ8ø³ãÛo¿ÍîÝ»mƒÁàÏŠ·—.]š²J¤É¯¦¦x¯²$I’$I’$éè|ðA.¿üòäýB$I’$M4B&¾“ÿ‡F¢Ñ(±X,Ñ6 ;ñ/S^^NF†,§»uëÖqàÀ¾üå/SUUEVVVÊvÃM®^¼x1/¿ü2eeeIû·mÛÆÙgŸMKKKÒ9Þ}÷],X0¤íš5khnn>ê¶©&qïܹ3é®å]]]äççÓ××7jÿwìØAAAAÒykkkYºt)µµµ‰öÇzý†3ܘöïßÏŒ3Žø{¤óÖ÷áÎßÖÖÆyçǦM›¯õx^ëcÇHÇK äHÎ;Òù~úÓŸrûí·óÊ+¯0kÖ¬QÏs¼Ÿ»ç.++ã²Ë.ã _ø‹/eôš.:;;innö3_}}}¢Z\NN ,Hú¼7ð3ßÊ•+™={všG$)] „H’$I’$IÒ±3"I’$iÒ02ùutt ©,2p»®®ŽÞÞ^འ„%%%CB#ñI„åååÌœ93Í#ÒXÛ½{7_ýêWùå/I{{;øÀ¸ì²Ë¸ñÆÉÎÎN´nruVVû÷ï'777igg'³gϦ§§gÔsŒEÛᎼ¸þ§jûÏÿüÏÜ}÷ÝI“æõúµ··óÅ/~‘ÇœÖÖÖÄ¿Oàˆ+MHa<®Ñh}ËÎÎNLl,???QÉk=~×úXÇq¤Ç§zìXŸ¯á¾×[o½Å¹çžËÓO?ÍêÕ«ú<Çòܶ_S_<à›*ÜÛÜÜLkkkâg#//oذG8¦¬¬ŒÌÌÌ4HÒDe D’$I’$I’ŽÝpÔ·0”$I’$éäçç'& ¦ÒÓÓC{{{ÊÐH$aëÖ­ìÙ³'Ñ> ™x8p2bQQ@`¼†§10wî\î¸ãî¸ãÞ}÷]ž~úi¾ýíoóüóÏóÐCz|AA---”——'íoii¡  `Hû¶¶6 ‡´]¸pá1µ=Z´¶¶¹«kkë¶¿úÕ¯øýßÿý¤}Çzý®¾újN8á6lØ@III¢êÅDúwu¬×èpοiÓ¦¤ ©x­“嵞ÆâùÚ¿?—]vßüæ7‡„AŽÕ‘Ë'>ñ‰¤ýÏ?ÿ<7ß|3¯¾ú*àµh¬¯õX9’ëv<Ÿ¯¸n¸3Î8ƒë®».©OÇ£JÇá>wYdrJõ9iàöHŸ“*++ýœ$I’$I’$I’4EøWI’$IÒ„  ƒTTT¤|¼««‹¦¦¦”¡‘šššï|=88²|ùræÎ;^CÓ0®¿þz¾õ­o±|ùrvíÚÅwÞ9$ÌQ\\Ì‹/¾Èé§ŸÎ3Ï<ÃÿñÓÐÐÀßüÍßpÞyç1gÎ.¹ä<òwÜqÏ>ûìïõ½ï}¬¬,.¸àúúúxä‘G¸óÎ;yî¹çŽ©íÑúÛ¿ý[>úÑ2{öl.¾øb222xòÉ'ùþ÷¿ŸÔîÙgŸ%33“sÎ9gÈ9Žåú}öÙÜrË-|éK_" Q__Ï×¾öµ£ËpßãXk4Úù/¹äz{{9ï¼óÈÉÉá¹çžã†nà»ßýnR[¯õ{ÆãZ…#¹nÇóù‚÷^O^{í5^~ùå£>ÇH÷¹‹[³f @€ 6ŒIttFª¤FG¬¤¯ða%5I’$I’$I’¤éÁ@ˆ$I’$iRÊÍÍMLvN,2‰2‰DûâÞ=;Uh¤¬¬ŒÌÌÌñÚ´‰D¸ûî»9÷ÜsÙ»w/ÅÅÅ\zé¥Üwß}Iíî¸ã>ýéOÓÔÔDiiibòøŠ+xì±Ç¸å–[ø³?û3Î9ç{ì1–/_>äûýä'?ᦛnâ†n ¯¯|ä#<þøãCª<nÛøDÛwùOµo¸ýáp˜Ç{Œ/|á |þóŸ'##ƒsÏ=—û+W&ŽýÕ¯~EUUÙÙÙÇõúÝÿýüå_þ%gœq»víbåÊ•ÜvÛmÜÿýÃŽ RWî{¤ûÖ÷òòr~ö³ŸñÅ/~‘믿žƒ²jÕ*îºë.>þñ{­Óp­uõ=’ëv4Ï×Hûn¾ùfººº˜1c©Œ×s×××GFFFʾhìtvvÒÜÜœ2ìF©¯¯çàÁƒäääPRR’øLRUUEuuubû„N`Ö¬Yi‘$I’$I’$I’Ò%Пê¯É’$I’”F÷ÜsÕÕÕé#¹`ÁB¡å+Ê U„X¸r!Kç-å¤'9!s<©úxµ 7nä /¤¶¶x/ørë­·rýõ×§­O×hüx­'¯ÁÏÆV<šê³Åà@j0L  ¤–——Ú‘4éÕÔÔPYY™æžH’$I’$IÒäxðÁ¹üòË“ö[!D’$I’4mõçõ“Σ(\DyQDŒ«XEìÐÒÖÝFsG3Û{¶#Æ[9oñêìWé | øÿÞ[ ƒC&rÜ^ºtiâŽîÒ`@€oûÛ\sÍ5̘1ƒ7ß|“Ï}îs|ö³ŸM´Ùºuk{˜~^£ñ㵞¼ç¹ÓÑëêꢩ©iذGmm-H´6‡ÃTUU%}FX¾|9sçÎMãh$I’$I’$I’4Ù‘$I’$MZÝt'‚Gºìgÿóe“Mpð’ä䜓‡î?´,üÊB:®ïHLOF£D"jkkéëë 77—ââ┡‘p8Ì’%KÈÎÎïË8­Ä9‡SùãHÚëׯçŸþéŸøò—¿LFF+V¬à¦›nâÚk¯óï=YxÆ×zòò¹;6±XlHÐc`øcàûz^^^Ò{ùš5k’¶KKKÉÊòWð’$I’$I’$I;þñ˜Ñ I’$IGàž{º:ÝÝÐ8ê ã¨B­´ÒÏÐÿ­Í#oØÇHËb“AÆq[ww7)ï"ÞÒÒÂæÍ›Ù·o_¢ýÀ;‰,[¶Œyóæ×þI’4]ôôôÐÞÞ>lØcË–-ìÝ»7Ñ~¤÷äø¶$it555TVV¦¹'’$I’$I’4y|ðA.¿üò¤ýÞžL’$I’t\tÒÉNv²ƒì<´ì´Ä÷ uàÀså“2°QN9§rêˆ¡Ž™ÌLÃ臗“““˜<:œX,–4!5>Aõ…^ ¹¹™ÖÖÖD…ŠÁw#¶bÅ æÌ™“æI’$I’$I’$IcÏ@ˆ$I’$MÃ;F y¼Ë»ôÐ3ä\yä%7‚ ¶bG1ÅÌc^F­#1Z•‘ŽŽŽ!“kãUG"‘uuuôö¾ÊÍÍ¥¸¸xØÐÈÒ¥K™1cÆxO’4ÉŃ‹ƒÃñímÛ¶Ñßß¼„øþ³fÍš¤í²²2233Ó<"I’$I’$I’$)ý „H’$IÒ8릛ìàÝCKm¼Ë»Iû¶³=±¾ƒtÑ•tŽæ3Ÿ,Húz:§ÙW@Ab}6³Ó4j¥[~~þˆUFzzzhooO‰D"lÙ²…½{÷&ÚƒÁ!•EoK’¦‡îîn‡ {lÞ¼™}ûö%Ú|©¬¬> ƒi$I’$I’$I’4y‘$I’¤c´›ÝCƒƒ÷íaOÒñdP0`YÀV°‚³9› R?æ3Ÿ4XSQvv6¡PˆP(4l•‘X,6d²o4¥¦¦†õë×S[[K__0ôîîƒC#¥¥¥deùk I𠆫2ß—ªÊTüµ¿¢¢‚êêj«LI’$I’$I’$IcÀ™’$I’4HŒÍ4°´Ð’r_#tÓt|y,!B|ÙWDA‚,bYþï™&`0H0¤¢¢"åã]]]455 ™(FÙ°aµµµ8p é|ï?04²bÅ æÌ™3^C“¤i-‹ öxûí·Ù½{w¢müµ»¨¨ˆp8LUUUÒkøÒ¥K  ­J’$I’$I’$IãÁG’$I’¦´:F u Ü×F}ô%#¼¤G aÂCö RL1ó˜—¦ÑJé•›››w gà¤ãŽ#‘Hb;. ©,2p»¼¼œŒŒŒñš$MZ477§|íF£Ô××sðàArrrX°`Aâu¶ªªŠêêêÄkïÊ•+™={všG$I’$I’$I’$)Î@ˆ$I’¤Ie{h¡…öCËÀõvÚÙÎvv°ƒw-=ô$?“™PÀ"Qphù²oà"éø ƒTVVRYY™òñ‘&.G"‘!—KJJ†„FâÁ‘òòrfΜ9žÃ“¤qÚ zÄ÷µ¶¶Òßß@^^^ÒëeUUUÒkhYY™™™i‘$I’$I’$I’¤Ãe D’$IRZõÑ—s´ÑF+­<ý;OSK-­´²ýÐÒJ+í´ÓIgÒñƒƒ'q°€‰}…&ö哟¦‘J:yyy#Véé顽½=iÒs|t$aëÖ­ìÙ³'Ñ> ©,204RTTD ¯áIÒéî±qذG]]û÷ïO´¿æ¥ {,[¶Œyó¬b&I’$I’$I’$M%B$I’$w]t±ƒĈÑB Í4»ÞN;9˜tüŒÓfPN9A‚„qg%Ö‹(J¬—PB9i¥¤tÈÎÎ&  …†­2‹ÅRNžÞ¸q#‘H„ÚÚZúúú€ä»å§ ”––’•å¯O$T¯W·Gz½ª¬¬ôõJ’$I’$I’$Išæü ¡$I’¤ÃÒAÇa‹, )¤”RŠ(b«XËZB„XŠ(b KÈ'?£’$iòÉÏϱÊHOOííí)C#‘H„-[¶°wïÞDû`08¤²Èàm¥Gww7Æ=6oÞ̾}ûí>—•••CB@Á`0£‘$I’$I’$I’¤©Ï@ˆ$I’Æ]ÔSOã¡eðz ìfw¢ý fPF!BSÌ)œ’xÄ÷-f±U=$IJƒììlB¡¡PhØ*#±XlHÈ RSSÃúõë©­­¥¯¯ZUbph¤´´”¬,¥u4†«öß—ªÚKü9¨¨¨ ºº:±‡ÉÏ7h+I’$I’$I’$Iéä_Ï%I’t\uÓÍ»¼K -D‰ÒLsb=¾ÝJ+ýôK.ó™OˆaÂ\È…Q”Ø.:´¤yd’$éhƒA‚Á )ïêꢩ©iH@!²aÃjkk9pà@ÒùV¢Y¾|9sçί¡M(±XlذÇÛo¿ÍîÝïnã×0ªJº–K—.%ðó—$I’$I’$I’$MdB$I’tØúé§…ꨣþÐÒH#uÔÑH#M4ÑJk¢}9SL %”RJU‰õ’CË"¥qD’$i"ÈÍÍM„;†30ì00è‰DÛqÁ`pHe‘ÛåååddL®Êb477§¼Ñh”úúz<@NN ,HŒ·ªªŠêêêÄ5X¹r%³gÏNóˆ$I’$I’$I’$IÇÊ@ˆ$I’:éL=âK-µ‰õ覀L2)¢ˆ2Ê(¡„òQ–ZJ(a KXÌb+{H’¤ã" RYYIeeeÊÇG LD"‘!‰’’’aC#'œp³fÍÏá%/ƒƒñ}­­­ô÷¿Wa-///©ßUUUIc)++#33s\û/I’$I’$I’$IB$I’¦‘:h¡…耥™æÄ¾Zj飀\r)¦˜0aJ(áC|ˆð€e KÈ&;Í#’$IzO^^ÞˆUFzzzhooO [ÄÑH„­[·²gÏžDû`08¤²ÈÀàHQQÀá_»»»ill6ìQWWÇþýû‡|ïTaeË–1oÞ¼c»X’$I’$I’$I’¤)Á@ˆ$IÒÑO?-´°íÐ2°ÊGÝ¡e?ïO4,¤ÒCËïð;\ÄE”Q–ØW@AG#I’t|egg ……BÃVioo§¾¾ž††êêꨫ«£¾¾žßüæ7üò—¿¤­­-ÑvÖ¬Y”••QVVÆ’%K(--¥¤¤„X,–8.þßpÇ-[¶ŒóÎ;ÒÒRÊËË)--% ‘•å¯ì$I’$I’$I’$I£ó¯Ë’$I“HŒXÊÊQ¢¼Å[‰ÀG9,`!B„ s1&LE„±’•ÌfvšG#I’4±,\¸… éì줮®Ž†††DØ£¶¶–-[¶‰D8£¾žÍÁ Ùá0¥¥¥|ä#I {”––2þüq•$I’$I’$I’$iª2"I’4tБò¸le+{Ø“h$HøÐREÕT'¶Ë(#“Ì4ŽD’$iêÉËËã„Nà„NHÝ €»î‚Ë/ߎI’$I’$I’$I’¦%!’$Iãhû¨¥–m‡–øzüënv @ˆK-¿ÏïSN9KYJ9å,a Y~”“$I’$I’$I’$I’$iÚr¡$IÒq#–²ÂG”(ÛØF?ý@r… ¸€"Š"L˜9‘™ÌLóH$I’$I’$I’$I’$IÒDe D’$éuÓM-µD‰òï$¾Æ×p€<òXÊR–±ŒU¬âb.&L8Qõc3Ò<I’$I’$I’$I’$I’4Y‘$IJ¡“Nšif#ÙĦ¤*uÔÑK/\åãB.L¬‡ SF™d¦y$’$I’$I’$I’$I’$i*2"I’¦­ì`ë¡e [x›·U>v°€L2Y„YÆ2ª¨J¬‡ 3yi…$I’$I’$I’$I’$IšŽ „H’¤)m{¡xð#¾¾“ä’Ë2–±‚œÃ9\˵,;´”QF9i…$I’$I’$I’$I’$IR2!’$iÒ릛F‰e#ÙÄ&¢‡–ml£Ÿ~²È¢”R„9…SøŸ L˜U¬âDN$“ÌtC’$I’$I’$I’$I’$é°‘$I“B7ݼÃ;I>Þæm¶²•Fé§Ÿ 2(¥”¬`%+¹ YÉJV°‚rÊÉ&;ÝÃ$I’$I’$I’$I’$I:. „H’¤ %F,QÝ#^íc#ÙÂr€ ÁDuu¬#|h9‘™ÉÌ4@’$I’$I’$I’$I’$iì‘$Iã®—^j©e3›y“7ÙÌfÞâ-ÞäMÞå]f0ƒ-Wp'rb¢Ú‡¡I’$I’$I’$I’$I’4Ý‘$Ic¦‹.Þæm6±)©âÇ›¼ÉïUûXÅ**¨àB.L¬—SNi$I’$I’$I’$I’$IÒÄd D’$³vÚy×U>Þâ-6³™È!‡å,çDNdëø<Ÿç$NâN`sÒÜ{I’$I’$I’$I’$I’¤ÉÇ@ˆ$I:l;Ø‘¨òñ:¯ó&oò:¯ó.ï0ŸùœÈ‰œÄI¬c'Z–²”,?vH’$I’$I’$I’$I’$7ÎÌ”$ICìf7oóv"üÿ% À\沜å¬bp«XE„ §¹ç’$I’$I’$I’$I’$IÓƒI’¦±=ìa+[‡?¶±~ú™ÃV°‚U¬¢šêDðc)K Hw÷%I’$I’$I’$I’$I’¦-!’$M=ô°‰Mü–ßòú¡åMÞ¤Ž:f2““8‰Õ¬æn`5«YÅ*Ê(KsÏ%I’$I’$I’$I’$I’”ŠI’¦˜ílçµCËo-›ØD=ä’KÅ¡å£|4±^N¹?$I’$I’$I’$I’$I’&!’$MR9H=õld#5‡–Ml"J€ AV±Šs9—?çÏ© ‚Õ¬&—Ü4÷\’$I’$I’$I’$I’$IÇÊ@ˆ$I“À.vño$BÙÈ«¼Jd‘ÅJVRAWs5•Tr:§SDQº»-I’$I’$I’$I’$I’¤1b D’¤ fÛ?^ã5~Ëoi¢ €E,âdNæ,ÎâOùSNædV±Šl²ÓÜñ6• IDATkI’$I’$I’$I’$I’$'!’$¥Q”h"ü_bÄÈ$“9‘S8…ÏóyNáNæd«~H’$I’$I’$I’$I’$ 0"IÒ¸i¦9)øñ2/ÓN;™dr'PI%ÃßPI%§r*3™™î.K’$I’$I’$I’$I’$i‚2"IÒ8œðÇ_ó׆?$I’$I’$I’$I’$I’tT „H’tŒê©ç%^â×üšjx•WÙÅ.²Èâ$N¢’J¾ÂW¨¤’S8…ÌHw—%I’$I’$I’$I’$I’4É‘$éìg?5ÔðÒ€¥…²Èb«¨¤’?àá|òÓÝeI’$I’$I’$I’$I’$MAB$IA3ͼÀ l`5Ôð ¯ÐM7‹YÌéœN5ÕTRÉ9œÃ<楻»’$I’$I’$I’$I’$Iš&2ÒÝI’&нìeø&ßäb.f! )¦˜Oói^à*©äû|Ÿ7xƒZx”Gù[þ–‹¹Ø0ˆ$I’¤„@ @oo/_ÿú×)//'77—•+Wr÷Ýwiûè£ræ™g2sæLfΜəgžÉüǤ¡×’$I’$I’$I’¤ÉÆ !’¤i©—^Þà ^äE^æe^â%6³™~úYÆ2ÎäL¾ÂW8ƒ38•SÉ&;Ý]–$I’4‰Üxã‰D(**â׿þ5ôGÄÂ… ¹âŠ+x饗¸îºë¸ë®»X»v-ýýý<ùä“\}õÕ<ñÄ|ðƒLó($I’$I’$I’$I™IÒ´pƒ¼Ækl`/ð"Ĉ1‹YœÂ)¬c·s;çr.‹X”îîJ’$Išä ¹ýöÛÛçž{.wÝu·ß~{"òÍo~“¯}ík\y啉vW]u»víâïÿþïùÙÏ~6îý–$I’$I’$I’$MB$ISÒð/ñ_‡–—x‰:â#|„¯óuÎá*¨ ƒŒtwW’$IÒsÝu× ÙwÖYg±eË–Äö«¯¾Êw¾ó!í.¹äþñÿq,»'I’$I’$I’$Iš „H’¦„}ìã%^JTyžç颋"Šø0æÛ|›5¬a«Hww%I’$MqåååCöƒAb±Xb»­­E‹†V(,,,¤µµu,»'I’$I’$I’$Iš „H’&¥ílçe^æ^`øü?zè!L˜5¬á;|‡u¬£œòtwU’$IÒ4”‘1z%ÂÂÂB¶oßÎ’%K’öo߾ŋU×$I’$I’$I’$IS„IÒ¤#ÆòŸDˆðÏñ&o’I&àœÃ9ü%ɇù0¤»«’$I’tXN;í4}ôQn¼ñƤý<ò§vZšz%I’$I’$I’$Iš, „H’&¤nºy‘yЧˆá×ü€Ó9K¹”oñ-Ö°†9ÌIsO%I’$éè|á _ࢋ.bÞ¼y¬]»€§žzН|å+<öØciî$I’$I’$I’$i¢3"Iš0¢D‰ZžäIv³›0aª¨â/ø Îç|æ3?ÝÝ”$I’¤ãâì³Ïæ‡?ü!÷wÇõ×_ÀêÕ«ùÑ~ć>ô¡4÷N’$I’$I’$I’4Ñ‘$¥Ív¶óÏ!ÂcÎÇ™ËÜtwQ’$I’$I’$I’$I’$Išv „H’†H¹ˆ‹¸•[ù=~l²ÓÝEI’$I’$I’$I’$I’$iZ3"I và~ÂOx™—YÀ.åRnã6Îç|²|Ë$I’$I’$I’$I’$I’& g÷JÒ4ÖA¿ä—ü ÿ“ßg;Û9Ÿóù1?æR.%—ÜtwO’$I’&¿§ž‚‡JÞWP÷ÝO?ýþ¾¥Ká¯þj|û&I’$I’$I’$Iš „HÒò_üßå»<ÌÃÌg>×ZÊ)Ow×$I’$ijyüq¸çÈÎNÞÿÔSï¯÷ö¢EB$I’$I’$I’$Ic"#Ý$›:ø>ßçNá\Î¥Ž:~Ĩ£Ž¯óuà ’$I’4®¸â½¯==Ãÿ—•W_Þ~J’$I’$I’$I’¦,+„HÒ$µƒü3ÿÌÝÜÍöpWð~À霞î®I’$IÒÔ÷¡AYÔÕ ß¦»>õ©ñë“$I’$I’$I’$iZ±Bˆ$M2µÔr37SFÿÈ?òI>É;¼Ã½ÜkD’$I’ÆÓ5×@vöð‡Ãpê©ã×I’$I’$I’$IÒ´b D’&‰7y“+¹’å,çá|ƒ&š¸“; Jw÷$I’$iú¹òJèéIýXN\{íøöG’$I’$I’$I’4­‘¤ n [¸Š«XÍj~Ëo¹ŸûÙÊVnæff1+ÝÝ“$I’¤éëÄ¡¢¡uwÃ'?9þ}’$I’$I’$I’$MB$i‚ª¥–?åO© ‚jø?â5^ãJ®$‹¬twO’$I’pÍ5™™¼/€SN•+ÓÓ'I’$I’$I’$IÒ´` D’&˜ì䝸+NäDžâ)¾Ëwy×¹†kÈ$sôH’$I’ÆÏ§>½½Éû23áÚkÓÓI’$I’$I’$IÒ´a D’&ˆnºùßb9˹û¸‹»ØÂª©¶"ˆ$I’$MTK–À™gBÆ€_³õöÂå—§¯O’$I’$I’$I’¤iÁ@ˆ$Mó0«XÅmÜÆgø [ØÂŸð'A$I’$i2¸újÞ[ÏÈ€Š‹ÓÛ'I’$I’$I’$IÒ”g D’Ò¨‘F>ÎǹŒË8ƒ3x‹·øß`6³ÓÝ5I’$IÒáúÃ?|=€k®I__$I’$I’$I’$IÓ†IJƒ~ú¹‡{¨ ‚läIžä'ü„RJÓÝ5I’$IÒ‘*(€ªª÷ üÁ¤»G’$I’$I’$I’¤iÀ@ˆ$³Ílæ,Îâs|Ž›¸‰7xƒ*ªÒÝ-I’$IÒ±¸ê*èï‡ .€ùóÓÝI’$I’$I’$IÒ4•îHÒtòù¿üÆIœÄ«¼ÊjV§»K’$I’¤twwÓÞÞN[[mmm´··³}ûvZZZhoo§½½––:¶oçeàæÿú/^YµŠ… ²xñb Y¸p!‹-bñâÅIë³fÍJ÷ð$I’$I’$I’$I“˜I»ÙÍgø ð7qÿÀ?CNº»%I’$IÓÒÞ½{‡:¶oßN{{;­­­‰õ¶¶6b±XÒ±ùùù,\¸¢¢¢Ä×SO=•E‹ñø¼yœqàáCçmmmåþçë»vír®A‘øù-Z””Ä×.\HF†Å~%I’$I’$I’$Iï3"IcìU^åR.¥Ÿ~žæiÎã¼twI’$I’¦œX,Fss3±XŒ–––ÄúàíÆÆFöìÙ“tl^^¡Pˆ¢¢"‚Á åååœ}öى펪]]]ìØ±#e¿âë[¶l¡¥¥…††zzzFìçpë%%%Ì;÷¨¯¥$I’$I’$I’$ir0"Icè!âZ®åÃ|˜ŸòSæ3?Ý]’$I’¤I¡³³“;wœ¸ÞÞÞÎÁƒ“އ7âa‰ÊÊÊ”!Š’’rrƧ‚cnn.¡PˆP(tXí;::†{,#²aÆÄcååå¸]XXHffæX Y’$I’$I’$I’4† „HÒè§ŸÿÍÿæK|‰ë¹ž»¹›l²ÓÝ-I’$IJ«‘©ªz ”*àPQQ‘2ì°xñb222Ò4Êã'??Ÿp8L8µíáh6mÚtØšáÖÇ3@#I’$I’$I’$I™I:Îúèãnà^îå»|—¸!Ý]’$I’¤1ÑÕÕÅŽ;† t %444ÐÓÓ“tüÀÂH’’æÎ›¦QNyyy‰ê#£¶Åb#>_555Äb1šššØ½{÷ï5Zp$¾^TTD «aK’$I’$I’$IÒ´f D’Ž£>úøþ„Ÿð~ÎϹˆ‹ÒÝ%I’$I:"#V™¸ÞÖÖF___âØxP``( § ,Z´ˆ¬,5•.ñçépŒö31°úÈàŸ‰ÜÜ\æÏŸ?bh$¾½dɲ³­®)I’$I’$I’$I‡Ë¿ºKÒqÒK/ÌóS~Ê¿óï†A$I’$M«AŒ4©¿¡¡½{÷&¯<Ÿ¼‡Y³fÍàG|]SO~~>ùùù„B!*++Gm?ÚÏ[4eýúõ466ÒÝÝtìàŸ·áÖ‹‹‹™7oÞX Y’$I’$I’$I’&!’tœÜÊ­<ȃ<Â#\ÀéîŽ$I’¤),Uņá&ßoß¾ÞÞÞı+6Ä'Ù‡Ãá”“ï­Ø £q¤ÕGFú9ŽF£lذX,Fkk+ýýý‰cSU¤n½°°ÌÌ̱²$I’$I’$I’$¥…I:îç~îàþ…1 "I’$é¨ ž?Üzss3»víJ:6>1~àDøŠŠŠ”ä/^LFFFšF)%ËÏÏ'‡GmÛÙÙÉÎ;Gü÷¯>R__ÏÁƒ“Žƒ£V ƒ”––2{öì±²$I’$I’$I’$7B$é½È‹TSÍ­ÜÊ•\™îîH’$Iš ºººØ±cLj“×ãÛ ôôô$?xòzeeeÊjK–,aΜ9i¥4~òòò…B„B!***Fm?ZȪ¦¦æˆBVí²’$I’$I’$I’”.B$éìg?ŸæÓTQÅ7øFº»#I’$iŒuttŒð¸ÞÚÚJâØøó“ÉÃápÊIæ………dff¦q¤Òäw$ÕGRýÛ¼½iÓ&š››Ù¾};½½½‰csss™?~ÊÀÖàõ%K–=–Ö$I’$I’$I’4‘¤cp·±“|ï‘w•$I’&£x…€Ñ‚tww'¯XŸð‡Y³fMʉàÁ`0M#”4šüü|òóóÕxF3ÚëFMMÍa¿n ·ß–$I’$I’$I’¤á‘¤£ô ¯ð¾Ã=ÜC1ÅéîŽ$I’¤CF«â1p{¤;ý¬âáþ% lŽÑ^“¢Ñ(6l8ìÊBí/Z´ˆ¬,Ý+I’$I’$I’$M'þ…P’ŽÒ_ó׬a ÿ‹ÿ•î®H’$IS^GGLjÕ;âëMMMìÞ½;騸„ê¨+**RN¬.**"¤i”’¦¢#©>ÒÕÕÅŽ;F ²E£QÖ¯_OCC===IÇÇ_ÇF ‘,Y²„9sæŒå°%I’$I’$I’$!’t^çu"DxœÇ àd1I’$éHuvv²sçÎÃñõööv<˜tüÀIÏñIÖ©&?—””“““¦QJÒ‘ÉÍÍ%  …¨¨¨µýha¹7²aÃZZZˆÅbIǦ Ë ·¾xñb222ÆjØ’$I’$I’$I’Ž’I: ÿÀ?°šÕ¬emº»"I’$M#ML¼ÝÚÚJâØT“ÃápÊÉÉ………dff¦q¤Òø[»v-O=õTº»1¥L…kšŸŸO8&Ú¶££#åëñÀõM›6ÑÜÜÌöíÛéííM:~po¸uƒx’$I’$I’$IÒø1"IG¨“Nâ!îäN«ƒH’$iÊ‹Åb#V766ÒÝÝtl^^ހǚ5k†L ...fÞ¼yiá{÷?Ûoݺ•åË—'¶ÿâ/þ‚;wò£ý(±/‰°víûñá–t‰a¬û&Äx§›¾¾¾twa\ÉÏñƹ馛xå•WØ·oßa7øšŽÅ¿¡TçüÏÿüOîºë.ž{î9z{{Y±bŸûÜç¸öÚk“^‹Ž·üü|òóó»úÈp¯ÿñíššb±ìÙ³'éØÁ¯ÿ#…HŠŠŠÆtÜ’$I’$I’$IÒTf D’ŽÐó™'žx‚Õ«W³uëV>ûÙÏÒÐÐÀm·Ýv\¿ÿ±ˆ¿ÞŽÃyo‰Hç½e¸õÉöÞ"I’$I’$I’$5ÿz&IGè)ž¢‚ J(IwW$I’$`ô»¸Ç·ç.îEEETVVN»»¸gggSUUÅO<ÁUW]•ØÿÚk¯±lÙ2þÿìÝy|Tõ½ÿñ×B«A–$„mX´q WEÐJ ¶Š€KÁjmµÈE4«¸ÕÔÚêíÏ…Bëk«tm·[Ü®µ”°„=„€ $!¿?r37CV á$áõô1Ïœ9ßs>ß™3ÉyϧS'Þ}÷]† yìµ×^cذa´hÑ"ˆ’¥ãÞÇÌe—]F³fÍ>|x£ê\ó‡?ü0pà@ž~úi† Ò !‡£|÷‘´´´j·Ý¿?¹¹¹•~N•-gee±hÑ"²³³),,ŒèçVUË)))´oß¾>§-I’$I’$I’$.&è$©±ùš7”$I’ŽPAA›7ofùòåddd0gÎf̘ÁwÞÉ„ 5jƒ "99™ØØX:tèÀ€8÷Üs7n÷Þ{/Ï?ÿ<™™™¤¥¥1vìX~ùË_²`Á–,Yš5kØ¿?ùùù¬Y³†¥K—²páBæÌ™Ãƒ>Hzz:&L`øðᤦ¦’œœÜdà eFŽÉ+¯¼µî…^`äÈ‘Œ=š D=öÊ+¯pñÅGî/X°€!C†Ð²eKzöìÉ-·Üž={¢ÆìÞ½›[n¹…p8LË–-IJJâÚk¯åþçj¬ï“O>á‚ . uëÖ´k׎ /¼×^{­Ú1¡P¨Ò×­²õµ©­lLÙøP(Äõ×_yüÓO?eäÈ‘´mÛ–¶mÛòío›O?ý´Òc¯Y³†Ë.»Œ„„„*ë<œ1µ9vmŸÇO?ý”‹.º(²¯‹.º(j_åç¿xñbž}öÙ¨õuùœTöü”­ËÎÎf̘1´mÛ–.]ºpõÕW“››[aÞË—/碋.¢M›6´k׎o}ë[¬X±¢Æç¾LmÎ©}ûö\zé¥lذ¡ÆZÊŽUTTDóæÍ …BÜyçGTkmÔöܪí|JJJ"a2'žx"û÷ï¯U=]\\ÉÉɤ¦¦2tèPÆŽKzz:Ó§Oç‰'ž`áÂ…,]º”5kÖpàÀöíÛÇš5kX²d ,`ÆŒŒ?ž´´4ÈÊÊâùçŸç¡‡bܸqœ{î¹ 0€N8!R4h£Fb„ Üyç̘1ƒ9sæ°páB233Ù¼y3ÅÅÅA?5’$I’$I’$IÒa •4¦¯Î“¤àk|+¹’ŸòÓ K‘$©Éš={6'N º ©NåççWú-èUuõ(¯eË–Õ~ zùåÄÄDbbüþ‡#±}ûvN9å¶lÙyO;í4^zé%öïßÏÈ‘#Y¹r%PzAw×®]Y¶l‰‰‰@éæO<ñW_}5_~ù%wÜq¡Pˆ§Ÿ~:rŒK.¹„ÓO?I“&q '°bÅ nºé&Þ~ûíj»¬^½šÑ£G3kÖ,Î9çÖ­[ǤI“X²dIÔ¸P(Ta?•­«l}mk«j_|ñçž{.Ó¦MãòË/'&&†W^y…iÓ¦ñæ›o’’’µ#F0}útN?ýt/^ÌE]TísPݘÚ»6Ïã_|Á°aø÷Þ{#Ÿ 0}út/^LŸ>}(..椓Nâƒ> !!!Rã–-[0`6l uëÖuúœTõÚ^pÁÜrË- :”¼¼<ÒÓÓiß¾=¿ÿýï#Û­Y³†aÆ1}út.¾øbš5kÆë¯¿Î<À§Ÿ~Z«Îµ9¿¦¦˜˜^}õU~ó›ßðî»ïÖº¦ªÎíÚÖZÓû¥¶¯áÑÎçÖ[oeÕªU,Z´¨Æy«jìܹ³ÚÏٲ圜ŠŠŠ¢Æ—uêé37%%ÅîP’tÊÕ%I’$I’$IG& 1wî\ƽÞ@ˆ$ž9‘x€IL ºI’š,!j öïßOnnn•ŽòžfggSXX5¾üE§Õ]xš’’Bûöíšåñ笳ÎbæÌ™œyæ™deeqÙe—ññÇpÊ)§0oÞ<úöíKff&“&Mâƒ>¨r_yyyôéÓ'ª3BÛ¶mY¿~=:tˆ¬[»v-áp¸Ú Ưºê*.¼ðBÆY·jÕ*N>ùä: „Ô¶¶ªöwõÕW3pà@n¿ýö¨õ¿ûÝïøä“O˜1cFÔ>þùÏrþùçW9çÊê­jLm]›çñꫯæ?þã?HOOÚ×#<ÂG}Äþðî¾ûnºvíÊ7ÞÙæÁdýúõ<þøãuþœTõÚΟ?Ÿ1cÆDÍçßø›6mЬ?~<ƒ ª0§gŸ}–+¯¼²Vá‹CUv~×EMO?ý4ßÿþ÷*r8µÖô~©íkx$óÙ°ao¾ù&3gÎdíÚµ¼÷Þ{ôîÝ»Æù¨îäååUûù]¶¼iÓ&vïÞ5öpšIIIM¾Ë–$UÇ@ˆ$I’$I’$=!’TGbˆá/ü…+¸"èR$Ij² „((ùùùµúVñ¼¼<¶mÛÆÁƒ#cË. ­ÍÅ¡;w¦yóæÎTU¹ï¾û())aÚ´i<üðÃäææòóŸÿ( tèÐÛn»û￟ƒ2mÚ´j÷wèÅå\pûöíãž{îaøðáµ>Y¶lÉÉɇu¼ªÖMmUí/11‘÷ߟ=zD­ß¶mÆ cÅŠQûØ»w/­Zµªv>‡·ª1µ=vmžÇªöµvíZÎ9çœHŸÏ?ÿœ+¯¼’?ü0²M¿~ýxöÙg9ãŒ3«®šæWöxe¯íÎ;£º”ìß¿Ÿøøø¨ŸOUÕ‘››KÇŽ(R_5mݺ•¤¤¤: „TWkMëçÜ:Üù„B!zôèÁå—_ÎÔ©S#݆Ô0Õôo„ò÷ý7B\\:t¨öße÷»uëFlll€3•¤ºg D’$I’$I’ŽžIª#!BÌe.ãWóÆ’$éˆQ¾þõ¯óÖ[oUùx\\ýû÷§ÿþœzê©ôë×N:Ñ¥Kiݺõ1¬Võ%33“)S¦ðî»ï2tèPzè!† À{ï½Çí·ßÎ[o½ÅàÁƒ™9s&ƒ  ''‡»îº‹W_}•­[·R\\Ùgù_½ìÞ½›{ï½—_|‘œœN;í4.¿ür&Ož\íÀÍ›7gß¾}´hÑ¢Úú&RÛÚªÚ_ll,EEE•ÖϾ}ûjÜÇáÎíp]›ç±yóæìÝ»—¸¸¸¨õ´mÛ6ªÛÏyçǬY³8p o½õ?úÑ¢"uùœÍk[Õœjs\¨ýù},kªj»£­õÐu‡snî|Žä} ÆáÀäää°}ûv¶nÝJvv6Ë—/ç£>bùòåìܹ³Ê±-[¶ä³Ï>«.’¤ÆÌ@ˆ$I’$I’$½ª!~©$I’$IÀ#û,'œpBd]ùîå÷‘MJJ ;väŠ+®`Ĉ‡:ÿüóÉÈÈ`„ ‘uŸ|ò W^y%+V¬¨vlbb"6l oß¾‘u•uémmU]Xÿ­o}‹Å‹s饗F­_²d ééé,[¶¬Ú:Fm]›çqøðáÌ›7ýèGQûzá…>|xÔº±cÇrï½÷rÏ=÷ðꫯ2kÖ¬#ª«¾]pÁÌŸ?Ÿôôô¨õ¯½öZ­Æ×öü®‹šÞxã£Úo]×ZÛ×ðHæcw†§¦Ïù²å7ràÀ¨±‡~Χ¦¦2|øðJ?óƒ‰I’$I’$I’$©é0"I’$IR-•6j#??¿Ú J³²²Xºt)yyylݺ5êÂà–-[F…Cª ‘tîÜÙî#uläÈ‘\vÙeüñ¬ðØèÑ£3f óæÍ‹ZÎ9çpûí·s÷Ýw“œœÌ† ¸ÿþû+Ýÿõ×_ÏÃ?LŸ>}صk3f̨48Ô=÷ÜÃ¥—^Jrr2ƒfÍš5üà?ছnªq>#FŒàî»ïæÑGå„Nàí·ßæ¿ÿû¿¸¶®]»òî»ï2hÐ þùÏrÝuבÍôéÓ=z4ÅÅÅ 6Œ-Zðæ›o2iÒ$~ýë_×XçѨí±kó}:çŸ>mÛ¶eÔ¨QÄÄÄð÷¿ÿ'Ÿ|²Vãçü>Òšš5kFFF?ÿùÏj¿u]km_ÃÃÏ!C…B,]ºôˆkSÍjú,.ûöíGÆÆÅÅÑ¡C‡¨ÏÜp8\éçq·n݈ p¦’$I’$I’$I:^…Jü*:I:,!BÌe.ãt)’$5Y³gÏfâĉA—!3û÷ï'77·Ú UË–³³³),,ŒŸP¡ÃHe!’””Ú·oÐ,¯¾úŠÎ;“͉'žõX~~>ÉÉÉdggÓ¦M›ÈúíÛ·sÛm·ñÚk¯±k×.úõëÇO~ò®¸â àÿ:¼ñÆÌš5‹%K–°gϺvíÊ%—\½÷Þ[!Lp¨wß}—©S§’™™I§N˜¾ñoðë_ÿšîÝ»QmÏ?ÿgžyæÕUÓü*{m«z½«Z¿|ùr¦NÊ[o½ELL _ÿú×yì±Çèׯ_ÔEð•©íù}45…B!Î9ç}ôQRSS+}ªzNÊo{4µVUgmϭÙÏàÁƒ‰‰‰áí·ß®rŽª\ù.Õ…=6mÚÄîÝ»£Æ–.kÓ±+))©ÊŽH’¤Ã“™™ PcGFI’$I’$IRÕB¡sçÎeܸèë— „HÒa2"IRý3"U/??¿Ê `]ÎËË‹{8Ã&&&Ð,%Õ·åË—3räHÖ­[t):ްsçÎ?Ï6oÞLNNEEEQãˇ"«ûlKII¡E‹ÍR’ŽoB$I’$I’$éèUiP=’$I’$éÅÇLJ ‡Ã5n›ŸŸ_ã·¨¯X±‚Í›7³}ûö ¼ÐVjB¡=ö&L U«V¬\¹’)S¦pã7]šš ê‚‹‡ÞߺukT÷”Ê‚‹áp¸ÒÏ .]ºÐ¬Y³g*I’$I’$I’$Ë@ˆ$I’$IMX||<ñññ$''“ššZãöyyyÕ^À›™™I^^7näË/¿ŒÛ²e˨ v« ‘$%% …êkÚ’±hÑ"}ôQî¹çbbbèÛ·/7Ýt×\sMÐ¥©‘¨êóáÐå7ràÀ¨±‡~>„Ãa† R᳡k×®œp ÍP’$I’$I’$Ij| „H’$I’¤ˆ² Gm”u©îÂà²É¶mÛ8xð`dlÙ7À×Ôy$))‰Î;Ó¼¹¿ÂŽÆÈ‘#9rdÐe¨©ÍÏð²åÚü ¯ª‹‡?Ã%I’$I’$I’¤úã_â$I’$IÒ)ß}$--­Úm÷ïßOnnnT·‘C/:ÎÊÊbÑ¢EdggSXX5þÐo—¯jÙo——t<«©ËSÙýÚtyJJJ"--Í.O’$I’$I’$IRf D’$I’$Õ»¸¸8’““INN®ÕöùùùU~S}^^YYY,]º4òXyeß\_þâåªB$]ºt¡Y³fõ1eI:jìܹ³V²mÛ6<G‡ª ”ÝïÖ­±±±õ5eI‡©¦÷~ùåCßûeÁ±òï÷p8\éÏÎ;Ó¼¹¿¢•$I’$I’$I’tdük£$I’$IRâãã‰'99™´´´·/ß}¤²‹Ç³²²X´h7näÀQcË:ÔÔy$99¹ÖIÿ§¦÷gYÈ+;;›={öD=ôý‡2dH…àGÙ²$I’$I’$I’$ B$I’$I’êÈáv9ôBôò÷³²²Xºt)yyylݺ•’’’ÈØÊ:TµÜ¥Kš5kV_S–SYªÂÛ·o§¸¸82¶|Ÿ²÷K8®ô}dI’$I’$I’$I •I’$I’¤ÄÇLJ ‡Ã5n[PPÀÎ;«ílPÖ}dÆ EOHH¨±óHBBݺu£]»võ5e©F‡¥ªZÞ¼y3»víŠ[”*^§¦¦Vz¾'&&Ð,%I’$I’$I’$©n‘$I’$IjàZ¶lIrr2ÉÉɤ¦¦Ö¸}MÕgffÖEõU-{Q½j²ÿ~rss« w”ÝÏÎΦ°°0jü¡a¦´´´J»ãf’$I’$I’$I’t<2"I’$I’ÔÄN÷‘üüü*/Ð/»¿bÅ 6oÞÌöíÛ)..ŽŒ‹‹£C‡•^ èr·n݈­Ïië94pT]ØcëÖ­”””DÆ–ŽÊŸ#áp¸Òs§K—.4kÖ,À™J’$I’$I’$IRÃf D’$I’$é8O|||¤ûBMÊ:‹T(ë>’Íž={¢Æ–u:©©óHÙ};5½®eË7näÀQc}]Ãá0C† ©ô5NHHh†’$I’$I’$I’Ôô‘$I’$IR­•6j£²î#å—³²²Xºti­;ITµÜ¹sgš7÷×\åÕôÜÈÉákŸ}Æï ؘ“Seç—ò]<ìü"I’$I’$I’$I ‹)—$I’$IR½8œî#û÷ï'77·Bp¡üý¬¬,-ZDvv6………Qã*t©,D’’’BûöíësÚõ¦|ê›6mb÷îÝQcË6eÏŰHÏÎfj«V¬=š/¯ºŠ¿öµÈó …š¥$I’$I’$I’$©¶ „H’$I’$)pqqq$''“œœLjjjÛçççWˆØ²e Ë—/géÒ¥lÙ²…¼¼¼¨±åÉ]épB’“*$&&S/s.((`çÎÕ†;Ê–srr(**Š_>Sº©, “’’B‹-*°m­œÓ~ýkxùe7¦N…ääz™¯$I’$I’$I’$©n‘$I’$IR£O8&׸íÞ½{Ù¾};[·n%'''jùÕ‹^å˃_râÔ#•×¢E :uêDçÎILLŒZîܹ3:uŠZÞ½{7Û·o'''‡mÛ¶±mÛ¶ ËeÇÿꫯ¢ŽÕ¦M›¨}%''súé§Ó©S'ºté9~§NèØ±ãÑ?‰]ºÀôép×]0w.ü×ÁÀ0dÜq\|1Ø)D’$I’$I’$I’,!’$I’$IjÒZ·nM¯^½èÕ«WÔú_ñ+>çsþȹê«(**"''‡œœ¶nÝZi¸cÅŠ‘å‚‚‚* p”…Hzöì.éÔ©IIItîÜ™øøøz}ª&”Þ–.…‡‚1c wo˜2®¿Z·¦6I’$I’$I’$IR• „H’$I’$é¸ó ¯p+·òpWEÖ7oÞœ¤¤$’’’8p`ûùòË/Ù²eK$DÒ®];ºté {ÄÄÄÔç4êÞС¥·Õ«á׿†»ï†iÓàškà¶Û [· +”$I’$I’$I’$ý/!’$I’$I:®,g9Wr%Ws5wq×Qí«]»v´k׎“N:©Žªk úõƒ3àž{àw¿ƒ™3áñÇá’KàG?‚³ÏºBI’$I’$I’$I:î5²¯(”$I’$I’ŽÜ¶pq*§òßüwÐå4|:Áw@VüéO°~=  ƒÁœ9PTt…’$I’$I’$I’tÜ2"I’$I’¤ãB>ù\Â%ÄÏ|æG\Ð%5-ZÀرðþû°d „ÃðƒÀI'ÁCÁ®]AW(I’$I’$I’$IÇ!’$I’$Ijòr«¸Š5¬a! I !è’¯¡Cá¹ç`Õ*¸øb¸ÿ~èÞÒÓK;ˆH’$I’$I’$I’Ž !’$I’$Ijò¦2•—y™¿òWúÒ7èrš†Þ½aÆ Ø¼¹42~içQ£ ##èê$I’$I’$I’$©É3"I’$I’¤&í)žâQå·ü–ó9?èršžvíJ»ƒde•†Bvî„#`Ð ˜3 ƒ®P’$I’$I’$I’š$!’$I’$Ij²þÎß™Ä$¦3ñŒºœ¦­Y³Òî o¿ ~ýûÃu×A0}ziPD’$I’$I’$I’Tg „H’$I’$©IZÁ ®à .çr~ÂO‚.çø’–VÚdõj˜0~õ«Ò`È 7ÀÊ•AW'I’$I’$I’$IM‚I’$I’$59;ØÁhF3€<Ã3„]Òñ©W/xðAX¿~Þ| €#`áB() ºBI’$I’$I’$Ij´ „H’$I’$©IÉ'ŸQŒ¢„æ18â‚.ImÛÂĉ°bÌŸ_ºnôh8ã ˜= ‚­O’$I’$I’$I’!!’$I’$Ij2J(á:®c5«y™—éD§ KRy110j¼þ:,[§œS¦@Ïž0}:ìØt…’$I’$I’$I’Ôh‘$I’$IR“q7wóWþÊs<ÇIœt9ªÎé§Ãœ9°aLš³fAJ L˜Ÿ~tu’$I’$I’$I’Ôà‘$I’$IR“ð{~σ<È,fñM¾t9ª­ÄÄÒî 7ÂìÙ™YÚ9dèPX¸JJ‚®P’$I’$I’$I’$!’$I’$IjôÞâ-&1‰óc&21èrt$Z¶ü¿î ¯¿ 0f œt̘ûö]¡$I’$I’$I’$5(B$I’$I’Ô¨}Æg\Â%Œa ÷q_Ðåèh…B0|xiw?†aÃà®» gO¸óÎÒN"’$I’$I’$I’$!’$I’$Ij¼rÉe4£éMožæibüuWÓ2p <ñ¬[·Þ øôî ãÆÁûï]$I’$I’$I’$Ê¿K’$I’$©Q:À¾Ãw(¤E,¢­‚.Iõ¥sg¸ãX»ž|>û Î>†…矇ââ +”$I’$I’$I’¤cÎ@ˆ$I’$I’J¸ŽëXÆ2°€.t º$ -ZÀ„ ð¯Á’%œ W^ '3fÀÞ½AW(I’$I’$I’$IÇŒI’$I’$5:Ó˜Æ\æòWþÊ)œt9 ÂСðÜs¥ÝBFŽ„ÿ¸4 ’ž6]$I’$I’$I’$Õ;!’$I’$IjTæ2—Ÿñ3f0ƒŒº­OŸÒî ›6Á}÷Á /@¯^0j¼óNÐÕI’$I’$I’$IR½1"I’$I’¤Fc)K¹†k˜ÊTþ“ÿ º5$íÛ—vY³ž}vì€!C`Ð ˜3ŠŠ‚®P’$I’$I’$I’ê”I’$I’$5 Ydq—1’‘ü‚_]ŽªØX;Þ}>üú÷‡ë®ƒ=`útÈË ºBI’$I’$I’$IªB$I’$I’Ôàíd'r!ÝéÎæ㯵Tii¥ÝAV­‚ñãaÆ èÞn¸¡t$I’$I’$I’$5bþå\’$I’$I Z!…Œe,{ÙË‹¼HkZ]’›p|6l€Ÿý þþ÷ÒÎ!£FAFFÐÕI’$I’$I’$IÒ1"I’$I’¤«„®çz>à^â%ºÒ5è’Ô˜µm éé°f ÌŸ0bœqÌž]z_’$I’$I’$I’ !’$I’$Ij°~ÆÏøÓÿþw*§]ŽšŠ˜˜Òî ¯¿~À”)ЫLŸ¹¹AW(I’$I’$I’$I52"I’$I’¤éyžgÓ˜Á 8û^zé¥J·ûä“OHII¡¨¨€7ß|“3Ï<“–-[Ò³gOžzꩨí÷ìÙÃí·ßNß¾}iÕªíÛ·gĈ,Z´¨Þç¤(- æÌõëá†`æLèÚ&L€åËI ¡Pˆââb~ö³ŸÑ³gOâââèׯ³fͪ°íÂ… 9ûì³iݺ5­[·æì³Ï®ò½!I’$I’$I’$©i3"I’$I’¤ç>àZ®%tnäFÒÓÓyì±Ç*ÝvæÌ™Lš4‰æÍ›³jÕ*¾óïpË-·°}ûvž{î9xà/^Ùþšk®¡¨¨ˆŒŒ vïÞÍÚµkIOOgæÌ™Çhvj’’J»ƒ¬_¿úUiçSN#`áB()©×ÃOž<™üü|222عs'O>ù$>ú(sçÎlóÞ{ïqíµ×róÍ7³nÝ:Ö®]Ë”)S?~<|ðA½Ö'I’$I’$I’$©á •”Ôó_2%©‰ b.sǸ K‘$©Éš={6'N º IR@ֱ޳9›38ƒ…,¤Í(,,¤W¯^¼öÚk¤¦¦F¶ÍÍÍ¥OŸ>¬ZµŠÎ;3aÂN=õTn½õÖÈ6/¾ø"³gÏŽtQhÛ¶-›6m¢]»vÇ|njD„üfÌ€—^‚¾}aòd˜8âãëôP¡PˆŸüä'Üwß}Që_~ùeî»ï>Þ{ï=.½ôRFŒÁäÉ“£¶›5kÿüç?ùÛßþV§uI’T233HKK ¸I’$I’$Ij¼B¡sçÎeܸèë—í"I’$I’¤ãK¾d£H&™çxŽf4 66–É“'WèòÛßþ–Q£FѹsgÞyçFµÍyçÇ'Ÿ|¹ß¿n»í66mÚTϳQ£Ç—vY¶ Î?î¼zö,ýŸ?×^{m…uƒfõêÕ‘ûË–-cÔ¨Q¶=ztäb[I’$I’$I’$IÇ!’$I’$Ij )är.g';y‘iC›¨Ç'NœÈ¼yóرcÅÅÅüæ7¿aÊ”)‘m²³³éׯ¡P(rëС[¶l‰lóì³Ï’““CŸ>}øÚ×¾Æ÷¿ÿ}^|ñEl¤«*v<ñ¬[ÿùŸðÔSÄ ð¯ÕÉ!zöìYa]BByyy‘ûÛ¶m‹„ŸÊëÒ¥ [·n­“:$I’$I’$I’$5B$I’$I’Ô ÜÌͼÃ;Ìg>ÝèVáñŽ;rÙe—ñÄOðâ‹/’˜˜È™gžÙ¦]»vlÞ¼™’’’¨[qqqd›^½zñ /°k×.ž}öYÌ<Àõ×__ÿ“TãÖ¥ LŸ7“OÂGÁ©§ÂСðüóPî<;\115ÿª¶K—.lß¾½ÂúíÛ·“˜˜xÄÇ–$I’$I’$I’Ô8‘$I’$IRàâ!f3›?ógþƒÿ¨r»ôôtüq ™9s&7ÝtSÔãÆ ãÅ_¬Õ1ãââ8õÔS™8q"¯¼ò sçÎ=ª9è8WÚäßÿ†%K !®¸N>fÌ€½{ëå°gœq .¬°~Á‚œqÆõrLI’$I’$I’$I —I’$I’$j󸛻y„GØj·0`'Ÿ|2Ó¦McåÊ•Œ7.êñiÓ¦qß}÷ñÌ3Ï››ËÞ½{yã79rdd›óÎ;?þñlܸ‘¢¢"¶nÝÊ#<ÂyçW/óS7t(,\«VÁEÁÝwC×®žÙÙuz¨©S§rÏ=÷ðç?ÿ™œœrrrøóŸÿÌOúSî¼óÎ:=–$I’$I’$I’¤†Ï@ˆ$I’$I’“I&˜Àøé¤×jLzz:¿øÅ/øáH‹-¢KMMåå—_fîܹôêÕ‹N:ñÀð£ý(²Í}÷ÝÇüùó9í´ÓhÛ¶-çž{.ÅÅÅüå/©Ó¹é8Ó·oiwuëà®»`Þ<èÝƃwß­“CœsÎ9üîw¿ã±Ç£GôèуÇ{Œßÿþ÷œyæ™ur I’$I’$I’$IG¨¤¤¤$è"$©1 b.sǸš7–$IGdöìÙLœ81è2$Iõl›8‹³À±ˆæ4¯Õ¸üü|:tèÀ_|A×®]ë¹Jé8/¾? ï¿iipóÍpÕUмvçº$IMAff&iiiW"I’$I’$IW(bîܹŒ}ý²B$I’$I’tÌíaqíiÏ\æÖ: ²k×.zè!&L˜`D [‹0v,¼÷,Yá0üàЯ<ôäå]¡$I’$I’$I’¤FÎ@ˆ$I’$I’Ž©bйЫØÎv^áÚÓ¾VãB¡]»vå_ÿú¿üå/ë¹J©  Ï=«VÁ¸qð‹_@žëÖ]$I’$I’$I’¤FÊ@ˆ$I’$I’Ž©tÒyƒ7xèN÷Z+))aï޽̛7víÚÕc…R=éÝ|6l€ûï‡_,]7jdd]$I’$I’$I’¤FÆ@ˆ$I’$I’Ž™Gy”ßð~Çï8›³ƒ.G F»v¥ÝA²²`þ|عFŒ€´4˜3 ƒ®P’$I’$I’$IR#` D’$I’$IÇÄ˼ÌT¦òñ]¾t9RðbbJ»ƒ¼ý6|ø!¤¦Âu×A÷î0}:äæ]¡$I’$I’$I’¤Ì@ˆ$I’$I’êÝG|Ä\Á5\ÃT¦]ŽÔð”uY¿n¸~õ+HI `åÊ «“$I’$I’$I’Ô‘$I’$IR½ÚÌfÆ0†4ÒxœÇƒ.GjØ’“K»ƒlØ3fÀÀ€0b,\%%AW(I’$I’$I’$©0"I’$I’¤zó_1’‘´¡ /ð-htIRãЦ LœË—Ãüù¥ëF†ÓO‡Ù³!??Øú$I’$I’$I’$Î@ˆ$I’$I’êÅAr5W³‘,` $]’ÔøÄÄÀ¨Qðúë°lœu¤§CÏžpç°ysÐJ’$I’$I’$I ˆI’$I’$Õ‹[¸…×x, }‚.GjüN?žxÖ®…ÿüOxê)‡aÂøôÓ «“$I’$I’$I’tŒ‘$I’$IR{’'™ÉL~ËoÌà Ë‘š–ÄD˜>6n„Ù³K;‡œr  BIIÐJ’$I’$I’$I: „H’$I’$©N½Ê«Lf2÷s?ßã{A—#5]qq¥ÝAþýoxýuHH€1cओ`Æ Ø·/è %I’$I’$I’$Õ#!’$I’$Iª3ËYÎwù.ßã{ü˜]Žt|…`øðÒî Ÿ}^wÝÉÉž^ÚID’$I’$I’$IR“c D’$I’$Iub+[¹ˆ‹È@žà‰ Ë‘ŽOýú•vY·®4ò·¿AïÞ0n¼÷^ÐÕI’$I’$I’$IªCB$I’$I’tÔòÉç.!–Xæ18â‚.I:¾uî wÜYYðä“°j  C‡ÂóÏCqqÐJ’$I’$I’$I:JB$I’$I’tTrïñ=¾à ^æe:Ò1è’$•iÑ&L€O>%K 9®¼²´“ÈCQ²©È IDATÁ®]AW(I’$I’$I’$é‘$I’$IÒQ¹ƒ;x‰—xžçéG¿ Ë‘T•¡Cá¹çà³Ïàâ‹áþû¡GHO‡õ냮N’$I’$I’$IÒa2"I’$I’¤#öOñ0ó[~Ë0†]ޤÚèÓfÌ€M›à¾û`þ|‡aÔ(xûí «“$I’$I’$I’TKB$I’$I’tD³˜ÉLæ§ü”ñŒºI‡«}ûÒî _|Ï> ¹¹¥]D ‚9s ¨(è %I’$I’$I’$UÃ@ˆ$I’$I’ÛJVr)—r —0iA—#éhÄÆÂØ±ðÎ;ðá‡Ð¿?\wtïÓ§ÃÎAW(I’$I’$I’$©B$I’$I’tXv°ƒÑŒ&•TžáB„‚.IR]IK+í²z5L˜¿úôè7ÜŸ}tu’$I’$I’$I’Ê1"I’$I’¤Z+ €ÑŒ¦˜bæ1–´ º$Iõ¡W/xðAX¿~/†ÔT5 22‚®N’$I’$I’$IB$I’$I’TK%”p×±œå,d!étI’ê[Û¶0q"¬\ óçCAŒ§Ÿ³g—Þ—$I’$I’$I’!’$I’$Iª•ócžçyæ1TRƒ.GÒ±SÚäõ×!3N9¦Lž=aútر£úñ™gÂÔ©PRr,*–$I’$I’$I’š<!’$I’$IªÑÓ<Í/ø3™É7ùfÐåH ÒgÀœ9°aLš³fAJ L˜Ë—W>æå—áƒà‘Gàû߇ââc[³$I’$I’$I’Ô‘$I’$IRµÞâ-&1‰»¹›¸!èr$5‰‰¥ÝA6n„Ù³áÃaÀ:.ŒîòË_Bóæ¥BþøG3 +]’$I’$I’$Ij „H’$I’$©JkXÃå\Î(Fq?÷]ޤ†¨eËÒî Ÿ~ ¯¿ ¥“O†3Jƒ"K–@QQéöÅÅðÚkpÁ°gO°µK’$I’$I’$IXó  $I’$IRÔK.r!=éÉ3}¢B"e·îݻӼ¹¿Š—$I’$I’$IRÝó¯P’$I’$IŠRB ×s=™d²”¥t¡KÐ%Ij¬žB¡ª/,„µkᬳ`ñbèÝ»^ÊÈËË#++«ÒÛºuë8xð „ÃaÂá0cÇŽ%Ó¿ À 'œP«ý~üñÇüéObË–-ÄÆÆÒ­[·È~ËßN:é$Ú´iS/s–$I’$I’$IRÓg D’$I’$IQîå^þÂ_x™—9…S‚.GRcµ?ÌšUú¨Na!lÝ ƒÃ?þÑá g,_¾œ+V°fÍvíÚT g >ü¨Â ¤¥¥‘––Vc=e·ŒŒŒ*C(åo©©©$%%Ñs!I’$I’$I’¤ãƒI’$I’$EÌe.÷q³˜ÅF]ޤÆìO‚;k·mQäåÁ¹çÂoÀgTؤ°°ìììJCŸ}ö{÷î ..Ž®]»‡IKKcìØ±ôïߟÔÔTºwïNóæÇæ×âÕ…EöïßϦM›* ‹,_¾œ‚‚‚È>* ‹„ÃazöìILLÌ1™‹$I’$I’$I’&!’$I’$I`)K¹†k¸•[™Ìä Ë‘ÔØíØmÚÀW_U|,6bb ¤¤4 rð`éÿwí¢äÜsÉ|à–µjéò‘••Åúõë)..¢ƒÇgâĉ‘û½zõ" ãÉž¸¸¸H½‡***bÆ •†EV¯^Íž={hÑ¢)))•†Eú÷ïO||ü±ž–$I’$I’$I’Ž1!’$I’$Ib-k¹œËÎpäÁ Ë‘ÔÜ~{é­°rsK";v°wýzrW®dϺulÜHñ¶mÄîÜIÛ={h[XÈ ûö±ú–[¸³\è£|—Þ½{s '=»zÓ¼yó*Ã"yyy•vIÉÈÈ +++²îÝ»WéׯmÛ¶=–S’$I’$I’$IR=1"I’$I’tœÛÉN.äBRHa.siF³ K’ÔÈ•-”ïòñÅ_°{÷nbccéÖ­[iPá߈ -Œ9ùd®jÝ:àY4L ¤¥¥‘––Vá±êÂ"ëÖ­ãàÁƒ‘}T©.ˆ"I’$I’$I’¤†Ç@ˆ$I’$IÒq¬BÆ1Ž=ì!ƒ ZãØ’jVXXHvvv¥áƒÏ>ûŒ½{÷G×®] ‡Ã¤¥¥1~üxRSS ‡ÃôèуfÍ  Õ¥êÂ"`ãÆ•†EV¬XA~~~dUEzöìILḺž–$I’$I’$I’ª` D’$I’$é86…)üÿÖBJÐåHj@ ؼys….YYY¬_¿žââb :@0|øp&Nœ¹ß«W/B¡PÀ3@‹-ªí²yóæ¨×¸,,²zõjöìÙÙGJJJ¥a‘þýû,§$I’$I’$I’tÜ3"I’$I’tœz€xЧ˜Ç|xTèÃ. :ÖZ´h9ÿ*“——WáœÎÈÈàóÏ?çË/¿Œì#%%¥ÒÎ"v¯‘$I’$I’$IÇ;!’$I’$Ilj/ù’ÑŒ¦#ù ¡¹¿’”¼¼¼J»|dee±víZJJJ€ÒÐGY'…áÇG.ŽïÛ·/íÚµ xRí%$$0tèP†ZᱪÞÞáp¸Bwß’$I’$I’$éxà_ý%I’$I’ŽEñ¾Ãvð>ïÓžöA—$—*눕•UmG„ò¡;"èx‘@ZZiii«ªcÎÛo¿]eÇœCovÌ‘$I’$I’$IMI’$I’¤ãÀÍÜÌR–²˜Åt£[ÐåHMÖظqc¥ V¬XA~~>-[¶$Gº|Lœ81r¡z=hÖ¬YÀ3‘®²÷O8fÔ¨QQUõÌÈÈ`åÊ•ìÛ·/²äääJ»‹ø”$I’$I’$I…I’$I’¤&î—ü’'x‚¿ñ7ÎäÌ Ë‘½‚‚Ö¬YS¡ËGVVV•Ý  }„Ãáz«¯|׃’’’z=Æ‘î? ÕKmË—/禛nâƒ>૯¾j_ãáÌéhçßÍkøüƒ™3gòæ›oR\\Lß¾}™2e ×\sÍQwíhÑ¢Eµï±¼¼<²²²¢º‹¼ýöÛ<óÌ3ìÞ½€ØØXºuëVig‘“O>™Ö­[U’$I’$I’$IuÅ@ˆ$I’$IRö/qwñÿø\Â%A—#5eWu+“””é,0|øðÈEã}ûö¥]»vÔ^RRRo‹CÑÐ\{íµÜ~ûí¼òÊ+,Y²„#FÔzìáÌ©¡Î¿±øæ7¿ÉÀyíµ×0`Ÿþ97Þx#ÙÙÙüä'?©×c'$$––FZZZ…Ǫzßgdd°víÚÈ{ª|Ø«|w‘>}úо}ûz­_’$I’$I’$©¼PÉñüf’tB„˜Ë\Æ1.èR$Ij²fÏžÍĉƒ.C’½e,ã<Îã»|—ßòÛ Ë‘œÍ›7WÚåcõêÕìÙ³(í6’’Ri§€þýûð,*Wߣ=F}ÕKAAÍš5;¢ñ‡S×±x޲£}ý?ùäY·f͆ ÂÖ­[ëªÄ:µÿ~6mÚT¡»Hu½õêÕË ‘ŽK™™™•±$I’$I’$Iµ …˜;w.ãÆE_¿P=’$I’$IÿŸ½;¯º¼óÿÿ<Ù! I ²!D°¢… *Ô@¥¨U«—NÆeÐZµµþDéØNË|™ÚZ[QÇ ¶–¶¢5ŽX¡.•*H¢lIØa !,!ëï&§9d°ÏÇ}+9÷çsîó¾OΉ—úyå­£h˜ÌdÆ0†Ÿóóp—#…EMMMð¯û?ýôÓ<ðÀ\sÍ5Œ1‚îÝ»“™™Éøñãyà((( ??ŸýèG¼ù曬^½š½{÷²zõjÞ|óMžzê)î¿ÿ~®¾újòòòºl¤-•••Ü}÷ÝäääGzz:7ß|3~øá[XXÈĉIHH ))‰+®¸‚µk×¶yî¼yó=z4qqqdggs÷ÝwÃ5Íš/ˆÁÛ-·ÜÒ©5Ú¨««#**Š@ À<<¶lÙ2&NœHbb"‰‰‰Lœ8‘eË–pÍÎîÙ²e\zé¥Áç¹øâ‹[=OóžW¯^Í•W^IJJJp®#Yû³Ï>cÔ¨QtïÞóÏ?ŸU«VQXXÈÈ‘#IHH`ܸq­öÐüØ¢¢".¾øbzôèABB—^z)Ÿ}öY«s[>¦ùgXZZ2·jÕ*víÚ2WZZJcccH W¯^ìÛ·¯Ã× œbccƒ€îºë.žzê©6O<þøãäççPPPÀ·¿ýmÆOnn.ݺu#77—ñãÇóOÿôOÌœ9“‚‚Š‹‹ƒI’$I’$I’¤Îˆ w’$I’$I:²ª¨âR.¥=˜Íl¢üO@:UTT´êðÑ|+--¥¡¡ý«ýùùùL:5ä/÷Ÿènºé&Î:ë,Þ{ï=’““)**bÚ´iŒ9²Ã.«W¯æ’K.aúôéÌš5‹ˆˆ^ýu®½öÚ6ÏŸ}:/¾ø" ,`âĉíÖÑÙµ§OŸÎ³Ï>KFF>ø S¦L!++‹Y³f‘‘‘Áw¾óî¹ç^zé¥V¯ß×¾ö5|ðA^|ñEêë냯Õ;ï¼Cvvv‡¯5ÀÒ¥Kùæ7¿6jhh`ðàÁ¼üòËÁ5ö÷Øc1zôèv_ƒ®,::ºÃß%-O5wY¼x1sæÌ¡²²2¸Fß¾}Ûì,2dÈâããå–$I’$I’$IÒq"Ðx2÷´—¤C Àæp ×ødI’tHž~úi¦Nî2$é¸TO=Wpïñïó>¹ä†»$é°uú(.. **Š~ýúµy1õàÁƒILL ó.Ž­ý/ÖOLLdÍš5ôìÙ38WRRBNNN‡n¸#Fp×]w…ÌÏš5‹o|ã>þö³8p Û¶më°¾CY£-m­;eÊÎ9çœV{˜1cü1/¼ðB»ïÌþ§L™ÂgœÁ}÷Ýrî/ùK–,YÂÌ™3Cžçí·ßfìØ±ÜÓ¡¬½`Á.¼ðBÊÊÊÈÌÌ ™[¿~=yyy”——‡¬˜={6_ûÚ×Bæg̘ÁÒ¥KCB9ý Ï>ûlž}öY†ÀüùóùÑ~Ä[o½rÞÚµkyçwøÉO~BII ï¿ÿ>¹¹'×?·:úýVRR|[†Úrrr:t(Æ #77—äää0ïBêØâÅ‹ÈËË s%’$I’$I’tü Ì™3‡k® ½~Ù@ˆ$u’I’Ž>!’tè¦1_ð þÿc£Â]ŽtPêêêX»vm›D¯X±‚ªª*bbbÈÊÊj3ô1tèPºuëætû_¬?aÂöìÙÃ÷¾÷=òóó‰Š:¸ÎA}úôáƒ> ÿþ!ó›6m"==ý Bm:éÌùm×ÞJJJ8ï¼óظqc»ïÌþÛ;·¼¼œqãÆQTTò<»wï¦{÷îÜÓ¡¬½sçÎ`ª¡¡ÈÈÈVsQQQÁ:-»uëVzõê2_RRÂèÑ£)++ 9·½ŸÉÌ™3Y¾|9?ûÙϸôÒK¹ýöÛ¹ôÒK[=_ÿþýùêW¿Ê¿ü˿ЧOŸƒz=NûöícÆ !¿›;Œ¬]»–ºº: uX¤å-;;›ˆˆˆ0ïD';!’$I’$I’tø „HÒb D’¤£Ï@ˆ$š™ÌänîæE^ä:® w9R‡Þzë-~øÃ²zõjÖ¬Y¼°¹gÏžäææ¶º 8ŒŒŒ0W}üØÿbýÊÊJyä~ûÛß²eËÎ<óL¾úÕ¯rûí·Ýî:QQQìÞ½›ØØØ>Ç–-[øÎw¾Ã믿ΦM›¨¯¯;Ø@HgÖ8˜}w´‡êêj©­­m÷ñÙtttð}¼¿nݺ±gÏžëìÈá®}8spp¯UK[¶lá´ÓNcíÚµlÚ´‰Ë.»ŒÂÂBÀA=Ÿ¬¦¦†ÒÒRV¯^r[µjÅÅÅìÛ·€øøxrss2dO>ùdH— éX1"I’$I’$I‡¯½@ˆJ’$I’$é0ŸùÜÃ=ü;ÿnDR+III̘1ƒÕ«WS\\ÌwÜÁìÙ³¹îºŽ_¤¦¦²iÓ¦VóÛ¶mk5wà 7ÏÂ… ©®®¦±±±Óû‰5ö—ššÒ¤ÙÆIMM=àcvÿ©©©lß¾=XsË[ËÀÆ¡8škﯼ¼¼ÕÜÆéÝ»÷A¯Ñ»woFÍìÙ³yòÉ'¹óÎ;[…A$I’$I’$I’tø¢Â]€$I’$I’Ï'|Â5\ÃÜÈýÜîr¤ƒrÑEqÑEPWWÇÚµk)..¹½þúë¬X±‚ªª*bbbÈÊÊ"''§ÕmèСtëÖ-œ[êÒëÖ­#++‹ÔÔT¾öµ¯1~üx²³³;|Ü„ xõÕW¹ë®»Bæßzë­VçþùÏföìÙ$''çÚ +´èÌ+??Ÿ_ÿú×|ûÛß™ÿÍo~C~~~‡íÌþ¿üå/³`Á®¸âŠùwß}—»îº‹¿þõ¯‡¸ƒ£»öþÞxã n¼ñƹßüæ7L˜0!dî@›nº‰û·£ªªŠO>ù¤ÍsìÒ±}ûö±aÆàïÄWº¿ÂÊÀJ*7T²cë* âëâIKK#ëœ,ÎøÊ\Þërúföepú`úgö'1"‘b膿#%I’$I’$I:Ñ‘$I’$I:Žmd#_á+œÍÙüœŸ‡»éDEEƒm©¨¨h)..¦  €âââàýúõk3,2xð`å–º¤[n¹…ÿøÇ 8;v0sæÌ"¦OŸÎرcILLdÒ¤IDFFRPPÀ÷¿ÿýVçžwÞyÜwß}|÷»ß%##ƒµk×òØcµ¹nff&ï½÷#FŒàí·ßæ›ßü&ëÖ­ëÔëá‡fܸqôèу¯|å+æÍ›ÇŒ3X°`ÁÛÿôéÓùÊW¾B}}=ãÆ#&&†wÞy‡[o½•Ÿþô§‡µ‡£¹öþžzê)¢¢¢øò—¿LCCóæÍcæÌ™¼óÎ;!çµ÷3lvÙe—ñÿøÜzë­tïÞ½ÕóŒ=š@ ÀÂ… hýÇ›ö~¿SRR ͤ¤¤óD ÕçWˆ—L]l  ì 좸iŒ8âèF7bˆ!žx¢ˆ"‘D"ˆ ‰¤¿=)$‘D$’HQ$@4Ñt§;±Ä¶Z+žxâˆ#‰$ºÑ8â‚kI’$I’$I’¤#Ï@ˆ$I’$IÒqj{¸œË‰'žWy•bÂ]’tT¤¤¤——G^^^«c…EJKKihh®ÑVX¤£ Êñª¹sC ^L^PPÀO<Á…^HUU™™™\~ùå<÷Üs®•““Ãüùóù—ùî¼óNçw¿úÕ¯6lXÈs<ÿüóÜ{l9’;v0xð`zè!žþùóf̘Á׿þu6lØ@¿~ý‚¡†Î¬ÑѾáïÝ' Äüùó¹ï¾ûøÖ·¾ÀùçŸÏüùó8p`‡¯[göŸÍ+¯¼Âw¾ón¹åêêê:t(?ùÉO˜>þ€Ï³—½TSòýá~­¤ò€çTQEu¬¯9Ò2(ÒÖ÷:Þòû^ô"–ØÃûI’$I’$I’t 4Ú“]’:%@€9Ìá® w)’$°ž~úi¦Nî2$©Kk +¹’…,ä}Þg ü é$SSSÃúõëÛ Œ±wï^ ã°HvvöIsѺt ÐMgÌ;—ßÿþ÷<ÿüóGd½®¬¶¶–uëÖµù»æóÏ?g÷îÝÄÆÆ’™™üý2tèP† FNNýû÷'222Ì;9tÍ‘]좚jv²“Ýì†Jö°‡}죂Šà¹;ØA5Õìa•T²}ìbUTƒ&»ØE-µ>wsˆ$™d∣;ÝI"‰8âˆ'žD‰#ŽDI 8âèA’IÎ%’HzD‰$²=Â/^ Ðf°S’$I’$I’tpsæÌáškB¯_¶Cˆ$I’$IÒqèîá Þà-Þ2 "µ#&&¦Ã eeeÁ¿Ôß²³ÈŠ+¨ªª ®‘••ÕfXdèСtëÖíXnIêò|ð?øÁxæ™gÂ]ÎS]]MYYY«.ÅÅŬY³†úúz 4`–ŸŸÏÔ©Sƒ÷ ìÎr¢‰k)¤ñµë©g';Ùê©f;‚ÝIö˜ìc»ÙÍNvRM5»ØEeÁ€Énv³—½ìd'•TÒ@C›ÏK, $Ô4šC#‰$’D=è&I&™M#‰$’I&‰$â9pgI’$I’$I’‡I’$I’¤ãÌ3<ÃLfò<Ïsç…»鸕‘‘AFFF›Ç***ÚükÿÏKOOþ…ÿ–·AƒÑ£Gcµé°5ŽD—1cÆpÇwpÖYg‰ÒŽ™ö>÷ÅÅÅ”””_—–¡n¸!ø;`àÀ$%%…y'žH"IiGZs°d/{©hûßß®Œ2 ) ™ÛÊÖv;™4eÚÝèÖáñTRíV"I’$I’$IêI’$I’¤ãȼÁmÜÆ£<ʦ„»é„•’’B^^yyy­ŽUWW³zõê6»‹´×)`ÿÛ‰Ü)@ǧà éuŽ–æÐÇþ]>V­ZEee%ÑÑÑôíÛ7¤ÓGó÷C† !>Þ®'ŠnM#…2h; x°ª¨¢²iì`GðûæûÍ£’J¶²•U¬ 9¾›Ým®›@BHH¤'=úûþsF’$I’$I’NtB$I’$I’ŽEq-×rWñ †»é¤ǰaÃ6lX«c555¬_¿¾ÍÎ"EEEìÝ»7¸FFFC‡mÕa¤ÿþDFFëmI'„ö>ƒÅÅÅ|öÙgìÙ³øûg0''‡Ñ£G3uêT?ƒ:,‰M#‹¬Cz|uÁ€H!¡’–ÝJ¶³2ÊXƲ¹Z­™Lr0ÒV`¤W‹‘Jjðû"÷å$I’$I’$#B$I’$I’Ž›ØÄD&ò¾À,fùŸ¥.*&&&xQy[***Zu&(((`åÊ•ìܹ3¸FVVV›EN;í4ºwï~,·$u9ÕÕÕ”••µú,·Û¥'???$ôa—u5QD‡¢98²í!A‘ýçV²2øý6¶QEU«µöˆ4ßomÄ•$I’$I’$…ƒI’$I’¤.n/{¹‚+ˆ$’Wx…XbÃ]’¤C”’’˜1c3fL«cmv5((( ¤¤„ÆÆÆà999­º‹ 4ˆ=zë-IGE{Ÿ‡âââv?ùùù~tÒJjÙdwêqûØÇ¶c+[Ù–ûå”SD[ØÂV¶¶"éIÏHoz“F§p »zî"¥.…(¢èÝ4¢‰>B;—$I’$I’¤“›I’$I’¤.¬‘Fþ`+x÷èMïp—$é(III!//¼¼¼VÇÚ눰hÑ¢v;"ì³#‚ºš¶:æwØ1§eèÃŽ9Òá‹%–Œ¦q°j¨ †Eš$IÙ— IDATƒ#Ía‘æûÙÈÇ|̶°9{3µÚ5zÒ“S8…Þô&•TÒI~ŸÖ4RI H$I’$I’$Im3"I’$I’Ô…=À¼Â+¼Îë fp¸Ë‘&qqqÁ‹à'Mšr¬¦¦†õë×·ÙYä³Ï>cÏž=Á5222Úì.Ò¿"##ñ5ÀÚ{o·ùÞlîò1uêTß›RC éMã`,þëbª"«H?3=ÙÄ&6³™­le3›)¤0xl [h¤1øø(¢‚á4ÒH'4ÒÈ ƒS8…L2ƒóÉ$­mK’$I’$IR—d D’$I’$©‹ú%¿ä?øf1‹/ñ¥p—#©‹Š‰‰ ^<ß–ŠŠ Š‹‹C:1,Z´ˆçž{ŽÊÊJ¢££éÛ·o›E† B||ü±Ü’Ž#ÕÕÕ¬^½ºU—âââv»×ìú°{tâK¬OäÔ¦q õÔÃ!›ÙL9åli›šÆg|ÆF6²™ÍÔò÷î#qÄч>!a‘S8…tÒéÓ4šE}4·,I’$I’$IÇ„I’$I’¤.èÞá6nã{|¹1ÜåH:Ž¥¤¤——G^^^«cÍa‘¶º‹”””ÐØØ\£ùâý–ÝEHRRÒ±Þ’Ž±öÞ'Í·f)))Á÷G~~~ð=3hÐ zôèÆH:žD oH#lne”QN9›F9å|ʧlf3Ø@U!=¥id ŠdA_ú’N:}éKiDb—"I’$I’$I]—I’$I’¤.æs>ç ®`2“y„GÂ]ޤXGa‘êêjÊÊÊZuyá…Úíü°ÿÍÎDz²²6»|¬X±‚ªª¿]DCVVVH§–A¡nݺ…y’N6¤5/ð…ÏÝÃ6²1Øe¤Œ²`Xd3›YÂ’`בfÍá”,²È ƒ,²È$³Up¤;ÝöV%I’$I’$©MB$I’$I’º­le“È@f1‹^H-)<âââB.üo©¶¶–uëÖµÙYäóÏ?g÷îÝÄÆÆ’™™Ùfw‘þýûé_]?VjjjX¿~}›>ŠŠŠØ»w/ðÉÏÏgêÔ©ÁûÙÙÙDDD„y'’thºÓܦё}죌26°õ¬§Œ2Ö±Ž2Êø+eóØÈFj¨ >&™d2É$‹,ÒI§ýH'L2éÛ4zÑëhoQ’$I’$IÒIÈ@ˆ$I’$IRQM5“™L=õüžßû—†%uYÑÑÑÁ@[***‚aƒæî"‹/fΜ9TVV×èÛ·o›E† B||ü±ÜÒ ¡åë¾ÿ­´´”†† ãÐG{?SI:YÄË€¦Ñ‘ *(£Œl ~-¦˜2Êø”O)£ŒMl¢‘Æàº™d’C餓A9-F_úMô±Ø¢$I’$I’¤ˆI’$I’¤. ‘Fná–±ŒE,âN wI’tÈRRRÈËË#//«¯¾:äX{¡…‚‚JJJhll ®Ñ2¤ÐÜ]$77—äääpl«Kè(ôQ\\ @TTýúõ }4?xð`ü I:þ¥4a k÷œ=ìa-kYÇ:Ö6RJYÇ:þÌŸYǺ`§‘H"I'þM£_ÓèK_²É¦ýèAcµ=I’$I’$IÇ !’$I’$I]ÀC<Ä\æò¯q:§‡»I:jZ†Eö·oß>6lØÐª»ÈK/½Äš5k¨¯¯®ÑVg‘œœ @ 8ÖÛ:bêêêX»vm›a+VPUU@LL YYYm†>†J·nݼIRwº3¤i´¥‘F6²1iy×YËZ¶³=x~2Éô£_04’M6@9 `I$«­I’$I’$Iê" „H’$I’$…Ùlfó}¾ÏÏøù䇻I ›ØØØ`¨aµµµ¬[·®ÍÎ"Ÿþ9»wﮑ™™Ùfw‘~ýúþÿ,¾ð¥å­°°êêj 4ø’ŸŸÏÔ©Sƒ÷³³³‰ˆˆóN$I‡#@€Œ¦1ŠQmž³‹]¬aM«ÐÈ–ð*¯² 4ò·îZ½èiùµýˆ&úXnO’$I’$IÒ1þÿó%I’$I’t{—w¹™›¹Ÿû¹•[Ã]Ž$uYÑÑÑí†E***Z+/^ÌܹsÙ±cGp¾}û¶ÙYäÔSO%!!áˆÕ»=-o¥¥¥444í‡>† Fzzú«G’t|J aM£-5Ô°žõï7 (`5«ÙÁŽà¹)¤ÓÎÀ¿¶$I’$I’¤“•I’$I’¤0YÍj¾ÊW¹ŒËø7þ-ÜåHÒq-%%…¼¼<òòòZk/œQPPÐn8£ew‘ÓO?äääƒZ·°°ÂÂÂvC(ùùùG-„"I:ùÄ u´eÛ(¡„bŠC¾¾Ìˬe-µÔO|02h¿‘E–aI’$I’$©‹2"I’$I’ÛÙÎD&Ò~<ÇsDî’$é„ÕQXd÷îݬ^½ºÕmîܹ¬[·Žºº:zõêEnn.½{÷¦¤¤„ââbª««èÞ½;¹¹¹äææòÅ/~‘)S¦ï÷ëר(ÿS¼$)b1‹yŽçx„GèK_F0"$(Ò›ÞáÚ‚$I’$I’‘$I’$I:d3™É.vq—ñ_üwpñ7r#ßä›LcZ¸K”$I’$¤žôd|ÓhVI%Ÿò)‹›Æ¯øñP›DF2’S8%Œ;$I’$IÒÉÆ@ˆ$I’$IÒ!(¥”wx‡Æ¦1i,bÿÇÿqð3~î%I’$I‡)‰$Æ4f[Ùì"²˜Å<˳ÁN"Ùd"#Á¹œKIá*_’$I’$I'8!’$I’$I‡à—ü’(¢¨¥€F™Ë\ÒHãü‚(ÿ³‹$I’$RIåâ¦Ñl;XƲ`HäøäAi$‡F3š<òÃÎâ,"ˆã$I’$I’t¢ðÊI’$I’¤Nj gx&iVO=›ÙÌE\ļA?ú…©BI’$IÒ±”Lr«N"å”ó>ïógþÌ{¼Ç˼Ì^öÒ“ž|‘/2ŠQŒf4çp $„±zI’$I’$¯ „H’$I’$uÒ¼ÁF6¶y¬Ž:V±Š<ò˜Ï|F0âW'I’$Iê ÒHcrÓ€¿ýûâr–³ˆE,d!ÿËÿòI$§rj°ƒÈhF3”¡„y’$I’$Iêê „H’$I’$uÒÓÌÕK’$I’$éX3"I’$I’Ô OñõÔ‡ÌEA œÇy<ÁœÎéaªN’$I’t¢Én×s=ð·Î•ïð YÈù#ó8Dp&g’O>£Í\à)$I’$I: ‘$I’$I:HæÏ¬deÈ\$‘d’ÉøWsu˜*“$I’$,Ná®nå”ó'þÄBR@?ä‡DÉp†"r!=èæÊ%I’$I’t¤‘$I’$I:HÏð ÑDSK-ÑDI$÷s?ðqÄ…»þà/V©©©aÍš5}>À¶õÛÈ]–Kò;É5QD{÷º:x^óëØ¬ªªŠºººN=W[Zî±ùul“tt,!!ØØX’’’‚a•¤¤$bbbHLL$>>žØØX’““ƒá–ξþ’$I’¤ãG_úrKÓ¨§žOø„ ø¿ãçüœ"ÉH&1‰|ò9›³ 8ðÂ’$I’$I:æ „H’$I’t”ìØ±ƒªªª[[sìÚµ+dn×®]ìÛ·ÊÊJª««Ù»wïŸ/11‘˜˜˜VþGDD/ðOMM%..ŽÈÈHzôè<§[·nÄÅÅEbbbȱæ.-5?gTÔßÿó‚A‚Û¿›IË.+Íáž–ÝUš%Íǚ×j>Ö² KYYYð~sP¨9¼²g—¶4¿Gšö)))ÄÅÅ‘@=HNN&111äÖÖ\JJJHI’$IR×I$yMã~îg [XÀ (`&3y€H# L`“Ïx’IwÙ’$I’$Ijb D’$I’¤vìÞ½›íÛ·SQQAEEEðû¶æ***عsg0ÌÑ|Q~[IHH¹`¾ùk¿~ý‚ÇšÃÍAý»:ÄÄÄ’’Òéî ¯æ N³–]SŽ¥ÚÚZvíÚÅ®]»¨©©i·ÛLËIuu5UUUìܹ“ÒÒÒ ÓÎ;; šÄÅŵzÏ÷ìÙ“””RRRÚü¾åœ$I’$éèëMo®nOò$á/¼Þ4®ã:"ˆà<Îãâ¦q&g†»dI’$I’¤“šI’$IÒI¡¢¢‚Í›7³e˶nÝÊæÍ›ƒ÷Û yÔÔÔ´Z§ùbö–¬÷ë×áÇӣG6;#ì?'uÑÑÑG%lÑiFµ×)gçÎlß¾õë×óé§Ÿ†®ö•vƒ#©©©ôîÝ›ÔÔTúôér¿e÷I’$IRçDÁȦñ0³müÿGü”Ÿò¾Ã)œÂ—ù2Ws5˜@,±á.[’$I’$é¤âÿ—$I’$—öîÝËúõë)//oðhë~mmmÈã“““IKK#55•ž={Ò«W/ tÀn^`.u,!!„„„ÃZcÇŽÁpH{y¶oßÎgŸ}ÆÖ­[Ùºu+[¶l¡¾¾>dÔÔÔ#}úô¡wïÞ¤§§Ó«W¯ÃªY’$I’Nt½èìÒH#ó1¿å·Ìc/ðI$q1s9—s1“Lr¸K–$I’$I:áy‹$I’$©K©©©aëÖ­lܸ‘²²²v¿nÚ´)¤‹@\\)))dddžžNzz:C‡ Þowddd••ELLLw)©#ÉÉÉ$''3`À€N=nïÞ½ÁßÍÁ‘–÷×­[LJ~HYYY« Xll,={ö þÎèè«$I’$ì8»i<Â#¬aM0r7 À…\Èå\Ε\I:éá.Y’$I’$é„d D’$I’tÌÔ×׳aÃÖ¬YCii)kÖ¬aÍš5”••±nÝ:ÊËËÙ¼ysÈcÒÒÒèÓ§YYYôéÓ‡¼¼¼àEÙÍh§¦¦ðD·nÝÈÉÉ!''ç€ç666» 5‡Í6lØÀÆY¿~=Ÿ~ú)óçϧ¼¼œšššàãâããéÛ·/iiideeѯ_?ú÷ï¼eggÓ­[·£¹MI’$IêrúÓŸ;›F¼Ækü–ßòp'w2šÑ\ÅU\É•d‘îr%I’$I’NB$I’$IGLMM ëÖ­ =ö~¬_¿>øùcbb‚Rgff2|øpÒÓÓƒÁ¬¬,ÒÒÒ zH:*½{÷¦wïÞ 6¬ÃsËËËÙ´iëׯgÓ¦MÁàȆ øýïÏš5kعsgðü´´´HsP$;;›þýû“˜˜x´·'I’$Ia“B _oÕTó&oò/ñÿñÿqw1”¡\ÍÕLa îr%I’$I’ŽkB$I’$IR__Ïš5kX¹r%Ë—/gùòå¬\¹’+V°nÝ:€¿ý¥þЯ_?N=õT&L˜¼0zÀ€ôéÓ‡ˆˆˆ0ïF’,--´´4†Þî9m†áÞ~ûmÖ¬YÃÖ­[ƒçöêÕ‹Aƒqê©§2xðààmРAv‘$I’tB‰#ŽIMcûø#ä%^b&3y”G9—s¹–kù_#ôp—+I’$I’tÜ1"I’$IjÓÖ­[ƒ+V «V­bß¾}ôîÝ;xAóE]Dnnn0ôqÊ)§„y’t줤¤’’™gžÙæñÝ»wSZZ¼­X±‚åË—óî»ï²fÍêëë ôë×/$$Òü;¶ÿþ†è$I’$×b‰ †Cj¨¡€æ2—‡y˜{¹—±Œåz®çJ®$™äp—+I’$I’t\0"I’$I'¹æŽ………,^¼˜Å‹STTDqq1111dee1tèP.»ì2rrrÈÉÉáôÓO§OŸ>a®^’Žñññ 6ŒaƵ:V[[˺uë(..¦°°¢¢"–/_Îüùó)))¡±±‘˜˜H^^^ðvæ™g’†ÝH’$IÒá‰!†‰Mã)ž v¹“;¹ÛÏx®æj®äJðß{$I’$I’Úc D’$I’N"•••|òÉ',Y²„¥K—òÉ'ŸPXXHuu5ÑÑÑœvÚi >œÛn»áÇ3dȲ²²á.]’NXÑÑÑÁ°]~~~È±ŠŠ V¬XÁÒ¥KYºt)K–,aÞ¼yTVVANNgžy&gœqÇçÌ3Ϥ_¿~aÚ‰$I’$u^ËÎ!?á'ü†ßð¿ü/ÿÀ?0i\ÅUÜÌÍŒa üoT’$I’$I-‘$I’¤XYY‹-báÂ…,Z´ˆ?þ˜††’““6l£GfêÔ© :”¼¼<ºuëî’%I-¤¤¤0räHFŽ2_VVìèTXXÈìÙ³™>}: ôéÓ‡#F0fÌF͹çžKLLL˜v I’$I/‰$nnå”3‡9<Çs\Àä’ËÜÈMÜDú‡»TI’$I’¤.Á@ˆ$I’$ êëëY²d ‹- †@6lØ@LL yyyŒ7އzˆ#F™™îr%I‡!##ƒŒŒ &Mšœ«ªªâ“O> þsà‡?ü!Û·o'!!‘#G"£F"111ŒÕK’$IÒ¥‘ÆM£ˆ"žçy~ÆÏx„Gø"_äFnäz®'„p—*I’$I’6B$I’$é8V^^Îo¼Áïÿ{ ¨¨¨ 11‘‘#GrË-·/þµó‡$ø9ÿüó9ÿüóƒsÅÅÅÁ.Q/½ô>ú(œyæ™äççsÙe—qÞyçÆÊ%I’$©cCÊã<Îc<Æ|æ3‹YLc÷r/×s=S™ÊÙœî2%I’$I’Ž9!’$I’tœùüóÏyùå—yùå—Y²d Ý»wgìØ±<òÈ#Œ7Ž¡C‡za¯Ô ãÇçÍ7ß wÒQ‘““CNN7Þx#7näÝwßå7Þà…^à?øiii\qÅ\uÕUŒ;–ÈÈÈ0W-I’$Im‹&š¯4­låøžæižâ)Îá¦2•k¹Ö®!’$I’$é¤áB’$I’tغu+3fÌà _ø§v?ýéO=z4o¼ñÛ¶mãøÓ¦MãôÓOï2a@ Ðê–’’¨Q£˜;wn¸Ë «æ×ãdxîpîuuuuL:µÕ|CCC«¹ÂÂB¾ô¥/‘˜˜ØáZ¾¿WÚÛ±xþñó0uêTêêêŽÊÚ‡#==k®¹†_üâ¬_¿žO>ù„iÓ¦ñþûŸOŸ>}¸óÎ;Yºti¸K•$I’¤¥’Ê·øEñqgqw‘AÿÄ?ñWþî%I’$I’Žº®q•$I’$©MK–,áºë®#++‹Gy„Ñ£G³`ÁÖ¯_ÏOúS&L˜@\\\¸ËlScccðkcc#µµµ,[¶ŒÛo¿iÓ¦ñ“Ÿü$̆Oóks2?Ñ«=¨Ÿ‡óÏ?Ÿxਭ$†΃>ÈÇÌÊ•+¹ûî»yýõ×>|x0À×VhJ’$I’º’<òxЧXËZ¦3?ñ'òÈã‹|‘ÿå©¥6Ü%J’$I’$B$I’$© *,,dòäÉœuÖY|þùç<ùä“”••ñóŸÿœ /¼ÈÈÈp—ØiQQQdffrã7òâ‹/òŸÿùŸá.I'‘’’/^Ì”)SêüO>ù„+¯¼’ØØXòóóOÈÀ‡Ž®n¸¿üå/¬]»6Ü¥´òÝï~—åË—óöÛo“Íu×]ǰaØ3gN¸Ë“$I’¤êE/¾Í·)¢ˆ·y›L2¹‘É&›ÇxŒÍlw‰’$I’$IG”I’$IêBöíÛǃ>ÈYgÅúõëùíoË_ÿúW¾ñoîòŽ˜sÏ=—²²²VóË–-ãÒK/%11‘ÄÄD.¾øb–-[rN  ðÙgŸ1jÔ(ºwïÎùçŸÏªU«(,,däÈ‘$$$0nܸ6/Ä^¶l'N >ÇĉCž£yý@ À‚ ˜={vÈ|gê팃]oÞ¼yŒ=š¸¸8²³³¹ûªªju^aa!'N$!!¤¤$®¸âŠv/NïÌk¿zõj®¼òJRRRZ½&2pàÀàcƌөJKKC~«V­b×®]!s¥¥¥m>ïo~ón¸á†VómÕ¨««#**Š@ pX],XÐæ{g×®]$&&†|šŸ¯å{îç]YYÉÝwßMNNqqq¤§§sóÍ7óá‡vX_sM-k¼å–[BÎY·n“'O&11‘´´4¦L™Â¶mÛZ­åç¡õçaÊ”)¼ú꫇ü„K `ìØ±Ìž=›¢¢Aé¬ IDAT"Î9箿þz¾üå/SRRîò¤.!P__Ï¿þë¿’Mll,ƒæ‰'žhuîï~÷;FE||<ñññŒ5Š?üáa¨Z’$éä ÀXÆò2/³–µü#ÿÈñ_ô¥/×p ïñ^¸K”$I’$I:" „H’$IR±cÇ.¹äžxâ f̘Á_þò&MšÔ©‹í~ø!ÙÙÙ!s«V­büøñLš4‰ââbJKK™2e “'OfýúõÁóš;5LŸ>gŸ}–72|øp¦L™ÂÃ?̬Y³Ø°a§v÷ÜsO«ç¸ä’K¸êª«X½zuðBî‹/¾˜U«VPWWGnn.Û·ogìØ±\{íµ”••ѳgOvíÚÕ©zVgÖ›ù¤ÕûªÙ[o½Åˆ#ZÍ·Õù£y®±±‘ÆÆFüñNí±¥±cÇòÑGÑ£Gêêê‚ó¯¼ò IIIÌ;78WWWÇ!Cøè£‚ï=8¸Ÿ÷M7ÝDrr2ï½÷•••¼öÚk¬ZµŠ‘#GvXßþ{mllä™gž 9ç–[ná¶ÛncãÆ|ôÑGÔÔÔpï½÷†œãç¡íÏÈ#xóÍ7;½ÿ®äÔSOåùçŸçÝwߥ¬¬Œ‘#Gòþû,©K¸ýöÛÙ»w/lß¾ÿþïÿæ?ÿó?C:ê¼ÿþûÜ|óÍÜyç”––RRRÂ?ÿó?»I’$éèK'éLg-kù)?e9Ë9ó8Ÿóù¿£»‚J’$I’¤ãW ±­+$Ií `s¸†kÂ]Š$I'¬§Ÿ~š©S§†»Œcª¡¡üü|V­ZÅk¯½Æé§Ÿî’Žˆ@ ¼hº¾¾žòòrÞzë-î»ï>~ðƒpã7Ï2e gœq÷Ýw_È¿üå/Y²d 3gÎ YwÁ‚\xá…”••‘™™2·~ýzòòò(//yŽsÎ9‡»îº+ä9f̘ÁÇÌ /¼Àw¿û]233¹ãŽ;‚ç<þøã¬Y³†'Ÿ|²Óõèµ9Üõ***8p`H׆n¸#F´Úë¬Y³øÆ7¾qÈÏxûí·C Ò¼×¢¢".½ôRæÌ™Ã¹çžrNgj8ûì³yöÙg>|8óçÏçG?úo½õV»5 4ˆ÷Þ{ÔÔÔvë;ÐÜö×žÆÆFÒÓÓyõÕW5jùùù<ðÀ<üðÃ,Z´ø[a̘1lܸ±Ã X[?ïÄÄDÖ¬YCÏž=ƒs%%%äääpÕxõÕW™ N]qÅŒ?žÛo¿=ä¼'žx‚·ß~›W^yåXU.Ia³xñbòòòÂ\‰$ýÝðc~Ìø§q÷r/_çëÄîÒ$I’$I’Ú˜3g×\zý²Iê$!’$}'c ä¹çžcêÔ©|øá‡Á‹ÜOm]ÔžžžÎóÏ?O~~~È|Ÿ>}øàƒèß¿È|yy9ãÆ£¨¨(dÝ;w’˜˜ü-PÙj.**І††>GII çw7n`åÊ•\wÝu|ôÑGÁsÌìÙ³9ûì³;]o{¯MË%?VëmÚ´‰ôôôC~î@ ÀîÝ»éÞ½{‡õì_[qq1ùùù<óÌ3Œ7®Õ9©aæÌ™,_¾œŸýìg\zé¥Ü~ûí\zé¥íÖгgOÊËË‰ŽŽn³¾£¸ùæ›4h>ø 6làòË/ç/ù ]tÏ<ó  `ÆŒ,]º”Y³fuú9'L˜Àž={øÞ÷¾G~~>QQQUûêlß¾”””à\}}=ÑÑÑõÙ:Ù?555ddd°uëÖë=žÔÖÖ2räH²²²˜7o^¸Ë‘޽Õ«W“““r¸¢¢"Øi  ÿþ,\¸¾}û†œ·víZ.¸àJKKQá’>B$ue+YÉ<ÁÓ}Ú¼X²9øÑæÜþ¹oݺ•>}ú´Z+===ä‚íAƒѽ{w–.] ÀŸþô'zôè ƒJ½r°ëmÙ²…[n¹…¬¬,¢¢¢‚çí¯½½¶5×Ù½t& Òìâ‹/f×®]ìÞ½»Íã©áúë¯gîܹìÙ³‡ââbJJJ˜8qb‡Ï_UUÕ©Ä‘vÉ%—;˜üêW¿âúë¯àÚk¯eöìÙüîw¿kµƒýy¿ôÒKœ{î¹ÜqÇôìÙ“ .¸€™3gR[[{ص· ƒDFF¶úlùyh[TT•••·éãDtt4÷Üso½õÖyIdzìììVs)))TTTï———sÊ)§´:/--M›6Íò$I’t1ˆ™Ìd+ø:_g3ÀäA¶±íÀ H’$I’$…™I’$Iê"N†ŽÉÉÉÜ|óÍÜ|óÍ<üðÃ!ÇRSSÿöî<,Êzÿÿøk@6YG@vÁ—DMŲ„¾jáR¹𥶛©©uÒ¬NiÇNºÌìduŽ•••iÙbYzŠ2 ­ìà’J悲ãÊâÂ*üþè7sÜnçãºæš™{îÏ}¿?7~^óÖ±cÇì$ÖÛ©S§.Èùl]@ªÊÉÉQ@@€Ý¶;ï¼So¾ù¦$éÍ7ßÔ½÷Þ{Që­ëñÆ'OOO%%%©¸¸Ø¶£ã9ZhzôhõÅ —âÚÏ™3G_~ù¥î¹çýüóÏçUC`` âââ´lÙ2½öÚkš:uªÃ@UÞÞÞ*//¿ s9ýû÷צM›TTT¤>ø@cÆŒ‘$1BŸ|ò‰òòòôÓO?©ÿþvãêúõöõõÕüùóµoß>¥¦¦jòäÉZ¶l™í<ߎ•——Ë××·^cƒ¦ðû ¨ k µ6AAA:tèPµí‡rJ€1"¡ô‚Ò”¦š¡EZ¤Öj­ÇôÁР€`À€Z³f¶nÝjt)—ĤI“´råJ;v̶mÀ€úþûï«íûÃ?Øuæ8 úä“OªmÿôÓO•`·mäÈ‘Z¹r¥>¬5kÖØ::\¬zëz¼7ê©§žRTT”­ã…£Eêýû÷×gŸ}Vm»µKŹœû|Œ3F=zôÐ{ï½§?ýéOÚ³gÏyÕpûí·ë•W^ÑÊ•+5~üø³žßßßßÐN f³Y]»vÕ+¯¼¢   Û"à-Z(88XóçÏW=äççg7®®_o“ɤÌÌLIF­/¿üR_ýõYk;[˜¦.ø~p¬  @þþþõÓЕ––jÞ¼yºá†äââbt9@ƒ×½{w}ñÅÕ¶þùçìw,.³ÌzLé€h®æê-½¥(Eiš¦é ]@5ÍŒ.ðǧÜ/Y²Dƒ Ò—_~©+¯¼Òè’.ª€€ wÕºëÚUÂÚñºvv¶n¹åíÝ»WÁÁÁzï½÷Îéz:99ÉÇÇGS¦L©S7Üpƒ’““«m?Û{çló<Ûû¼ªo¼Q&“IÆ ³Û>tèP¹¸¸è¦›nª6¦®_ïÄÄDyxx¨wïÞòññѵ×^«ââb½óÎ;g­kþüùºí¶Ûäåå¥É“'ÛB5Íí|Þ¿Ž\Îß›7o¶-oì’’’tõÕWë—_~Qbb¢¢¢¢Œ. hzõê¥Å‹kÁ‚ŠŒŒTdd¤,X ·ÞzKW_}µÑåà,¼ä¥™š©T¥ê¯ú«ÞÐj£6zNÏ©HEF— Seee¥ÑE@cb’I˵\£4ÊèR¸l-Z´Èáü¦ ´´TO?ý´ž{î9uìØQ³gÏÖСCë¼øh*>üðC­ZµJK–,©Óþû÷ï×wÞY­ p1õéÓGK–,Q«V­Œ.圥¤¤è™gžÑÒ¥K5pà@½öÚkŠŒŒ4º,À8~(-ñgu¨3k0;66ÖàJàüÔI-ÔB=£gä-o=©'u—îR353º4p™3™LZ¾|¹F²_¿L‡0ÀÉ“'•••¥;wjãÆúꫯ´téR½ùæ›òððиq㔟Ÿ¯áÇëÊ+¯Ô믿®ãÇ]6`8“ɤM›6é¹çžÓƒ>Xçq­[·V÷îÝmI€‹í½÷ÞS=e¤¢¢Bß|óFŒ¡Î;kË–-Z¾|¹¾úê+ š4Oyê=¢}Ú§±«©šªÎê¬ô‘*Eh\z|LœƒÂÂBåçç«  @ùùùµ>ÎÏÏW^^žÝ¶òòòjÇtqq‘¯¯¯üüüäçç§èèhuèÐAš:uªzè!5J·Þz«úôé#gggf/>>^“'OV·nÝê5îùçŸ×ý÷߯±cÇ^¤Ê€ÿY¿~½^}õU£Ë¨—]»véÃ?ÔÛo¿­ýû÷«W¯^úðÃ5|øp99ñ¹2` ýCÿÐݺ[ëqÖhÅ+^ÿÔ?ÕU].4!B4Yååå:zôhµÛ‘#GtäÈ=z´Z¸Ã쨨¨¨v<777[˜£j°Ãb±Ø;zÝúØÓÓ³ÆZ,X ÷Þ{Oo½õ–/^¬ÀÀ@ >\Æ SŸ>}äááq1/Ð`TVžû§m6kÖL¯¿þú¬¨Ù¢E‹Œ.á¬*++µeË}ñÅZ±b…vìØ¡   3F÷Þ{¯:vìht‰Ð µU[}¨µI›4MÓÔC=t¯îÕÓzZþò7º<ÐpY(**² u>|ØaØ£êë………ÕŽããã#Êßß_~~~ ²…7Ìf³Ã0‡ŸŸŸÜÝÝ/ÚüZ´h¡©S§jêÔ©Ú»w¯V¬X¡+Vèõ×_—»»»z÷î­êúë¯WLL Ÿäp(;;[?üðƒÖ¬Y£5kÖ(77Waaa>|¸.\¨øøx:P@=]­«µQµB+ôýE˵\³5[“5YÍX–."þò Á)))Ñþýû«…:Ž;VcàãÔ©SvÇ0™Lò÷÷·»©cÇŽv3o...ͺ£5kÖ,Íš5K‡Ö÷߯/¾øBO=õ”¦OŸ.///]sÍ5Š‹‹Sll¬þïÿþO¾¾¾F— 0@jjª’’’´aÃ%%%)%%EÎÎÎêÚµ«î»ï> >^=zôP«V­Œ.påççkëÖ­Ú°aƒ6nܨ7*??_ÞÞÞ¶ `\\œ®¹æyyy].Ð8}ø¡4z´ÄŸÕ Î’““%I±±±W—Öïú]Ó4M_ëkݧûôýC¾âC[À¹1™LZ¾|¹F²_¿Ü´VG¸  •““£C‡éàÁƒÊÍÍÕ¡C‡”››«ƒêСC¶×‹ŠŠìƶlÙR RHHˆ®ºê*+88XÉÉÉ7nœ­‹‡···A3l\¬ŸøÞµkWM™2E’”­äädÛ'Ã/\¸PeeeòõõU§N£Ž;*66VÝ»wWóæÍ ž .¬?ßSRR´sçN%''ë·ß~See¥BBB«Y³f)..N={öl°àrÒ^íµFkô‘>ÒMÑgúL/ëeýY6º4p!ÀΙ<uôÈËËSVV– ìÆº»»ÛuêèÒ¥‹ `·-44Tµ.L-))ÑUW]u±§Ú$„††*44Tƒ–$?~\Û¶m³Ý¶nݪ÷Þ{O§NR³fÍÔ¾}{]y啺òÊ+ÕµkWµoß^rrr2x&Ð4;vL¿ÿþ»¶oß®­[·ê×_Õ¯¿þªãÇËÙÙYÑÑѺòÊ+uÛm·Ù~v‡……]6àÿ©‘JP‚fi–Fi”nÖÍzM¯)\áF—.B€&¢¤¤DGµ z¤¦¦Ú…>233UXXh7®jÈ#44T±±±¶`G}Bh¼½½¯øøx»íÖOš·~Úü;ï¼£Y³f©²²R®®® WÇŽ#‹ÅbwœŸÒÒReffjçÎJIIQjjªÝMúãçw»víÔ±cG9R±±±êÖ­›<== ®p6f™õoý[#4B5QÕY µP·é6£Kà2››«ÌÌL[¨#77WÊÉÉQff¦rrrtìØ1ÛþÍš5SPPÂÃì:¨oß¾ SË–-ª–-[*00PÍšñ¿ MÁ™D¤?>•~÷îÝÚµk—vïÞ­={öhõêÕÚ½{·Š‹‹%IþþþjÛ¶­:tè ¶mÛªM›6ŠŒŒTdd¤BBBŒš48………JKKÓ´ÿ~íÞ½ÛvËÈÈPEE…œœœ©víÚéŠ+®Ð°aÃÔ®];µmÛV‘‘‘2™LFOpú«¿vh‡fi–ÆiœVi•^Õ«2Ëlti ‘beÐÀUëæaýÄðììl¥§§ëĉ¶ýÏìè1hÐ Ûcë}«V­zà¬Z´h¡k®¹F×\sÝöŠŠ eddØ2ÿþûïÚ½{·Ö­[§ŒŒ •——Kúã½h ‡T½EEE)**J¡¡¡rrr2bjpÁ;vÌø°Þ[§¥¥)//϶oË–-Õ¶m[µoß^7Üpƒíqtt´ÜÜÜ œàbk®æú§þ©!¢;t‡:ª£ÞÒ[¨F—!V€*))±-ÍÌÌTzzºÒÓÓ•™™©ÌÌL¥¥¥éäÉ“¶ýÍf³ÂÃéèèhõéÓG­ZµRDD„ÂÃÃ&g„¦Àú ö‘‘‘êׯŸÝkååå¶÷nZZšöï߯´´4íÛ·Oß}÷222TZZ*IruuUxx¸¢¢¢n -………ÙƒY ÀP•••:xð rrr”••¥œœeggÛnÖÐÇñãÇmcBBBl¸T Ä5oÞÜÀ‚%h‡vh²&ë&ݤô€ž×ór uG ¸ˆJJJ”••e×Ñ£j‡´´4>}ZRõÎݺu“Åb±=ŽŽ–¯¯¯Á3j׬Y3Û‚gG***l ¨«~r~vv¶¶mÛ¦ÜÜ\ÖÇj­ÖF— !Ày8uê”RSS«-nOKKÓtèÐ!Û¾f³Ùöéà1114hÝâÑ-Z8àÒprrRxx¸ÂÃÃ_ã~yyyv!ªª÷Û·oWvv¶<¨ŠŠ Ûwww™Íf™Íf[¸ª¦çòññ¹Sp Ù~Väåå)//¯Æç‡Vyy¹m¬õgˆõçÅ•W^©*44ÔÐ “ŸŸŸ3\ŽFj¤º©›Fh„®ÒUzOïi ]h„ga]˜îèvàÀÛ‚t³ÙlëæÑµkWýéO’Åb‘ÅbQ›6mX@ ÔƒÙlVll¬bcckܧ¸¸X999ÊÍÍÕáÇuäÈåææêÈ‘#:|ø°>¬íÛ·Û^+))±ïíí­   ªE‹¶°HÕÇŽžóÉÿÀÅUQQ¡¼¼<;vÌâ¨úøÌçÖïñÇ«²²Òv'''( @jÙ²¥"##Õ£G¨eË–jÙ²¥F.€¡¢­Ÿõ³¦hŠnÒMš©™zFÏÈINF—0!hòJKK•––¦}ûö)55Õvo}|òäII’›››¢¢¢Ô¦MuèÐA7ß|³-ðѺukyzz< iqwwWëÖ­Õºuë:í_XX¨ƒÚ-·H¬ Ë8`·è¼¨¨¨Úqš7o^cXÄÇÇGÞÞÞòöö–Ùl¶=¶Þüüüäãã#ggç }9€£  @Ç·Ý •ŸŸo·íøñã5†> ªÓÙÙÙaX+,,ÌúhÙ²¥‚‚‚l€€99±hÐx¸Ë]oè õPM×tmÓ6½¯÷ÕBt—Ž@“púôi¥¥¥i÷îÝÚ³g~ÿýwíÙ³G{öìQzzºNŸ>-IjÑ¢…Ú´i#‹Å¢AƒÙº{X,…‡‡³°hÄ|||äã㣶mÛÖyLQQQ] ª>?tè~ÿý÷j à«v+¨ªyóæ¶ˆ¯¯¯|}}í‚#>>>òôô”«««Ìf³\]]åéé)///¹ººÊÏÏOîîîòðð\]]åããs¡.šˆ¢¢"«  @¥¥¥:~ü¸N:¥’’åçç«´´T'NœÐÉ“'UZZª¼¼<Û:ª¾ç?î0Ìaåååe÷>·†:¢££ÏÚ¡‡÷7 )™¨‰ŠU¬þ¬?ëZ]«/õ¥¢mtY "€ËJVV–öìÙc ~Xé©©*--•$¨]»vjß¾½úôé£èèh[ðÃÏÏÏàhH<<<äáá¡ÐÐÐsâĉj- ·vT(,,TVV– íæ—””غ­^wwwùúúÊÕÕUÞÞÞjÞ¼¹ÜÜÜl'''ùúúJ’­[‰uœ³³³mὯ¯¯œœœlã]\\äåå%Iòóó“Éd’$»íÒÝ”š7o~N׫)(++Ó‰'lÏKKKí¾¶'NœPYY™JJJtêÔ)UTTØBÇWyy¹Š‹‹UTT¤Ó§O«°°PÒ]9***l!òòr?~Ü6Þ:¦°°P¥¥¥¶qµqDòðð°:,‹]WœÚNUß3àì®ÒUúY?kˆ†¨§zêS}ªÿÓÿ]h`„ Ñ9uê”víÚ¥]»v)%%Åüسgm‘­···Ú¶m«víÚiäÈ‘j×®Úµk§¶mÛÊl6<M…———¼¼¼rAŽwâÄ •––*??ßnIIIµîÖ‰5``}­¤¤D©©©’dëbbíQ5œ——wAj¶Q¬¬+k'Gj{Í‘ú„¬×¥®j»U_«àdÚ8_Öëa j˜L&[ÑËËK...¶@Ž«««,‹$Uë2ãææ&___».3nnnv"/XÁZ¯õº]·«¿úk±ëVÝjtY !€+??_»víÒÎ5<Æh IDAT;máß~ûMiiiª¨¨‹‹‹¢££Õ¾}{%$$hÒ¤I¶È…Z| ‰µG‹-.Ù9ÏìXQYY©üü|ÛëÖ`Š•5xbe ›XY»\XêôéÓÕÎ{f°âl¬õÕ•ŸŸŸ¼½½ë¼¿µ‹Š#ÖN+U]5LQ5ˆXµC‹$»®,’ÎÚ‘4-îr×R-ÕÃzXc5VÊÐ#zÄè²@A †ËËËÓÎ;•’’¢ÔÔTÛãýû÷«²²R®®®ŠŽŽVLLŒF¥Ž;*&&F1115.Î\ÖPŒá,g½¨e‘EÓ5]yÊÓ?ô£Ë \2Ú±c‡vìØ¡_ýÕöøØ±c’þøõ:(&&F½{÷VLLŒ:tè ¨¨(>Ф= ¨@Ó8ªP¯è™ÄßÎhÊ„à‚+++Ó®]»ì‚Û·oWZZš$É××W:uRçÎ5zôhuèÐA;vTpp°Á•ÐpÝ¢[ä,gݦÛT®rýKÿ’“œŒ. „@ÎKZZš-ða ìÚµKeeerqqQ‡Ô©S'Mœ8щŒŒ4ºl¥‘)7¹i”F©LezSo  ‰"€:ËÎÎVrr²íöóÏ?ëðáÃ’¤ÅÄĨoß¾zøá‡£Ž;ÊÃÃÃસ¼ Ñ}¦Ï4\ÃÕ\ÍõŠ^1º$`!¨¦´´T;vìЖ-[´eËmÞ¼Y¿þú«Nž<)ÅÄĨ[·nzòÉ'ÕµkWuîÜY¾¾¾F— @“1Pµ\Ë5B#¤ =©'. \bBš¸'Nè×_ÕæÍ›m;v¨¬¬LžžžêÒ¥‹ºu릻îºKÝ»wW§NäêêjtÙ4yC4DoêMÝ¡;ä'?MÕT£K—€&äôéÓÚµk—’““m·_~ùE¥¥¥òññQçÎuÝu×iúôéŠU‡äììltÙ ã5^ÊЃzP! ÑH4º$p‰¸ŒíÛ·O›6mÒ¦M›ôË/¿hóæÍ***’···bcc§|PÝ»wWëÖ­.–¥K¥ŒŒÿ=ÿõ×?îŸ{Î~¿„)6öÒÕœáq=®ƒ:¨ñ/‹,Šÿ} @S@ à2QPP íÛ·kÆ JJJÒ¦M›tèÐ!5kÖLíÚµSll¬FŽ©øøxuíÚ•Îp6S§JùùR³*Jwu•fÏþßó’iòd!0Ü-Ð^íÕp W²’¨@£K€F¨¢¢B)))JJJÒ† ´qãF¥¦¦J’¢££uÕUWéÑGÕÕW_­nݺÉÃÃÃàŠ =Zzýõ?BµùóŸ/M=@-œä¤÷õ¾z¨‡Fk´¾Ö×jƲ.küŸ?@#PTT¤ÿþ÷¿úᇴqãFmذAùùùòòòÒ5×\£qãÆ©gÏžºúê«åïïot¹py3FzõÕÚ÷ ”®»îÒÔœ…Yf}¢OÔK½4K³4OóŒ. \DB ÂÂBmÚ´ÉÖ$))IÅÅÅ V=4kÖ,ÅÅÅéꫯ–«««ÑåÀå).N •²³¿îê*/9;_Úº€Z\©+õ/ýK·ëvÅ+^Ã4Ìè’ÀEB  HOO×Úµkµ~ýzmܸQ»ví’“““:vì¨øøxÝ~û튋‹SëÖ­.š“I;VzñE©¬¬ú륥t˜q§µZ«‰š¨xÅ+@F—.!ÈÉÉÑÚµkm·}ûöÉÝÝ]W]u•†®¸¸8õêÕKf³ÙèR i3FzþyǯEFJ±±—¶ Žh¾Õ·ºW÷êS}jt9à" p 9rD?þø£6lØ ÄÄDmÞ¼YNNNêÚµ«þüç?+!!Aqqqòðð0ºT@U]»JmÛJ{öØowu•î¸Ã’€ºð‘k±ú©Ÿ–i™nÑ-F—.0!Á‰'ôÓO?)11Q‰‰‰Ú²e‹L&“ºvíª¸¸8=òÈ#êß¿¿|}}.p6ãÆIsçJeeÿÛVZ*m\M@Ü 4Q5ESÔW}¤ £K€  ¼¼\?ýô“Ö¬Y£ÿüç?Ú²e‹*++Õ¥KõíÛW³gÏVïÞ½åããct©€úºõViöìÿ=7™¤Î¥+®0®& Žž×óZ¥UzBOh‘]¸€„œ£¬¬,­Y³FkÖ¬Qbb¢òóóe±X4`À=úè£êÝ»·üýý.p¾Ú´‘ºv•¶m“**¤fͤñã® ¨/yé=£Ûu»&j¢º«»Ñ%€ „@@Y»€¬ZµJ‰‰‰Ú¼y³ÜÝݧY³f)!!A±±±F— ¸Æ—fÌø#R^.mtE@ݦÛôš^ÓÃzXßé;£Ë€Zìß¿_ß|óõõ×_«  @‹E zä‘G4pà@y{{]&àb5JúË_þx|íµRx¸±õõ`’Ió4OqŠÓ*­Ò 2º$p¨¢²²Rÿýïõé§ŸjåÊ•JII‘———®¿þz=ûì³8p Z·nmt™€K-4TŠ—Ö¯—n¿Ýèj€z»V×êÏú³ÓcºY7Ë$“Ñ%€óD 4yeeeúþûïõÙgŸiåÊ•ÊÊÊRTT”†ª—_~Yñññruu5ºLÀ%TPP ¬¬,eee);;[™™™ŠtqÑhggÍݶM-,PHHˆÂ¦ÐÐP¹¹¹]6P«'õ¤º¨‹ÖhnÔF—ÎÐ$:uJß~û­>úè#}ñÅÊÏÏWÇŽ5vìX 4Hqqq2™ød,¸Ü”••éàÁƒÊÈÈPNNŽ233íB999ÊÈÈЩS§lcÜÝݦðÐP­íÛWÛùEYŸ}¦ÜÜ\UTTØö THHˆ"""¢ððp………Ùm 4bÚ€$©“:i€hžæà2@ 4GÕ—_~©U«V髯¾RQQ‘®½öZÍš5KÆ Sûöí.pŠŠŠ”““£ììlåää(55ÕöØzŸ––¦Ó§OÛÆ˜Íf…„„(44TêÙ³§í¹ÅbQHHˆ‚ƒƒåäääðœyyy5žoË–-JMMU^^žmWWWùûû+44´Úy¬ÛZµj%ooï‹~½Ð4=¬‡• mÒ&]­«.œ!à²vôèQ}ôÑGúàƒ”””$777õïß_/¿ü²¬€€£KœEii©Ž9bì83„‘••¥‚‚Û777µhѸˆµ a„„„(**JžžžçU›Ùl–ÙlVLLLûœT©ZûÎ;•˜˜¨ôôt•——ÛÆ¸»»; ‹T½ŒŒ”³³óyÕ€¦çÝ îê®Z ¥Zjt9à<—'NhåÊ•úàƒôõ×_ËÅÅEC† ÑŠ+4`À5oÞÜèÿŸ5,ᨛG]ºz„„„(66¶Zp"$$D&“ÉÀ™ý‡‡‡,‹,K­ûÕÖm$%%ÅöÜÊQ·‘3»ŽDDDÈÇÇçbOÌTMÕ}ºOyÊ“Yf£Ëçˆ@¸,”””è믿ÖG}¤O?ýTEEEêÛ·¯Þxã >\ÞÞÞF—MJ]ºzdffª°°Ð6ÆÚÃj8³«‡ÅbQDD„\\\ œÙÅS—n#ÅÅÅÊÎÎvžIMMUbb¢222TVVfsæuuÔu„n#MËŸô'ݯûõ‰>ÑݺÛèrÀ9"­ŠŠ mܸQ}ô‘>øà=zT×^{­ž~úi3F-[¶4ºD¸,Õ¥«ÇTQQaÓØºz4Tîîîõî6rf'11QÙÙÙÊÍÍUee¥mŒÙlv±n —¯¯ïÅž".oyëfݬô1! ÑÙ±c‡/^¬eË–)''G±±±zä‘G4zôh…‡‡]4Z%%%:zô¨Ãu[FF†Ž?nÓÔ»z4Tuí6rìØ1‡_çóé6ÒªU+5kÆ??4t·èÒ(e)Ka 3ºpøÐ(?~\Ë—/×›o¾©Ÿ~úImÚ´Ñ„ 4f̵oßÞèò ÁsÔ1âÌÐÇÁƒkìêa±Xg·ðßzÆÉî°yjr!»XïÛ´i#??¿K1MÔàfÝ,/yéc}¬©šjt9à Zrr²-Z¤¥K—ª¬¬LC† Ñܹsuà 7Èd2]®jW3»­¶mÛjÊ”)ºýöÛ«Õy®êÒmDú#ÔVS˜-%%E{÷î­w·‘ÈÈHyyy]y€¦£ÚI’vk7SeCù!h$L2i¹–k”F] Ðà:tH¯¾úª^|ñE¹ººjòäÉš6mšÌf³Ñ¥hd-Z¤ &]¸j[““£}ûö)??ß¶ÕÐg~Z¾u[«V­äíím଻Ôá‰Kq>£!ÃÅšKC¹F ¥«ªõ)77W›6mÒ›o¾©œœ}òÉ'jÛ¶­Ý˜ÿüç?zúé§õøã«W¯^ruuUNNŽV­Z¥'žxB¶cΙ3G¿üò‹ž}öYµk×NÅÅÅÚ²e‹zè!mݺնŸÉdR—.]ôÆo¨S§NÚ³g&Ož¬þýûë‰'ž¸´¥jë6RS@Žn#hh’““%©ÖÎ:ã™eÖ?ôݧûŒ.8`2™´|ùre¿~™@Ô ºÜÜ\=óÌ3zýõ×e6›õ—¿üE'N”§§§Ñ¥h¤„Ðø°h¹:! K«¶z-Z¤gŸ}V[·n•¯¯¯$)33SÆ ÓÚµk¾Õ¯_?Û1ƒ‚‚´}ûvµlÙÒn¿””ÅÄÄØB¶mÛfë,"ýÑ…$..N¹¹¹d®FhJa;4>B q°È¢‰š¨™šit)Àš!Í ª\Ž=ªçŸ^ .”ÙlÖ¼yót÷ÝwËÝÝÝèÒ\@5-4¶nËÊÊRAAmÿª -‹bcc«…>"##åååeà¬.½‚‚Í™3G+W®Tvv¶Ìf³  I“&éꫯ®uì¶mÛ4cÆ mذAÎÎΊ‹‹ÓôéÓ5`À€ǘL&Iª¶ßÑöºÔfg½—¤»ï¾[o¼ñ†$iÇŽzä‘G´~ýzIR\\œæÍ›§N:U;÷Þ½{5cÆ ­]»Ö¶H½¦°€£z­ÛÒÓÓ5eÊ}÷ÝwjÞ¼¹úõë§—^zIþþþuïh.5]·Ï?ÿ\Ï=÷œ’““¬áÇëoûÛYÌ×vþ3¹¹¹©¸¸XRÝ®§$íܹS3fÌÐúõëåìì¬ë¯¿^/½ôR­5UµcÇÍœ9S?üðƒ$éºë®ÓóÏ?ïðëV—ë}.&L˜ äädÍ›7OsçΕ$ÍŸ?_÷ß×7!!Áîšž:uÊ&©ªcÇŽvû9zŸùûû«¤¤ä¼æ`4³Ù¬ØØØZÜŸÜ«ú³|çÎJLL¬Üswww©zÕhƒ{à¼ä¥:at ž„€z+,,Ô‹/¾¨ùóçËÍÍMsçÎÕý÷ß/£KPuéê‘––¦Ó§OÛÆœÙÕ#66¶ÚBá¼7e·ß~»ºuë¦üQ~~~JIIÑ< ž={ÖÚÉa÷îÝ=z´.\¨^½zéÀš8q¢Xë¸ÊÊJ‡_GÛëR›uœ£sîÝ»WýúõÓìÙ³õöÛoËÉÉI«W¯ÖСCµnÝ:…‡‡Ûãþûïל9sôþûïëûï¿×M7ÝT¯yX·ÝsÏ=zðÁõþûï+//OÓ¦MÓÃ?¬·Þz«NãÍ¥¦ë6tèPýûßÿÖ7ß|£ÂÂB=òÈ#zàôöÛo×X{mÇ«zîÒÒRÅÇÇë®»î’T÷ë¹oß>Ýxãš3gŽm¿5kÖè–[n©µ&«½{÷êÆoÔSO=e›Ç矮êûï¿Wtt´Ýêr½ÏÕ½÷Þ«»îºËY³fxà:[½ÖmŸ}ö™†jÛöûï¿ëúë¯WVVVÆ×Ö•äl:ÏËËStt´Ž=z^ç’¤I“&)??_K—.•T÷ë9nÜ8õèÑCÓ¦M³Ûïí·ßÖwÞyÖ9Œ;VW]uUµñóçÏ×–-[ôî»ïÚÍ¡®×Û‘³]ƒ“'O*((H'Nüñ©ÔÍ›7W~~¾\]]Ïzlé!?þ¸–,Y¢ž={jРA:t¨ÂÂÂž®uëÖéå—_ÖþýûõÓO?©M›6u:¤ââbeggWûSõwMFF†ÊÊÊlcêÒm$22RÎÎÎÎ Crr²$ÕÚÁ`¼ Vj¥×õºÑ¥L&“–/_®Q£ì×/€z"‚¦jåÊ•š5k–RSS5eÊ=þøãv …àB"@ͬ]=uó¨kWG réêqá¹¾ÿþ:uê”þú׿*!!AÍšÕ­spp°6oÞ¬ÐÐÐz¯¦mçS[MÇ ÖÏ?ÿ¬ÈÈH»íTß¾}•’’bwŒ“'OªyóæµÎ§.s;vì˜]7ˆ’’yxx¨¢¢¢NãÏ'RŸãÖv¼>ø@ûÛßôË/¿ÈËËKRݯgMûåææ*$$ä¬s¨iüþýûÕ«W/».õ¹ÞŽœíšž8qBÁÁÁv¼¼<¹¹¹õØUhõêÕúꫯ´zõjõïß_¯¾újµ³0™LŠŒŒÔˆ#4cÆ ×ë<¨›ªÝFjê:’››k{oÔ¥ÛHxx8NÒÈ€Æa„FÈE.Z¦eF—¨)R·qMÖÖ­[5}út­_¿^#GŽÔ—_~)‹ÅbtY@“1}útmÞ¼YÙÙÙÊÊÊRqq±í5OOOEDDØÈÆÇÇ+44Tááá QDD„‚‚‚äââbà `õÑGé©§žÒäÉ“uøðauíÚU#FŒÐ¤I“jý9rD ²6«£G***ÊákÕ¶Õ' R›ªáIrss«S£¾>¬G}TkÖ¬Qnn®]àê|ìÚµKÓ§O×·ß~k ƒHu¿žGŽqd¨k¸¡¦ñ!!!:räHµíóz§¤¤ØuèhÕª•²²²êý7___ÝrË-ºå–[tòäI?^S§NÕ;ï¼SmßœoÙ8 ³Ù,³Ù¬˜˜˜÷9uê”222”““£ÌÌLÛﻬ¬,íØ±C«W¯Vnn®]·kG«ððpÝyç=zô¥˜—5g9«Bgú€†ÅÉè@ÔŸŸ¯x@=zôPii©6nܨåË—€säëë«ùóçkß¾}JMMÕäÉ“µlÙ23¦Öqþþþç×…ÉdRII‰Ý¶cÇŽ]°Ú¬tìØ1UVVV»:uêœjoHÆ'OOO%%%©¸¸Ø6·óqòäI1BÏ=÷œ:uêd÷Z]¯g@@€rss«ûèÑ£uª! À® ˆUNNÎE!éßÿþ·† f{>pà@}óÍ7çuLOOO½øâ‹úì³Ïη<€‰!ÀNee¥Þ}÷]Íœ9Seeezá…ôÀÈɉ)`„ Ø=/**RNNŽRSS•­œœÛ}RR’rrr”––f×ÁÀl6+$$D¡¡¡¶{‹Åb·-$$D&“éRO¯I1™LÊÈÈPxx¸4zôhõëׯÆNV}úôQbb¢ÆoÛ¶mÛ63F)))µŽ VzzºÚ¶mkÛ¶~ýús®­¦÷È€ôý÷ßkøðávÛøáM›6M›7o®µN#Ôçý¾qãF-[¶L~~~¶mçt™8q¢zöì©;î¸Ã®¦ÊÊÊ:_Ïþýûë³Ï>Ó´iÓìöûöÛoëTCBB‚>ùä=ôÐCvÛ?ýôS%$$œÃ¬ÎÍk¯½¦o¿ýVÛ¶m³m{衇4hÐ =Úîº[}òÉ'Z¸p¡¾ûî;I\»ýû÷W{Ï6kÖÌ®ûŠÕÅè$ƒêòòòªý®:ó÷Wnn®íëáêê*Ûï¦N:©_¿~v¿·ÂÃÃåëëkðÌh„›””M˜0A?ýô“î¿ÿ~Í;×ᢠÆñððÅb©µ{_ii©Ž9b··êBÜäädeffª°°Ð6ÆÝóGa, IDATÝ]f³ÙaXÄz%OOÏK1ÍËÖ=÷Ü£^xAÑÑÑÊÏÏ×K/½tÖ…÷ýë_5|øp…††êÚk¯Õ¾}ût×]wé8ëùúõë§Ç{L/¾ø¢üüü´aÃýë_ÿ:çÚÂÂÂôã?ªGZ»v­î¾ûneddhΜ92dˆNŸ>­¾}ûÊÕÕUëÖ­ÓĉõÊ+¯Ôý]B5ÍÅ‘^½ziæÌ™zì±ÇªôôtÍ;÷œÏýïÿ[Û¶mÓÏ?ÿìðõº^Ï9sæ¨OŸ>òööÖàÁƒåìì¬ÄÄD=óÌ3uªcöìÙêÛ·¯|||4dÈ™L&}þùçš?¾¾ÿþûsžßÙ+77W?ýô“Þxã åææê›o¾±[äߪU+=óÌ3JHHÐO<¡Þ½{ËÃÃCZ¾|¹^{í5­^½Úî¸ãÆÓóÏ?¯N:ÉÙÙY¿ýö›yäÝwß}vûÅÅÅÉd2)))é¢ÍñrW\\¬ìììj¿cªþ®ÉÈÈPYY™mŒ»»»Ý˜˜j¿k"##åììlàÌh\„•––êÙgŸÕ³Ï>«.]ºè¿ÿý¯ºvíjtYΑ«««BCCªØØØ÷³vqô©íÉÉÉZµjÝFΑuþÖŽ’”˜˜¨… ªwïÞ:~ü¸ÂÂÂ4lØ0½óÎ;µ«sçÎz÷Ýw5cÆ %''+00P“&MÒý÷ß_ëù$é…^дiÓÔ½{w:uJ×_½^ýuµjÕêœj›?¾n»í6eee©U«V¶pBTT”>þøc=ú裺çž{T^^®Ž;êå—_ÖСCÖ)Õ­Kƒ£¹Õ4ßúì[Ó\í¿dÉ=üðÃêÙ³§òóóÕ®];=ñÄZ²dÉYÏåhÛ´iÓTRR¢æÍ›;œs]¯§ÅbÑêÕ«5cÆ M:U&“I½zõÒÒ¥KSmÎgjÛ¶­V¯^­™3gjúô钤뮻N«W¯Vttt­×µ¶íg:óëîææ&uëÖM·Ýv›n½õV¹¹¹U7hÐ Y,ýýïׄ TPP õë×OëÖ­S›6mlûþøãZ¼x±Æ¯ôôt9;;«C‡ºë®»4yòd»ãVTTÐ µU»z8êFe½·:³«‡ÅbQ\\œÝˆùøø8+.O¦Jú¢@½˜dÒr-×(2ºà‚øñÇuï½÷êÀš;w®¦NÊ'10Ü¢E‹4a£Ëÿ_^^žÃEÁÖmYYY*((°íïææ¦-ZÔÚm$22R^^^Î š–3C€ŽBééé*//·9³«Ç™?ËCCCEÀµJNN–¤ZCªãYÿíóC}hp%À“ɤåË—kÔ(ûõËt ‰*++Óßÿþw=ýôÓŠ×'Ÿ|¢víÚ]€Èl6+66ö¼»œ¹ÐØQ·‘3»Ž³Ð΢¦àžõ~ß¾}ÊÏÏ·í_5¸¢˜˜ <Øîgq«V­äíímà¬ÀÙ  Ú¹s§ÆŽ«½{÷êµ×^Ó½÷ÞktI9Y,Y,–Z÷«mÑrJJÊY-;ú¤z-¸\Õ¶«©«Ç™a»˜˜7ް—!!4!•••z饗ô裪[·nÚºu«Ú´ictYšsé6Ru!ôÎ;•˜˜Xm´»»»Ã°HÕû¨¨(@h0ÎÖÕcïÞ½*((°í_5 g±X[íg]dd¤¼¼¼ œ¸”„ÐD;vLwÜq‡Ö¬Y£'Ÿ|R>ú¨œ. ª©O·‘š>5?%%ÅöÜÊÕÕUþþþµv‰ˆˆÏÅž"€ÿÇÞ½‡GYßùÿN"$’ptÛ5¶"¡ ruÀzµ\mQ»µ´Õ*«®¢ßï¶Z»þ„µkUöÛ֭ʵmѵ«ˆZKZEÀb%(­ÐZK€œ9†s8$Ìï:Ó&' ¹“ð|pq Üsßs¿?sºoÈçu¿;±ætõØ´iÕÕÕñmìê!I’$I’$I’Ž…I’NùùùLŸ>C‡ñÖ[o1vìØ K’$I’¤ã–‘‘AFF999 ®sàÀJKK^¿  €¼¼<ŠŠŠ8|øp|›æt2dˆ!{é$sèÐ!¶mÛÖ`£ÒÒRJJJZÜÕcèСtïÞ=À‘I’$I’$I’¤ŽÊ@ˆ$IÜO<ÁÝwßÍE]Ä‚ èÛ·oÐ%I’$IR›IMM=aÝFÊËˉF£@ân#u»Ž 8ž={¶Å0%§XWDÝ<šÛÕ#77·^,++‹P(àÈ$I’$I’$IRgf D’¤NêСC|ó›ßä¹çžã»ßý.÷ÝwIIIA—%I’$IíRkw©±ÛˆÔ6uõ¨ú(..f÷îÝñmê~nëvõ‡Ã 4ˆ.]º82I’$I’$I’$!’$uJÛ·oçšk®aÕªU¼ôÒK\yå•A—$I’$IÞ±t©;=//¯^·8HI‰Ý>œ^½zµö¥¥9]=6nÜÈ‘#GâÛÔìê‡;vl½Ïœ]=$I’$I’$IRGa D’¤NfíÚµL™2€ßýîwœuÖYW$I’$I'—æt9xð Û·oO8™½  €åË—³iÓ&öíÛߦnׂD]GÌ)§øß¾êØj~>êêQTTÄž={âÛ4ÔÕ£æçî’$I’$I’$©³ñ'ƒ’$u"ï¾û.S¦LáÌ3Ïä—¿ü%§vZÐ%I’$I’HII!;;;>q½!‰ºÄnW¬XAii)›7o®×¡±n#áp˜ŒŒŒ¶¦TOcïéXè#Ñ{º©®ÙÙÙŽJ’$I’$I’$)B$Iê$òòò¸ú꫹ð Y¸p!iiiA—$I’$I:N'ªÛHaa!{÷îoc·h½c·M½cA߇’$I’$I’$IÍãOQ$Iê,XÀW¿úUf̘Á“O>éD I’$I:‰´v·‘Da;3œ\½wê†>ÊËˉF£ñmjvªIÔÕcøðáôêÕ+ÀQI’$I’$I’$u|Ε$©ƒ{î¹çøçþgî¾ûn~øaB¡PÐ%I’$I’Ú¡æt9tèÛ¶mK8ñ¿  €·ÿô6Eʨ[ YÀ@HMIÜm¤fdРAtéÒ¥í«f9pà;vìh°«GAAEEE>|8¾MÝ®‘H¤ÞknWI’$I’$I’¤¶áOd$IêÀ^xán¾ùfî¼óNæÌ™t9’$I’¤®k×®µºìboó6yäñ>ïóþB*©L<2‘Q;GqQÞEl-ÞZ+DŸŸÏâŋٸqcƒÝFê:’••å…NÙÕ#¶làÀôìÙ3ÀQI’$I’$I’$©&!’$uP?ÿùÏ™1cwÝusçÎ ºI’$IR'PEkXCÞß~½Ã;âaÂDˆð òy>OzR:ô.jø±u©LÈÏϧ¸¸˜Ý»wÇ·©Û}Ân#õ8p€ÒÒÒzÏéñvõ2dÉÉÉŽL’$I’$I’$I-e D’¤èÍ7ßäÆodÖ¬Y†A$I’$IÇ¥€‚xä×üš]ì"‹,Æ1ŽyÌã ® ›ì?nÝn# ©¬¬Lؽ¢f·‘M›6Q]]ߦ³v©ÙÕ#Ñó»éÚµ+}úô‰9QWAƒÑ£GG%I’$I’$I’¤Öb D’¤æü#×^{-×\s ßÿþ÷ƒ.G’$I’ÔÁ”SÎ2–‘G¯ó:ÅÓ‡>\ÄE|—ï2ŽqäÒp€ãDKKK#‡\§9ÝFJJJصkW|›””z÷î0,»:t(Ý»woõ1ÆB/5kß½~=,//+,,¤ªª*¾M¬«G¬öœœ»zH’$I’$I’$©!’$u ÅÅÅ\vÙeŒ9’Ÿþô§îJ§’$I’¤¶·—½¬de¼ H>ù¤‘ÆXÆò-¾E„çpI$]jƒZÚm$Q—–t©Ûu$33“¤¤ÄÏOS]= ¨¨¨¨5–Ü^½ÈÛ¾ÇGŒ #7—)S¦ÔÚ÷àÁƒIOO?qO $I’$I’$I’:%!’$uû÷ïçŠ+® OŸ>¼üòˤ¤¤]’$I’$©ª¢Š5¬‰@–²”jª9‡sˆasÇ8RI ºÔ®9ÝF>ÌæÍ›)**¢¬¬ŒââbJKK)--¥¨¨ˆ÷ߟÂÂBöïßß&55•M¯^½(//§¤¤„òòrŽ9_¯_¿~dff2hÐ ²³³5j ++‹Aƒ‘••Åi§vtåÛnã¾ùóá¿þ FnµçD’$I’$I’$I—I’:ˆ[o½•ÂÂBòóóéÑ£GÐåH’$I’Ú‘ â7y“Ýì&L˜f2“‹¸ˆ>ô ºÌv¡K—. 86ºÞ®]»())¡¤¤„ÒÒRŠ‹‹)++£¢¢‚3Î8#ð0`@<,Ò¢‹7<ölÚS¦ÀÊ•0|øqŽL’$I’$I’$I'!’$u?üáyþùçY¼x1Æ ºI’$IRÀÊ(c9ËÉ#Å,¦”RúÒ— ¹Gx„ILbþûñxôìÙ“ž={ræ™g¶Î’“á¿ÿƇ©SaÅ èÕ«uö%I’$I’$I’¤NÉ@ˆ$IíÜ|ÀwÜÁ·¿ým.½ôÒ Ë‘$I’$`{x÷â]@òÉ'4Æ2–Û¹Îá’H ºTµDz:¼þ:œw\{-¼öœâÛK’$I’$I’$©yüÉ’$IíØÁƒ¹á†3f ßùÎw‚.G’$I’ÔFª¨b kâ·y›(QF0‚æ0‡qŒ#•Ô KÕñÊΆW^ à߀gž º"I’$I’$I’$uB$IjǾóï°aÃ^~ùe’’¼Ê«$I’$uVG8Â|€,g98@˜0"Ìd&s1½ét©j #GÂÂ…på•ðÿwÝtE’$I’$I’$Iê „H’ÔNýîw¿ã?ÿó?ùñÌðáÃ.G’$I’t‚P€ü–ß²íœÆiLd"ó8“™ÌP†]¦ÚÊå—ÃܹpÏ=ÃÕW]‘$I’$I’$I’Ú9!’$µCÕÕÕÜzë­\|ñÅÜ|óÍA—#I’$I:¶±·x‹<òXÂ6°ntã|Îçnî&B„‘Œ$D(èR”»î‚‚øÒ—à­·à¼ó‚®H’$I’$I’$Ií˜I’Ú¡gžy†?þñ|øá‡A—"I’$I:F•T²‚ñ. ð!BŒ`Ó™N„ãO )A—ªöä‰' °®º Þ{º"I’$I’$I’$µSB$Ijg***øö·¿Ím·ÝFNNNÐåH’$I’š©šj>äÃxd9Ë9À„‰a6³‰!ƒŒ KU{–œ ?ûŒ—^ ï¾ ={]•$I’$I’$I’Ú!!’$µ3<òßùÎw®D’$I’Ô” âßðv°ƒ~ôã.àqçó|žÁØáA-”ž¯¿ç×^ ‹Ã)þw¾$I’$I’$I’jó'H’$µ#»víâ‡?ü!÷Ýw½zõ ºI’$IR[ÙÊÛ¼Myüš_³‘t§;cÃ=ÜC„#IˆPÐ¥ª£0^y.¸¾ùMxúé +’$I’$I’$IR;c D’¤vä?øÉÉÉÜrË-A—"I’$Iö³Ÿwy7Þä> DˆŒàZ®%B„ L +]ƒ.UQn.<ÿäÃxdË8ÈA„‰a6³™Ìdzâdzu0=zÀ¢E0z4\wÝÑ?ŸâóK’$I’$I’$¬üI‘$IíÀ+¯¼ÂÕW_M( ºI’$Iê (ˆ@–°„ì$“LÆ3ž'x‚˸Œ ºLéø  ‹ÃÀ­·Â“O]‘$I’$I’$I’b D’¤€ýõ¯åã?æ©§ž ºI’$Iê06³™wx‡<òø¿¢BNåTF3š{¹—F2’ïÕ Ï=×^ g·ÝtE’$I’$I’$I €I’¶hÑ"úôéÃùçŸt)’$I’Ôníc¿ãwñ. «YM2ÉœÍÙ\ÇuDˆpÐ….A—*µk®ÿw¸ã2¦N º"I’$I’$I’$µ1!’$léÒ¥\tÑEœrЇeI’$IŠ©¦šù0y‡w8Ä!„‰a6³¹„KèA K•‚sß}°i|éK°lŒtE’$I’$I’$IjCÎ<•$)`ï¿ÿ>wÝuWÐeH’$IRà (ˆ@~ͯÙÅ.²Èbã˜Ç<.çr0 è2¥öeÞO:éA—*u|ÆÁâÅ0q"Ü}7<úhÐI’$I’$I’$©•‘$)@7ndذaA—!I’$I'LñÈ›¼ÉnvÇ 3™ÉE\Dú]¦Ô9}îsðì³G»„ ·ÞtE’$I’$I’$IjEB$I І :thÐeH’$IÒ1+£Œå,'<^ã5J(¡/}¹ y„Gˆ!L8è2¥“Ç´iðç?ìY0x0L™tE’$I’$I’$Ij%B$I Pqq1cÆŒ º I’$Ij¶½ìe%+ã]@òÉ'4Æ2–Û¸Îá’H ºTéäõoÿý+|éK°lœ}vÐI’$I’$I’$©øSYI’´gÏzôèt’$I’Ô *ªÈ'Ÿ¹Ìe“èMo.áòÈ#B„%,a;XÂf3›\rãaÑ£GóÚk¯%|Ü5kÖ0pà@ªªªXºt)çž{.©©© :”ùóç×ZÏž=ÜsÏ=œqÆtëÖž={2iÒ$/^ܺO€Ô…BðÌ3ð¹ÏÁå—Cqq37 Q]]Í¿ÿû¿3tèPRRRø‡øþë¿þ«Þº¯¾ú*£G¦{÷îtïÞ½ÑÏ»$I’$I’$I’Z‡I’´oß>ºuët’$I’TK<ÅSLg:½éÍ(FñO&Ìóßle+«XÅæ!B*© gÖ¬Y<öØc ï›7oßøÆ78å”Søøã¹æšk¸ãŽ;زe /¼ð=ôo¿ýv|ýo¼‘ªª*òòòصk6l`Ö¬YÌ›7¯5ž©ãëÒ^|N=®¼öíkÖf·Ür •••äåå±cÇž~úi~ðƒ°páÂø:+W®ä¦›nâöÛogãÆlذo}ë[̘1ƒ÷ß¿µF$I’$I’$I’¤:N ºI’Nfû÷ï7"I’$)pe”±œåä‘ÇbSJ)§q™È÷ù>“™ÌP†¶øq¯¹æî¾ûnÖ®]KNNN|ùöíÛy饗øøãx衇¸÷Þ{¹îºë8÷ÜsùÁ~À#<ÂĉX²d %%%ñ.‹½{÷æŠ+®àŠ+®8¾ÁKYïÞðúë0z4\{-¼ò $'7ºIÿþýyðÁã¿à‚ ˜7o>ø ×^{-sçÎå{ßû×_}|½/ùËìܹ“9sæðÒK/µÎx$I’$I’$I’T‹B$I ÐÁƒIMM|%]I’$Ij-ÛÙΫ¼Ê½ÜË(F‘M67qp;·³ŠU”SÎ ¼ÀLfS K—.ÜrË-õº„<óÌ3L™2…~ýúðî»ï2uêÔZëL˜05kÖÄÿ~æ™gò¯ÿú¯”””S-ÒI+†_þòòàÞ{›\ý¦›nª·l̘1üå/‰ÿ}õêÕL™2¥ÞzS§N%??ÿ¸Ê•$I’$I’$IRóÙ!D’¤E£Ñ K$I’t¨¤’¬ ïo¿>àB„Á"D˜ÃÆ3žRNø¾gΜɧ>õ)~øaúöíKuu5?üáyñÅãëñÿðõ¶MJúûõl~ñ‹_pçwrúé§3tèPFÍUW]ÅÔ©S …B'¼n©S7ž{®»† ƒ[nipÕ¡C‡Ö[–‘‘AEEEüï›7oŽºjêß¿?ååå'¤dI’$I’$I’$5Í@ˆ$I’$I’ÔÉáðA<²œåàaÂDˆ0›ÙDˆAF«×Ò·o_¾ð…/ðä“Oòÿïÿå•W^!33“sÏ=7¾N=øÃþ@VVVƒ3lØ0þ÷ÿ—ƒòç?ÿ™÷Þ{‡zˆE‹1þüV‡Ôá]{-üéOpûí0x0\qEÂÕj±Ò¿¶lÙ Aƒj-ß²e ™™™'¤\I’$I’$I’$5­éŸìH’$I’$Ij÷ (à)žb:ÓéG?F1ŠGy” 2xœÇÙÈFÖ³ž'y’iLk“0H̬Y³øÑ~ÄáÇ™7o·Ýv[­û/¼ðB^yå•f=VJJ gŸ}63gÎä7Þ`áÂ…­Q²Ô9Ý?\ýÑßøÃ1?ÌÈ‘#yõÕWë-_´h#GŽ<ž %I’$I’$I’Ôv‘$I’$I’: mlã-Þ"<–°„ l Ý8Ÿó¹›»‰a$#  ºTÎ:ë,>ýéOsÿý÷ó§?ý‰éӧ׺ÿþûïgÒ¤I¤¥¥qÅWššÊÊ•+yôÑGyíµ×˜0a3gÎdâĉdff²mÛ6æÍ›Ç„ ‚’Ô1…BðÌ3P\ S§Â{ïAÿþ-~˜»ï¾›+®¸‚^½z1iÒ$–,YÂw¾óÞxã]µ$I’$I’$I’`‡I’$I’$©¨¤’<ò¸—{Å(úӟ븎|ò™Ît–°„ì` K˜ÍlrÉma˜Y³fñðÃóµ¯}®]»Öº/''‡×_… 2lØ0N;í4zè!î¼óÎø:>ø /¿ü2#FŒ ==ñãÇS]]ÍÏþó¶ŠÔ±uí /¾))pùå°o_‹âüóÏç'?ù =öC† aÈ!<öØcüô§?åÜsÏm…¢%I’$I’$I’”ˆB$I’$I’¤v¨šj>äCòþök9Ë9À„‰a6³™Ä$zÑ+èR›%‰ššÊ7¾ñ„÷1‚×_½Áí'NœÈĉ[©:é$Ó§¼þ:Œ7Þ/¼IID£Ñ7©{ßUW]ÅUW]ÕÚ•J’$I’$I’$©B$I’$I’¤v¢€‚xä7ü†ì ý¸€ xœÇù<Ÿg0ƒƒ.³ÅvîÜÉc=Æ 7ÜÀ€‚.GÀðáðÒK0iüŸÿsæ]‘$I’$I’$I’ZÈ@ˆ$I’$I’­låmÞ&<~ͯÙÈFºÓ1Œáî!B„‘Œ$D(èRY(¢[·n\rÉ%<ûì³A—#©¦ñãáÙgáúëaèPh ƒ$I’$I’$I’Ú'!’$I’$IRÙÏ~ÞåÝxÕ¬&‰$F0‚k¹–&0®t ºÔ&]‚¤ÆüÓ?ÁÚµpûíG»†LštE’$I’$I’$Ij&!’$I’$IR+©¦šù0YÆ2r0a"D˜Íl&3™žô ºTI'³„ášk`ùrøÌg‚®H’$I’$I’$IÍ` D’$I’$I: (ˆ@òÈ£‚ úÓŸ Là žàR.eƒ‚.S’þ.‚§Ÿ†‚˜:V®„þýƒ®J’$I’$I’$IM0"I’$I’$‡-la)KÉ#_ñ+ )¤;ÝÃf3›F2’¡ K•¤†¥¦Â¢E0f \q,] ݺ]•$I’$I’$I’a D’$I’$Ij}ìãwü.Þd5«I&™íP§Ê IDAT³9›ë¸Ž&0®t ºTIj™>}ކBÎ?n¼.„¤¤ «’$I’$I’$IR „H’$I’$I¨¦šù0y‡w8Ä!„‰a6³¹„KèA K•¤ã÷éOÃË/ÃäÉðíoÃC]‘$I’$I’$I’` D’$I’$Iª£€‚xd KØÉN2Éd<ã™Ç<.çr0 è2%©uL˜?þ1|å+0x0|ýëAW$I’$I’$I’¤ „H’$I’$餷™Í¼Ã;ä‘ǼAEœÊ©Œf4÷r/"ä’t™’Ôvnº þò¸í68ýt¸øâ +’$I’$I’$IRB$I’$I’tÒÙÇ>~Çïâ]@V³šd’9›³¹žë‰á.  ]‚.U’‚óÐC°i|á °bœuVÐI’$I’$I’$©!’$I’$Iêôª¨b kâwx‡C"L˜f3›K¸„ôºTIj?B!˜?.º¦N…•+¡_¿ «’$I’$I’$IÒß‘$I’$IR§T@A<òk~Í.v‘EãÇ<æqWMvÐeJRû–š ‹Á˜1G;…äå]&I’$I’$I’¤À‘$I’$IR§PN9ËXFy¼ÎëSL:éœÇyÜÇ}DˆKnÐeJRÇÓ·ïÑPÈØ±pà °páÑî!’$I’$I’$I ”I’$I’$uH{ÙËJVÆ»€¬f5©¤2’‘|‰/!ÂD&rŠÿ&IÇïÿþ÷aòd¸ÿ~xðÁ +’$I’$I’$I:éùÓpI’$I’$uUT±†5ñÈR–RM5çp"ÌaãG*©A—*IÓÀ~7ß §Ÿ~´[HLq1LŸ_ýêÑß’$I’$I’$IjuB$I’$I’ÔnP€¼É›ìf7aÂDˆ0“™\ÄEô¡OÐeJÒÉãŸÿ>þøh(dà@¸è"X½.½¶lÍ› „H’$I’$I’$µ!’$I’$Ij7Ê(c9ËÉ#×xJèK_.äBá&1‰a ºLI:¹Í™EE0mÌ ³fÁ¡CGï+(€+`ìØ`k”$I’$I’$I: ‘$I’$IR`ö²—•¬ŒwÉ'Ÿ4ÒËXnã6"D8‡sH")èR%I1¡ÌŸçŸ_ÿúÑeG޽íÒžyÆ@ˆ$I’$I’$IR0"I’$I’¤6SEkX€,e)G8ÂF!Âæ0Žq¤’t©’¤†TWÃìÙðá‡õï;|~ñ xüqèÑ£ík“$I’$I’$I:‰‘$I’$IR«*  ù¿b{&B„™Ìäb.¦7½ƒ.S’Ô{÷Âôéðæ› ¯ …ÌœÙvuI’$I’$I’$„ „H’$I’$鄊@–³œ<ò(£ŒÓ8‰Läû|ŸÉLf(Cƒ.S’t,.¹Þ}·ñu¢Qøñ „H’$I’$I’$µ2!’$I’$I:.ÛØÆ[¼ïR@ÝèÆùœÏ,f!Â9œCIA—*I:^7ÞøI’$I’$I’¤“„B$I’$I’:©ÿáø_c';Íh†2´ÙÛPïò~ÃvÐ~\À<Îã\Â% aHë/IjÿÎ9V¬€W_…o~ÊË¡ºúï÷ïÙ/¿ ÿôOÁÕ(I’$I’$I’Ô‰‘$I’$IÆöíÛÙ¼y3›7of×®]ôë×ÌÌL233éÖ­[Ðåµ;ØÁ-ÜÂB"D2Éä‘ÇÍÜÜà6[ÙÊÛ¼My,a Ø@7ºq>çs÷!ÂHF"Ô†#‘$uS¦@$r´kÈ …>|´kÈ“OW dëÖ­”——SXXÀàÁƒÉÌÌä´ÓN;AÅK’$I’$I’$u\B$I’$IR öïßOYYååålÙ²…ÒÒR¶lÙRkYII [·nåàÁƒ >Ω§žÊ€èׯYYYdffÒ¯_?²³³ë-;å”Îû_"¿âWÜÈTP@”(!B,aI­@È~öó.ïÆ»€|À„1‚Lg:"Œg<)¤5IRG’–³g Üy'üò—pä,] 6À°aµV¯¬¬¤¤¤„²²2Š‹‹)++£¨¨ˆ²²2JJJ())¡´´´ÁcJJ ÙÙÙ 0€••Å AƒÈÊÊbàÀdee1`ÀÒÒÒÚbô’$I’$I’$Iè¼³$I’$IR ***(--¥¬¬ŒÒÒR***â®»¬¦ÔÔT²³³ÉÊÊ";;›óÎ;ŒŒŒZ˲²²èß¿?»wïnpëÖ­#//²²2***ÝGVVVÂ}dff’””Ô–OÛ1ÛÍnîâ.æ3Ÿ!Žp$~_U¼É›¼Ïûü–ß’GËXÆA&L„³™Í$&Ñ‹^ŽB’Ôá BÅ3ϰkÊN»ÿ~º²|æL„õŽÙåååD£Ñøfñcð™gžÉÅ_œðX]÷\¢¬¬Œ‚‚Þÿ}-ZDQQ‡Ž?nÝc~8®õ˜ÙÙÙ 2„äää ž-I’$I’$I’¤ãb D’$I’$5[eeeƒ¡ŽšË¶lÙBuuu|»ÔÔÔZ‹p8ÌØ±cë…0H×®]›]OFFäää4Ywc”üüüøÊ:Tou'ŽÖ 0€^½‚ R,g9×s=å”ýÛ¯ºv±‹s9—l²‰áiž&B„,²¨X’Ô8p€ÒÒÒzŒšÁŒšŒdàk]º°sÝ:Š*+ÉÎÎ&‰Ô;®<¸ÙÝ»Âá0áp¸Ñuê†Rcµ•––’——×` %QX$v;|øð@õ’$I’$I’$I‰‘$I’$é$wàÀvìØÑdУ9a‰p8œpeFFF@£;*--´´4²³³ÉÍÍmtݦB/ëÖ­kV襡Î#- ½4Z+•|—ïòüI$QMuƒëv¡ ÿ¿ðüÇ Ù·$©s©¢ˆ…'…>bºvíJŸ>}ê…=kvá8p ={öló±4'0Zóü§îx X¾|9›6mbß¾}ñmu«Ûu¤%áI’$I’$I’¤ãåO%$I’$Iê¤ê^»±5Õ옛››0èп’““]ëIKKkÖÕÇ¡ñç86™´9Ïq¢àHSÏñJVò%¾D!…D‰6¨¢Šø ùO„$©SHt¬‚(,,¤ªª*¾Mì8 :äääÔ A 2¤CŸÄÆØTX4Q·‘ØíŠ+(--eóæÍ9r$¾MÍÀlC]G²²²…Bm1TI’$I’$I’Ô‰‘$I’$©iª{E충x× ! 4ˆ.]º8ÂŽ¥9W ‡ú]Xê¾fùùùTTTPXXÈÞ½{ëí£npä£k?â­1oA¢D›Uk”(ËXÆA’BÊ1Y’Ô>:tˆmÛ¶5ö())a×®]ñmRRRèÝ»wü˜’““Ô)SjgLzzz€#k_šs¬OôZÄ^ƒX@´¸¸˜Ý»wÇ·©ynÖPhdèСtïÞ½-†)I’$I’$I’:(!’$I’$¬²²²Ñî±eEEE>|8¾]×®]éÓ§O­PGì Þ5— 0€^½z8B5÷*äÐtègݺu|4õ#¢[¢ÐH­óQHªN" AI:B4t44rƒ¼Sõ“N™Ô:•$±cAÍ.uo7mÚT+üY7@˜››[/h™™IRRR€#뜺víÚ¬ã|Ýc|Í×7??ŸÅ‹7ùºÖ¼½¾¾®’$I’$I’$¼ „H’$I’Ô bWŠn*èQZZÊÎ;km›ø uäææÖêá¤ÎÎ---p8L8nrÝŠŠ 6lØÀÇÛ>fýÎõ”.£ø`1å‡ËÙÝÆÎäTv¯¤²G%d=`òW&“º"µÞÄÒºÝb²²²èß¿?ÉÉÉ­?hI:I3vìØz“ðЩ–ÈÈÈ ##ƒ‘Œlt½°cÇŽ£ïÏY¥TÜP;¨”ŸŸOEEE½Iȱ}4‰­ …Zs¸’Ô®UTT4ØÍ#6¹¿îùAÍïØØ¹A¢N:¹ddd››Û¢n#5ßkk×®%//ÂÂBªªªâÛÄÂEuÃ"5o‡ bT’$I’$I’¤Ä@ˆ$I’$é¤WYYÙd'²²2ŠŠŠ8|øp|»ØÕ˜kNÏÉÉ©7i~àÀôìÙ3Àêd›»²|cjN0Mô¹(((`ñâÅ '™Ö|ß7É´¡ÏYCMkN”n(8âdiIm¡±Éò5ocºvíJŸ>}êuüªö0ªö$ÖM,''§Áujv«û9(((`ùòålÚ´‰}ûöÅ·©zªÙéÆÐ“$I’$I’$ImÇÿ‰—$I’$u(MM@ýyóæÍ9r$¾]Ý è5'p:]:6±I¦M‰´ê<²víZ–/_Nqq1»wï®·†‚#5?¿YYY„B¡Öª¤¦f·£ºÝÈÏÏgñâÅõ&rǾšê<â÷Ô±:tˆmÛ¶%œ»]¿~=;wîŒoS÷û!''‡)S¦Ôú~ï5oý¼K’$I’$I’NVþD[’$I’Ô"MMÀŽý¹¼¼œh4ß®îìp8ÌØ±c€-é˜Å®TÞ”¦j±+–—””°k×®zûh*8b@Mj\cbËŠŠŠØ³gO|›ºrssëMþ4h]ºt pd’ŽUs ±ãt¢ØºuëšìT3,bG I’$I’$IRgä I’$IRü ½M= ©ªªŠo—ššZkBtFF999õ&L;éªó™4iK–, º ©Yj^­<''§Ñu+++~ÆncW-/**âðáõöÑ»wï&ƒ#¤gÏž­=d©ÍÄ‚¢‰>3±IÜu»ôÔ¼ÊÝ€¨!+I5edd››Û¬n#‰¾ƒÖ®]K^^^ÂÇ$ ‹Ô¼2dÉÉÉm1LI’$I’$I’Ž™I’$Iê¤8ÀŽ;š z³{÷îZÛÖœ¨™••E8N8Á¹-&kÖ}üÔÔTúôéÃ9çœÃ¿øE®¿þzºvíÚª5œh±1Õì ÒÒíuÛcÙÔ¯µæÄÞýØíí1[K[¾Žu÷ ­ÿµöø~ûÛß2oÞ<–.]Juu5gœqßúÖ·¸ñÆë{)--´´´xG‚Æ4Õ1iݺu 'Ã×í˜ÔP€Äî Ò(--m0ìQZZʦM›Ø·o_|›º]=bAš®[»XG?oh軳=ß:Ëq¤¡}ÂÉõ<·õ¾[ãøÝ’n# u)гkv9ìÒ¥ }ûöM‰Ý>œ^½zSÝ’$I’$I’$B$I’$©ƒ©{%î†&"לÌõ' 'êäÑ5[*ÖšXYYIyy9¿ÿýï™?>ÿùŸÿÉ/ùKÎ8㌀+m¾Ø˜:‚†jýÍo~ÓjÝÞ³³é,ÏÑÅ_Ìg?ûYÞ|óMÎ:ë,>ùän½õVŠŠŠøö·¿Ý&5ddd‘‘Ñäz‡bÛ¶m ~_°|ùrJKKÙ¹sg½}4ÉÊÊ"33“¤¤¤Öª:™Æ&EÇnkžGtíÚ•>}úÔ ŠÖíêÑ^&EwÆóh_ßÝí©–­=-ÈZZsßA¿cÇíÆº…5†‹¯› ÃÕì€ÔVa8I’$I’$IÒÉ+m—º’¤$Dˆ…,d:Óƒ.E@(báÂ…LŸîûI’jzê©§˜9sfÐe´©ÊÊÊ&eeeRUUß.55µÖ„à†& <˜ôôôGx|»BôSO=ÅÃ?̇~HÏž=Û¸²cwúè#JJJ¨¬¬¤´´”U«VÅ÷1gÎf͚ŴiÓâûèÈa¦Ìœ9“É“'óýï¿Öò>úˆË.»ŒôôtÒÓÓ¹ì²Ëøè£êmÿÑGqùå—Ç×ûüç?_o½]»vqÇw‡IMM%++‹›nº‰ßÿþ÷MÖ·víZ.»ì2N=õTzöìÉÕW_MaaaÂu-ZÄØ±cIMMeèСÜqÇìÙ³§Ö:± b¡P(þûæ›onÑco­±ý&Z¶~ýz¾ð…/‘‘‘p½D ¹úê«éÙ³'§žz*—_~9úÓŸê­×œ×ª!Íy?Äê-**âÊ+¯$==þýûóå/™íÛ·×{ÌšÏW=¸ä’KX·n]³ÆÝÔëØÜ÷o"kÖ¬aòäÉtïÞ=zp饗òæ›o6YO¢š-oÎç¡9ãkêµlê=FkM&èÓ§lƳÔ~¥¥¥‘Mnn.S¦LaæÌ™<ðÀ<þøã¼ð ,_¾œõë×sèÐ!öïßÏúõëY¶l ,`öìÙL›6p8LEEyyyÌ;—¯|å+Lš4‰³Î:‹^½zÅ÷1jÔ(¦L™Â׿þõø>^|ñE–/_NAAA­À‰‚WQQ?§xê©§xàøú׿Δ)S5jµÎ'f̘ÁܹsÉËË£²²’œœœzç»w零²’õë׳dÉ’„çáp¸C…AšÒžÏšúîš}Œ:žcæÉriLsŸçæœwÏyd"Í}m›{NØ’sUßë4‰D¸á†˜={vüX½jÕ*JKKÙ¿?%%%¬ZµŠE‹1gΦL™BVV¼øâ‹üë¿þ+S§NeÔ¨Q 0 þ=>nÜ8¦OŸÎ¬Y³˜;w.Ï?ÿ÷ÜsÑÇ{,:{öìèŒ3¢‘H$zæ™gF³²²¢¡P( ħ¦¦FÃáptìØ±ÑiÓ¦Eo¿ýöèý÷ß}òÉ'£‹-Š®Zµ*ZRR­ªª zˆíNSÿ|}ÿý÷£ŸùÌgâÿä“O¢ŒÎŸ??ºyóæèæÍ›£O?ýttÀ€ÑO>ù¤Öz™™™ÑýèGÑ-[¶D·mÛ]°`A4G‹ŠŠâë]yå•Ñx Z^^=pà@tõêÕѱcÇ6Y×_ÿú×è Aƒâulݺ5º`Á‚è˜1cn DŸ|òÉè¾}û¢eeeÑn¸!zã7¶èùhîcœˆZ-›4iRtÅŠÑýû÷G_ýõ&Ÿ# :f̘è‚ ¢[·n¿VƒŽnذ!¾^s_«Dµ5÷ýÛvòäÉÑ7Þx#ºgÏžhaaaôꫯŽÞtÓM>_Û¶m‹þüç?žuÖYM޹¡:¥Þº>þøãè§>õ©è’%K¢ûöí‹®]»6:~üøzûjîkšhys?¯%¯eKÞSwÞygôòË/oðþ“ÕÁƒ£%%%µŽ]sæÌ‰Þ~ûíµŽ]½zõªuÜjɱ«ºº:èavXû÷ï®_¿¾Þk3mÚ´èØ±c£áp8šœœ\ëuÉÈȈžyæ™ÑH$1cFtöìÙÑÇ{,ú /øšD;îyCSµ':FM›6­Þ1ª%ß³uìÇ‘Ø6Íyžcë6uÞu"ß-{SµµäüÏãwë9Ž«V­Š®Zµ*è2$IM˜=ïgçE#‘HÂû¿úÕ¯F¿÷½ïE£ÑhôÏþs´o߾џýìgÑ]»vEß{ï½è°aâo½õV|ý«¯¾:zÇwD7nÜ=tèPtûöíÑW_}5:yòä¶Ž$I’$INCóMC»S’ÔL!B,da¼m²t<já%I'»§žzŠ™3g¶é>+++)++£´´4~[QQQoÙ–-[¨®®Žo—ššJFFÙÙÙdeeÅoë.8p ]»vmÓ1u&¡PˆÆþùºoß>ú÷ïÏÞ½{øò—¿Ìç>÷9fÍšUk½G}”>ø€ Ä×ûìg?Ë=÷ÜSk½Ÿüä'¬Y³†Ç€ôôt6mÚDïÞ½ãëlذp8Üh]3fÌ`Ô¨QõêxöÙgùÊW¾Òè¶pôjð§Ÿ~z½+R7õ|4ç1Ž·ÖD5„B!Þzë-&NœØ¬ÚbÛüô§?妛nªµüÑGåøÏ>û,Ðü×*QmÍ}?Ķ}ùå—¹òÊ+ãË>þøc.ºè"JJJâËz¾~ñ‹_pÝu×5ëõièulI½u]ýõ\zé¥Ì˜1£VýŸþô§kí«¡×/Q=u—7÷óÐØøZòZ6õž*,,déҥ̛7 6°råJ†Þàúj\eeeÂã_ÝeÅÅÅ:t(¾]×®]éÓ§O“ÇÄЫW¯GØv<ÈöíÛk=oµž×ââbvïÞß&55µÞóW÷vèСtïÞ=À‘µõ¼¡©Ú£6lØÀ¸qãj£Zò=[×ÉxITosžçDwÈ÷Ãñ¼¶‰jkÉùŸÇï`:tˆmÛ¶Õ;>×¼]¿~=;wîŒo“’’BïÞ½kGÂáp­cÊàÁƒÛ¤›d~~>¹¹¹­¾/IÒ±›ÎtŽ>ÂÊa+yóÍ7ÉÉɉ߷}ûvN?ýt>þøcúõëÇ 7ÜÀÙgŸÍ]wÝ_ç•W^á©§žŠwIOO§¤¤„=z´ùX$I’$Iꌚoj D’ZÈ@ˆN$!’”؉ „8p€;v4ðH4© ##£Ñ€GÍej}MMìÜ»w/™™™ñ‰™™™¼÷Þ{ 2¤Öz6làüóϧ¬¬¬Ñõ6oÞÌ…^Ⱥuë˜ó™ÏP\\ ÀyçÇÙgŸÍý÷ßÏ€Úp$’$I’$uNB$é1¢É@ˆ$%ÖÜ@È'Ÿ|²eË())aëÖ­ñÉ—›7o¦´´”}ûöÕZ¿_¿~ôë×ÌÌL233éß¿?ÙÙÙôë׬¬,233éׯ§ËÀYç IDATvZk MǨ©‰¿ÿýïùÚ׾ƚ5k8å”SØ·o)))µÖ;pàééé>|€.]ºÔš USZZû÷ï`×®]|÷»ßå•W^aëÖÿŸ½;¯ª¾ó?þÊN[d#!˜"Ú¢€Š Xp—ª¸ÔV¨Óv¬¶V¦¶Õ©c+­mÕßXm©Kíf Ój‹¸TCÝB%‚´n ²a ²Ýßpo³’°ådy=yäq’“sn>ß»Cò}ŸÏfÆŒÃôéÓùÚ×¾F\\\«uµVGKcÚ¼y3ßýîwyþùç)--m4yº½.æ6§ÖƒY×–Öö9ÔǪ¥Ûlïóá@õ´÷6tíÝî`êmiß?þ¸ÍIô‡3‘·½¯‡ÖnïpËöŽEKMM ååå”””PZZJyyyäØ^×Þcwjj*C† !==Ï~ö³G­SÆÞ½{yꩧذa›6mbÓ¦M‘Ž¥¥¥‚¤ýúõ#++‹ÌÌÌH%##ƒììl222ÈÊÊ"--í°&ÒêàtÕó†¶joïûôÁ¼Ï6Õ#í[KëÛ{Þu$Ÿí{{k;˜óßÝG]]eeelܸ‘’’6lعPBqqq‹¬âããIOOï† Â!CÈÎÎæâ‹/nñ9ÔIêÂû|p˃wÜq¼óÎ; 4ˆºº:rssù¿ÿû?ÆìëDÕô;ÑÑÑ‘sõë×sã7òüóÏ3lØ0&NœÈE]Ä…^h^’$I’¤CÐÚ|Óö_ŠJ’$I’:™»ï¾›_üâ­~àÀŒ3†“N:‰N8œœœÈ¤Ò”””¬TGÛܹs¹è¢‹"_4ˆ’’† Öh»’’ Ôh»µk×6ºtKú÷ïÏ=÷ÜÃ=÷ÜÖ-[X´h÷Ýw¯½öüã[ÝoРA”––¶ØE¢©3fpÜqÇQPP@VVVäêÑóòùƒ©õH«¨¨höš,))iÎjïcÕ’ö>ö6[º¿JKKéöšÞö¡Ö;pà@¶lÙrHÝ‹¢¢¢Ø»wo£ }Ͷ;Ô×CØá<–êšâââ"G›ª­­¥¼¼œ²²2JJJX¿~=………¬ZµŠÕ«WS^^Nyy9«W¯n´_LL Ï>û,guÖQ©yåʕ̘1£ÕÉÏŸüä'9餓8å”SøÄ'>™ ëùE×ÐYÏŽ”ÃyŸõ8Ò~í=ï:’χö޽½µÌù_g¹ßuøbbb"!Ʀ***">ø€%K–°bÅ þñPTTDQQQ£íccc)((`„ U¾$© 4ˆK.¹„¹sçòßÿýßüå/!===}ÁøU«V‘‘‘ÑêísÌ1<õÔSìÝ»—þóŸ,[¶ŒÿøÇ,X°€G}´#†"I’$IR` D’$IR—5wî\î»ï>¶mÛFII ÅÅÅ‘eÃuO>ù$sæÌitUý„„RRRHNN&33“ŒŒŒÈ²áº!C†0`À€G©¶üïÿþ/‹-Š\å`êÔ©<ùä“Üxã¶}ê©§˜:ujäë³Ï>›—_~™‹/¾¸Ñv¯½ö³fÍâÍ7ßöMžÛ°aYYY 4ˆ+®¸‚iÓ¦5›8ÚÔYgÅŸÿügfÍšÕhý¢E‹šm»dÉþð‡?4z¾µvïÖs‡Së‘–ŸŸßì O=õT£‰Þí}¬ZÒÞçÃÁhíþúë_ÿÚîÛhíq<œz'OžL~~>3gÎŒ¬{ë­·¸òÊ+Y»ví÷MOO§¨¨ˆc=6²îÕW_m±îö¼Zßá<–-ñêâÓž={Z<&7]WTTÔ(tÑ«W¯ÈqxÚ´i­§‡Jß¾}Zý&L`÷îÝlÙ²¥Yí%%%¬[·Ž•+WòÌ3ϰcÇŽÈ~áó‹ÌÌLrssÕ^æääЧOŸ£V»¬3Ÿ7„÷=\‡ó>Û#‡ª½ç]‡ó|hª½coomsþçñ»k«®®>à1­¥® i—]v™Ç4IêafÍšÅ9çœÃM7ÝÄ<À7¾ñFߟ2e ùË_¸öÚkÛ¼­„„FÍèÑ£™>}:999B$I’$I:‚ „H’$IêÒILL$33“¼¼¼nÛprjK“T×®]Kqq1eeeÔ××Gök89µµàHff&ÙÙÙÄÅÅí!÷xUUU”––²téRyäJKKyñÅéß¿d›Ûn»)S¦Ð¯_?.¼ðB¢¢¢X°`÷Üs/¿ürd»Ù³gsá…RWWÇ”)Sˆç•W^áÚk¯åç?ÿy£Ÿû•¯|…Ÿþô§ >œíÛ·3gΜ6'çÏž=›É“'Ó·o_.¸àbbbÈÏÏç'?ùI³mO9ånºé&n¹å233)**âöÛooñv‡ Â믿θqãx饗øò—¿Ì† ê6§Ö#-ü3>ó™ÏP__Ï‚ ˜3g¯¼òJ£úÚûX5ÕÞçÃÁhzEGGó /ðð÷û6Z{§Þ[o½•‹/¾˜ÌÌL>ýéOóÁð¥/}©ÙÄ–L›6[n¹…{ï½—°xñbzè¡·mÏ롵ñÎcÙÔ¤I“ˆŠŠ¢  à öÓ¡©ªª¢¢¢¢Í Ç† صkW£}“““Csss[=®vñññ‘+¨è£éùExbmII ………,\¸°ÅàËB#™™™äääÓCíÖºÒy´þÞy0ç}¶§GÇÁœwêó¡©ö޽½µÌùŸÇïÎkÛ¶mÍBMIMÿÛô¸œ——×옔‘‘qDBj’¤®é„Nàøãç¶ÛnãÿøG³ yÜvÛmL›6ÄÄDÎ?ÿ|zõêÅÒ¥K¹çž{xæ™g8ýôÓ¹æšk˜õ©Ð€B@£^½z…rssC“&M ]vÙe¡n¸!tÛm·…æÎZ°`Ahùòå¡M›6…êêêcWÒô>NHHeff†Î;ï¼Ðc=ªªªjq¿U«V…Î9çœPRRR()))tÎ9ç„V­ZÕl»wß}74}úôPÿþýCIII¡“O>9ôä“O6Ú&???tÑE…ŠsÌ1¡o~ó›¡;w¶YÿêÕ«Cçž{n()))Ô§OŸÐYgZ³fMd>>4|øðÐ_þò—ƒ¾Ã©µáãÑÒºöþº!¼íš5kBguV¨OŸ>¡¤¤¤Ð¹çžZ»vm³íÛóXµVC{ž­íÛÚú†÷Wß¾}CçŸ~èý÷ßEGG·kü­=Ží­·5K–, Mš4)Ô«W¯PvvvèŽ;îh×x6oÞúüç?Îc{0ç„í=ÿkïÏöø}äTUU…6mÚZ¾|yhþüù-ŸûôéÓæ±ùÎ;ï ýêW¿ ½øâ‹¡>ø T]]ÝácY¾|yhùòåþs%Iç²ýÿÂ,XB·Þzk‹Û¯X±"tî¹ç†úöíJLL M™2%”ŸŸùþK/½š>}zhàÀ¡^½z…†ºùæ›ýŒ$I’$I‡¨µù¦Qû¿)Ij§(¢˜Ç<.çò¶7–Úżyóš]YI’zº_üâ\sÍ5A—Ñ.{öìiõ é ×mܸ‘êêêÈ~ñññ 8°ÍÎ#C† aÀ€ŽPÒÁX³f çw~øaÐ¥¨‡k«+VxY^^N]]]d¿övÅÊÊÊ">>>ÀvOUUUðJï6l ¦¦&²O¸ÛHø±i©ëÈСC‰µY´$u-uõhØaª¸¸˜ÒÒRþ /99ù€¥rssINNpT­+,,h³«§$)Xá¿}Îg>°ïÿ•)))¼ÿþû 2$ÈÒ$I’$I­Ï7õ¯€’$I’tILL$33³ÍÉ-mMÎ]»v­“s¥.$**Šûî»™3gÒ»woþñpýõ×óõ¯=èÒÔMUUUQQQqÀ€GK!DØ7‰´á1$77·Õ㊂ӫW/rssÉÍÍ=àvšHœŸŸH‰ÃKIÒ¡kéÿ|ì›:uªÁ>IRà¶oßù‡aI’$I’:7{,I’$I$11±]“<¡ùDφ“~×­[GAAAd]CM'µÉÈÈ --˜˜˜£5T©GX¸p!÷Þ{/·Þz+ÑÑÑ{ì±|ãßà‹_übÐ¥©‹9Ð{~Óu 5}ÏÏËËó=¿‡HNN&99™‘#G¶ºÍÞ½{Ùºuk‹WŸKQYYÙ§¥óˆ¦WŸÏÎÎ&..®#†)IʶmÛš½Ÿ6 {lÛ¶-²}¸+dÃ0æ¤I“usÊÎΦ_¿~ŽJ’¤æ¢¢¢èÝ»7gŸ}6?þxÐåH’$I’¤6‘$I’¤N¨==¡í«Å¶ûjñêB"©¹óÎ;óÎ;/è2ÔIµÕ*¼l«+TxòhÓ÷h'å«- dff¶ÙŬ¥n# Ï#.\ȇ~H}}}dŸ¦ç '7‡×eddÕC•¤ÃÒô˜ÝR裨¨ˆÚÚÚÈ>MßGŽÙì}qذaDGG82I’MÃNƒ’$I’$©ó3"I’$I]XøJÞmMö„¶''¯]»¶]““[ dee´‡,IÙ³gÏ»w„×5 á…¯Þð}3}útÄ0%õ@m½w·ùÞ•——ç{—$I’$I’$©S3"I’$I=Dbb"¹¹¹äææ¶¹mÓ«…7œì¼nÝ: ضm¥¥¥®¨´Õy$--˜˜˜£9\Ij—ðdѶ‚ÅÅÅlß¾½Ñ¾MßóòòòZ|ÏKOO÷ áêÒâããÛ@=ÐUöÃÝFÚºÊ~KK¯²/©©u7 /Ûên”——gw#I’$I’$IR—g D’$I’ÔL{®PUUEEEE«WÉ/,,dÛ¶mlذ]»v5ûmGÂÛ8)KÒÁj«+RxYVVÖh²hÓ®H¹¹¹Lš4©Ù{Tvv6qqqŽPê|Ú>ݶm[£°HÃe¸cYIIIdûp—–Â"áÉÜÙÙÙôë×ïhQÒQ¶wï^¶nÝÚ,TÖpYTTDeeedŸ–šMß'>¾]ÝFÔ}!Üm¤­î -u1`׳…ŸW-uó/?úè#êêê"û4}^åååÙÅF’$I’$I’¤nÄ¿ÚK’$I’tˆ"“Bt5qh<1´¥ ú­M _±¹¥àHà úÙÙÙôë×ïhYkëy^×Úó¨ásfäÈ‘>$é(iog²mÛ¶µ:¹íÚµ¬[·ŽmÛ¶E¶gàÀdffÒo\?²ûdsLŸc…F|/ïzÂ!ã…=ŠŠŠ¨¬¬ŒìÓ0À(5 eggàÈ$I’$I’$IÒÑf D’$I’¤ÐÞ‰¡Ðvg‡µk×¶«³CkGììй4œz Ç½=frss[|ܽò·$uNÉÉÉäååµ»ÛȺÍëx¾ïó¼6ü5 ‡2â¡üO6l ¦¦&²OÓ°@ÓŽ™™™äääÓÃìÑÂçu­uŒ)..¦´´”P(Ù'Ü¡+##£Y§®†KI’$I’$I’$gH’$I’ÔÉ$''“œœÜævá Ak‚uëÖQPPÀ¦M›Ø±cG³ŸÑVp$##ƒôôt¢££ÖP»µ¶‚=­Mmìi©“‡ÁIê9Ù–»ßäþ†'x‚j˜Æ4îá.¾öbb¯Ýw,8Pð ??¿ÍàAKƒÜÜÜv“ôDUUU·ØÍ#|ß·Ô™:uj³ûÜã»$I’$I’$I:þUA’$I’¤.*!!ÌÌL2339rä·Ý³gσ ………,\¸°ÙÄÅ„„RRRÚ ŽdeeÑ¿ÿ£=äÀ5¼JûîÏ¢¢"jkk#ûõêÕ«ÑýÕZÐcèСôíÛ7ÀJ’:‹Ílæ—ü’‡y˜÷yŸ±Œåîàó|žRšm”蜠aWª¦]*ÂAÒ¢¢"*++#û4 1´Ôu$;;›¸¸¸£r?¡a¸¦éýÔpÏÀ#÷Oîáû©§œ+I’$I’$I’¤Že D’$I’¤ 11‘ÄÄD233ÉËË;à¶ C-Ö®]Kqq1eeeÔ××Gökzh)8Ò'VUUQQQqÀ Gј"*þ^Áî7v7Ú7Üi¥áЖƟ‘‘ATTT@#”$u%¯ò*s™ËŸø‰$2ƒ|…¯0ŠQ‡}Û Ã¤:h©ÛHx¹xñâÏš›†F222?6=Çi)ìÑR¨³áXš:333ÉÉÉ!&&&°qI’$I’$I’¤žË@ˆ$I’$Ij$11‘ÜÜ\rss¸]uu5[¶liµSFøJãÅÅÅlß¾½Ñ¾­]m¼i€$==èèèCGÓɬ­ÕYZZJ(jµ¶¼¼h)4²nÝ: Ù¸q#;wîŒìÓ0<Ú4,^æääЧOŸƒª·­ZŠ‹‹Ù´i;vìˆìî‚®%//¯Y-vî’$I’$I’$II’$I’tHâãã#W?ЄQ€ÊÊÊÈÕÄKKK)--¥¼¼œM›6Q^^ÎâÅ‹)))aóæÍÔÔÔDöKHH`ðàÁ 2„ÔÔTÒÓÓÉÈÈ 55•þýû³yóæÈí–••QRRByy9åååB½{÷ŽLÒÒÒ8þøã9ýôÓ#ÁŽ´´4233IMM%>>¾Yý?å§üˆqû˜Û© ‚y ƒH’Ï{¼ÇÏøò(uÔqð 2‰IA—Ö¦†çê6RQQAqq17n¤¤¤„ 6PRR¦M›((( ¤¤¤Y83--ŒŒ ²²²"?#++ €7R\\Üè6ËÊÊ"ûFEEEΆ ÂðáÃ9í´ÓÈÎÎnt›)))GïΑ$I’$I’$Iê B$I’$IÒQ×§OFŒÁˆ#ÚÜ6èO-//§¸¸˜òòrÖ­[Ç’%KèU\Ìø;Yž™ô™••ÅøñãIMM„?RSSÉÌÌ<è+7K,³™Í©œÊU\Å8Æ1ŸùŒbÔaÝ®$©ç©§žçyžû¹Ÿxá çîà‹|‘~ô º¼#.%%…””N8á„V·©©©¡¬¬,iúxï½÷xõÕWþᇼ?lXä¸?räÈHX$##ƒììlÒÒÒˆ‹‹ë ÑI’$I’$I’$Ë@ˆ$I’$IêTRSSIMM=àÄQæÏ‡+®àÁ;®0`*SYÎr®äJ&0;¹“YÌêÐ$I]S%•<ÁÌakYË$&1y\Â%=¾ëT\\YYY‘. -ºüò}Ëùó;¦(I’$I’$I’¤. :è$I’$I’º’,²x‰—¸™›¹‘¹”KÙÁŽ Ë’$uR¥”r ·M6ß䛜ʩ¬f5p—õø0ˆ$I’$I’$I’I’$I’¤ƒK,³™Í ¼Àb3žñ¼Å[A—%IêDÞç}®åZŽáã1¾Í·ÙÀæ2—‘Œ º¯ó:ãÏJV]–$©¼ÌËLa gp5Ôð/PH!Wp1Ä]ž$I’$I’$I’º1!’$I’$IGȦ°’•Ã1Ld"s˜tI’¤£¤€¦2•)L¡†^äE^æe¦1-èÒ$I’$I’$I’ÔC‘$I’$I:‚RIå9žãü€oñ-.á¶³=è²$IGÈ"q:§s§ð*¯FÂ!’$I’$I’$IRG2"I’$I’t„EÍÍÜL>ù,e)cÃR–]–$é0¼ÁLÝÿ¯žz±ˆ|ò#ÁI’$I’$I’$©£‘$I’$I:J&3™·x‹ã9žÉLfs º,IÒAx—w¹œË™Àö°'ÒäLÎ º4I’$I’$I’$õpB$I’$I’Ž¢Á æ9žãü€oñ-.æb¶±-è²$ImØÌfþ‹ÿâDNämÞfó( ÀŽ ’$I’$I’$Iê4 „H’$I’$eQDq37³ˆE¼ÁŒa ¯ózÐeI’Z°“ÜÂ- c¿ç÷<ÄC¬f5—qQD]ž$I’$I’$I’a D’$I’$©ƒœÁ¬d%ŸâSœÎéÜÅ]„]–$ ¨§žGy”ã8޹Ìå‡üwx‡ç߉!&èò$I’$I’$I’¤f „H’$I’$u Á æYžånîæ{|‹¸ˆ *‚.K’z´¿ów&1‰k¹– ¹òO¾Å·èE¯ K“$I’$I’$I’Ze D’$I’$©ƒEÅ,f‘O>…2†1,aIÐeIR³‰MÌd&™Hozó&o2—¹ fpÐ¥I’$I’$I’$Im2"I’$I’Ó9•¬äDNä Î`6³©§>è²$©ÛÛÃf3›c9–¥,å/ü…E,âDN º4I’$I’$I’$©Ý „H’$I’$hƒXÈBîæn~ÂO¸ˆ‹ØÊÖ Ë’¤nëE^d£¸—{ù!?d5«¹€ ‚.K’$I’$I’$I:hB$I’$I’E³˜E¬f5'q‹YtY’Ô­TPÁWù*gs6Çs<üðÃÜ{ï½Ì›7/²ÍÒ¥K¹ú꫹á†øðÃY¿~=×_=3fÌà7Þ°úÃ×~ü?ð8s?÷3•©StY’tÄma pßæÛ|—ïòWþJ:éA—¥ôôc¿$I’$I’$IR˜I’$I’Ô¥¥¥¥ñãÿ˜áÇ“””ÄgœÁ<À½÷ÞÙæ®»îâöÛoçóŸÿ<ƒ&55•«®ºŠþð‡ÜyçVäÌd&°‘Œa å¯A—$IGÌë¼ÎhF³†5PÀlfí¯·{,ý’$I’$I’$Iûø3I’$I’Ô¥]}õÕÍÖ}úÓŸæÝwß|ýæ›orÁ4Ûî /¤°°ðh–סÆ2–7y“©Lå\Îe³¨¥6è²$é°<Ê£La y䱂L`BÐ%)`û%I’$I’$I’ö1"I’$I’º´aÆ5[—œœÌ¶mÛ"_—••‘ššÚl»´´4JKKfy®/}y‚'xœÇy„G˜ÊTŠ)º,I:hµÔò_üÿÁðŸü'æÏ `@Ðe©ðØ/I’$I’$I’´I’$I’Ô¥EG·ýë´´4ÊËË›­///'==ýh”¸™ÌäïüÍlf cxŽç‚.I’Úm [8›³ù9?g>ó¹“;‰ö×ÙÚÏc¿$I’$I’$IÒ>þM’$I’$u{cÇŽåé§Ÿn¶~Á‚Œ;6€Š:ÆHF²”¥Lcçq³˜E 5A—%I´’•œÌÉ|ÄG,a —riÐ%© ê©Ç~I’$I’$I’Ô³Ä]€$I’$IÒÑöï|‡óÏ?Ÿ0mÚ4^|ñE¾ÿýïóÜsÝ»sF_úò;~ÇÙœÍu\ÇJVòO0„!A—&IÍü‘?2“™œÊ©ü?BJÐ%©‹êÉÇ~I’$I’$I’ÔsØ!D’$I’$u{§œr =ö÷Ýw999äääpß}÷ñË_þ’ñãÇ]^‡˜ÉLÞà *¨` cx–gƒ.I’y€¸‚+ø _á9ž3 ¢Ãâ±_’$I’$I’$õv‘$I’$I]V(j÷÷.ºè".ºè¢£]R§ö)>Åë¼Îu\ÇùœÏ7øws7qÄ]š¤,Dˆð~Èù>ßg6³ƒ.I˜Ç~I’$I’$I’¤±Cˆ$I’$IRÒ‡>ü†ßð8ó(r*§²žõA—%©‡ª¥–ÿà?ø1?æa6 "I’$I’$I’$!’$I’$I=ÐLfòo°‡=œÌÉ<Ã3A—$©‡©¤’ ¹yÌc ø2_º$I’$I’$I’$©K1"I’$I’ÔC}’O²ŒeLg:p³˜E5ÕA—%©(£Œ38ƒ¬à^á\Î º$I’$I’$I’$©Ë1"I’$I’Ôƒ%’È\æò8ó(r*§²žõA—%©+¡„)La»XÂÆ26è’$I’$I’$I’¤.É@ˆ$I’$I’˜ÉL–³œ½ìå$Nâü1è’$uC¥”2•©ÔQÇ˼Ì1tI’$I’$I’$IR—e D’$I’$IÏñ,c_ä‹\ÎåÌbÕT]–¤n¢”RÎäLê¨ã%^"“Ì K’$I’$I’$I’º4!’$I’$IŠèE/æ0‡_ókã1&1‰u¬ º,I]Ü6p§"dD’$I’$I’$I:B „H’$I’$©™«¸Šå,§†Nâ$æ3?è’$uQE1…)ÄËßød]’$I’$I’$I’Ô-‘$I’$IR‹Žã8–²”«¹š+¸‚¯òUö²7è²$u!›ØÄd&“D¯òªaI’$I’$I’$é2"I’$I’¤Võ¢s˜Ãù#ó™Ï)œÂ|tY’º€ílçÎ!‘D±ˆÁ º$I’$I’$I’$©[1"I’$I’¤6Mg:ËXF=õŒe,ó˜tI’:±*ª¸ © ‚gy–A º$I’$I’$I’$©Û1"I’$I’¤vÁ^çu®æj>Çç˜ÉLö°'è²$u2õÔ3ƒ¬bÏò,9ä]’$I’$I’$I’Ô-‘$I’$IR»õ¢s˜Ã“<ÉÓ<Í$&ñ>ï]–¤Nä›|“…,äižf4£ƒ.G’$I’$I’$Iê¶bƒ.@’$I’$©MwÝëÖýëëðç_ýjãí.½¦M븺z°‹¹˜±Œå ®`,c™Ë\®äÊ Ë’°òC~ÆÏ˜Ï|Nã´ ËQWUZ ?ù ìÝû¯u……û– ý pË-žÞ±õI’$I’$I’$uB$I’$IRçwß}P^11ÿZ¿ü忾®©¤$!(‡^ánâ&>ÏçyŽçxˆ‡èMï K“€_ñ+f3›Ÿós¦3=èrÔ•­_<°ï¸ݤÑyøØ__uupå•B$I’$I’$IRÝö&’$I’$I»ê*ˆÝúhíàsŸ ¶Î(æ0‡§xŠ…,ädNf k‚.KR{ƒ7¸–kùßá:® ºuu'Bfæ¾ÀGkÇýº:ÈÈØ·­$I’$I’$IRe D’$I’$u~W^ ÕÕÞ&;N>¹cêQ3q+XA?úñi>Í<tI’:ÈV¶r9—s*§ò~t9ꢢ`ÆŒ}ÝÀZ_üâ¾m%I’$I’$I’z(!’$I’$©ó;†oýûqqpõÕN X9¼Æk|¯qW1“™|ÌÇA—%é(ª¥–K¹€ßó{bˆ ¸"uW^ù¯`-©©Ù·$I’$I’$IRf D’$I’$u W]Õú•ÂkjàŠ+:¶µ(–XîäNžâ)žáÆ1ŽÕ¬º,IGÉ·ù6ËXÆŸøƒt9êNF†ãŽkýûÇèQW$I’$I’$IR'd D’$I’$u _øBëW ÿÔ§`äÈŽ­GôY>Ë VL2ãÏ#<tI’ްßñ;îç~ã1Æ26èrÔ͘Ñr4.þýß;¾I’$I’$I’¤NÆ@ˆ$I’$IêÂWŠj¼>.¾øÅ`jÒ e(¯ð 7q_å«Ìd&»ÙtY’Ž€•¬ä?ønäF>Çç‚.GÝÕ•WBmmóõ55pùå_$I’$I’$IR'c D’$I’$u3gBLLãuµµN íÄb‰e6³ù3æžaãx›·ƒ.KÒaØÃ¾ÀÏxîäΠËQw–› cÇ6ƒFEÁ¸qû‚¢’$I’$I’$I=œI’$I’Ôu\y%Ô×ÿëëèh˜0† ¬$µÏ\ÀJV2L`s˜tI’Ñwù.›ØÄ¯ø±Ä]Žº»¦aИ˜}ë$I’$I’$I’d D’$I’$u!™™pÊ)û‚ °oé¤Ð.#›l^æenâ&näFf2“Ýìº,Ia‹x€ø?#‡œ ËQOð¹Ï5ƒÖ×Ãe—W$I’$I’$IR'b D’$I’$u-3füëóP¦O®´Xb™ÍlþÊ_yÇ8V±*è²$µÃvð%¾Ägù,WqUÐ娧HM…ÓOß×$&Î8ÒÓƒ®J’$I’$I’$©S0"I’$I’º–éÓ÷u‰Š‚Ï|fßDQu9S™Êr–3ˆAL`s˜tI’Úðu¾NU<ÄCA—¢ž¦aô*ÃH’$I’$I’$IaB$I’$IR×2p œuÖ¾î 'ˆªËÉ"‹—x‰›¹™¹‘Ì ’Ê Ë’Ô‚§xŠ'x‚ÇxŒT ⩃]zé¾î ÑÑpÉ%AW#I’$I’$I’ÔiÄ]€$I’$IêÞjjjزe [·neË–-”——³e˶lÙ®]»Ø¹s'»víâãÊJvïÜɶ­[ùøãÙ»w/{««ùxÏžF··}÷n.…x:cÕ_ú½þµATúö%**о}ûÒ;)‰¤>}0x0IIIôîÝ›~ýú‘œœÌàÁƒ4hP£”””޽ƒz¸Xb™ÍlNã4®â*Æ1ŽyÌc4£ƒ.MÒ~[ØÂ5\ÃWù*çq^Ð娃Õ×׳cÇvíÚEee%•••ìܹ“;v°{÷n*++ÙµkÛ·o' ±mÛ6víÚEmm-{ö졪ªŠšš*++#·¶{÷nª««[üÙá}æï_wyr2½zõ"11±ÅýèÝ»wäëþýûMŸ>}ˆ‹‹‹ìKßýç  ˆ,ûöíKŸ>}èÓ§ýúõ£_¿~‘¯ûöíÙV’$I’$I’$)hB$I’$IÒ!«­­¥¨¨ˆ 6PTTù|ãGñÑP\ZJÅ®]ö‰ÅÅ1(:š¾@¿úzúÖÖÒ'" ôz1@¿&?³ü xø¸¦†½55‘ï×;öÿÌÀÇû?¶[bbø8:šÑÑT›kk©¬«ktû±11 0€¡C‡’›KVv6Æ #++‹ìýŸ§¦zuü#í3|†å,çJ®d"¹“;™Å¬ Ë’ÜÂ-ÄË]Üt): µµµ”——³uëV¶nÝJEE‘ÐfEEE£õ[·neçÎ|üñÇ­Þf¯^½…&bbb"ˤ¤$âãã0`½{÷Ž| y¨ >>ž¤¤¤o?؈Ú¼žü+pÒ’ÊÊJjœ„C*;w®.<ݽ{7›7o¦®®.ò½†a—p¥%½{÷¦ÿþ¤¤¤’’ÂÀ-Ã!Ó†ëSSS‰õÏ2’$I’$I’$éÈñ/’$I’$©Meeeüãÿà½÷ÞãÝwßåÝþ“wV¯fýÆT™Mv\Yõõ ­©á$ H÷/Ñ &iv¨ºº} T[€­@9°¹®Žò­[ùhëV6¬\ÉëqqÌJ«« íß§R#†çØ‘#9þøã9öØc1bÇw\«“ZÕ¶! á%^âvnçFnäU^åQe‚.Mê± )äQå ž _³ˆž:ƒ;w²qãFÊËË)..Ž,ËÊÊ(--¥¤¤„òòrÊËË …Böíß¿?Œ|¤¤¤0tèÐH€!Ü£ÿþ‘®IIIôïß?üèÎjkkÙµk;vì ²²’Ý»wGº¡ìÞ½›;v4 ÒóöÛoGB7 ;¡DEE‘ššJjj*™™™¤¥¥‘žžNFF©©© 2„ÔÔT²²²"AI’$I’$I’¤1"I’$I’"êëëùàƒX±b+V¬`ea!+ )­¨ ol,#bbQ]ÍçB!ކÙ@z}=ìÝdù‡¤µÿ£™Pª«¨6ïîÞÍ»o½Å;«Wó›ØX>¬©¡¦¾ž˜èhF ƘñãsÒIœ´ÿcРA4š®/†f3›Ó9/ð&0yÌc c‚.Mêqê©çë|ILâr.ºœ«´´”¢¢">úè£H7®õë×G>w¿€}5RSS#!ƒ!C†0~üxRSSÉÈÈ ==½QçŠîè8\±±±$''7êhr0jkk#‘­[·6 èlÚ´‰òòrV¯^Mii)ååå:ž$''“““ÃСC6lXäó¡C‡’““CZZÚ‘¦$I’$I’$Iê „H’$I’ÔƒíÚµ‹×_Å‹Sðò˼ñÆìÚ³‡Ø¨(ŽgLu5ß …|Ȩ­…“{’xà˜ýSÂ+÷w©Öo×׳rÝ:V|ô÷?ù$›ö‡I²ÓÒ˜4y2§LšÄ©§žÊ¨Q£œ„Û†39“·x‹«¸Š‰Lä.îb³‚.KêQæa )äMÞ$Ѝ ËéÖŠ‹‹y÷Ýwyï½÷þÕëÝwY¿~=UUUDGG“‘‘ œ{€ÀСC#'¢¢|¬:‹p@'55µÍmC¡P¤“K8ìò§?ý‰’’êëëèÕ«¹¹¹Œ1‚c=6Ò­lĈdddí¡I’$I’$I’¤NÂ@ˆ$I’$I=Hee%ûÛßÈÏϧॗXµv-uõõ|"!IÕÕ\ 18!"± vûJ0bÿÇtˆEÊ•Àee,ùãùþŸþÄöÚZú&&òé‰9íÌ39ûì³ÉËË#:::ÀtN©¤òÏq;·ó-¾Å+¼Âc<Æ]šÔíUPÁ­ÜÊòŸœÈ‰A—Ó-ÔÕÕñÁðÖ[o±zõjÞyçHø£²²€¾}ûF&ö_zé¥ ><Ò"++‹¸¸¸€G¡£%**Š´´4ÒÒÒ8ñÄ–_sÕÕÕlܸ1yÿý÷yï½÷X´h=ô»víþõ<:öØc9î¸ã8ñÄ=z4ŸøÄ'<ß$I’$I’$©›1"I’$IR7·fÍž{î9ž_¸‚%K¨©­å¤¸8Ψ®æ¿I@ºá£"8kÿuuÔk€‚={XòòË<´x1ßûÞ÷<`gŸwçþÛ¿qÖYg1hР ËîTbˆa6³9ƒ3ø_` c˜Ç<&0!èÒ¤ní{|xâù>ߺ”.©¢¢‚U«V±jÕ*Þ~ûmÞzë-Ö¬YÃÇLLL Ç{,Ÿüä'™:u*×]w]dâ~zzzÐ¥«‹'77—ÜÜÜ¿_RRé4^Λ7ÿøÇÔÕÕÑ»woFŽÉèÑ£5jT$(’œœÜÁ#‘$I’$I’$IGŠI’$I’º¡•+W2oÞ<æýæ7¬ß´‰AqqL«­åáPˆ³Ôêê Kì‘¢÷\ Au5«€ç·oçùyó¸ú÷¿§.âÔOšÏ]uÓ§O'555Ø¢;‰)La%+™ÉLÎà îâ.n࢈ º4©Ûùy„GxéKß ËéôªªªxóÍ7Y¶lË–-céÒ¥|ôÑG 4ˆÑ£G3iÒ$®»î:FÅÈ‘#éÕ«WÀU«;ÊÈÈ ##ƒ3Î8£Ñú={ö°fÍšH@iÕªU<õÔSlݺ€aÆ1qâDÆÏ„ ;v¬ÏQI’$I’$I’º!’$I’$uï¾û.¿ûÝï˜÷Ûßòκuä$$pÅÞ½\Œ«©!&èÕ¢Qû?nª­eð"ð¯¿Îw–-ã×_Ïg&OæŠ/|K/½”~ýú[lÀRIå9žãÿñÿøßâe^æ1#¯l.I·s;Ùd3“™A—Ò)}øá‡¼öÚk‘È[o½EMM ©©©Œ?ž¯|å+œ|òÉœxâ‰dff]®Dbb"ãÆcܸqÖ³jÕ*–/_βe˸ãŽ;ؼy3qqqŒ3† &0aÂN;í4rrrª^’$I’$I’$ˆI’$I’º°úúzþö·¿1ç§?噿þ•ÌØX¦×Ôð0iï^{'t1}K€KB!ªêêö…C^y…^}•ÿ¼þz®¼ê*¾~ýõŒ5*àJƒE7s3™Èçù¿æ×<ÆcÄt9®]»X¶lùùùäççSXXHll,#FŒàÔSOåßøyyy|êSŸ"*Ê#¯ºŽÌÌL2339çœs"늋‹),,dñâÅðÈ#PUUEnn.S§NeêÔ©œyæ™ 80ÀÊ%I’$I’$IR˜I’$I’º ;vðàƒòа±´”³bcY ño55D]œŽˆ^ÀÀuu̯­åœ_<ü0“'MbÖ·¿Íg?ûÙ;ùø Î`%+™ÉLNã4~ĸ‰›ˆ2%–ðrÉåJ® º”ÀÔÕÕ±xñbžyæ-ZÄŠ+ˆŠŠbܸqœsÎ9üÏÿü§œr A—*qáÈ\@UUK–,!??ŸE‹ñè£ …8餓˜:u*çw“&M":Ú3PI’$I’$I’‚` D’$I’¤.¤²²’û￟ŸÞuõÌ—kk¹^Sti:Š’oÿYSà ÀÏ—.eú%—0zäH~xÇœþùWŒÁ æYžå~îç;|‡%,á—ü’R‚.Mê’Þå]þÀø-¿%¶‡ý긶¶–—^z‰?ýéOüùϦ¬¬ŒãŽ;ŽiÓ¦që­·2yòd t™R‡ëÕ«gžy&gžy&Û·o祗^bÑ¢E<õÔSÜu×]¤§§sÑE1}út&OžLllÏzÿ$I’$I’$)H^²I’$ýöî<®«2ïÿøë+à†¸ )j©h®hi¢f¥¹„ÙbZM™3e6™•Õí]ÓdÓtÿ¤¼­i²Ål™¸­{šÓš,‹t*+)qÉ41Í,— ÷\sCùý¡r‹¢‚Ûy={œ~¯ss½ÏáÔ18ŸsI’¤ ++‹gžy†gÅC‡r÷¯¿²<;›ÀÙA‡Ó).ÞËÉa^n.õ¾ý–«zöäü6mHMM :^ B„Ì`¦0…9Ì¡5­™Îô cI%ÒÿãÿÑ”¦\ÇuAG9%rss™2e ¿ÿýï©Y³&Ý»wgöìÙÜsÏ=,Z´ˆï¾ûŽQ£FÑ»wo‹A¤}ªV­ÊÕW_ÍóÏ?ÏâÅ‹ùöÛo¹ë®»˜5k‰‰‰ÄÆÆrë­·òé§Ÿ’››t\I’$I’$I’N{„H’$I’TÌ}ýõ×´kÝš?ÿñü~óf–egóࣩ¥[Kà={˜ TKO§K—. 0€Í›7-èÄ<æO<s1I$±‡=AÇ’JŒå,ç_ü‹ÿâ¿(sšÿØxãÆ<ýôÓy3€,Z´ˆ?ýéO,[¶Œ9sæðç?ÿ™¦M›S*š5kÆÃ?ÌܹsYºt)C† aáÂ…tëÖfÍšñÌ3ϰiÓ¦ cJ’$I’$I’tÚ:½³'I’$IR ¶{÷nþô§?Ñ®m[ª|ÿ=é99ü¨t0+m€É99ŒÏÍå½×^#¾qcÞÿý c"†>àF0‚ÇxŒÞôf#ƒŽ%•£Eêp ×å¤ùúë¯éß¿?uêÔá‘G¡{÷î,X°€3fpÿý÷tD©DkРüã™9s&óçϧ[·n$%%Q§Nn¹åæÎtDI’$I’$I’N;„H’$I’T mذî]»òˆŒÊÉᳬ,JÅÚuÀ·ÙÙ\²a½zõbذaäææë” b0ƒ™Æ4¾áZÑŠ4Ò‚Ž%k[ÙÊ«¼ÊÝÜM8áAÇ9á–,Y 7Ü@›6m˜7oÏ>û,+W®äùçŸ'>>>èxÒi©eË–¼ð ¬X±‚§Ÿ~š¹sç’ÀUW]eaˆ$I’$I’$I'!’$I’$3K–,áÂvíX:kŸggs :Ô$ ;Õç&xmÏ’ss–”Dß>}ÈÌÌ<Å)Їv´ã+¾âΡ3I"‰=ì :–T,½Ê«d‘Å­Üt”jíÚµÜu×]4oÞœùóç3aÂæÍ›Çí·ßN¥J•‚ŽwD‰‰E»ƒµÿ©ÞŸJ¯¨¨(î¸ãÒÓÓIIIaíÚµ´mÛ–[n¹…5kÖO’$I’$I’¤Ï‚I’$I’БŋsA»vTûùg¾ÊÎ¦Õ Þ¨KQüˆý±îçd *SAå§"Ç eÏþýÎ;ÜØ·o©œ) :ÕyŸ÷Áçq.åRÖ²6èXR±²‡=¼È‹ÜÂ-Dtœæ­·Þ¢yóæLœ8‘矞ùóçÓ»wïS2v(Ê[ŽÕž=‡ÞAŽ´¿‚úÆárëþJ«ãý~eœ’ìòË/gÖ¬Y¼ûî»L:•¦M›òúë¯K’$I’$I’¤Í‚I’$I’Š‰Í›7sÙ%—ÐpëV¦dgSó$Œ‘{Àׂ–cñÉaÆ(N‚Êtð¹9•º)ÙÙ¼ÿÞ{$%%˜$X!B f0Ó˜ÆR–’@Ó˜t,©ØxŸ÷ù¸›»ƒŽrBdee1`ÀúôéÃï~÷;~øáHxxø)˰¿ïxŠñ>ù¤hw¢ößïpu¥Ui-¼£gϞǼŸ“!  …ÈÈÈ W¯^DEEQ³fMn¼ñF6lØP`߃Û\ …0`Àaû¼÷Þ{\xá…y߃{ï½—­[¾Ë>kAm/åË—Ïë³`Á®¸â ¢¢¢ˆŠŠ¢G,X°ààÝ×¹9Ú8S§NÍ—o¿mÛ¶ŪU«òÚÂÃÃóúM:õˆÓÓÓéÞ½;‘‘‘T®\™Ë.»Œ?ü°PÇU˜ö-[¶pï½÷Ò AÊ—/O­Zµèß¿?_~ùe¾íÜþÀkàhçåà±—.]Ê5×\CµjÕ›óTéÕ«Ÿ}öŸþ9·ÝV¸ÿI’$I’$I’¤ÿcAˆ$I’$IÅÀ3#FpaXA=¾ZÐc€½€›ÀÌ}_ï9¨OîÁQQÆÜ ¬foÆàþƒú..~,Û÷ùZà†BæY$=÷mÿ#{ Oz+öõÉÚ7ÆßØoð/ X{P%÷€et!³á{öðcFï¼óÎ)­x b0ƒI#å,§­˜Â” c1xð`ž}öÙ×5Š;ððp/^Ìo~óî½÷^Ö­[Ç›o¾ÉðáÃó=Ì|óÍ7“Í”)Sزe Ë—/gðàÁŒ5êJŠ×y*Tár./Tÿâ|¾ýöÛüóŸÿä­·Þ"!!á˜öq2åæî½ 0€;3իWóÕW_±cÇî¿ÿþûÔ–›››·Œ=ú°ýaïƒö7ß|37ndæÌ™lܸ‘{î9ø.{ø¬µï_víÚEÛ¶mó®‡%K–˜˜HÏž=Y¶l?þø#7Þx#½zõbÅŠîïàñ snŽ6NçÎùꫯ¨\¹r^qì½>ªT©Â›o¾™×–MÓ¦Mùꫯèܹóaó}ÿý÷ôéÓ‡x€_~ù…™3g²}ûvzôèqÌçñ`7ß|3U«VeÆŒlÙ²…I“&±dÉÚ·oÈv]…=ÿû÷qçwrÿý÷³jÕ*&MštÄã8Ú¶mËøñãyýõ×ý»Š$I’$I’$IEÊ=Üo%$I b<㹞냎¢Ó@(büøñ\½×“$(99™C:e233©Z¹2/eeqË)ïpï€>Ú6gs謡¶-¨­0Ž4Æ»ì-ÌØo1ÐXy@ÛMì-Î|Ðön)D¦s€jHFîû¼è<ô>þ˜Ô9(÷‰:7Ç«GXU®¾šño½ÀèÅÓ¯üÊ0 <ÌÃü?þezJVVqqq|øá‡ÄÇÇçµoذ³Ï>›Å‹S£F úõëǹçžËþð‡¼>'N$999o憨¨(V®\IåÊ•Oùq¨diNsºÑQ®£8_§­Zµ¢eË–¼þúë'dÇ+ òà(âÝwߥW¯ÿ»›-^¼˜®]»²råÊBm¸çiÝ~›6mâì³Ï.pF’¢Œ0hÐ 6oÞÌØ±c¸ñÆ9çœsxàüwÐW_}•ôôtFŽYÐnòW˜sS˜qrss©U«ï¾û.çŸ>—\r >ø C‡%-- Ø[DqÑE±zõê#ÎñÛßþ–Ë.»Œ›nº)_¶¦M›æ;GE9·GEEñÓO?×¶|ùr4hpÔ1 {^ÜÇgŸ}vÄ"˜ üîw¿cÑ¢EÌ;7è(:ÁæÌ™@›6mN"I:’ý¿û|“7ÒS’$I’$ápÏ›:Cˆ$I’$I[°`»²²èx ÇÌ=h)ŒjìÁãd:Òú\Ÿ½³…ècöhìÈïðþ?S€>´_±oßûEïϰ·e2ù‹AŠ›N99|5cFÐ1Š•ÊTæMÞäE^äq'‘DVrU 4èÙFMÏž=©Q£Ó§O窫®Ê×§S§N¤§§ç}nÞ¼9÷ßÿ!˜KšÅ,±ˆ›¹¹ÐÛ×ëtùò大§sÛm·÷¾N¶NòßÍêׯÏêÕ§æ¿;ÕªUcãÆã¿“¿ñÆ|öÙg$''çµM™2…>}½ƒ^qÅ|üñLJ´¤0ç¦0ã„B!zôèÁ'Ÿ|ÀÊ•+Ù²e —\r åË—gùòå¼÷Þ{ôèÑãˆÅ Ÿ~ú)ݺuË×Ö¤I“£àE‡¸êª«ø÷¿ÿ7³I\\\¡Ç(êùo×®Ýñ>In»í6¾þúk~úé§ £H’$I’$I’TbX"I’$IRÀ~ùåbÌpðㆿ€3pöÎjqäÇ%‹®¨cT;ès9ͽž‚ÏcaÏíöš„Zbê[xˆ½³‰\µo»â,Xw> d Ó™ÎOüD+Zñ“cà@&L˜ÀúõëÈÉÉáÅ_äî»ïÎë“‘‘AãÆ …ByKttt¾‡¦ÇÇ/¿üÂÙgŸM³f͸å–[˜8qâ }xY%ßk¼Fsš“@B‘¶+Ž×éþ‡Ç›6mZämOµjÕòßÍÊ•+wRþÝüå—_0`gžy&áááy߇ãõÝwßñŸÿùŸ¼õÖ[TªT)¯}Æ Ô¯_?ß÷< Ë?þX¨}æÜvœË.»,¯ dìØ±üö·¿à†n`ܸq¼ÿþû\~ùåG͵~ýzbbb u Çê­·Þ¢]»vÜu×]DGGÓ©S'FŽIVVV¡¶/êù¯X±â >‚£yóæ…¾f$I’$I’$I’!’$I’$nÿ•ÛÎq ›Ø; Æ4 “¢Í$ä1ÀšÚ7aû:ƒJ.°ã ¾ßÃ9À;û¾gÛ€¨búhqІ6Ìe.]èÂå\NIäsJ3ÄÄÄpÍ5×ðòË/0qâDbccó½É½råʬZµŠÜÜÜ|KNÎÿe‹‹ãwÞaóæÍŒ7Ž:0|øp pJGÅ×nvó&oÒŸþEÞ¶8^§Õ«WÈ+RÜtÓMDFF2mÚ4233ó¾Çcûöí\{íµ<ñÄ´hÑ"ߺ˜˜6nÜxÈ÷<77—;¾ƒ»ÂŽÓ½{w¾üòKvîÜÉo¼Aß¾}¸öÚk™0a›6mbæÌ™tïÞý¨cV¯^ý˜¯­P(Ä®]»òµ4KK•*Uxúé§Yºt)Ë–-ã®»îbܸqy¹æTÿ“m‘ôþ§%I’$I’$IÒÑY"I’$IRÀš6mJ(âë ƒ`:ð{g½ß×v¢'<ctÞ- ý“Bn)0µ€ö/€óø¼èüp0¸•½…-Gs¢gZ)¬¹¡Íz€WùU¦2ãÇ«¼Ê_ù+‰$²šÕGßðÎ*d¶ã±x/,ŒK¯¸âŒVòõ£Ó˜FœË¹|ȇ§lì-ZдiS†Ê¢E‹¸þúëó­:t(>ú(¯½ö6l`ûöí|òÉ'\qÀ÷¶S§NŒ3†+VÍš5kxúé§éÔ©Ó);o˜@{ÚS‡:Ç´}q»NË–-Ëí·ßΈ#X¹rå1SIP§Nf̘AVV}ôguø;È\À<À?þÈîÝ»Y²d wÞyìwÙ—_~™ôôt^xá…×'%%1tèPþõ¯±aönÝJJJ }ûöeèСns,Š2Îå—_NRR7ÝtS¾ö¾}ûòÄOpÙe—j̇~˜G}”)S¦°}ûvæÏŸÏïÿûC ¡ ’˜˜ÈC=ÄŠ+ضm~ø!ûÛß ì;`À.\È®]»X»v-#FŒà’K.É×çp×À©:ÿ'ÓŠ+xê©§¸ýöÛ‰ˆˆ:Ž$I¥RyÊ“Y¨W]H’$I’¤âÄ‚I’$I’Š;q¡KOò8¡¾©üäìí¢=\ ìŒ÷ÀíB…l;Ñc®½0™½q@=à`l!3ÕÞÆ ZÀ£À(öÎP øŠüÅk€{g Èuð˜O¿Û·Ÿ»€‚­=±^v……qóÍ7Ÿ‚ÑNçqs™K7ºq—ñ ’CÎ){ðàÁ<þøãÜvÛm”-[6ߺøøx&MšÄøñ㉋‹ãŒ3Î`øðáÜwß}y}}ôQÞ}÷]ZµjETT;v$''‡7Þxã”äWñ–C)¤Ð›ÞǵŸâv>ôÐCÔ¬Y“«¯¾šmÛ¶×±¯ý³88›CAmEíûôÓOó»ßýŽJ•*q×]wågÔÿÿø™™™´oßž¨¨(®¾ú꼂œ£UPÛàÁƒùæ›o¨X±"¡P(oÙ¯~ýú¼ýöÛŒ7ކ R«V-}ôQFE¯^½8’¢œ›¢ŒsÙe— …èÝ;ÿµÞ«W/"""¸üò˘k¿–-[òú믓””DLL W^y%×]w]¾›ÃÃSO=EÙ²e9ï¼óˆå…^àþçé;eÊ*T¨ÀÅ_LåÊ•éС™™™¼öÚkùöw¸k °çåÀœE™qædÛºu+½{÷¦N:üéO…)¯•$I'C*°“AÇ$I’$IEÊÍÍÍ :„$•$!BŒg<×sýÑ;KG …?~ü!oS•¤Ò.99™C:¥²³³iOì²e|”ítÂü$„‡óŸ=Ä#<tœéüƒ;¹“¶´e,c©Mí“:ÞÎ;‰ŽŽfÉ’%Ô©sl38H‡“J*éÌw|Gšó~ŠãuºlÙ2.¼ðBêÕ«Çĉ©Y³fБ$Åš5kèÕ«Ë—/'--FI'Áœ9shÓ¦MÀI$IGòþÀt¦3ƒAG‘$I’$I8Üó¦>_"I’$IR1ÎkcÇ2-Â÷"ëDÙ \A£æÍy衇‚ŽSbõ£Ó˜Æ*VÑŠVü›Ÿ´±6oÞÌO˜<™÷*V$1"‚Ÿ‚¤å} Cx8ÕÏ9‡_}EÓ¦MƒŽtÚéG?f3› l ­˜Ä¤ #I…òoþM,±$t”S¢oß¾,^¼˜aÆ‘œœLÆ ùïÿþoÖ¬Yt4©TY½z5ÿýßÿMƒ xì±Ç¸ûî»ùî»ï¸á†‚Ž&I’K,kYK9AG‘$I’$IE`Aˆ$I’$IÅP÷îÝI›9“ qq´ç9`OСT¬­ú–)ÃU@ÏnàÓ/¾ fÍšAÇ:m5§93™É¥\Ê•\É`“EVб¤#ú„OèF7B„‚ŽrÊ”+WŽûî»%K–pûí·óì³ÏR·n]úôéÃgŸ}FnnnÐ¥ÓRnn.Ÿ~ú)×_=õêÕãÙgŸeÀ€,[¶ŒaÆQ©R¥ #J’¤ƒÔ¡Ùd³ŽuAG‘$I’$IE`Aˆ$I’$IÅT|||8çw 6ä¯ý+ݺu#==´´4n¼ñFÊ—/tLI’tû BV²2à$’$I’$©(,‘$I’$©hÙ²%ÓfÍ"åƒXÙ¢­ë˜t0r™À?€–ô/S† ûöeñ?0ò¹çˆŠŠ :^©ÕŒf|É—\Ã5ô¤'ƒÌnvKÊó ŸÐ†Ô§~ÐQŠsÏ=——^z‰µk×òÑGѶm[ž{î9š6mJ‹-xøá‡™:u*»ví :ªT¬ìÚµ‹Ï>ûŒ?ÿùÏÄÇÇÓ¬Y3FEûöíùøãY³f /¼ðçœsNÐQ%IRT Õ¨æ !’$I’$•0„H’$I’T‚\~ùåÌþúkÞž0eÍ›sp^D¯;‚§“j9ð pVDÂÂhý›ß°ðÛoùß×^#...èxbïÃ3É$ówþÎ+¼ÂE\Är–KàS>¥Ý‚ŽQ,…‡‡“˜˜Èßþö7V®\ÉÔ©SéÚµ+o¼ñ]ºt!::š=zðä“O2wî\öìÙtdé”Ú³gsæÌá¯ý+—^z)ÑÑÑtíÚ•ñãÇsÉ%—ššÊªU«x饗¸ä’K:²$I:Fu¨Ã VC’$I’$!’$I’$•0¡Pˆ«¯¾š9óç3cÆ â¯»Ž»""83<œ»i€ªž¶ÿ® ãìPˆ15jðÿõ_ü”‘Á˜±ciÒ¤IÐU€~ôã+¾"“LZÓšñ¯ #©”ÛÊVf3›®t :J±ÆÅ_ÌsÏ=ÇÒ¥KY¶l#GޤjÕª<ùä“´iÓ†š5kríµ×2bľøâ vì°$S§—íÛ·óùçŸóä“OrÍ5×P£F xê©§¨^½:Ï=÷Ë—/gÉ’%Œ9’N:Q¦Œ¿n’$étД¦|Ë·AÇ$I’$IEàkš$I’$I*ÁÎ?ÿ|Î?ÿ|ž~öY^yåÆüïÿòÂ÷ßsV¹r\¿k}€¶A‡T‘d“€qeÊ ‘ $vëÆ›Ò«W/ߺ]B4¥)_ò%CÂõ\Ï=ÜÓŸ|ò Ÿþ9O=õkÖ¬!<<œ–-[Ò¡CÚ·oOûöíiܸ1¡P(èøÒQåææ²xñbf͚ŬY³˜1c , ;;›ZµjѾ}{þüç?Ó­[7Z¶léu-IÒi®%-y׃Ž!I’$I’ŠÀ'$I’$I: œqÆ<øàƒ<øàƒ,\¸·Þz‹qÿøO-_N͈ºgeÑHªV‡X| L ãÃPˆm99thÛ–Çûö¥oß¾Ô¨Q#èˆ:å)ÏHF’@ƒÄt¦3žñ4 AÐÑTÊÌd&u©K-j¥D …Bœ{{î¹Üwß}¬ZµŠ9sæ0gÎÒÒÒøßÿý_vîÜITT7¦yóæ´iÓ†øøxZµjELLLÀG¡Òì×_å‡~`áÂ…Ì™3‡o¿ý–yóæ±~ýz"""8çœsèÔ©÷Þ{oÞu+I’J—´`ËØÎv"‰ :Ž$I’$I* B$I’$I:ÍÄÇÇORRsæÌ!%%…Éï½Gßyó(\Î¥»wÓHÊœ·4ZL>>Œˆàû¬,¢*Tà’K.á¯W\ÁUW]E­Z>¸}º¸‰›hG;®çzZӚьæ:® :–J‘YÌ¢=탎qZª]»6µkצgÏždee1oÞ<¾þúkÒÓÓùæ›oxÿý÷Ù¼y3°wÆ‘–-[Ò¢E š7oN£FhÔ¨ÕªU ò0tšÙ´i?üðßÿ=‹-â›o¾á›o¾áÇ jÕªœsÎ9´lÙ’ë®»ŽÖ­[ÓªU+"""‚ .I’×’–ìa‹XD AÇ‘$I’$I…`Aˆ$I’$I§±6mÚЦM†ʆ øè£ø÷äɈykBf1‹! áz®g Å(ÊR6èh*f3›?òÇ c” ´mÛ–¶mÛækÿé§ŸòÊOOOgâĉ<õÔSìÚµ Ø;ÓW£FhÒ¤I^‘HãÆiذ!‘‘¾™Y‡Ú¾};K–,á‡~È+þØ¿¬_¿€råÊѨQ#Z´hÁí·ßNË–-iÙ²%uëÖ 8½$I*®ÒH"ù†o,‘$I’$©„° D’$I’¤R¢zõêôíÛ—¾}û°jÕ*ÒÒÒ˜6mŸ|ò #¾ý–=¹¹ÔŠˆ MNñ{öÐh4BA†/!²ÅÀà[`aÙ²ÌÌÍe}VaaœÓ¢—^|1Ã.ºˆnݺl`Rå)ÏHFÒ‰NÜÊ­ÌaãOCM§±å,g-k9ŸóƒŽRªÕ«Wzõêqå•Wæk_µjß~û-Ë–-Ë[ÆŒÃâÅ‹ÉÉÉ ZµjÔªU‹ÚµkÓ A4hïsýúõ)S¦L‡¥“hÓ¦M,[¶ŒU«V±zõê¼ëcÿçåË—“›› @­Zµˆ§E‹\uÕU4hЀæÍ›Ó´iSÂÂÂ>I’T’”¡ ÍhF:éAG‘$I’$I…dAˆ$I’$I¥TíÚµ¹îºë¸îºëظq#_~ù%óæÍãë¹syçË/yòçŸÙ“›KÕðpš–)CÓÝ»i ù–rCP6°wÖïö}ý¾LGDð}VY{öP±\9Z4kFëöíÖª­[·æ¼óÎsp-×Ò’–ô¡çqÉ$Ó‡>AÇÒij³ˆ ‚Ö´:Š P»vmj×®}Hû®]»Xºt)Ë–-ã§Ÿ~âçŸæçŸfÁ‚¤¤¤°zõê¼b€òåËsÖYgQ³fMjÕªE­Zµ¨Q£uêÔÉ÷µfÍšŽ,''‡uëÖ±víZV­ZźuëX¹r%k×®eõêÕ¬Y³†5kÖ°bÅ 233(S¦ ±±±Ô¯_ŸºuërÑEQ·n]êÖ­KÆ iРåʕƿI’¤“å.à ¾:†$I’$I*$ B$I’$IÑÑÑôèу=zäµmÛ¶ùó瓞žÎwß}Çâ… I]´ˆŸV¯fOn.eB!Ίˆà, ÞîÝœ œ ÔÛ÷µp%kv‘]À:àç}ËŠ}ËOeʰ""‚÷ìaCVÊ–¥qƒ4nÑ‚ÞMšϹçžK“&M|#·Ž¨1™Á †0„¸ø€—y™ T:šN3_ò%-hAE*EEP®\9š7oNóæÍ \¿{÷n222ò E222X·n«V­bΜ9¬Y³†Õ«W³cÇŽ¼m¨Q£Õ«W§zõêDGGç-111DGGçk¯^½:QQQDEEªÃ.Q¶nÝʯ¿þÊÆÙ¸q#6lÈûºÿÏ·¯[·§õê IDAT.o怊+R»vmjÖ¬Ill,­[·¦FyuëÖåÌ3ϤlÙ²©$I*m.æbžçy6³™ªT :Ž$I’$I: B$I’$IÒaUªT‰ .¸€ .¸ _û®]»øá‡X¼x1K–,aÅŠü´|9.[ÆŠU«X¿eK^ß2¡1ááÄ”)CLn.1YYÔÈÍå  Pˆ*Q@å}ÞÿèrUò”D‘ÿ;Ø[Ä‘—m_Àæ}Þl¶íûó6`ýþ%,Œõaaü ±.'‡­ÙÙyû  £VL uëÖ嬆 ézÖYÔ«WÆÓ¸qcêÖ­K(T’Ê]Tœ”§<#ÉÅ\Ì­ÜÊB2žñœÍÙAGÓidó8ó‚Ž¡¬lÙ²4l؆ ±ßÖ­[YµjU¾(,VÈÈÈ ===¯ˆá×_-p?U«V%22’J•*Q©R%ªU«FÙ²e©V­•*U¢jÕªTªT‰ˆˆ"##)[¶,åË—§B… „‡‡ç•T«V €Ê•+ç+œ¬Zµj÷Óˆˆ*UªT`¦mÛ¶‘µ¯@ó@¹¹¹lÞ¼9ïsNNNÞqmÚ´)ï¼dgg³sçN233Ù½{7Û·o'++‹mÛ¶±iÓ&¶oßζmÛØ¶m›7oÎûóöíÛóíÿ@UªTÉ+¦Ù_\Ó²e˼¶ØØXj×®7sËáŽM’$)Hé À|AOzF’$I’$•!’$I’$©ÈÊ•+G‹-hÑ¢EëwîÜÉÏ?ÿœ÷ðéúõëó-‹W®$mݺ½Yþú+Û33Ù}@!ÆÉR©B*–/O¥Š©Ã±±T¯QƒF11ÄÄÄP³fMbbb8ãŒ3¨W¯±±±Îô¡“î®á<Îãnà<Î#™dnà† cé4±€ô¢WÐ1¨¨(š4iB“&M Õ?;;;ßÌ[·neëÖ­ù "¶mÛÆÂ… ™2e ­Zµ"22’-[¶ð믿æ_äää°cÇvíÚuôAT®\9*V¬˜W´FåÊ•©R¥J^ñKÍš5ó _*UªDdd$U«V%**ŠÊ•+ç›i%<Ü_¹H’¤’/šhâ‰'•T B$I’$I*üí„$I’$I:á*T¨P¤P¼·roÙ²…;v°sçÎ|oõÞoóæÍäæææ}Þÿ0ç~û愽oê®X±"+V¤jÕªÇyTÒÉSŸú¤’Ê<@_ú2‰Iü¿Q‘ŠGßX:Œ5¬á~¡ïI §FÔ¨Qã°}’““yæ™g¸öÚk=z4‘‘‘GÜçþÙ7öìÙÖ}3ˆx/ÏÎÎfëÖ­n{¤¢’ƒïÿŠŠŠÊ+Î…By¨R¥ eʔɛÅD’$IëLgRI :†$I’$I* B$I’$IR±AµjÕ¨V­ZÐQ¤@”£#Iºð{~O;Ú1žñÄt4•P X`AˆNˆÌÌLî¸ãÆŒÃðáÃyà…BGÝ®lÙ²yÅÕ«W?Ù1%I’tt¡ /ò"ëYO 1AÇ‘$I’$IGP&è’$I’$I’þOozó5_EèÀXÆI%ÔC 5©t•ptìØ‘””&OžÌ!C U "I’¤’éR.¥å˜ÈÄ £H’$I’¤£° D’$I’$I*fêQÏùœA âFn¤ýØÁŽ c©„YÈBZÒ2è*áRSSIHH ++‹Ù³g“˜˜t$I’$d©Hwº3 AG‘$I’$IGaAˆ$I’$I’T EÁ_ø ïð)¤Ð–¶,`AбT‚,`ñÄC%Xrr2‰‰‰tíÚ•´´4ââ₎$I’¤Sä®ác>f3›ƒŽ"I’$I’ŽÀ‚I’$I’$©ëE/æ1*T¡Ø #©„XÌbšÑ,è*233éß¿?ƒ bذaŒ;–ÈÈÈ cI’$éêIOB„˜Ä¤ £H’$I’¤#° D’$I’$I*æêR—Ïùœ?ðúíûg;ÛƒŽ¥bl3›ÙÄ&Ò0è(*a222èØ±#)))Lž<™!C† …‚Ž%I’¤S¬*UéB&0!è(’$I’$é,‘$I’$I’J€pÂI"‰wy—ø€¶´å¾ :–Š©%,° DE’ššJBBYYYÌž=›ÄÄÄ #I’$)@7p)¤°žõAG‘$I’$I‡aAˆ$I’$I’T‚\ÅUÌcÑDÓžö$“t$CKYJaÔ¥nÐQTB$''“˜˜H×®]IKK#...èH’$I XúP‘мÆkAG‘$I’$I‡aAˆ$I’$I’TœÅYLe*ðwr'ýèÇv¶KÅÈR–rgQ–²AGQ1—™™Iÿþý4hÆ cìØ±DFFK’$IÅ@*ð[~ËK¼D.¹AÇ‘$I’$I° D’$I’$I* '‰$&2‘IL"æ3?èX*&–±Œ†4 :†Š¹ŒŒ :vìHJJ “'OfÈ!„B¡ cI’$©¹ÛYÊR¦25è(’$I’$©„H’$I’$I%Ø•\É<æC íiÏHFIÅÀR–Z¢#JMM%!!¬¬,fÏžMbbbБ$I’T µ¤%èÀ˼tI’$I’T B$I’$I’¤îLÎä3>cC¸û¸‰›ØÆ¶ c)@?ó3õ¨t SÉÉÉ$&&ÒµkWÒÒÒˆ‹‹ :’$I’б;¸ƒwx‡¬:Š$I’$I:ˆ!’$I’$IÒi œp’Hâ#>âc>&ÒI:–²šÕÔ¦vÐ1TÌdffÒ¿ İaÃ;v,‘‘‘AÇ’$IR1×—¾ÄËFE’$I’$Ä‚I’$I’$é4Òn|ÅWÔ çs>#t$b›ØÄNvZ¢|222èØ±#)))Lž<™!C† …‚Ž%I’¤ ‚îã>þ‡ÿa낎#I’$I’`Aˆ$I’$I’tš9“3ùŒÏÂîã>~ÃoØÂ– céYÅ* B”'55•„„²²²˜={6‰‰‰AG’$IR s;·S™Ê¾t@’$I’¤bÆ‚I’$I’$é4FI$ñ1“FíhÇ<æK§ÀjVP‹Z'QqœœLbb"]»v%--¸¸¸ #I’$©*Oyþƒÿàyžg3›ƒŽ#I’$I’ö± D’$I’$I:u¥+é¤Szt ƒos-V±Šr”#šè £(@™™™ôïߟAƒ1lØ0ÆŽKdddб$I’T‚ÝÅ]„æÿWJ’$I’TŒX"I’$I’$æjPƒÉLfCøà®ñ®§±Õ¬¦µ :Š’‘‘AÇŽIIIaòäÉ 2„PÈëA’$Iǧ2•ù#d#XŪ ãH’$I’$,‘$I’$I’J…0ÂH"‰ù˜™Ì¤­˜Å¬ cé$ø…_¨IÍ c( ©©©$$$••ÅìÙ³ILL :’$I’N#÷r/1Äð0E’$I’$aAˆ$I’$I’Tªt¡ ó˜Gšp13’‘AGÒ ¶ Dt  99™ÄÄDºvíJZZqqqAG’$IÒi¦<åyœÇy׸Н‚Ž#I’$IR©gAˆ$I’$I’TÊÔ ÿæß<Â#ü?p5W³‰MAÇÒ ²‰M„”2™™™ôïߟAƒ1lØ0ÆŽKdddб$I’tšêC:Ðû¹?è(’$I’$•z„H’$I’$I¥PˆC¦ð%_ÒšÖÌdfбtlbÕ¨t "tìØ‘””&OžÌ!C…BAÇ’$IÒi,DˆŒàs>ç_ü+è8’$I’$•j„H’$I’$I¥Xg:3y4£èÄ7s3ÿÁ8ã¤$I’$I² D’$I’$I*åÎà &1‰'y’ÿ⿸š«ÙÈÆ cé9CH霜Lbb"]»v%--¸¸¸ #I’$©”y–g)Cîçþ £H’$I’TjY"I’$I’$‰!3˜)Lá+¾¢5­™ÁŒ céld£!§±ÌÌLú÷ïÏ Aƒ6lcÇŽ%222èX’$I*…ªP…—x‰Wy•ø(è8’$I’$•J„H’$I’$IÊÓ‰NÌcñÄÓ‰N$‘ÄöK…”E;ÙIªE'AFF;v$%%…É“'3dÈB¡Pб$I’TŠõ¤'×r-È6¶G’$I’¤RÇ‚I:>ÿÏ|ðÁ®KOOçÌ3Ï$;;€ÔÔTÚµkGùòå©_¿>¯¼òJ¾þ[·nå Q£FT¬X‘*Uª˜˜HJJÊI?I’$I’ C ð#Ácþøc’’’¨W¯DGGså•WòᇞÒc’$I’$© íhÇlfÓ’–t¦3I$±‡=AÇÒaìŸ!¤N¢!33“þýû3hÐ † ÆØ±c‰ŒŒ :–$I’tˆ>ôa¸…[ø‘ƒŽ#I’$IR©aAˆ$ƒ°ˆ0 tÈ,!£G¦gÏžÔ¨Q€éÓ§sÕUWåëÓ©S'ÒÓÓó>7oÞœû￟•+Wžüà’$I’$ƒbH!…Œà1ãR.e-kƒŽ¥ì/q†’/##ƒŽ;’’’ÂäÉ“2d¡P(èX’$IÒa=ÇsÔ£}èCYAÇ‘$I’$©T° D’ŽÑÀ™0aëׯ ''‡_|‘»ï¾;¯OFF7& å-ÑÑѬ^½:¯Ï¸qãøå—_8ûì³iÖ¬·Ür 'N$77÷”“$I’$I‡"Ä`“FKYJ LcZбtìœ!¤¤KMM%!!¬¬,fÏžMbbbБ$I’¤£*OyÆ2–, ‰¤ ãH’$I’T*X"IÇ(&&†k®¹†—_~€‰'K»víòúT®\™U«V‘›››oÉÉÉÉëÇ;ï¼ÃæÍ›7n:t`øðá 0à”“$I’$IGÓ–¶|Í×´§=]èBIìaOб´ÏþB,)¹’““ILL¤k×®¤¥¥t$I’$©ÐšÓœçxŽ¿ð&21è8’$I’$ö,‘¤ã0xð`^zé%²²²5j÷ÜsO¾õ]ºtaâÄÂý ³\¹rœ{î¹ 8É“'3~üø“Y’$I’¤ãV…*¼Å[Œ`ó8ÝéÎÖKÀ.v{ßÌ«’%33“þýû3hÐ † ÆØ±c‰ŒŒ :–$I’Td·r+ÈÜÈ|æG’$I’¤Óš!’tZ´hAÓ¦M:t(‹-âúë¯Ï·~èС<ú裼öÚklذíÛ·óÉ'ŸpÅWäõéÔ©cÆŒaÅŠdgg³fÍž~úi:uêtªG’$I’¤B b0ƒI#å,'¾à‹ c•zÙdNxÀITtìØ‘””&OžÌ!C…BAÇ’$I’ŽÙs­b(a ²!„|)!Û' ÷o.®‘™÷œó¼'C0ÃyæÆ4äAåÑ ãH’$I’T©T :€$UT¹¹¹AG$I’$©\ªCf2“$’¸™›YÄ"^äE∠:Z¥—CŽ«ƒ”cÉÉÉ 8¸¸8–/_NË–-ƒŽ$I’$Wr%ÛÙÎMÜD 1 ehБ$I’$Iªü˜8I’$I’$IÇÄ0†±Œe¤‘F;Ú1ŸùAG:!„pµ‰ò())‰ÄÄDzôèÁÒ¥K-ƒH’$é„3’‘ÜÎí g8oðFÐq$I’$Iª,„H’$I’$I:fÎäLRH¡=èMo~ÃoÈ!'èX•Vžßr&33“!C†0bÄ&NœÈŒ3¨Y³fб$I’¤@ü–ßr-×r)—2—¹AÇ‘$I’$©Â«tI’$I’$I•[êð/Ñ›Þ g8ð3˜Ac­Ò©BrÉ%‡"‰ :Î /--‹.ºˆÔÔTæÍ›GbbbБ$I’¤@…ñ'þDª0€Ìd&ýét,I’$I’*,W‘$I’$I’t\ f0KYÊ6ÐŽv¼Å[AGªtªø W ^rr2;v$++‹åË—[‘$I’âü‘a ã.a3‚Ž$I’$IR…e!D’$I’$IÒqÓžö¤BOzrç1ŠQd‘t¬JãઠÙdœäÄ–””Dbb"=zô`éÒ¥´lÙ2èH’$IR¹"Äø#É`ó/I’$I’¤ ©JÐ$I’$I’$XjS›Ìà—ü’¸ù˜Ì  M‚ŽVá¹BH°233>|8/¼ð÷ß?cÇŽ% K’$I*—B„xœÇ bCÈ$“ë¸.èX’$I’$U(B$I’$I’$b0ƒéHG2v´ãyž§7½ƒŽU¡,„¸êÊñ—––ÆE]Djj*óæÍ#111èH’$IR¹w°R‡: cØÀ=Üt,I’$I’*Œˆ H’$I’$I:qÎé|ÀœË¹œÏùŒb”e†£Pjd’p’Krr2;v$++‹åË—[‘$I’Jé>îãža"¹†kÈ&;èH’$I’$UB$I’$I’$ªµx˜Æ4žáÎælÖ°&èXRmj°‹]'9q$%%‘˜˜H=Xºt)-[¶ :’$I’T!]Ã5¼Â+¼ÌË\ÄEìfwБ$I’$I*÷,„H’$I’$I*3˜å,g7»9‹³ø :R…S‹Z€…ã!33“!C†0bÄ&NœÈŒ3¨Y³fб$I’¤ ­/}y›·YÆ2zЃl :’$I’$Iåš…I’$I’$IåÆù!ÿäŸô§?}èÃ(F±}AǪ0\!äøHKK£K—.Ì;—yóæ1nÜ8B¡Pб$I’¤J¡XÊR2Èà,Îâ_ü+èH’$I’$•[U‚ I’$I’$I‡ªNu’HâlÎf#xŸ÷™Å,ZÒ2èhåÞÁBv²3à$•Wrr2$..ŽåË—Ó²¥¯KI’¤Ê(==Ù³gS­Zµ £œ°FVÉ3ÝŸáçñ?gÐâAüì?? :’$I’$IùìÝ»—K.¹„ØØØÀ2X‘$I’$I’T. f0?á' d íiÏT¦r1«\«A "ˆp…c$))‰o¼‘0uêTjÖ¬t$I’$#ëׯ§S§NtèÐ!è('´‘ŒäNîä¡nQ»[mã1"ˆ:–$I’$I¤¤¤°~ýú@ !þ”,I’$I’$©Ü:Óø¹š«È@F1Š}ì :V¹"DMjZ)c™™™ 2„#F0qâDf̘aD’$I:"‰äAdÓH"‰ó8Íl:–$I’$I内I’$I’$IåZuª3™É<Çs<˳t¦3_óuбʭÚÔ¶R†ÒÒÒèÒ¥ sçÎeÞ¼yŒ7ŽP(t,I’$é„r5W³ˆEü‡ÿОö,fqБ$I’$I*,„H’$I’$Iª1ˆñ/ö±ö´g6³ƒŽT.Õ¢;ÙtŒJ!99™Ž;’••ÅòåËILL :’$I’tÂ:‹³ø˜éD'zЃñŒg?ûƒŽ%I’$IR ,„H’$I’$Iª0~ÀøÂ2빞}ì :V¹R‹Z|ÇwAǨ𒒒HLL¤G,]º”–-[I’$I:áÕ¡³˜ÅÃ<Ì<@ú°…-AÇ’$I’$)0B$I’$I’$U(ÑD3™Éü•¿2‹Yt¢«Yt¬r£6µÙÅ® cTX™™™ 2„#F0qâDf̘AÍš5ƒŽ%I’$é€!ná³˜ÏøŒ¶´åïü=èX’$I’$ÂBˆ$I’$I’¤ iø'ÿ$‡ÎäLf1+èHåB-j±“AǨÒÒÒèÒ¥ sçÎeÞ¼yŒ7ŽP(t,I’$I…ø)?å>¡}8ŸóÌ`Ëñ’TÆB¡¡PˆÖ­[“]äóÇòØe¿’$IA±"I’$I’$©ÂjCÞç}†0„˸Œë¹ž½ì :Öqµí¬bÉ$3“™l`ò!×r-çs>íiOsšó!µ\KNN¦cÇŽdee±|ùrƒŽ$I’$©u¨ÃÓ<ÍlfówþN[Ú²˜ÅAÇ’¤JgõêÕ<÷ÜsAǨ4Ê¢”b±E’$d!D’$I’$IR…M4“™Ì+¼Â˼L':ñ_ë¸ø”O©G=H ݸ‚+ø”OI%•éLçïüù˜µ¬¥ U‚Ž[n%%%‘˜˜H=Xºt)-[¶ :’$I’¤R¸˜‹ù„OhCzЃ;¹“L2ƒŽ%I•Êoû[²²²‚Ž!I’¤ÃX‘$I’$I’T)\ÄE¬`QDq&g2“™AG:æÚІ4Èû}.¹d‘Å^ö’MvÞ㵨E;Ú±\ËÌÌdÈ!Œ1‚‰'2cÆ jÖ¬t,I’$IG 1™Ç<&3™)L¡=í]-D’ÊHBBkÖ¬áÙgŸ :Š$I’c!D’$I’$IR¥Ñ‚$“Ì5\Ãå\Î`³›ÝAÇ:fªR•ÑŒ.võ"èJW"‰<ŽÉÊ¿´´4ºtéÂܹs™7oãÆ# K’$IÒQb#øœÏ9ÓèJW®çz¾åÛ £IR…v÷Ýwpÿý÷³wïÞÇïÛ·‡zˆvíÚQ£F jÔ¨A»víxä‘G ¬2’ššÊ…^H­ZµhР7Üp»wý~Þ /¼@·nݨ[·.ÕªU£uëÖŒ;–;v”jN»wïæú믧AƒÔ®]›þýû³fÍš#žG¸c}ÿ) åûÍ“O>ÉÏþs6lHÕªUiܸ1_|1 ,k‡>¾gÏnºé&5jD•*ÿ{qÉ’% 4ˆøøx¢¢¢¨_¿>½zõâÿøGsup_áœ3I’tü…rsssƒ!IIˆ³˜Å@E•@(bÖ¬Y èëI’•””İaÂŽ!I’*¸×x¡ ¥-˜Å,NåÔ #ßò-iÌw|WèóQDñq ·çdåWrr2$..Ž×^{–-[I’*­””:tèpI*¿U/ò"·p ըƓ¸P(Äk¯½F¿~ýò=%Ÿ3I’NDÇóýƒ¢®7u…I’$I’$I•Rú³‚T£èÀK¼t¤c¢uÆ0¢ˆ*ôù,²èF·ãªKJJ"11‘=z°téRË ’$IR%v%Wò_pçÑ—¾ô¡©¤K’*œˆˆˆ¼UB~÷»ß‘™™YäØÇœE‹Q·n]ž}öY¾ùæ¾ùæžV9óJ IDATyæêÔ©Ã{ï½Çã?ÀÃ?̺uëhÞ¼9 .dçμ÷Þ{üë_ÿ*°ßgžy†3fФI^~ùe6oÞÌîÝ»Y¶lguŸ}ö<ð@ØsJIIÉwÌfÍš±nÝ:zè¡RÏ£4c-Ïäæææû0sæLÆϺuëØ·o[·nåÍ7ߤW¯^aíãP|ðûÛߨ¹sg¾ç{÷îͼyóؾ};ûöícݺu<öØcäæærÿý÷Ñ9“$IÁ°"I’$I’$©ÒjNs²k¸†+¹’Á f7»ƒŽUæF3šýì/ô¹ÚÔæÇüø8'*2332d#FŒ`âĉ̘1ƒš5kK’$IÒ1C Oó4ïñ©¤’@ãO&E_Ì,I*èÒK/å´ÓNcÆ üéO*rÜÁ<{ì1®¹æN>ùdN>ùd®½öZ&Mš”oÌœ9s˜2e ]»v¥V­ZtëÖÉ“'ØïÔ©Sx饗¸ä’KhРÕ«Wçç?ÿy^‰âðÕ+ŠSÔ1ß|óÍRÏ£4cKÒ¬Y3–-[Æ£>ÊóÏ?϶mÛ¸à‚ ˜?~Øó;tžçwµjÕÊ÷ø­·ÞÊ“O>Éi§F5ˆç–[¾_aøßÿþw‘û*îœI’¤`„r «…J’Š"Ä,f1%–JPÔ^’t¢KJJbذaAÇ$I•̼Á5\Cq¼ÌË$t¤2u—ñ*¯’EVÞcDp>ç3‡9& ^ZZ]t©©©¼ôÒK$&&I’N)))tèÐ!à$’T:~ÿªœ²ÈâIžäîá$Nâ·ü–Á :–$•[¡PøßŠ/¾ø"W]u5â믿Îû°C/A¬^½:™™™lÞ¼™ äÛßæÍ›iذ!Õ«Wg÷îÝDGG³wï^222¨[·nÞ¸ŒŒ êÕ«—oß5kÖd÷îÝDFFæ{üЕ1¢¢¢Ø·o_Xs*ê˜ÑÑÑìÙ³§Tó(ÍØÂÎë¡–-[Æ•W^Éš5kò=þË_þ’çž{ކ –¸CŸß¶m111ùž›5k—_~y‘Û¾ßpÏ™$I'¢ãùþAQ×›ºBˆ$I’$I’¤B?úñ1s'ñ3~Æ‹¼t¤2u;·“Mv¾Ç"‰äü" DåCrr2;v$++‹åË—[‘$I’N`QD1ŠQ¬béÌÕ\Mozó)ŸM’*„Ë.»Œ6mÚ°iÓ&žxâ‰ãz샅œœrrrØ¿?û÷ïÏW\ÈÊÊ*jó £S§N|ùå—,]º”‡~˜‹/¾˜ZµjñÖ[o1jÔ¨Rïïð2À}÷ÝGnn.Çç‹/¾`Ïž=äææ²k×®²˜‚$I:Î,„H’$I’$I:a4£‹XÄHF2ˆA f0ßñ]бÊÄù1]èBªä=–EÝé`ª`%%%‘˜˜H=Xºt)-[¶ :’$I’¤r  Mx‰—x÷ø†ohG;†1ŒtÒƒŽ&IåZdd$wÝu?üp¡cÚ´iÀßÿþ÷ÏÍ;€üàyïÕ,^¼8߸äääÛ&$|¿Úï?ÿùϼUA û®¢Žy0SiæQš±ð¿7²³³ Œ¨R¥ :ub̘1Ìž=›•+Wðü#ì}gõêÕ<øàƒ´iÓ†èèhÞyçb·+éœI’¤`X‘$I’$I’tB©BäA^çuþÆß8‹³*ͧÁŽc\¾UBêP‡38#ÀDÇΊ+xê©§ }.33“!C†0bÄ&NœÈŒ3¨Y³æqN(I’$©¼ëF7þÅ¿x‰—x›·iMk~ÃoØÉΠ£IR¹uÅWкuk¶lÙRäó¿þõ¯yþùçÙ²e [¶laÚ´iŒ=:ߘ¾}ûpÓM7‘œœÌ®]»X¸pa¡+aŒ1"o›©S§²fÍöìÙÃÞ½{ùÏþßÿüg:uêö<Š:fŸ>}J=ÒŒ8ù䓘={6™™™ùrsÎ9¼ð ¬[·Žììl6oÞÌ_ÿúW€|c‹ÛGIš6m |_êÙ¶mÛ·ogöìÙ 6ì¨Î™$I F(·4µXI!BÌbtU¡PˆY³f1p ¯'I:TRRR‰o8J’$•…4Ò¸ŒËXÁ &3™_ñ«cÞäMîáÞáêQ/€”áË%—38ƒÏù€>ôáu^8UÙÛ»w/ ¬^½š¿üå/ 2$ï¹´´4.ºè"RSSy饗HLL .¨$‰””:tèpI*¿xö°‡ÇxŒ‡xˆÚÔæîá®!Ѝ £IR`®Bqø%†Ï=÷\¾÷c}~ïÞ½$&&XMâ nݺ1þ|ªV­ÊÖ­[i×®ëÖ­Ë7梋.âÕW_-°ï›o¾™)S¦›¹¤Ë!Ωÿþ¼öÚkùž‹gåʕԫW¯Tó(ÍX€k®¹†iÓ¦šý`¾Â 6Œ§Ÿ~ºÄ}:ÏÂÎÇ#<ÂØ±c <>xð`žþùÛ…{Î$I:Ï÷ŠºÞÔB$I’$I’$°šÒ”d’ËX†3œÁ æ;¾Ë{~ k¸’+ù˜ÉÈ“†'DˆÛ¸-ï¿Á/ÈÊÊbÙ²e<ðÀœ{î¹Ô©S‡öíÛsË-·ðÆo°mÛ¶ c—ÚC=Äš5k€ïÿ!|ùòå$''Ó±cG²²²X¾|¹eI’$Ia«Nuîà¾äK.äBnâ&~ÈyØÏþ ãIR¹rå•WrÊ)§ú\µjÕxûí·yðÁiÛ¶-ÑÑÑT¯^¶mÛòÐCå+FÔ¯_ŸE‹Ñ·o_jÖ¬I½zõøÕ¯~ÅôéÓ Ý÷þð,XÀÅ_L“&MˆŠŠ¢zõê$$$0zôh>þøã°ç0}út†JLL 5kÖ¤_¿~,Z´(¯ØPšy”f,Àã?Î 7Ü@óæÍ‰ŠÊ_<|ÿý÷:t(-[¶$**Š ЩS'ž~úiž|òɰöQ’Ñ£GóÈ#pê©§R­Z5ZµjÅ„ xæ™gŽêœI’¤`¸Bˆ$•’+„¨,¹Bˆ$ÎB$IRæ2—! ádNæe^æ4NãlÎf+È" €ÙÌæb.8iñ²È"žx¾ánþóÍ|þ×ÏYºt)ß}÷7¦[·nüìg?cõêÕ,\¸ÿûß´mÛ–®]»Ò½{wºtéRnÿ!÷Ë/¿$!!¬¬ï¿&‘‘‘Ô«WÛn»»îº‹0uêTjÖ¬pRIø û’*.¿i-k¹Ÿûy–g9•S™À.æbBýÉí’$UFÅ­6"IÒ‰®<¬Rå˜Y’$I’$I’*€ ¸€øˆË¹œŸñ3ºÑRÈ!€"ø¿âΡ! N›_vv6+W®dÁ‚,Y²„oÛ} #aÖ=³8§Ë9Üÿýœ}öÙœyæ™yÿ€{ÐÎ;ùðÃY°` ,`Ê”)ìß¿ŸV­ZѳgOzöìI=¨_¿~@³ûŸÜÜ\~õ«_å{,''‡ŒŒ ÆÏĉ7n\@é$I’$U&ÍhÆÓ<Í-ÜÂxÆs—ÑžöÜÍÝô¥¯ÅI’$IR¹`!D’$I’$I’hF3’Iæ2.ã^É÷Ü~öóßq×1‡9%üÞáÅ‹³cÇ5jÄ9çœÃïNþíÓÛsΆs @W»ví¼â,ˆL:µÜDž{î9-ZTàÓ³³³HKK;î™$I’$Un§q3™ÉíÜνÜKúó#~ÄÜÁ%\BAG”$I’$À,„H’$I’$IÒ!6²‘, ‚ö³?ßsYdñ&o2‹Y\Ê¥Ç-SI &¹Hi•ׂÈÖ­[=zt‘ÏgggóÄOо}{†zL³H’$I:ñü˜ó:¯ó)Ÿò0sWqw0Žq\Ã5DtDI’$IÒ ÈBˆ$I’$I’$M6—p »Ù]  rPˆ×s=]éJ,±Ç&Çq,€”¤¼DFÍ®]» ¬r¸n¸¶mÛrÖYg•éñ%I’$ à ÎàyžçNîäwüŽ‘ŒäAänáZ®¥&5ƒŽ(IR™*éý8I’, !’$I’$I’tÀ]ÜÅr–YÈ%—ÝìfØÜ29ny*€”$ˆ‚È{ï½ÇôéÓKüÇ稨(²²²xñÅ-„H’$I:¦~À˜Æ4îå^åQ~ÃoÏxnànäÆcö’$I’$ÊBˆ$I’$I’$ð±ŸýDE6ÙäRx!‹,ÞäM^äE®äÊR§"@Jr¬ "{÷îeذaDDD““Sàù¨¨(²³³©Q£ýû÷gàÀôîÝ»Lç(I’$IEiIKžà &0'y’'x‚Gy”A b4£ù!? :¢$I’$©³"I’$I’$Iüƒð5_ó&o2ƒ,g9DKnUCB„ÎpºÒ•xâ‹Ýoe*€”¤¬ "<ð©©©ùÊ ‘‘‘äææIbb"C† ¡_¿~T­Zõ¸ÌQ’$I’×€ÜÃ=Œc³˜ÅC<ÄT¦Ò™ÎŒbq‘DS’$I’TÉX‘$I’$I’¤C´¢£Ü6³™yÌc&3y›·É!‡"È!‡\rÙË^®ã:Þâ­|û8‘ %9š‚ÈçŸÎï~÷;rrr…BDDD››K=g S¸—{¹‡{Ä ®çz~̃Ž)I’$Iªà,„H’$I’$IRêQAn;³wòçÿþ™÷¼È'­>áúÕדÓ1ÇÈ(® òì³Ï …ÈÍÍeË–-|òÉ'4lØ0oI’$IªNã4žà à¦1'y’§xŠŽtd(C¹œË]5D’$I’tD,„H’$I’$IR ²³³Y¹re¡+€\ØíB:të@â¿-€”C ">ø`±+ˆgAD’$IREp'1êÀ-…’Hâ6nãn¡}Æ0~Á/áÏ•’$I’¤ðX‘$I’$I’¤ÃWq㫸D,ˆH’$Iª¨:ЧyšGx„™Ìäyž'‘DNåT®à †2”¦4 :¦$I’$©œ³"I’$I’$é„g¤â° "I’$©2©C†¸­bÏóã3¦3§xŠ{¸‡Ó9K¸„A âN :ª$I’$)B$I’$I’$Ux@. "’$I’*ªxy€XÆ2f3›§xŠ L Ä 28₎*I’$I:N,„H’$I’$Iªp,€¨¬X‘$I’TÑDÁÙnðÿàÌd&ws7·r+=èÁe\F?úQv‘$I’¤ÊÌBˆ$I’$I’¤rÏˆŽ "’$I’*’ªTå‚·L2y›·™Ílnæf†1Œv´ã.àR.å‡ü0踒$I’¤2f!D’$I’$IR¹cDå…I’$IE4Ñô9p{‚'x‹·˜Ã¦0… L ú¸ý„ŸADБU†233IOOgÆ ¤§§³~ýú¼{€&Mš›w߸qcbcc‰ŽŽ8¹$I’¤£a!D’$I’$IRà,€¨¢° "I’$©"¨Mm.9pË!‡÷yŸ¹Ìå5^ãwüŽ4 7½éCzÓ›ZÔ:âc}ÅW4¤!u¨S†3ÐA{öìÉ+wlذ7æûuð±mÛ¶åÛ®aÆ4jÔˆøøx>úè#6mÚÄ7ß|“o\½zõˆ‹‹£qãÆÄÅÅåûu°4Ò¤IªW¯~Üæ,I’$)|B$I’$I’$w@TYX‘$I’TÞEÉÙnò ÿæßÌaoð/ðթιœKúðK~Iq¥Úg:0•©ô¡Ï±˜B¥´gÏ222ò•: »OOO'777o»˜˜˜|Ž3Ï<3ßï7nL||8¼èáªGç`q¤$Å•tG_å`I§¤UGÊëj,’$IÒ‘òÿn%I’$I’$•Ȉt|X‘$I’TEÁYnwr'»ØÅK¼Ä0†¹MÖÛhF3“™Lc?àG¥¤¢ÇÆIKK#+++o›èèè|E„„„B qqq¾¯QT¯^V­ZѪU«bÇ•´ºKJJ 6l`ûöíù¶‹‰‰)vµ‹#’$IªHü¿VI’$I’$IT¶È¡sssLR:s—Uæ²Þ_Y; 𧬳¾ûî»L™2…äädrrr8õÔS¹ñƹúê«ËõkØ‚ˆ$I’¤ò¨µ¨JU"ˆ`?û‹»Ÿý¤B[Úr÷q·Idqá=Ö®]KvöÿV$‰ŽŽÎw‡ò~øJª|ޤ8’‘‘Q൵jÕ*6lØ@zzz¾÷!Â)Ž4mÚ”¨¨¨c=UI’$©HB$I’$I’$UºÈárss‹,”gs—×ýËc—uÖ_üâ´mÛ–ùóçsÆgðå—_2räHÒÒÒ¸ûî»Ëì8ÇšI’$IåÅ"Id‰…ø~µ€ßðOœ þx;þ³#ï¢üÿþ÷¿ääää/®èqð¾I“&Ô­[÷˜ÍO•G¸Å‘ÌÌL¶mÛVd))ÜâÈáe¤¸¸8š7oN­ZµŽõT%I’t²"I’$I’$€*{D*ÌôéÓiÛ¶-mÛ¶eÚ´itîܹBBgAD’$IRöîÝËü*óÉŠÌÊ÷x(7D(çûdÈÌ…ˆCžÜ¡­!26eðÑWq §ZôhÚ´)uêÔ9¾’ø_é` ©({÷îeëÖ­%G6mÚÄþýû 쿸UG|ýK’$©´,„H’$I’$I' íØ±ƒñãÇóÆo°aÃbbb8÷Üs1b?ùÉOŠÜîàùY»v-7Þx#ï¾û.5jÔ 11‘É“'¸ÐþÓO?eìØ±,^¼€.]ºððÃsÆgä÷ÙgŸ1fÌ-ZDdd$=zô`òäÉ…føôÓO7n‹- sçÎ<úè£öY”´´´°³‡sœ9sæðÐC‘’’Bll,ýû÷ç¾ûî£víÚÅæ8x.}Í :”©S§†uÍš5´lÙ2oü—_~Illl¾c§¦¦º:LýúõÙ»wo±+ "’$I’ŽFI+$¼Oß’Nî®\ˆö@Ä7TÛ^“¶žDLV õ3ëÓ$² -ªµàÔZ§rzÌéü(þGÔŠ«qÀÌ g*¹jÕª…UÙ·o[¶l)ðç'##ƒ7’’’ÂܹsK\!§°{WÈ‘$IÒAB$I’$I’¤JÈHÉ®¾újÚ·oÏûï¿OݺuYµj7Ýt?ýéO -”››K(âºë®ã–[náÅ_$##ƒQ£FqÛm·ñ—¿ü%oìW_}EïÞ½™0aÓ¦M¾/Oüò—¿dáÂ…´nÝ€Õ«WÓ»woÆÏ´iÓˆˆˆà­·Þâ²Ë.+pü¯¾úŠÄÄDî½÷Þ¼±óæÍ£_¿~$''_âÜÏ~ë­·š=Üãôë×§Ÿ~š·ß~›o¿ý–qãÆqÓM7å͹¤sYÜù.)k‹-øä“O:t(ÿüç?ó¶Û¿?mÚ´á¯ý+-Z´(tß'N¤sçÎ%ž¯ŠÌ‚ˆ$I’$€={öyQúá…CÅÄÄä»=!!!ï÷5>ªA‹F-h߆¨æQÐ< ÉIåTÕªUÃ*ŽdddYÂ:XY»v-ÙÙÙyÛV‰‰‰)´D"I’¤Ê+”[Ü¿´I’ b³ÈÀ £¨…BÌš5‹}=IÒ¡’’’6lXÐ1$IªPJ*€tîÜù„/€^<¨]»6ÿýï©W¯^Þc©©©´jժ؂ÂÁ}½þúëôë×/ï±/¾ø‚=z°~ýú¼Ç®ºê*Î:ë,F•oûI“&±bÅ ¦OŸÀ AƒèرcqÓ¦Mãšk®É—窫®¢mÛ¶Œ;6ߨgŸ}–•+W¹ªHqÙSSS9ûì³ d?Òãdddкuk¶nÝZàØ‡ŸÛâ !áf8óÌ3ùË_þÂücæÍ›Ç£>Ê;3oÜÚµkINNfÊ”)¤¦¦òÁpÊ)§9—Êîð‚ÈŠ+,ˆH*S)))%^'Iåß¿TQVô8ü~Æ lß¾=ßv‡= »oÖ¬UªøY³RyR\qäà}ZZYYYyÛDGGY9ô>..î„}ßP’$éHÏ÷ŠºÞÔBˆ$•’…•% !’T8 !’$•ÌHé^<èÕ«»wïæ®»î¢gÏž¥ºÈ% ±mÛ6bbbòÛ»w/Õ«WgÿþýyÅÆÆòá‡Ò¼yþIMMM¥S§NyŸ¼ZÔ¸ôôtâââòå.jì¦M›èÞ½;«V­*uöœœ¢¢¢ÂÊ^šã„Sþ(©NV€É“'óÅ_ðä“Opþùç3bÄÎ?ÿüûlÞ¼9 `̘1ÄÆÆ;IeÍ ª%UT~ÿRÐÂ)z¬[·Žo¿ý6o›ªU«R¿~ýW hÞ¼9‘‘‘ÎNÒ±VTqäЕ֮]Ë®]»ò¶©V­õêÕ+±8KDDD€³“$I*?ÊC!Ä¿$I’$I’T”T™0a‚Rš={6&L`äÈ‘lÞ¼™víÚ1`ÀFŒATTT‰ÛZR€ïÿÑüðbÖ-[ -ÄÅűeË–ÇöØÖ­[iÑ¢E¡™ªW¯^bn(˜=22²@öp³yófn¿ývÞzë-ÒÓÓÉÉÉ +C¸ÂÉ pÅWðÃþG}”ôôtRSS9ï¼ó Ýçš5kÊ4ceR»ví¼â,ˆL:Õ‚ˆ$I’t^¤}èEهߧ¥¥±sçμm¿H»U«VtîܹÀEÚ5²è! øþý”˜˜ŠW\ùì믿fÉ’%%–Ï¿?XF³|&I’t|X‘$I’$I’Ê! ÇÞI'ĤI“˜4i[¶láwÞáñÇgñâÅüõ¯-“c4hЀ7(Vlܸ‘ ä—žž^`5Ž­[·ºÏU«V(J”µp3hÐ ~ðƒ°dÉâããóVZ9ޯ˓O>™Î;3sæLþïÿþ›o¾Ù?eÀ‚ˆ$I’ž¢>ÿÐû´´4²²²ò¶‰ŽŽÎ·ŠGQE?_Ò±R½zuZµjE«V­ŠNqdýúõìØ±#ßv111Å®6Ò¸qcš5kVª•{%I’”Ÿÿ'%I’$I’$•@Ž¿P(DZZñññ4hЀK/½”ÄÄÄ"WÅ8={öäÕW_eôèÑùíµ×ò.°èÕ«¯¿þ:£FÊ7îwÞ)°ÏsÏ=—… Ò¿ÿ|/^¼˜Q£FñÑG•Iöp³lÙ2fΜIݺuóÆìÞ½;ìã”åëùꫯæþûïgçÎ|üñÇ…Ž)lu…Ï‚ˆ$I’N4eQôHHH gÏž.„Ž‹‹;n?ã~œèèhêׯOûöí0`W\qU«V=.YŽD(*ô繃ó*?ë¯,E‹c}L(çùh9cyìwß}—)S¦œœLNN§žzßeeÿ IDAT*7Þx#W_}u™})‹âÈÆIIIÉ[!éPáGš6mÖª¾’$I' !’$I’$IR,€”×]w¿ÿýïiݺ5Û·ogòäÉùŠGëÞ{ï¥{÷îÔ©S‡¾}û …˜3g“&MbáÂ…yãÆO·nݨ]»6}úô!22’ ðÀØçøñãéÛ·/999tïÞªU«’œœÌðáÃyâ‰'Ê,{¸ÇéÔ©cÇŽåŽ;î qãÆ¬]»–‰'†}œ&MšðþûïÓ±cGÞ{ï=†JZZÚe¾à‚ øÕ¯~ÅðáéQ£Fç;wîL(bÉ’%G´dAD’$IѾ}ûزeKÞEÉE]¸¼víZ²³³ó¶‹ŽŽÎwqr‡ ½hùX¯èx$rssó öìÙCzz:ÿüç?yæ™gøýïÏ«¯¾Ê©§žpÒÒ98¯ò >ž“N:)ÀÙ•­êÕ«Ó²eKZ¶lÉ¥—^JRR½zõâã?®Tó”NÓ§O§mÛ¶´mÛ–iӦѹsçã^ ×Á祤ïë‹#›6mbÿþýö_\y¤Y³fÔ®]ûxLW’$é˜ åVôµü$é8 b³ÈÀ £¨…BÌš5‹}=IÒ¡’’’6lXÐ1$I:*%@:wîlD*c/¿ü2sçÎåùçŸ:ŠŠpxAdÅŠD¤J.%% Ø‹¼$©<òûWùRÒ'ɼ÷‚àâV¬?Ôõ×_OÆ ó­úøé§Ÿ2vìX/^ |ÿ!?ü0gœqF¾m?ýôSÆÇ¢E‹€ïWh|ôÑGóÛ±cãÇç7Þ`Æ ÄÄÄpî¹ç2bÄ~ò“Ÿ›ûpC‡eêÔ©yϯ]»–o¼‘wß}—5j˜˜ÈäÉ“ ülN΢¬\¹’1cưtéR"##éܹ3¿þõ¯9÷Üsóe-ìC ?ï…=Îù)é\„3¿ƒûøê«¯3f ï½÷Û·o/4çá™Ã=ÏsæÌᡇ"%%…ØØXú÷ïÏ}÷Ý—ïÏÚ‘¾ÂÝqó8|žá¾.Â=îgŸ}Ƙ1cX´h‘‘‘ôèуɓ'Ó¼yó#:ö‘|ͶoßNË–-ÉÈÈ(ñœTeU,ì¾²%IRÙ:žïu½©…I*% !*KB$©pB$I‘)8¡Pˆ?ün¸©S§Ò¾}û #)LD¤ÊÏ ª%UT~ÿ:>Â-z¤§§ç»è9&&¦Ø‹w7nL||ýôÓ\uÕU|ûí·Œ7ŽP(Ä´iÓJ=ߢ²”´ÿâ¶=tÿ¥=o%wõêÕtïÞñãÇsÁÁ[o½Å“O>Éûï¿TÇ.Í×ìÖ[oå‹/¾`îܹ%ž“MFFF±çlܸ‘µk×’·M8Å‘ÆàÌ$IR,„HRd!DeÉBˆ$ÎBˆ$©"°"•¡Pˆ¨¨(FŽÉc=t "RåãÕ’**¿={ö”XòÈÈÈ`ãÆù¶ §èÑ´iS¢¢¢šYÅVR!ä»ï¾£Q£FìÚµ €«®ºŠ³Î:‹Q£Få7iÒ$V¬XÁôéÓóƵmÛ–±cÇæ÷ì³Ï²råJ&Ož @íÚµùïÿK½zõòƤ¦¦ÒªU«£.„¼þúëôë×/ß~Ï>ûlÖ¯_Ÿ÷X¸9 sÅWлwo ”÷Ø_|Ái§Vf…pÏOQû+ÍüB¡ï½÷ݺu+rÎ…å ç<&##ƒÖ­[³uëÖRÏ7…í¿¸yºÿ£y]vÜAƒѱcÇn¦M›Æ5×\sÄÇçk¶víZ’““™2e ©©©|ðÁœrÊ)EŽWñÂ)ޤ¥¥‘•••·Mtt4111%–Gâââ|ÏX’¤JÂBˆ$U@BT–,„HRá,„H’Ê# ’tüY‘*>/¨–TQùý«pá=Ö¯_ÏŽ;òmNÑ£Y³fT©R% ™J*„ìÚµ‹ØØØ¼BHll,~ø!Í›7Ï7.55•N:åzŠ·iÓ&ºwïΪU«èÕ«»wïæ®»î¢gÏž¥úz—TÙ¶m[¾OæÏÉÉ!**Šýû÷ç=nÎÂÄÆÆòÑGѸqãRç ·îù)j¥™_(â»ï¾£FÅÎçðã†sž‹ÛþHæ[š|á\wø¸£y]”féééÄÅÅñ±Ãùš…B!š7o΀3f ±±±ÅfWÙ§8²nÝ:öíÛ—·M¸Å‘ØØX"""œ$I*Iy(„ø“´$I’$I’TŒ5kÖpýõ׳téR¾ûî;7nL·nÝxä‘GèÚµ+mÚ´ :¢$UZµk×Î+~lÛ¶E‹±páB.\ÈÔ©SøÑ~ÄwÞÉ%—\d\I’¤JåÛo¿å†n`Íš5lذôôt233óž¯V­±±±4iÒ„FqÚi§ÑµkWâããiÔ¨ñññ4lØF8 •ƪU«ò­&°eË–B/(‹‹cË–-y¿ßºu+-Z´(tŸÕ«WÏûïÙ³g3aÂFŽÉæÍ›i×® `ĈG½êË¡%€ÈÈÈå€psfË–-4hÐà¨2–ähÏOiçWš2ÈAáœçÍ›7sûí·óÖ[o‘žžNNNN¡û:Òù†»ÿp…{ÞÂ=nQn {ìX|ÍÖ¬YSâ•­˜˜bbbHHH(vܦM›øæ›oX·n›6mÊ»_¿~=Ÿ~ú)óçÏ'==½{÷æmMll,7¦E‹<õÔSÔ©SçXOI’$U0ÖG%I’$I’$) ‰‰‰AG$I’$ €§Ÿ~š /¼0ï÷ 4È[äP7nÌWŽhРÛ¶m#77·À¯Ý»wç;餓˜4i«W¯æë¯¿fäȑ̜9“Ë/¿üØN¬”9 S¿~ý|%˜Ò…Bù.ð†ïËî‡;Úós4ó+Kƒ ¢fÍš,Y²„ÌÌ̼ ‡;Òù†»ÿp…{ÞÂ=nƒ HOO/ðøÖ­[øØ’$IRq\!D’$I’$I*F‹-˜?>ÙÙÙ¬\¹’ °dÉÆŒÃŽ;hÔ¨çœs;wæì³ÏæÌ3Ï$ û„’͈#HJJ :J©íß¿?°cþ:­^½:Í›7çì³Ïf̘1Çlõ›P(tTjoÆ ãÉ'Ÿ¤JßNÂÎ;ùðÃY°` ,`ÅŠìß¿ŸV­ZѳgOî¼óNzôèAýúõƒŽ*I’TéÔ©S‡_|1ßc{öìaãÆlذ¡ÀýçŸλï¾ËúõëÙ±cG¾íbbbˆ‹‹£qãÆEÞ7kÖÌÿïÐSO=Å;ï¼ÃÊ•+óëÙ³'¯¾ú*£GÎ7öµ×^Ë[ÅàÜsÏeáÂ…ôïß?߸ŋ3jÔ(>úè#àûŸÓÒÒˆ§Aƒ\zé¥$&&¹B¡Êâ½–ps¦[·n,X°€Áƒç=¶råJ.¿ürV­ZUìqcccY»v-§žzjÞc‹-*0.ÜóSÔ¹8šù•¥eË–1sæLêÖ­›÷Xaå†#}=„»ÿp…{ÞÂ=n¯^½xýõ×5jT¾Çßyç#>v¸*Òû-•IFFF¡/z¿nÝ:öíÛ—·Mtt4111yžqÆ$&&øû166–ˆ?ó[’$ÏŸ¤%I’$I’¤0T©R…:СCÆW  rï½÷Z ÈØ±c9çœs‚ŽqD »àxÉÍÍÍ+gäææ²k×.¾úê+æÌ™ÃÏ~ö3fΜI¯^½ËW^téÒ…ßüæ7<úè£AG9!”T7nœI’¤U¯^V­ZѪU«bÇWÙ¸q#)))dddX}"œâHÓ¦M‰ŠŠ:–Ó}ˆŒŒdÁ‚<ðÀG|ìptîÜ™P(Ä’%KJT¨pŠiiideeåmsxÑ#!!ž={øû,..Î÷Œ%IR™ åZ –¤R b³ÈÀ £¨…BÌš5‹}=IÒ¡’’’6lXÐ1$I*•à "‹/¶ r¤¦¦2dÈ’““ƒŽR!µZÇܹs¹ãŽ;øä“OŽÛ1˳®]»2}útš5kt”J§¤HÏž=-€H\JJ :t8‰$•Žß¿ŽÌÌL¶mÛVìŶ6l ===ßÏáGâãã©Zµj€³+_/¢ZµjÔ¯_ŸöíÛ3`À®¸â ªU«V`»ÿûߌ;–ŋߗæ~øa~ô£å÷å—_rûí·³`Á²³³9ýôÿgïÎë¨ý¿„U‚I@ÀÖ[l’ª°ÙÄ¥—Û‚öVQomT¢^1¸°µT„¢µ¢uoËb­H•*ÇŸ ‰`%.­`í#¨¬aÙIB~„ IH€$“åýš'Ï1sf2ŸïI 0g>óý6wÞyg‰™Þzë-}ôQ-ZÄÎ;騱#—]v&L U«VGÍ?{öl233Y»v-;wæá‡—TŠþ3RÞúÊä,ÏâÅ‹3f 999œrÊ)Œ1‚±cÇVxÌÍ›7“‘‘ÁüùóÙ³gçw=öXøß˜ÅÛVöõ)ïµ¨ìøŽüY¨Ì¿ÑåuÞ¸q#·ß~;o¼ñÛ·oçôÓOçÞ{ïåŠ+®8®ñ©²_ÿXÆQ™×íXŽ»lÙ2ÆŒÃ;ï¼CDDýúõã‘G¡gÏžÇuìÊ|ÏúöíKdd$ÙÙÙåŽ_E*SôXµjùùùá}¢££úgNñcLLL€#“$IA¨Éóå]oj!D’Ž‘…U% !’T6 !’¤úÀ‚H͘ê멊Y‘/¨–TWùû«v©lqdÆ nºé&ÆÏÿøG,XÀ~ðƒÏ—·_Y뮿þzn½õVþøÇ?²mÛ6222¸ýöÛyæ™gNhÛã=ÖŠ+¸è¢‹?~<Ï>û,5bþüù•º#fEÞzë-¾õ­oE¥‹‹.ºˆ &ðì³Ïðꫯrá…²`ÁºwïÀ5×\CïÞ½Y¼x1mÚ´aùòåŒ5ŠsÎ9'œ¥x|ee;–ñTôýýüóÏIKKcܸq<ûì³DFF2oÞ<.½ôR.\H§N* ))‰_üâBŽCEÌÌL ’$IªPñ»ñññG½È¦¢ ‚³³³½ X’êÊ=**&''[”$Iõ–3„HÒ1r†U%g‘¤²9Cˆ$©!p‘ªÑ£G/^L»víJ=ÁÛo¿Í AƒÊÜ·¼bBYë#""xå•W¸ôÒKÃë>ûì3Î;ï<Ö®]{BÛï±®¾új’’’ÈÈÈ(±ÿŒ3øÉO~R©BHññ Ù½{7Ÿþ9sæÌá·¿ý-3fÌ --«®ºŠï}ï{¥Ž3yòd>üðC^xáZµjÅW_}EÛ¶mÃÛ|ñÅ$$$”š¥¬lÇ:ž£}¯ºê*¾ûÝïrÇw”XÿôÓOóñÇ3uêÔcʼiÓ&RRRøì³ÏÊ|õ g‘t$ï°/©®ò÷WývàÀ6oÞ̶mÛŽz‘ñªU«ÈÏÏïW™âH||<111ŽN’j¿ÊÎü”››[âMLLÌQÇÄÄеkWZ´hàè$IRCRf±"IÇÈBˆª’…I*›…IRCdAäø´mÛ– 6иqãRÏEDD°{÷nš7o^æ¾ÇZÙºuk‰‹zöïßO³fÍJÜ}ðx¶=ÞcÅÆÆòÞ{ïÑ¥K—ûoÙ²…víÚUºR¬Y³ftîÜ™””î¸ãN?ýô£ç‹/¾ _¿~¬_¿€óÏ?Ÿ={öpÏ=÷ššJTTÙT—÷ºëxŽöý-ïkmذÁƒ³|ùòcÊ|àÀâããÙ¼ys™Ï7d@$UÄ ª%ÕUþþR±mÛ¶õbåõë׳zõjòòòÂûDGGSay$..ÎãKªWöîÝ[aÉ£¸Œw¸ŠŠñññœzê©ež”$I Rm(„”ýî–$I’$I’¤Ebb"‰‰‰dff–*ˆŒ7΂HvîÜYîEü@¹eãqä^›6mZnéâX¶=Þý7oÞLlll©}õÂûŠr•wœ¸¸¸‰Ù³g3aÂn¾ùf6mÚD¯^½øÑ~Ĉ#*õfýñŒ§¼ïï–-[èÚµk™Ï5kÖì˜3GEE±cÇŽ ÇÐTTÉÌÌ´"I’¤z%&&†˜˜zöìyÔíŽVY¶l¡P踋#±±±DFFV÷P%©\•)z¬]»¶Ôù“#‹‰‰‰¥~ÏuîÜù¨ç÷$I’ttþMJ’$I’$Iª…,ˆTN«V­ÈÏÏ?®»FDD°ÿ~š6m^·uëÖªŒW­ÚµkGnnn©Y0rss«ü8ëׯ/U°X¿~=íÚµ ~ÒI'1yòd&OžÌæÍ›yë­·˜2e ‹-⥗^ªÔqªj<íÚµcùò奊5Gªlæüü|N:é¤cÎQX‘$I’*çX‹#ÅwÈ?übê•+W’••ÅêÕ«Ù¹sgxŸ¦M›Ò¶mÛ ‹#:t Q£FÕ=TIõHeŠkÖ¬á믿ïÓ¤IN>ùäðïž„„’““ßݺtéâï$I’¤`!D’$I’$Iª,ˆ”íä“OfÇŽ%Š •˪U«èÑ£GxÝ;ï¼S•ñªÕùçŸÏ+¯¼BFFF‰õo¼ñF•'55•—_~™Ûn»­Äú¿þõ¯¤¦¦†?ˆˆ`õêÕtêÔ‰víÚqÅW––VªHRÞÏcUŽç‚ .`Á‚\~ùå%Ö/Z´ˆŒŒ >øàƒcʼcÇŽSx°"I’$U¯ââHEŽv‘vqq¤¢‹´Ë›}Ä‹´¥ú¯¼Y‹/£­ZµŠ]»v…÷9²|vdÑÃY‹$I’j' !’$I’$IRdA¤È駟Κ5kŽ«’––Æ]wÝÅ#Y¥Ç7nƒ¦uëÖ\rÉ%DDDðꫯ2yòd,XPbÛ믿ž‡~˜îÝ»³}ûv¦NZ¢4бcG/^LRRo¿ý6?ûÙÏX½zu•Žgüøñ\rÉ%0xð`š4iÂÂ… ¹ñÆyì±ÇŽ9óš5k8ýôÓ9G]`D’$Iªš5kFBB GÝ®¢»ûçää°nÝ:¶oß^b¿˜˜˜£Î6OçΉŠòÒ"©6)¯èqøãêÕ«ÉËË ï]¢ V^Ñ#..®^7”$Ij(üW›$I’$I’T4Ô‚È÷¿ÿ}rrrèÕ«W‰õÅc*~,,,,µïÃ?LFF}úôaÏž=œwÞy<ùä“tîÜ™ˆˆˆð>‡­Ã¿NYëOtÛcÙ?!!yóæ1fÌFMdd$ä¹çž«Ty¡¼c©GÌ›7;[n¹€þýû3oÞ<ºwïÞ. ñè£2pà@vîÜIÇŽ¹ì²Ëxî¹çJ|½É“'óÓŸþ”µk×Ò¹sçpAãXÆSÑ÷·k×®üå/áÎ;ïäúë¯'??ŸoûÛL›6K/½ô˜3ðÁ¤¥¥UøšÖ@$I’¤úåDŠ#‡Ï°|ùrÖ­[Gnnn‰cU¦8rê©§Ò¸qãêªT¯U¦è±jÕ*òóóÃûDGG—ø111±Ü™$I’TEí>IR)D0“™ cXÐQTDDD0sæL† óçI’7}útÒÓÓƒŽ!IR½rdAdÑ¢Eõ¢ òÅ_pÝuוš©¢![¶l_|1_~ùeÐQªDmÏ Aƒxþùçéܹs`ŽWEÔÔT ’ªUNN‰‰‰'‘¤cãï/5TûöícëÖ­G½(½2Å‘².JïÒ¥ -[¶ ptRÍ:pà›7o>jkýúõ|õÕW„÷;²èQÖcÇŽiÓ¦M€£“$IÔìùƒò®7u†I’$I’$©¨¯3ˆœvÚiôéÓ‡_|‘«®º*è85.""‚)S¦0|øpš7oΧŸ~ÊÈ‘#¹ù曃Žv\jãx^|ñE’’’êLÄ@$I’$ˆâ Ñ‹g(ÏþýûÙ²eK¹…‘âG6lØÀÁƒK}ý£]è~ê©§Òºuëš®t\*úù/~¬èç?11ÑŸI’$0 !’$I’$IRTŸ "“&M⦛nj…¿ýío<òÈ#ÜsÏ=DFFÒ£GFÅ5×\t´ãRÇóÎ;ïð»ßý.°ãWĈ$I’¤ 4mÚ´RÅ‘ŠfHÈÉÉáoû›3$¨V¨ªrœ!G’$I5&¢ðð¿J’*A3™É0†U¼±Tò¦ð’¤†núô餧§C’¤íȂȢE‹êLADªÏ**€¤¦¦Z‘T«äääõBQIªüý%Õ¼mÛ¶õ"üõë׳jÕ*òóóÃû­8Sb®½{÷–*#OÑ£¬ÇSO=•Æ8:I’$©&Ï”w½©3„H’$I’$I*¥>Í "ÕeÎ"I’$©¡ˆ‰‰!&&†ž={u»£GŠgY½z5yyyá}¢££KDÊzŒ‹‹ó¼FrxÑ£¼ÇuëÖ±=c; ²‹>Ú|܆ø–ñáï{Ïž=Kýêøÿ¼íŸ|•ù{ÙK;Ú‘L2ƒ̹œKSš8bI’$éøY‘$I’$I’tÌ,ˆHUÈ$I’$UÊGŽV>(.ެY³†¯¿þ:¼Ï‘åƒ#‹g#éÒ¥ 5ªî¡ÖÅ%mÛ¶•ûš¯^½š;w†÷9²¤“@rrr©×¼C‡•z­sÉe‹b6³™À¢ˆâ,Î"õÐÒŸþD$I’TgX‘$I’$I’tÂ,ˆH•cD’$I’j—fÍš‘@BBÂQ·«LqäÈY+ ¨˜r´ÙFâããéܹ3QQu÷2®òfc9üqõêÕäåå…÷‰ŽŽgŽVô¨êÙXb‰eè¡`=ëÉ"‹!f1‹‰L¤9ÍéMoRH!•T0€&4©² ’$IRUª»ÿ’$I’$I’TkY‘ŠX‘$I’¤ú¡*Š#ëׯ'''' 77—-[¶”د}ûötèÐN:°fÍ6lØÀÆKlwòÉ'.u¤¤¤”šÉ#>>žfÍšUý‹P$Z†3(*ˆ„‘EÓ˜ÆXÆÒ’–œË¹¤ZzÓ›H"N.I’¤†ÂBˆ$I’$I’¤ÀYQ]aD’$I’T“š5kF·nÝèÖ­ÛQ·Û·o¹¹¹áYEÖ®]ˆ 8óÍ7øä’KèС;v @bcc‰ŽŽ®‰aÔ $~ho "!B<ȃŒe,­hÅ9œcAD’$I5ÂBˆ$I’$I’¤ZÇ‚ˆj ’$I’¤º ::š®]»Òµk×’Oüç?E¿úUgjŽV™Ä$Æ2–S8…s8‡RH%•>ô!Ï_I’$©jX‘$I’$I’TëYQM±"I’$I’Ž×ᑃäS>%›lB„˜ÈDÆ2–ö´g I&™R,ˆH’$é„X‘$I’$I’TçTTùûÝwsp÷n.¶ ¢ X‘$I’$IÕ!’HzZÒI§€>â#²È"›lÆ3žíl'–XúÓŸTRI&™žô :º$I’ê !’$I’$I’ê¼pAdÛ62ç̓ݻÙÕ§—\Â[K—:ƒˆÂ,€H’$I’¤ 4¢‰‡– 2‘!²Èb cøš¯‰#ŽRH%•4Ò8Ó‚Ž.I’¤ZÌBˆ$I’$I’¤º/‚{ï…%K 9æÏ§ej*··@©D,ˆ4@$I’$IRmtxA$“LòÉçc>&th¹…[ØËÞ‘ ¸€.t :º$I’j !’$I’$I’ê¦ÂB˜3î»>ú.¾¸¨rÎ9¥6 Ï ’˜Hff¦‘z̈$I’$Iª‹¢ˆ:jA$ƒ ö±H&™R¸ éLç £K’$)@B$I’$I’$Õ=¯½¿ø|ø!\~9üáлw¥w· RX‘$I’$IõÑ‘‘½ì%‡²É&DˆÑŒf?ûI TRI&™ó8Nt :º$I’j…I’$I’$IuGVÜs,\©©°t)ôésÂ_Ö‚HÝaD’$I’$5DÍhFÊ¡%“Lö°‡wy—,²È&›gy–DRIe0ƒiG» £K’$©Y‘$I’$I’Tû½ûnÑŒ o½UTùÇ?à{ß«¶ÃY©=,€H’$I’$•ÖœæáâÀnv³˜Å„‘EÏð yä•(ˆ|ŸïÓ–¶'—$IRU²"I’$I’$©öúðC¸óNxã <¸h†ääaA¤æX‘$I’$I:v-hQ¢ ²‹],a ¡CËS<ÀœA )ámcˆ 2¶$I’N…I’$I’$IµÏÊ•pï½0cFÑL o½çtª0 "ULj$I’$IRÕkIË‘ìä=Þ+Q‰ ‚^ô"™dRH!4ÚÐ&àä’$I:B$I’$I’$Õ[¶ÀƒÂԩйsQ!äÇ?†Z^¢° Ry@$I’$I’j^+Z•(ˆlf3‹YL6Ù„1iDI/z‘J*É$3€œÄI'—$IÒÑX‘$I’$I’¼Ý»áÑGá7¿&M`üx¸õÖ¢ÿ®ƒ,ˆ|È$I’$IRíÓŽv 9´ld# YHY„1‰I4¢gqV¸H’L2ÍhprI’$ÎBˆ$I’$I’¤à<Ï<÷Ü{öÀwÀ-·@‹A'«R © bD’$I’$©îiO{†Z6°wx'\™ÈD¢ˆ*QI!…h¢N.I’Ô°Y‘$I’$I’Œ Šfùä¸ñF7Úµ :U¨O‘R~ÈÁ¾IXcD’$I’$©®ê@‡‘\rYÄ"B„˜ËÜ2 "ýéOSšœ\’$©a±"I’$I’$©f­ZU4#È /@j*|ð|ç;A§ T]*ˆT4È iƒ˜Òw /ñ½é]£Ù$I’$I’T=b‰-QYÇ:²É&Dˆ™Ìd"iNszÓ›RH%•   MN.I’T¿Y‘$I’$I’T3v‰¡K˜;~øÃ SÕJµ© RQäÈ@ )d)K¹XÂ"‰¬Ò<’$I’$I ^<ñåDf0ƒ‰L¤-èK_’I&…2Æ48¹$IRýb!D’$I’$IRõ*,„gŸ…±c¡  ¨rãåéÉʪɂȱ@ŽAò(}èÃÓ<Íõ\_/$I’$I’j±# "+YIYd“ÍÓ<Í&Ð’–œË¹¤’J2ÉœÃ9D$I’Nï¸J’$I’$Iª>~7ß ï¿#FÀøñtª:¯* "'Z)Ë™œÉÍÜL&™\Æe´£]U¿’$I’$IªÅ-ÃDB„È"‹iLc,cKDRI¥7½mV’$éY‘$I’$I’Tõ¶o‡qãà±Ç wox÷]øÞ÷‚NUoYÉËËãÿø .dÁ‚Ü}÷ÝìÞ½›øøx ĹçžËŠ+X°`ÿú׿øîw¿ËÀ¹÷Þ{éß¿?mÛ¶=¡L÷q³™Í]ÜÅt¦WÅ0%I’$I’TG%@ú¡¾)ˆ„ñ 2–±´¢çpN¸ Ò‡>Dpl³ßJ’$54B$I’$I’$UÂBxá¸ãÈ˃‡†Q£ Ò;ûդƓœœLrr2wÝuW™‘nݺ1hÐ &L˜P%#µ¢ñ?å§\Çuô¥o•~}I’$I’$Õ]G+ˆLbcË)œÂ9œC )D$I’Êa!D’$I’$IRÕøç?ᦛà½÷ŠùKhÓ&èT¢tA¤¦\É•<ÅSŒd$ÿà4¢Q[’$I’$IuÇá‘ ø7ÿ&›lB„˜ÈDÆ2–t`H&™R,ˆH’$Þ–O’$I’$IÒ‰Ù·î¾’’Šfyÿ}˜6Í2ˆxœÇYÆ2çñ £H’$I’$©hD#zÒ“tÒ™Å,6±‰¥,åNî`ãH"‰xâÆ0¦3e, 8µ$IR0œ!D’$I’$IÒñËÊ‚ôtøê«¢An¿9 „¾ÑƒÜÊ­ÜÍÝüˆG\Б$I’$I’T‡4¢‰‡– 2( €øˆ!²Èb cøš¯‰#ŽRH%•4Ò8Ó‚Ž.I’Tíœ!D’$I’$IÒ±Û±22`à@8í4øôSÈÌ´ ¢2Ý˽´¥-™dE’$I’$Iu\qA$“Læ2—-la)KÉ ƒml#ƒ H(1ƒÈW|tlI’¤ja!D’$I’$IÒ±™;Î<f΄gž×^ƒÎƒN¥Z¬9Í™Ìd^äEÞæí ãH’$I’$©‰"*\™Ï|v²³TA¤+]éF7†3œéLg5«ƒŽ-I’T%,„H’$I’$IªœáG?‚K/…ÔTX¾†:•êˆË¹œ‹¹˜QŒ"¼ ãH’$I’$©ž:² ²•­,b餳žõŒf4éL7ºq7ð<ϳ†5AÇ–$I:.B$I’$I’$Uìå—á;ߥKáÍ7‹fiÛ6èTªc¦0…¬à·ü6è(’$I’$Ij šÑŒRJDæ3Ÿ«¹š•¬äçüœS95\™Íl6³9èØ’$I•b!D’$I’$IRùvì€n(šdà@øè£¢ÙA¤ãÐnŒe,˜à]%I’$I’ˆæ4'•TÆ3¾DAd(CYÆ2~ÊO9…SJD¶²5èØ’$IeŠ :€$I’$I’¤ZêÍ7ág?ƒ¼ýÞyƇHOªfÜÆmt¥+·pKÐQ$I’$I’¤*Ñ e(OðËXV¢ 2›Ù¤‘F[Ú’BJ¸ ²ŸýAÇ–$Iu”ïìJ’$I’$I Q^^Qùã ¡øðCèÛ7èTj`šÐ„ßó{æZ$I’$I’¤ú&–ØpAd+XËZžã9zÒ“™Ì DÒHc<ã ₎-I’ꈨ H’$I’$Iªa_~ C‡ÂgŸÁsÏÁUWH Øp%W2’‘œÇy´ EБ$I’$I’¤jOó1!Bd‘ÅÆð5_G)¤J*i¤q§]’$U’3„H’$I’$IõÅš50p deÁ›oZQ•N:‰$r37SHaÐq$I’$I’¤z!Š(I$“Læ2—-la)KÉ ƒml#ƒ H žøð "_ñUб%IÒQX‘$I’$I’êƒ%K ) òòàý÷aÀ€ IÇ-’Hã1³˜?òÇ ãH’$I’$IõÒá‘ùÌg';KDF3š®t¥ÝÎp¦3Õ¬:¶$I:Œ…I’$I’$©®{á4Î>²³¡k× I',‘DÒIçvng;ÛƒŽ#I’$I’$Õ{GD¶²•E,"tÖ³žÑŒ¦3éF7nàžçyÖ²6èØ’$5hB$I’$I’¤ºì׿†k®Ñ£á•W U« IUæ~î§B~Á/‚Ž"I’$I’$58ÍiN )% "ó™ÏÕ\ÍJVòs~N':… "³™Í¶[’¤ÅBˆ$I’$I’TÂí·Ã=÷ÀCÁ¤IùÍ龈ˆ øÕ¯~E×®]iÚ´)§Ÿ~:>úh©/5wî\Î=÷\Z´hA‹-8÷Üsyíµ×jr4R™bˆa"ù¿ãC><ê¶Gû¹ýøãéÔ©ùùù,\¸³Ï>›èèhºvíÊþð‡Ûïܹ“;=zмysN:é$ÒÒÒøÛßþV5“$I’$©†y®HRUhNsRIe<ãKD†2”e,ã§ü”v´+QÙÊÖ cW Ï?J’j+ !’$I’$IR]sàüÏÿÀ£ÂŸÿ ·ÝVæf#FŒ`ïÞ½„B!¶nÝÊ“O>É#<ÂÌ™3ÃÛ,Y²„k¯½–Ñ£Góå—_òÅ_0räH®¾újÞÿýš‘T®k¸†R¸8ÈÁr·ËÈÈ`Ê”)e>7mÚ4n¼ñF¢¢¢øì³ÏøñÌ­·ÞÊÆ™5k÷ß? ,øæ˜×\C~~>¡Pˆ;vðÅ_‘‘Á´iÓªzx’$I’$ÕÏIªj-hA*©<Àd‘U¢ ’CWr%íiOOz† "ÛØtìãâùGIRmQXXXtIªK"ˆ`&3ư £¨ˆˆˆ`æÌ™ æÏ“$núô餧§C’¤Úi×.øÑàÝwáå—!-­ÌÍ"""¸÷Þ{¹ï¾ûJ¬ýõ×¹ï¾ûX²d —_~9iiiŒ1¢Äv>ú(o¿ý6ùË_ªgÒ1ø„OèC~Ç︞ëËÜ&//ÓN;7Þxƒž={†×oÙ²…îÝ»óÙgŸÑ¾}{†ÎYgÅÿýßÿ…·™3gÓ§Oßá¯U«V¬]»–Ö­[WïÀ$©ÈÉÉ 111à$’tlüý%©Þ8ô^tÄìÙž+’Tãv²“÷xСåC>$‚zÑ‹d’I!…ó9Ÿ“8)è¨òü£$©,5yþ ¼ëM!D’$I’$Iª+rsaÀøç?aÑ¢rË Å®½öÚRëúöíËþóŸðç|ðC† )µÝ%—\>)íLÎäfn&“L6³¹Ìm7n̈#Jݥ逸žbÈ!´o߀wß}—K.¹¤Ä6 àã?þío›Ûo¿µk×VñH$I’$I ŽçŠ$Õ´V´ Ï ²”¥ä’Ë_ù+©¤’M6Wp's2I$1–±Ìe._óuбËäùGIRme!D’$I’$Iª >ÿúõƒ={`ñbèÕ«Â]ºvíZj]LL Û¶m ¾aÆðU‡ëС¹¹¹'YªJ÷qÍhÆ]ÜUî6ééé¼üòËlÞ\T)((àw¿û#GŽ o³zõjN?ýt"""ÂmÛ¶eýúõámf̘Á¦M›èÞ½;ÿõ_ÿÅu×]Çœ9spÂmI’$IR]æ¹"IA;…S‘?óg’I&DˆK¹´DA$Dˆ½ì :v˜ç%Iµ‘…I’$I’$©¶ûì34Úµƒ¬,(ãÍû²DFV|ú¯C‡lܸ±Ôú7{ŒA¥êÓŠV<ÄCü?°˜ÅenÓ®];þû¿ÿ›'žx€9sæËÙgŸÞ¦uëÖ¬[·ŽÂÂÂámN;í4þú׿²}ûvf̘Aß¾}¹ÿþû¹þúë«w’$I’$U#ÏIªmÚÓž¡ e*SYÊRÖ³ž?ñ'Id.sI#Ö´.QÙǾÀòzþQ’TY‘$I’$I’j³Ï>ƒó΃¸8xã¢RHêÓ§sçÎ-µþÕW_¥OŸ>Uz,éD]É• f0#Ien“‘‘Áã?N^^Ó¦McÔ¨Q%ž¿øÅ/˜7o^•O:Qó8ßá;<ÎãŒdd©çÏ<óLÎ8ã ÆǧŸ~ʰaÃJúÒÒàÛ߆×^ƒ–-ƒN$Õ*{ØCOzÒŸþ<Ïó¥žß»w/mÛ¶åóÏ?§cÇŽ$”$ËÉÉ 111à$’tlüý%©Þ(~/zÖ¬`sHR XÉJB„È"‹·y›5¬¡%-9—sI=´ô¦7‘'x/uÏ?J’ŠÕäùƒò®7u†I’$I’$©6ùà8ï<èÝþþwË RšÓœÉLæE^ämÞ.ñÜöíÛ™8q"Ç÷ÍXI’$I’$©I tÒyžçYÍjV°‚‡y˜bxˆ‡H"‰6´ Ï ’C9xLÇðü£$©¶±"I’$I’$ÕŸ~ \gŸ ¯¾ ÍšHªµ.çr.æbF1Š<ò€¢;#uìØ‘þóŸ<øàƒ'”$I’$I’¤â‚È,f±‰M¬`ñ1Äð ’DèÀ†„ "…–ûõ<ÿ(Iª¢‚ I’$I’$ X½.ººw‡¿ü¢£ƒN$ÕzS˜Â™œÉoù-ÿÇÿQXXþ›µ’$I’$I’¶â‚H:éä Ÿò)Ùd"ÄD&2–±´§=H2ɤBúA€ç%Iµ’…I’$I’$)h›6AZ´n ¯¿-ZHªºÑ±Œe¸‚+èD§ #I’$I’$Iª"‰¤ç¡%t (à#>"‹,²Éf<ãÙÎv:Ð •T’I¦'=ƒŽ.IR ‘A$I’$I’´;à‚   Þ|bb‚N$Õ)cKqÜÆmAG‘$I’$I’TG5¢‰$’A³˜Åf6³”¥ÜÉÜÁœÉ™ÄÇ0†1é¬deÀ©%Ir†I’$I’$)8{öÀ!°q#deAllЉ¤:§)M™Æ4.àæ1‹¸(èH’$I’$I’ê¸â‚HqI$Ÿ|>æcB„È"‹Û¹ì$Ž8RH!•TÎç|ºÒ5èè’¤ÆBˆ$I’$I’„à¿ÿþýoX¸ºv :‘TgÏù\ÎåŒf4ÿâ_Dt$I’$I’$IõHQá‚H&™% "!BdÁ>ö•(ˆ\È…t¦sÐÑ%Iõ\dÐ$I’$I’¤é†`ñb˜7þë¿‚N#ÕyS™J.¹<ÌÃAG‘$I’$I’TÏD2Éd>óÙÉN–²” 2ØÆ6F3š.t¡ÝÎp¦3Õ¬:¶$©²"I’$I’$Õ´ßü^x^|ƒN#Õ §r*ws7÷s?_ðEÐq$I’$I’$5 GD¶²•E,"tÖ³žQŒ¢3éF7nàžçyÖ²6èØ’¤zÀBˆ$I’$I’T“^~î¹&O†!C‚N#Õ+·q]éÊ-ÜtI’$I’$I Xsš“BJ‰‚È|æs5W³’•üœŸÓ‰Ná‚Èlf³…-AÇ–$ÕAQA$I’$I’Œ?„áÃáºë`ôè ÓHõNšð{~Ï 1—¹ ÁÒ•$I’$I’¤àµ ©‡€Ýìf1‹ "‹,žáòÈ#„ðvßçû´¥mÀÉ%Iµ…I’$I’$©&¬_—\ýúÁït©ÞÀ®äJF2’ó8´:’$I’$I’$•pdAd»XÂB‡–§xŠ"øß"…RI%4ÚÐ&àä’¤Ú&2è’$I’$IR½·w/\v´l ³fA”÷i‘ªÓd&³ƒ<ÀAG‘$I’$I’¤ µ¤%©¤ò°”¥lg;çï a9äpWÐŽv$‘DÌf6;ØtlIR-à;Ï’$I’$IRu*,„«¯†•+aÉhãÝ»¤êK,ãO&™\ÅU|‹oI’$I’¤ê·kWÑÍHòó¿Y·reÑãôé߬‹Š‚aÊn^"Iª•ZÑªÄ "›ØÄ–M6!BLc‘DÒ‹^¤’J2É d ­iprIRM³"I’$I’$U§)S`Î… [· ÓH Æ(FñÏ1ŠQ¼É›áõ«YÍD&2’‘œÁ&”$I’$©Š½ÿ>üÉ<¢ê IDATìgШDF­+,,z9²èñàA((€ÓNƒÁƒƒÉ)I:f§p C-ÙÈB’E!BLbhÄYœ.’¤B4Ñ'—$U·È H’$I’$IõÖ{ïÁرðË_ÂÀA§‘”F4â1#Dˆ—x‰<ò˜Ä$Nçtã1f3;舒$I’$U­þý!&¦¨ð‘—Wô‘Ÿ_ôQüyAœt¤¤V’tÚÓž¡ e*SYÊRÖ±Ž?ñ'Id.sI#V´"‰$Æ2–!ö±/èØ’¤j`!D’$I’$Iª[·Â•WBj*ÜqGÐi¤©ý¸–k™ÀÎäLîâ.ö±"XÌâ ãI’$I’Tµ¢¢àŠ+ I“ò·iÜþçŠ%IõF,± e(OðËXV¢ 2‹Y¤‘F[Ú’BJ¸ ²ŸýAÇ–$U !’$I’$IRU+,„ë®+ºãâsÏA¤§á¤ ä’Ë.v±Œe¬d%PH!KXp:I’$I’ªÁO~”ÿ|^^Ñ6’¤z-ޏpAd%+YËZžã9zÒ“ÌDÒHc<ã âGùóC’TkùN´$I’$I’TÕ~ýk˜7fÌ€ví‚N#589Èt¦Óî¼Â+RH>ù%¶ÙÆ6¾äË`J’$I’T]ú÷‡¸¸òŸ…ääšË#Iªâ‰D¾äKV°‚Çyœx†gJD&2‘,²È#/èØ’¤J°"I’$I’$U¥… aüxxðAè×/è4Rƒ4ÜÈìfw¹oZFÁû¼_ÃÉ$I’$IªfpÕUФIéçš4áÃÍV’D g8Oð_ñ+XÁd&Gò(ýé_¢ ’C9tlIRüÛ½$I’$I’TU¶lŸü.½F:Ô`]È…@Qé£Þk¼ÆF°•­U8 Ij,„H’$I’$IUaΘ1ž|Ú´ :Ôàý˜óïÑžö4¦q™Û|Àä“_ÃÉ$I’$IªW_ û÷pãÆpíµÅ‘$ÕmGD>á~ůhF3&21\Æ0¦2õ˜ "Z¾Å·˜Ãœj‰$Õ?B$I’$I’¤µy3Üp\=\tQÐi$rgñ1s6gÓˆF¥žßÏ~–³<€d’$I’$U³Ÿþò» B^\yepy$IõF$‘ô¤g‰‚ÈR–rw0žñ$‘Dq cÓ™Î2–•ûõ (`! ØÊV.ã2~ÌÙÌæ$ÕuB$I’$I’¤5bDÑ]'M :‰¤#´£oñWqU©çшðRI’$I’Tͺuƒ³Î‚ˆˆ¢^½ G SI’ê¡F4"‘D2È`³ØÌf–²”[¹•½ìe c8“3‰'>\YÉÊðþò!»ØÀAð*¯r:§ó/2&IªK,„H’$I’$I'âφ—^‚§ž‚6m‚N#© Miʳ<ËgpÓ™^ÓÑ%©Nˆ :€$I’$I’Tg-[ÂĉA'‘TIpïó>q_ò%ÿá?ìaÍiÎÁƒÙ°a¹¹¹¬]»–ÜÜ\Ö­[þ|Æ ¬Y³†7Ò¦M:tè@ÇŽ‰ ?ÆÇLJãâ∎ŽzÈ’$IúÿìÝy|[Õ>üG^´xß$[‹m¢ÄYÀ"l KHqYšBÛ ´3ÍÐRRh ´C;@;¼ÀL‡It aiK)KÒ–²d m $%¡â„@â$lb[²Ûòny=ïp/ºZ¯¼ÉËóõÇY×w9çJº›ÎsÑ ÓÚÚŠ––ůÇãQ<÷ù|ðz½ø-h4¸ö¼ó`2™`4QTT$ÿ+ž¡°°0ÙU$"¢, ip|úó]|¹È•Ñ bC ¸Ïãy<ŠGa…uRÊÚÞÞ!ººº044„ÞÞ^ô÷÷£¿¿½½½BWWFFFÐÑÑqèëëC çÛÑÑ‘‘Oê,ÍCk\Éðð0:;;U×#tžñäää 55UÕ¸©©©ÈÉÉ ®×ëa0äçÙÙÙHKû¤ÙzJJ rss£Ž¼üŒŒ èt:èt:ddd -- ÙÙÙÐh4ÈËËäææ"%%E^Fè4D3!DDDDDDDDD£±kðûßúÓ'¡"𲆆†àñxäp‡Ëå—ڿ„'¯|®Ó\p|ÕοtÂãñ`xø³;ÑeeeÁf³Ád2Áf³aîܹ°Z­(..F{{»ñx<8pà¼^/<bÙùùùr8Dú•B#Á!~)EDDDDD4; Ä vø|>Å0©q©$''G{¢¸¸‹/†Éd‚K«Ü40¯×+ÏëÃ?”C#¡ JÓÒÒ£Ñ7H¢ýt9DDD‰ØýèEoÜñàU¼ŠX€ÿÆc36ËÁ¿ß¾¾>ôõõ¡½½=æß@@?ÒßÁáŽDäååA£ÑÈa†ÌÌLyÿ¨Õj‘™™)›••…ôôt@zz:ìv»ü¿ÐCp"Xð<â OÄ388ˆîînUã@OO†Kቔ‰´ŒÖÖVÅ<ü~¿üwww7å×h``===ªË@‘H¯CNN ƒü·^¯GVVVÔ¿³³³a0ä¿322““1C” „%j`¸é&à _Ö®Mviˆf-©ÑŒßïGss3\.WÄdž†Ec½^‹Å‚Sþt ¬_²âLã™(ßR.‡5¤ÇüüüQ•Ëï÷G-˾}ûb–)´ f³Y*±X,c^oDDDDDD4qúúúà÷ûçªÒóHÃ<OØ]¿ƒÏóóóQZZŠ¥K—*†IçŠV«:nÌå–Îe£•³¡¡o½õ\.ZZZ088Væàr—3Ò0³Ù F3ærÑô500€—ú_BzV:5ƒñ'04<„ÁÔAü+þ7}ÿ& ÜDH!ƒÁ 理ž(¤ý‘Á`@^^ôz=222äž&¤kááiúôôtdee…õtA“GêYEöövŸõ¦-DäpP @oo/:::ÐÓÓ#‡dûúúÐÓÓƒÎÎN¸á˜üü|äää 77W‰H¿ùùù‡çææ"//¹¹¹ì™Æ!DDDDDDDDD‰zà ±øóŸ“]¢©¿¿­­­1C.— ^¯WÑ£Gh¨Âáp„…+l6Û„A'}™XQQs¼XÁ‘ššìܹŠF6Ò—ÑÂ#Á½‘°a ÑØõõõ© vøý~8ΰ;Š%¤ó9‡Ã5,Q\\ŒÔÔÔI¯§Tµ¤àK¬uSWW'ŸûJ 5%:1C#ÁÃL&SÄ»£Qò !ÐÖÖ†ÖÖÖ¸---òßÝÝÝÀ_¬Ð a­šSRÞ•ŽŒŽ ätç ·/()Ä9gžûìrÃûŒŒ èõzäåå!##c\“4u¥¥¥ÉÇ.º¬®®.tuu¡«« }}}èèè{§éììDgg§<¬³³S> Âæ­ÑhPXXˆ‚‚‚˜Ò¯ô<++kBëLÓ ’‰ˆˆˆˆˆˆˆˆÑÖÜwpë­@yy²KC4­´µµÅ z„Þ54èQUU€(++Cvvvk—¸ñŽ9r¯¾ú*ššš00ðÙÝð¤†5ñ‚#%%%HII™èªM Ò â;¤a>ŸOÑ»#ÔÏÏχÝnh˜‰  ƒp‰'¸ç”hë½®®{÷î…ßï» ÞsJ¬ ÉdÜ‚ˆh&ëíí…ÇãÛí†ÏçSüív»áñxàõzáóùÐÖÖ!„bz½^Ö ½¼¼‡C1lß)ûÐÔÖ„’Ô”jKaO³Ã#ŠQŒ” S› â“_¢$ÉÎÎFvv6ŒFã˜æ300 DÚÛÛÑÞÞ®I?ž8qBñ<´G9­V‹ÂÂBF9ŸOy466Âår¡¡¡N§.— 'OžDoo¯<~VVJKKQ\\ ³ÙŒåË—GìaÀh4B¯×'±fDTPP€‚‚ÕãwttÀívÃëõ*B#Rèëã?FSSš››188(Og2™`µZa³Ù`³Ù`µZQZZ ›Í&‘‘1U¤1à·2DDDDDDDDDjÝ?PX|ãÉ. ј¨ z¨ 8¦¡ñŽ477£¦¦FU (Ò#ADDDDD3_pã|5=xx½^ +æ!<‚äÛíöˆ½=X­Väåå%©¶4÷B¯—Mhkk‹ù>•Îý~?|>†††óˆô$I$àBDª³³õõõ¨««C}}}ØßÁ=H½—••Án·ãüóÏGii©Üл´´9¼ñÑŒ•››‹ÜÜ\œvÚi1ÇBÀív£©©I"s:¨­­Å®]»ÐÔԤؾbΜ9°Ûí˜3gŽâïòòr^ûO~+GDDDDDDDD¤Fk+ðË_ÿñ»K¦)JMУ±±]]]ò4Z­………r#»ÝŽªª*E£©ËðÔÔÔ$ÖŽ&["Á‘àF3¡µµµp¹\aw^U±ÙlÐjµ]U"""""R!ôØ_MØ#˜N§CAA¢Á¼t©={_ ‰¦×ëa±X`±XTO©›ÐÏ€Ô ISS:;;Ö+4:¬¸¸˜×cˆf™¦¦&;vLz>ZZZ‹En„½|ùrØív”––Ê!ƒÁäšÑt Ñh`6›a6›cŽ×ÒÒ§Ó‰ÆÆF444ÈÛ§¿üå/¨««“o0–šš ›Í9õÔS±`ÁÑ&!DDDDDDDDDjüìgŸAþùŸ“]š…ü~̇ßïGCCº»»åi¤†6Ñ‚ÒcII RRR’X;šî‚ïÀêp8¢Ž'Ýy5^pÄãñ`ddDžN Ž7Š },++CvvödT—ˆˆˆˆhÆÎ5ãõŽàr¹ÐÒÒ‚ÁÁAÅôÁ ÛƒÏ=£5t7›ÍÐh4Iª-ÑøP{óIpO9Ñ‚$R$~¿?ìœø,¸ÚÛH¤ o¬@4}H×ÃŽ9"?¾ÿþûrLúìÛívœqÆX¿~½¼ý9í´Ó•••äÑlRTT„¢¢",[¶,âÿ¥Pl]]|Ý¿®®Ï?ÿ …ÜpC²KBÓ„š GCC†††äiB³;Å—ôÁÿ#šÉt:ªàÈÀÀZZZ c‚?c555عs'Nž<™ppÄjµò‹&""""Jª¾¾¾¨½ „s:rC*IpƒpéœÒáp„ “Î9M&ÒÒØŒˆh*>£jI°XÛŒºº:ùzU{{»búÐíE¬HÌf3·DQ!pâÄ 8p@ñë÷û‘ššŠ¹s碢¢ë֭í·ÞŠ… bÁ‚ì郈fŸ"$òøããßÿýß¡ÓépÖYgá /ÄÊ•+qî¹çκ€!DDDDDDDDD±<öpÆÀÙg'»$” @ €¶¶¶¸AІ9R£é‹h»ÝÖ°»¼¼YYYI¬M„ÑG‚ÂHwQmhh@ww·N48º=•žF¤§§wµi–kkkÃ_þò¼þúëØ³gŽ?Ž´´4,_¾W^y%V®\‰+Vð:1Ñ$1¸ôÒKq饗ÊÃä°ÞSO=…ÿøÇÐjµr@䢋.Š+–6³#3»vDDDDDDDDDcÑÓüáÀ=÷$»$$¸¡N¤ÆØÁÁBƒÒWƒc—––òËc"ŠKmp$Òö*48ÒÔÔ„ÎÎNy­V‹Â° Hh/$ååålDHDDDtsµ=xx½^ +æÚ \~~¾âfÁ“­V+òòò’T["¢©)¸’xçÎ@ø¶;Z¤¦¦~¿>ŸCCCŠyDÚvÇ ’$p¡Ù£®®üã±sçNìݻ˗/Ǿð¬\¹UUU €M!eeeØ´i6mÚhjjÂîÝ»±{÷n<óÌ3øÉO~‚üü|¬]»Ÿÿüç±nÝ:dgg'¹Ôã""""""""¢hž{€/9Ù%™b5œ–].ÚÛÛÓ© z”••Íø»ÿÑÔc0`·Ûa·Ûcާ&8¢fûé‘Û?"""šî¤»ÌÇ v z—y»ÝŽªªªˆ…y—y¢©å’K.Á®]»&mú±.o¢ç7SéõzX,X,ÕÓDêÝ)tßPWW‡ææf8Nô÷÷‡-3Vh$t{wš¹¼^/¶oߎ§Ÿ~ÿøÇ?PPP€Ë.» O=õÖ¬YƒÜÜÜd‘ˆˆT²Ùl¸öÚkqíµ×>úè#¼ôÒKx饗°qãF¤§§ãòË/dž p饗B«Õ&¹ÄãƒßEó›ßëÖFc²K2­© zÄ»C¾Ùl†ÃákèœÌ;äk4Ås½^ÂÂBœqÆX¿~=6lØ0¥/$k4!"ñ“m²Êm]Lô2©±žÇ"™õ˜Èeÿõ¯Ń>ˆ={ö`xxóæÍ÷¾õ-\wÝuaŸýñ0Á¿ßÚÚZU=$EzdIDDD4Yü~Ü`‡ô¼¥¥ƒƒƒŠéƒðJÇ3v»=jƒ^³Ù÷½½˜ Ûĉ0ÞõšÊû¤©þòxì3Ë—/Çm·Ý†Ë/¿o¼ñ.¹ä’)WÆ©¦¡¡<ò}ôQøý~|õ«_Å~ô#Øl¶„ç5™×¢µ7e „ˆ(A „Ðxb „ˆ(2BˆˆhJxäà–[¯ÈÌLvi&•ôEg¬†ÄŠ/8¥/7gJÐC­XþyäÜ{ï½x÷Ýw‘››;É%‹ y&wSi¹ãm¦B:„¥K—ÊÃ>úè#TUUÁívûò’¥¿¿­­­qƒ#¡ 7ƒmF{,--ENNNkGSTÑt5[·_Á=’Å zøý~8Nttt(¦nøª¦¬ÉdBZZZ’j›<œx<¤x¦rãÛéV†Ùrþ¬–”Œ”®¿¶··+¦W³ 6÷£cÑÐЀn¸/¿ü2V¯^»îº çž{n²‹¥Jqq1Þÿ}˜L&ÅðÚÚZTTT02‰™:x<ö™ôôt)×»ÑtÐßßßþö·øÉO~ǃÛo¿wÜqGB½iO…@%b»ØžìbÐ @lßÎ÷Q¨mÛ¶%»DDDB¬^-ÄúõÉ.Ÿjkk‡»víO<ñ„¸ï¾ûÄ–-[ÄÕW_-ªªª„Ýniii€ü«×ë…Ùl‡CTWW‹Í›7‹;ï¼SlÛ¶M¼øâ‹bÿþýÂétŠ‘‘‘dW/)â]bݼy³øá¨öþûk׊¬¬,‘••%Ö®]+Þÿý°ißÿ}qÙe—Éã­Y³&l¼öövqóÍ7‹9sæN'JJJÄu×]'Þzë­¸åýýçþgÅÿÄå—_.²²²„ÉdÿôOÿ$ZZZFUÎhÞ}÷]qÉ%—ˆŒŒ ‘-.½ôRñÊ+¯„•5RÙ£Õ)˜šõo]¨©Ÿ4݇~(®ºê*‘——µœ¡Ó©]Ï/¼ð‚8ï¼ó„N§åååâæ›o ×75óUPjßj—{øða±víZ‘™™)rrrÄ•WEñZm IDAT^)Nž<9êeæ5óûý"//OÍ*™qúûû…Óéû÷ï/¾ø¢Ø¶m›¸óÎ;ÅæÍ›Euuµ¼IMM Û‡ØívQUU%®¾új±eËqß}÷‰'žxBìÚµK>|X´··'»z4Áöïß/öïߟìb%l&l¿€p:âðáÃâ7ÞPìÇ·lÙ"6nÜ(ª««…Ãáf³9ì|0Ò9áÆÅ–-[ç…o¼ñ†8|ø°p:É®ò´ÀóHžGÏc¦ŸGJuR[æHõözª™g¬é#½~Ñ^µç®jÞ{‘–@èt:yœ±|F‚—7wî\yþUUUŠñâ-£¾¾^Q¾'Nˆ®®.Űúúúˆe˜ÍçωêííUì«wìØ!¶nݪØW¯ZµJ,Z´H˜Íæ°óîÐsïà}µtþ¼¯žÍçàÏ>û¬ÈÉÉ .»wïNvq–••%€ªqÕnCÆrM/Þþ8Þ¾2’Ñ^ŸLt_£ö¸j¼¯GÆ*{¬c 5eMt¾£Ù¯eŸËX¯KŸÑÛÛ+þô§?©jÈ£f=KãnÛ¶Môôôˆææf±iÓ&qÝuת¾ÑÊoþ±¦ –èz‹·Ü?üP”––ÊŸŸÏ'ž|òIqî¹çŽyÙ‰¼fßùÎwĺuëT­“Ùl´áÃxû$60¾fBƒj"š¦âö+¸Ñ¨´¯ n4*ío¥F£&¬qP~~¾X´h‘¢Ñèí·ß.¶nݪh4úÑG‰þþþdWyFây$Ï#ƒç1Î##•ùª«®ŠXæhËË<y?Ä_m]ã }Þßß/–/_.~øa!ÄØ>#¡óß¿¿8å”SÄ{ï½§Gí2Þ{ï=±|ùrÅ´###âÔSOï¾ûnÔ2ðüybIçÝRØ3øÜ[ {JçßZ­6b€Äl6‹E‹‰U«V)ž[·n;vìP„=‡‡‡“]å1{ì±ÇDJJЏ馛D___²‹3*_ùÊWÄÍ7ß÷š}¢û¡ÑL;–ýq,c½>©f¿ ö¸j¢®GF+{$jËšè|¥ÿ%º_ë>e¬e‰6í¹çž+ž|òIáóùäuTVVÖ˜_ÍrFóÞŽ¶^BÇ‹7€X¼x±xæ™gDKKK̺¨§4<Úñf"ï1âšk®µµµ¢½½]Üxãâì³Ïëׯ—‡}ãß_üâã–uªûðÃEee¥°Z­âøñ㪦™ ͧÿ$""•4Ð`;¶ã\d¢8¢váED4Ë=òÈ#ؼys²‹ADD³Ù‹/W]¸Ý€Ñ˜”"ô÷÷£µµÍÍÍp¹\Q½^/†‡‡åéôz=, ÌfsÔG›Í†ÜÜܤÔk¦‰×5xOOŠ‹‹ÑÝÝ ¸öÚk±|ùr|ûÛßVŒ÷ÀààÁƒxòÉ'åñ–.]ŠÛn»M1Þ/ùK:t?ûÙÏÙÙÙ8yò$ äqêëëa·ÛãvY«ìÏ?ÿ<®¸â Å|W¬X§Ó)S[ÎH6lØ€µk×bãÆò°ãÇcÁ‚ŠrE*g´²‡W»~¢Í/‘úi4¼þúë¸à‚ ¢Ö9RyÕ¬çHü~?N=õT´¶¶&\_5"Í?V=‚ç?–÷E¤ånܸgžyfØçæ×¿þ5¾úÕ¯ŽzÙj^³††ìÙ³>ø êëëñüsçÎ:>©ç÷ûcîßš››ÑØØˆÁÁAy½^üüü¸û9³Ù F“ÄÚQ°šš€ÃáHrIˆˆ3Û¯¾¾>yßç÷ûá÷ûÞKÚšš000 ˜^Ú7J¿Ò¾0Ú°ââb¤¦¦NX}HžGò<2x³á<2R™?Ž‹.º(n™¥é#½žjç™Èû!Þÿ$±êšÈ²à›ßü&ÚÛÛñôÓOÛg$xyµµµX·n¶oߎ³Î:K1N"˨¬¬Ä¯~õ+,[¶ ðòË/ã§?ý)^{í5Å´<žºúúúbc„s»ÝaïÙüüü˜ÇÁÏF#ÒÓÓ“TÛpµµµ8ãŒ3ðýïwß}w²‹3j½½½¸ãŽ;ð›ßügŸ}6ª««qÅWÀjµ*ÆKt?4šëjcÙ'"Ñë“jö j«&êzd´²G;¦PSÖDçI¼ýÚxíSÆR–H4 ~õ«_áúë¯W àðÞ{ïá׿þuBËIô½k½§f¸F£Á³Ï>‹/}éK ×%Þ²¢o&òÓh4ؽ{7V®\ p¹\°Z­ŠaMMMp8ðx<1Ë:ttt`õêÕBàïÿ{Üóèɼþ­½)!DD b „Æ!DD‘1BDDIwÓMÀ›oŸ^ÀO@mmmqƒ###òtj‚eeeÈÎÎ÷2Stñ¾TéîîFII‰Ü§¤¤o½õÊËËãÕ××ã¼óÎCsssÌñ<.¼ðBÔÖÖV¯^ÞÞ^üð‡?ĪU«––6.e×h4hkkC~~¾¡ËU³žcM?šú&R>5_!„Ž7–÷E"ós»Ý0›Í£^¶š×L£Ñ ¼¼ëׯǭ·ÞŠ’’’˜e§ñ§&8Ú@V§Ó¡   î~³¤¤)))I¬ÝìÀ@MW‰n¿¤s½xÁiXh¸?Æ zX­Väåå{½iâñ<’ç‘Áó˜ ç‘‘ÊÜß߃Á0ª2':ω„$2ßXó{æ™gðãÿï¼ó²²²ŒÏ9u]]V­Z…Ç{ ^xaØ8‰,ãg?ûŽ?އz°nÝ:|ó›ßĺuë–Ëóç™!ô˜&ÒqMðsŸÏ‡¡¡!Å<¢ÓD;Î þ,·ë¯¿GŽÁ[o½5#®tttàå—_ÆŸþô'¼üòËX½z5zè!ùP‰î‡Fs]m,ûãD%²MV³_ëqÕX¯G&RGµeMt¾‰Œ?Þû”±”%Úxn·ÅÅÅŠáõõõ¨ªª‚ËåJh9‰¼·ã­—Xõˆ¶®[ZZPXX8.u íx3‘÷˜F£Agg§üäÈÈRSSÆ¥¥¥©:¾›Ž?ŽE‹ᥗ^Âe—]s\Bˆˆ¦!Bh<1BD!DD”t W\Üw߸Îö¿ø¾ñoDý¿ÍfÃYg…eË–aÙ²e(..†ÍfƒÉd‚V«×²Ðøˆ÷ÅÄÛo¿¯ýë8tè -- ===ÐétŠñ²³³å;á§§§‡}‘*1 èííðÉ€wß}7^xáø|>œ~úéX¿~=¾ùÍoƽßh?„W[ÎHÒÒÒÐÛÛ÷½=–†Ÿ?øÁðÊ+¯Àív+ަ¾¡ÔÎ_m=Ô®7µËö¹˲#M«¦n4uy½^x<455¡¾¾o¿ý6:„÷ß?¬±­ä¢‹. »«-?BˆhºJdûõÃþ÷ÜsOÌq222°`ÁùwÑ¢E0(,,DQQŠŠŠØ{Ç,ÁóHžGÆ›G¢u‹4|*G&ÒRmm`9–@ÈXëmYÇŽÃÊ•+ñÚk¯añâÅòð±|F¤åÍŸ?íííxüñÇQ]]6N"Ëðù|X¸p!àv»Q]]#GŽ@£QöÊÈóçÙkxx---hiiAkk+|>jkkqìØ1ù7ÞûöŽ;îÀüÇLHùN?ýtTWWOØü“©§§›6mBVVžxâ cÛ©v,ûãhÆûúd´áj«&êzd"eW[ÖDç›è~m<÷)c)K"u ]G‰\‡VûÞŽ·^b•/‘ã…±¾Þ±>‡‰¼ÇÆãk:š?>®»î:ÜqÇ1Ç› ±Åº‰ˆˆˆˆˆˆˆˆf§8v xðÁqŸõúõëa0àr¹àv»át:áv»w8ojjÂsÏ=N‡ââbX­V9‘ž›L¦°/`iêØ¶m®¼òJùyQQš››qÊ)§(ÆknnFQQ‘b¼ÚÚÚ¸wÇËÍÍÅ<€x---xíµ×°uëV¼ñÆøýï?®u‰Dm9#),,DKKKÜ»mE¢ÑhÐß߯ø¢¢­­-l¼±®Ÿ±ÔoŸOnÙÒÒǃÎÎN8pðÉ>J ‚Áh4Âd2)†™L&Fù9o0;ð<2:žGª7ÝÎ#§²‰¨kOOÖ¯_ûï¿_Æç=t×]waÞ¼y¨®®Æ /¼€³Ï>{ÔË0¨ªªÂ³Ï>‹£GbË–-3òu¦èäcŸÏ¯×«8Ήtìzc†œœG=ö‰vwýñPZZŠ'NLØü“)33ÿó?ÿƒ%K–ÈÃÆ² Q;íXöÇÑLÖ~%‘ãªd_T[ÖD%º®ÇsŸ2Ö²DÒÖÖ†‚‚ŰææfÆ„—“È{;Þz‘æ¯æøSâñxÂz; ­Ëxš¨÷ØLÑÓÓ—Ë…²²²dEBˆˆˆˆˆˆˆˆˆ‚ýå/€^TUû¬F#®»îº¨ÿïëëCss³ ~›û?""Jв²²˜çz¿ß—Ë¿ß/ÿJû:¿ß††¼õÖ[p¹\hii » ¬^¯G~~>òóóåýô<Ò0³ÙÌF»Ó Ï#y9^¦ÛyädKä½<u½á†pöÙgãúë¯W”I1.|å+€ßþö·øÂ¾€Ý»w+Þû‰.ãºë®Ã=÷܃®®.¼ûî»—Éóç飯¯/âqH´a###ŠyèõzÅqGYYÎ9眈Ç&6›-©¡Ök¯½6lÀßþö7|îsŸKZ9ÆJ£Ñ ¾¾>lûœ––†¬¬,ùùX¶!j§U»?Nö¶6µÇU“y=2ÚzR[ÖDç›èºï}ÊXÊÉ«¯¾ÖSÂÿøG¬^½:áå$r¬o½ê?%þóŸËŽT—ñ4Ö÷ØL÷ýïz½k×®MvQÔDD”ˆíb{²‹A3±};ßODD¡¶mÛ–ì"Ñl¶aƒ«V%»1õööŠ>úH¼ñÆbÇŽbëÖ­âöÛo7nÕÕÕÂáp³Ù,(~óóóÅ¢E‹ÄªU«ÄÆÅí·ß.¶nÝ*vìØ!Þxã ñÑG‰dWoZ ¾ÄÚ××'êëëÅ3Ï<#.¾øbQQQ!>øàÅø|ð°Z­âÑGGx½^ñØc «Õ*Nœ8!W__/–,Y"~÷»ß‰––ÑÙÙ)^zé%aµZÅóÏ?¯Xþš5kÄáÇE n·[üà?W]uUܲÛl6ñæ›oŠñç?ÿYØl¶ˆõŠVßDÊÉ{ï½'æÎ+víÚ%º»»Å¡C‡Dee¥x衇b.S!6mÚ$¾øÅ/ŠÆÆFÑÕÕ%^yå±fÍš°qÕ®Ÿhë"‘úær»Úõ¼fÍñõ¯]Ô×׋þþ~qâÄ ±iÓ¦Q×7”Úù«­‡Úõ¦v¹}ô‘(--?þ¸ðz½¢µµUlß¾],[¶lLïÉxõ;ï¼óDUU•ªu@‘ÅÚ_­ZµJ,Z´Häææ*öUZ­V˜Ífáp8DuuuÔýÕÐÐP²«G*íß¿_ìß¿?ÙÅ "JØTØ~õöö §Ó)öïß/^|ñEñÄOˆ­[·Š;ï¼SlÞ¼YTWW‹ªª*±hÑ"‘ŸŸv¨Óé„Ùl‹-UUU¢ººZlÞ¼YÜyçbëÖ­â‰'ž/¾ø¢Ø¿¿p:bpp0©õ-xÉóÈxë,–éx©¶ÌÑŒuž‘†%ò^k]C‡ýâ¿K–,½½½ÇËg$Òòž}öYqê©§ ·Û-Ktýýý¢  @üÛ¿ý[Äeòü9¹‚Ͻƒ‚¯KÇ 999aÇ z½^q¼põÕW‹-[¶(ŽvíÚ%>,œNç´<ÿâ¿(²³³Åk¯½–좌±bÅ ñæ›oŠÎÎNÑÓÓ#öïß/.¾øbq÷ÝwËãe?¤vZµûãXÛÚPã}}2ÚpµÇUu=2’hëImYïXökã±O 6¯û²eËÄöíÛEKK‹¼ŽÊÊÊD}}}ÂËí±f¤õ"„úãOižçwžxê©§bÖ%ÖºHd¸‰½ÇF{Ü3 ‹[o½U¤¤¤ˆgŸ}VÕ4“yý Z{Óé¿æ‰ˆ&!4ž!"ŠŒ""Jš‘!Š‹…¸ï¾d—d\ôõõ)mÛ¶MÑpH Žh4š„‚#‡]]]É®^ÒEjle±XĺuëÄ/ùK"N÷Þ{ï‰K/½TdffŠÌÌLq饗Š÷Þ{/l¼>ø@¬_¿^äææŠÌÌL±|ùrñÜsÏ)ÆyõÕWÅ•W^) …V«sæÌ·Ür‹èììŒ[þ;vˆ9sæ­V+N=õTñ /„Õ+Z}-g4o¾ù¦¨ªªz½^”––Š{ï½WÕ2}>ŸØ°aƒ0"33S|þóŸ aãª]?ÑÖ…Úú…¾ÔHd={<±qãFa2™„V«‹/Û·ou}C©"õP³ÞYîáÇÅÚµkEff¦ÈÊÊ«W¯GŽõ²Õ¼fçœsŽ8ï¼óbÖ¶jkk‡ŽôÈÎÎŽØ U zHQ·mÛ¦hˆ:–PlS¡A5ÑhLÇí— ÝOßyçbË–-Š}µÙl©©©Q…‡3·lÙ"î»ï>9@"¶··'»ÊÓÏ#Õ—3žGN¯óÈDßjêœÈ<£›È{y,u4L§Ó…½þ¡ãŒö3¸B§Ó©˜ÿ“O>9ªe ŠSN9E¸\®ˆÿçùóø‘®çJûòàkº‘öåiiiª÷åÁçàÒ¾Üét&»Ê“b``@lذA¤¦¦Š[o½5,5üýï_ÿú×Å©§ž*´Z­0 âŒ3Î>ø QŒ›è~(Ñi…ˆ¿?"ö¾2ÔD\ŸŒ6\íqÕx_Œ&ÖzR[ÖDæ«v]OÔ>%Øx¼îGŽ«W¯YYY"33S¬]»VÔÖÖŽz9ñÞÛ‰¬µÇŸR}êëëEuuµÈÎÎŽZ—Xë"ÖqH´õ©æ=¦ö'ÑcÛ©èĉbÅŠB«ÕЧŸ~ZõtS!¢ùôŸDD¤’lÇv\ƒkâL‡F£ÁöíÛú®#"šíyälÞ¼9ÙÅ "¢Ù裀SOÞ|8÷Üd—fÒô÷÷£µµÍÍÍp¹\Q=FFFäéôz=, ÌfsÔDz²2dgg'±vDD¤–ßï¹hnnFcc#åiôz=òóóãîJJJ’’’ÄÚQ²ÔÔÔG’KBD”˜Ù²ýêëë“÷õ~¿~¿?ì¹4¬©© Šé¥céWÚ÷G{Îc"¢égÇŽعs'~ó›ß$»(ÓN___Ôýk¤ý­ÛíFhsÎüü|ž4ÖþÖh4"===Iµú~÷»ßá†n@FF¾÷½ïá†n€N§Kv±ˆˆ Ñh¶ÿ4y|>þû¿ÿ?ûÙÏ0oÞ<<ñÄ8ãŒ3TO?™×¢µ7M›ð%Mû÷iiÀé§'»$“J§ÓÁb±Àb±Ä¼X900€–––¨ …kjj°sçNœÅ± TU$!"¢ñ±bÅ Üxã Ý%{º9Æ vHü^¯â&8€ò[ÚÙíöˆû'Þgj0›ÍøùÏŽ[o½>ø þë¿þ wß}7®¸â lذk×®e•ˆ&•t³ö29Ün7vìØ§žz o¿ý6–,Y‚­[·âºë®ƒ^¯OvñF"""""""""ÉþýÀ™g&»3‚ôEg¼Bj‚#aw­/Ô#§<Eó‹{J.²Ë²Q’Q‚ªþ*Þµžˆ¦½þþ~´¶¶Æ y¸\®°†(j‚l|BDDD“M:FI$¤Ñ××·Qn]]ü~?šššÐÙÙ¶L5¡iXqq1RSSÇ»êDDÓÞtk”*<âíC¤ç~¿_1½N§CAAba·ÛQUUqb2™–Ææ—ÓUyy9~úÓŸâÇ?þ1ž}öY<ù䓸¾€ÜÜ\\y啸üòËqÉ%— 333ÙE%¢nºío§£††¼ôÒKxþùçñúë¯#33W]uþó?ÿ+W®LvñÆHˆˆˆˆˆˆˆˆˆ@ààAàê«“]’Y!€üðßÿÉo_E@=ò‘tÈCJPß°m¢ )¤@^xåùeìÎÀ³kžÅÀÀ€<,ô.²ÑKJJ’’2é뀈féîØñ‚###òt¡Aªªª°íXYY²³³“X;"""¢ñc0`·Ûa·ÛU|‡÷h€kjjäç¡Ç[@ä;¼G ’X­Vètº‰¨:‘n$/Øár¹ÐÒÒ¢¸¡   JÛt»Ýu;Ï›ËÌNøÚ×¾†¯}íkhjjÂöíÛñÜsÏaýúõÐét¸è¢‹°jÕ*|îsŸÃ²eË"%"šº»»±oß>ìÙ³¯¼ò <ˆœœ¬Y³Ï<ó ª««a0’]ÌqÅ@œ8´·³‡õ¡ï“`G‚?Íh›— þ)@æb.òSCÿóÙO! ¡»@ô+ï$úXWW‡½{÷¢±±]]]òr¥»ÿÅ Žð®±D‰Ú ‡ÛíVÜíMjl"mc***¶=6› Z­6‰µ#"""šú  ,KÜ^*%j×ÖÖ&ÔÈ8V¯$ldLD³šð^ð0µá=é\šá=6› ßýîwñÝï~^¯;wîÄÿýßÿáž{îÁ-·Ü‚¼¼<œþùX¹r%V®\‰3Î8ƒ×‹‰ˆ¦€ÎÎNìÝ»{öìÁž={PSSƒ¡¡!,X°]tî½÷^\pÁ3úØ€"""""""""Ø¿HO–,IvI’¢ ]ð}úÓ‚´¢-Ÿþxᕃmh“ÿîBWØ|Ò‘Ž„…6æ`*Q©z„ŽcÀØïÆ£öN²j‚#N§ò4Z­………q{)++CZ/½Mw±¶Á Uš›•75AÒÒR¤§§'©fDDDD$5VKjÈ«s]]üÜï÷+¦—nD+4<Ìd2ñ¼’ˆ¦´àsæXÁ¿ß¦¦&tvv*¦ÖIÛ?‡Ãu;ɵÐd3™LrÏ!B>|»wïÆž={pÿý÷ã{ßûrrrpþùçã¼óÎCee%ŒFc²‹ND4£ŒŒàĉ8pàöïß¿ýío8x𠆇‡±hÑ"¬\¹·Ür V®\‰’’’dwÒð쑈ˆˆˆˆˆˆˆÞ}¨¨ôúd—dÌúÑ/‡9¤@‡ð½ðÊÿoE+úѯ˜‡¡…(„ &  ° b˜#øy²’TëÄŒGp¤¹¹555û„6Ñ!(P IDATôÈáDÉ¡&èÂ?ׇƒ0"""¢Y"¸‡Ãwüà;áGk -ÝŒÀï÷ÃëõbxxX1HwÂ$±ÙlÈÍͨêÑ 'õ|o»% óù|RÌC xo£ìv{ÌÑt¡Ñh°dÉ,Y²7Ýt„¨­­•"Û¶mCCC ´´•••r@¤²²f³9É5 "šž†‡‡qüøqÔÔÔàÀ8pà<ˆ®®.¤§§£¢¢UUU¸í¶Û°råJ˜L¦d9iø­?,XìRDÔ‡>øáG3šá‚Kî¡#Ú07ÜŠy衇˜aF>òa…‹±X1,øÇ~) $ þb<ô±¶¶.— n·B|öÚ08B4~Ô=BïJ*õü#}æìv;ªªªU, ÊËËy'R""""R-8@¢V¤»í‡>¯««ÃÎ;ÑÔÔ„Åô‘î¶Ü;ôyII RRRÆ»êD4‡ÒâmWü~Øõ*à³kVÁÛ©ÌÐíJQQ´Zm’jK4ù4 ***PQQo¼ÐÒÒ‚È–Ÿxâ Üyç ßXdéÒ¥X´h,X€  333™Õ "šRÜn7Ž=ŠcÇŽáÈ‘#8xð :„žžhµZ,Y²‡6l@ee%–.] N—ìbO „À‰ÀÕWOøb0|ðÀ7Ü1{íþ wä &˜PôéO! qNƒñÓiXðÿib©½[¬tÇÅxÁǃ‘‘yºà/ᣅGÊÊÊ=Õ%šT~¿?âç%¸!KCCº»»åit: ¢=¤G6‚#"""¢©Bí $j|×ÔÔ$Üà;ZÄh4òfDI¢&0& S“zï``Œh|aõêÕX½zµ<Ìï÷Ëw´?pà^xáüô§?ÅÀÀ4 ÊÊʰ`Á9$²páB,\¸EEEI¬ ÑÄÁÇŒ£GÊáÚÚZ;v ~¿——‡… âôÓOÇW¿úU8,^¼˜ç"q0BDDDDDDDD42Ô×óæjòhC[X‘þöÀƒ|ÖÐ_}Xïs0U¨ ë¹Ã l°A ÞqoºÒëõ°X,qƒ#ýýýhmmÙ·o\.¼^/†‡‡Ãæ«×‘ÒÒRäääLFu‰bŠô~lllÄàà <Ôˆ%^ÐÃl6C£Ñ$±vDDDDD+øÆqÇ—nPÚx<´¹"ñù|RÌ#ôx.¾øb\|ñÅò°¡¡!ÔÕÕÉ  =н{÷â±ÇCWW€OÂ% .Äܹså°èœ9s`·ÛQRR’¬ê©288ˆ“'O¢¾¾uuu¨««C}}=Nœ8cÇŽ!¬V+,X€ÊÊJlܸ§v-ZÄíÜ(1BDDDDDDDDÔЊ@H?úá†[q4¡IѳGðß=èQÌ.90à #Œ0Á„r”ãLœ LŠáf˜‘…¬É®-M:NUpd``---{Lòìܹ'OžL88bµZ‘——7Õ¥FMУ¡¡AѸ,ô=ép8 Y‚ÿGDDDDD‰ ¾AZ‘z%m¬^WW¿ß¦¦&tvv†-SMhDV\\ŒÔÔÔñ®:Ñ„’ñ>+Òséîש‡ËàÏ…tóƒHŸ“É„´46ù#š®ÒÒÒ0þ|ÌŸ??ìrH䨱cøè£°oß>œ‡ˆ$±ÙlÐjÙ‹*/éúI¼`‡ËåBKK‹¢WK@„ îÝ2Úû™=\‘¤´´¥¥¥¸ä’KÂþ'…0ƒ9‚—^z ü±¼?•ö£v»]¾þü7¯åQ<Áß%ÕÕÕ…ýOOOGii)ìv;¬V+–/_®©©= ±c „ˆˆˆˆˆˆˆˆf„ô hF3šÐ'œpÃæOÜpà 'zÑ+O“ŠT£æÌ4˜Mé8%}ÎÃJ£VXa‚ 6ØPŒbhÁ4½I ***bŽ/8ÒÜÜŒÆÆFEƒ©±C¼^GØÈaêéïïGkkkÌÞ<\.WXC-5AÒÒRäää$±vDDDDD4™  ,KÜsOIhãûHA’ÚÚÚ„ßÇꕄ祳‹šRð0µ!%»Ý1¸dµZ¡Óé’T["šÉòóóáp8"Þ¨··W¾;cc#\.át:ñÎ;ï ¡¡½½Ÿ}/’••…ÒÒRX­VX­V”––¢¸¸%%%(..†Ñh„ÙlæÝû‰fááaø|>x½^¸Ýnx<ø|>y›át:ÑØØˆææfÅñ¶Éd’ƒØóæÍÃÊ•+åðšÝn‡ÍfcÏeS_""""""""šÒħ½x¸à„MhB3šÑ€Eø£ò4:è`†XPŒb,Ã2\‚Käp‡” &˜‚àÿ»øÇ?€5&±¦DSÃhƒ#Á )êêê°wï^444 »»[žF§Ó¡   np¤¤¤)))]Õ- ­­-nÐÃívC!O'5 ¾‹ièëS^^ެ¬¬$ÖŽˆˆˆˆˆf éT-©¬Æýuuuò9k{{»bzé¼4Vh$x˜Édb#·)¤¯¯OU°Ãï÷Ãét¢££C1}p€HzGÔ÷Cqq1RSS“T[""u222°xñb,^¼8ê8íííp:hhh#MMMp¹\¨©©×ë…×ëUL£Óé`2™äý¡Ñh„ÅbÑh ^XXÈë¹D“, µµn·>ŸO{x½^ÅpŸÏ§½jµZ˜L&X­VùxèŠ+®€ÅbAYY™tÕëõI¬!%‚g,DDDDDDDD”4C‚ .4 AîÝ£aá ÈÓä#XPŠRXaÅ98X`…6Ø`&˜+ÈÉ“À)§Œoåˆf8µÁ‘àÆ¡Rp$¸‹qà“/# ¡½”——Ϻ†‘ÖgpãµAŠŠŠ°0Nii)ÒÓÓ“X;"""""¢Ø‚{!‰t—ôPÁ=DD Hç¦~¿^¯ÃÃÊyDê!"ZÄf³ñŽê*I72ˆ÷úHÃ|>†††óí±Tê½#VЇˆh6ÊËËC^^žê›…n“¥¿?øàƒˆ=Há¡»XûKé—Á;¢OÄ =GûmnnVÌ#RÏy‹-Šøý oÊ5ó0BDDDDDDDD¦}hF3ê>ýqÁ¥xÞ€ á³/ró‘;ì0ÃŒ T`VÁ‹ÜÛÇ©8¹˜€/Õ]. ªjüçKD0 °Ûí°Ûí1ÇS‰tw×РC¤Ç²²²)G×Xõ—ÕÔ?RÐc:ÔŸˆˆˆˆˆh"HÔ n -¨PWW‡;w¢©© Šé#õBÜ6ôùLi¾ ]_‘7†ÞÈøì7xýI繡ëÔf³A«Õ&©¶DD3“Úž»„ðù|ðz½ðù|hkkCkk«üÛÖÖ†¶¶6Ô××£¦¦Fþhˆ$55ÈÍÍEnn.òòò››‹œœäääÈÿ/xXNN{2¦¤B ½½èèè@gggØ£ßï8¼££mmmŠ›eI233QPP€ÂÂBùÑf³aÙ²e(((PüO걇Ÿ…ÙßѨ bPîÝã$Nʽ|?ïB—<~! QúéÏB,Äj¬F)JQ†2”£%(A*’t'¨æf ¤$9Ë&"ãñûý¨­­•‡S™ˆ2Ô=âõb6›áp8ÂÊ;{H!"""""šhjÃJÔ!jjjBD ’ÆIéÝ1ø|6^r0f5€¿|2}¤`ŒÔ{ÇLÆÍ&“ &Sb½µwuuÉ‘––98"5¦oooGgg'¼^/>üðCEÃùÐåHRRR››‹üü| yÿ£×ëa0——½^ŒŒ äææB¯×#33999ÐëõÈÊÊBvv6ôz=²³³‘••…ôôtù‘f†žž  §§@èííE @{{;úúúà÷ûÐ×ׇöövôöö¢³³@ÝÝÝèêê’‡E s@zzºZÊÏÏ—ÿ.((Àœ9säPSh¸Cú[§ÓMò¢éŽ""""""""Šh#pÁ…ú(?.¸0Œa€Z9ìQ†28àP´¶¶ÊwVoii‘¥;­üñÇØ¿¿<<4@RPP£Ñˆ¢¢"ù·¸¸XñÜh4Âd2¡¨¨ó^¦ÇãQ<—îôL£Ñ(æ]TT‹Å‚ÓO?]~î9Õƒïý=\ß}=þ_êÿC–w©&""¥ììldggã”SNÕôÑz`èèè€ßïØ¨ÿã?F___XÿŽŽŒŒŒÄ]¦2ÑétÈÈÈ@ZZ²³³¡Ñh——ÈË˃F£Avv6ÒÒÒ‘‘N'‡R(Æ Ï/xÝH½Ï£å(E©Ü»‡š$×f”ü~ ¿Ÿ¢&33óçÏÇüùócŽÚƒ‡ô+õÜñöÛo£¹¹­­­ò4v=Ÿ>ÏÈÈC‹Ë—/ÇW\!÷î!ýLX}‰ˆˆˆˆˆhz0 °Ùl°ÙlªÆQ6"…8’à–ÅŸ>z‚æ©Óéä ‡Éd‚ÑhDyyyÔpIQQ‘ªÞ;ÊQŽMY›à„;°¹P×𔈈HÜÜ\äææ¢´´t\æ'õÑÙÙ‰¾¾>ùo)1<<,‡¤°‰4pBÈ7jjjŠ\â"f»X˜hA›¬¬ÿŸ½;«ªNü?þºx‘M Q¤Ì KMÄ]p_ÑÜÍ\-5mlLm™, ušòWãÞŒ¹ç®)¦™+i¹€+"傆‚ŠâEP~÷+á.–÷ó>îCsÎûÏ8ÝåÍÇÉ:ÃXæøÌYÞ\\\(V¬ŽŽŽØÛÛ[‹=wÎ"sçÏ" !""""""""…X:éÄÇ NC ±Äf)}$óû›ÏÅ(ÆÓ$>þ÷?Ë–56‡ˆ"§3ޤ¦¦Zgiа!‡ÇÇÔ³'^^^¸¸¸äQZ)jlllðððÀÃÃ#Çë$''[g ©ðÖ[üòÑGÖò‡³³óÉÚ•®<Ã3„B#±†5T ÂÙ—ˆˆÈ£²··ÇÞÞ>_î7CƽÊ#™%•œ¸×6–,YÂáÇ?~|–åw4$³|ñgNNNØÙÙž!ED !""""""""Ü-nñ ¿XKÇ9ný9Ž8ÒH 4¥©D%¼ñ¦=í³>ÊS;ì >ƒdB4CˆˆÜGñâÅ©P¡*üþE–jÕªAÕª§ÉÎÙÙggçßùÁïyyæÉ¾`/{éHG d%+ Oö-""RPÝY–rU}‡âÈ‘#tïÞ=Oö'"O† !""""""""DIDÍa{ÇíGH!7Üðùãö"/âƒÕ¨†?þ¸âú€=Q¿ýöö~”ˆˆˆˆˆˆˆˆHAç…ÛØFúД¦Ìb}èct,ùnܸat yD*„ˆˆˆˆˆˆˆˆä#×¹ÎQŽr„#æ0Gÿ¸ÅK*¿Oå솾øâ‡èÀ(FᇾøRŠRA””®®`2DDDDDDDDD¤Pp‰oø†wy—¾ôå8Ç #ÌèX"""r³Ù¬BˆH! Bˆˆˆˆˆˆˆˆˆ’HÊRúˆ&š£å~!ƒ ì±Ç?ªQ®tÅ;îFÇ/\®^£Sˆˆˆˆˆˆˆˆˆ*Å(Æd&ãƒÃÎ/üÂ|=öFG~Ÿ!Äb±CD‘ !""""""""OPIDÍaK¬õçSœ²?|ñ¥:ÕéOªS|¨Nu̘Ž_4$'ƒ³³Ñ)DDDDDDDDD ¥! ÁozЃœà¾¡4¥Ž%""Rä988h†‘B@…‘Ç •T¢ˆâ8ÈAüqK" €R”¢ê· ‚¨F5ªP… TÀ„ÉàôE\r²fy‚ZÑŠíl§#©O}Ö°†*T1:–ˆˆH‘f6›±X,ddd`2éóJ‘‚J…‘\:Ïyrýì·?ŽqŒtÒ)A jPƒçxŽnt£Ú·2”1:¶ÜËÕ«*„ˆˆˆˆˆˆˆˆˆœæ4KYŠ3ÎFÇ)2 !‹Åà$"ò(T‘B'Ž8¶±­le7»9Æ1ns›gx†ºÔåU^%€êPG.5¶¶Ð«Ìš7oÞ}ŒÉýúåm.‘"*„¶°…Žt¤XÇ:¼ð2:–ˆˆH¡g6ÿþKò4CˆHÁ¦Bˆˆˆˆˆˆˆˆx1İm|ÿÇí~ÁŒ™zÔãE^$€êRO<Ž*ùAïÞ0cƽŸ7™àÅó.ˆˆˆˆˆˆˆˆH@»ÙM[ÚZK!U©jt,‘B-s†BD 6BDDDDDDD¤À‰%–ílg;ØÈFâˆÃjS›^ô¢-hD#̘Ž*ùQ£Fàé ññÙŸ³µ…¶m¡T©¼Ï%""""""""R„U¤";ÙIg:Ó†|Ã74¥©Ñ±DDD ­ÌB,‹ÁIDäQ¨""""""""ùÞ N°‰MÖY@ÎsGiHCB ¥ͨK]ì±7:ª&¼ôüë_pófÖçnÝ‚¾}É%""""""""RÄ•¢›ØÄКÖÌf6}èct,‘BI3„ˆ*„ˆˆˆˆˆˆˆH¾“B ;ÙI¬bG8‚#ŽÔ§>¯ò*hDcSœâFG•‚ªwoøøãìËÍfèÐ!ï󈈈ˆˆˆˆˆˆÅ)Î0 ô¥/Ç9NaFÇ)tÌf3&“I…‘N…ÉŽqŒ•¬d=ëÙÁnq‹ÚÔæE^¤5­©O}lõV†<.µkƒ¯/ÄÄüß2;;èÖ J”0.—ˆˆˆˆˆˆˆˆˆ`ÂDa”¢oò&g8Ãÿãÿé=b‘ÇÈd2Q¼xq,‹ÑQDäè¿EDDDDDDÄ·¹Íö°òÛQŽâŽ;miË\æÒ’–”¦´Ñ1¥0ëÛ&M‚´´ß§¥AŸ>Æf«‘Œ¤<åy‰—8Ö°gœŽ%""Rh˜ÍfÍ"RÀÙ@DDDDDDDŠŽÛÜf+[ÊPÊQŽ4`ËhO{¾ç{Îsžù̧}T‘'綾þ¯ P²$´ha\ɦ3ÙÌf~â'ZЂ‹\4:’ˆˆH¡áàà BˆH§BˆˆˆˆˆˆˆˆõÙÏ~£#‰ˆˆxf³Y…‘N…ylRHá+¾"˜`|ñe&3éE/p€h¢ÏxžåY£cŠüŸž=!#<=IªZ•¤¤$ë===Ýèt"""""""""r‡²”eÛ¨A ‚â~0:’ˆˆHæààÀ7ŒŽ!"ÀÖè"""""""Rðã3˜Á\æ’B èÀJVÒ–¶ØêíÉÛ·osåÊ’’’¸råJ–û7¸|ù2ééé$''sóæM®_¿Î7°X,\¿~›7orõêUnݺÅåË—ÉÈÈ ))éžû¹Ójà‡øxþùÔS9ÊêêêŠMöß³âææ†Éd¢dÉ’ØÚÚâì쌽½=ŽŽŽ˜ÍfpttÄÞÞggglmm)Y²$%J”ÀÕÕ5˽dÉ’”,Y“ÉôPçSDDDDDDDD¤°r‰լ¦ýhIKþËéF7£c‰ˆˆH*„ˆ|úF†ˆˆˆˆˆˆˆ<”[Üb5«ùœÏ‰ o¼y—wéO<ð0:ž(11‘ß~û„„8þ< \¼x‘‹/f+|\¹r…äää»n+³Dáââ‚NNN/^œ%Jààà€ÙlÆÃÃ#KÉ"³°áää„]¶mþ¹ÐqxØäæ–e\N %iii\»v[·nqõêUëãÔÔTRRRHJJ">>ž””RSS¹víiii\½zÕZn¹\\\¬%WWW\\\pwwÇÝÝÒ¥KS¶lYJ—.MéÒ¥ñðð T©R÷ùÛ)øŠSœ,àu^§½øœÏ£c‰ˆˆ8f³‹Åbt y*„ˆˆˆˆˆˆˆH®\â_üq;ÃZÑŠU¬¢í°!û¬ R8¤§§ϯ¿þÊéÓ§9{ö,gΜÉRú¸xñ" ¤¥¥eY7³¸PºtiÜÝÝ)[¶,Ï>ûl¶Y1\]]qssËòØÞÞÞ #Î[7oÞÌR¹ÛL)—/_¶þ|ðàA빿xñb–mÙÙÙe9׸»»óôÓO[ïåË—ÇÓÓ[[½=(""""""""S1Š1ƒxãÍ«¼J,±Lf²Ñ±DDD Í"Rðé_É‘œ`*S™Ç<ŠSœ d(CñÅ×èhò\¸pØØXΜ9Ù3gøõ×_³ü|þüynß¾ @±bÅ([¶,åË—· êÖ­k-~xxxP¦Lk D¥ƒ³···ž¯ÜJOO·–C.\¸p×ÙYbbb8}ú4çÏŸçÖ­[@Ö¿G///ž~úi*T¨`ýÙÇLJ2eÊ<îCy¬Æ2<®qñ/ýò"‘R!D¤àÓ§ñ"""""""r_ûØÇt¦³€”§<çï¼Æk¸âjt4É¥¤¤$bcc­÷èèh>̉'¸zõªuœ››>>>xzzR³fM:w§'åÊ•ÃÇLJòåË«ä‘ØÚÚâé鉧§gŽÆg^çÎ#>>ÞúsTT«V­â×_%==€âÅ‹ãååEµjÕ¨^½:>>>Ö»··766ú`]DDDDDDDDŒ7¸áFozsŽs,`fÌFÇÉ÷Ìf3—.]2:†ˆ<}r/"""""""ÙdÁ Vð!ò#?Ò€,béL1ŠOîãæÍ›9r„¨¨(¢¢¢8~ü8111ÄÄÄ’’¸ßú IDAT@‰%ðõõÅ××—æÍ›óÚk¯áëë‹åÊ•SÙ£sss£N:Ô©Sç®Ï§§§sîÜ9NžÝ«ÛÜf)KyŸ÷‰&šÎtæ_ü‹@Ž&rûömN:Å¡C‡ˆŠŠ²þyüøqÒÓÓ±³³£J•*<ûì³´k×ÎúÅ}???Ê•+gt|ÉÇlmm)_¾<åË—'(((ÛógÏžÍR‰‰‰aÍš5|òÉ'¤¥¥aggGåÊ•©Q£†µ$R£F *V¬¨EDDDDDDDDä‰jF3¶³6´!˜`Ö±<ŒŽ%""’o©"Rð©""""""""Üæ6ËXÆxÆsŒc´£s™KmjMø½üqäÈ"##‰ŒŒdß¾}>|˜ëׯc2™ðööÆßߟjÖ¬IõêÕ©R¥ vvvFG—BÈËË ///š6mšeyZZGÍRPš5kqqqdddàèèHõêÕ©S§P¥J•DDDDDDDDD䱪A ¶³Ö´&@¾å[üð3:–ˆˆH¾d6›±X,FÇ‘G BˆˆˆˆˆˆˆH–Yùã'èJW–±ŒªT5:Z‘vöìYkùcÏž=ìÝ»—ääd¨U«õë×gÈ!øûûS½zuœœœŒŽ,‚þþþøûûÓ»woëòääd¢££9tè‡bï޽̙3‹Å‚‹‹ /¼ð‚µ €———G!""""""""…7ÞüÀ´£MhÂzÖó<ÏKDD$ßÑ !"Ÿ !"""""""EPi|Í×¼ÏûÄG/z±’•<˳FG+r2228tè[¶láûï¿'22’³gÏbccCÕªU  Gàïï¯Y?¤Àqvv&00ÀÀ@ë²´´4­ZµŠþóŸÜ¾}///êÕ«G“&M¦F˜L&@DDDDDDDD "<ØÂ^äEšÑŒµ¬¥! Ž%""’¯˜ÍfBD 8BDDDDDDDŠ›Üd! ™ÈD~åWzÑ‹µ¬Å?££)GeË–-lÙ²…­[·’@©R¥hÒ¤ #FŒ €^xggg££Š<vvvÔ©S‡:uê0tèP®^½Ê¾}ûسg»wïf„ üå/¡téÒ4kÖŒ   ‚ƒƒyöY×DDDDDDDD$gœqf-kéCZÑŠå,§5­Ž%""’o888`±XŒŽ!"@…‘" 4f2“÷yŸDÄ Æ2–gxÆèhEÂùóçY»v-[¶lá»ï¾#>>gggš4iÂØ±c æ¹çžÃÆÆÆè¨"†qqq!((ˆ   nß¾Íþýû­å©±cÇ’œœL¹rå&((ˆöíÛS¦Lƒ“‹ˆˆˆˆˆˆˆH~f=‹XÄ`Ó‰N,`]éjt,‘|ÁÁÁA3„ˆp*„ˆˆˆˆˆˆˆbd°Œe¼Ë»ÄÇk¼ÆXÆRŽrFG+ôŽ;ÆÊ•+Y±b{öìÁl6Ó°aCFŒApp0uêÔÁÖVo͈܋ µkצvíÚüõ¯%==½{÷Z‹U¯¿þ:ƒ&00Î;BåÊ•Ž-""""""""ùP1Š1‹Y¸àBOzòþÃ@KDDÄpf³Y…‘Nß:)¤v±‹±Œe;ÛéF7Ö±ŽJT2:V¡Í’%KX³f ûöíã©§ž¢]»vŒ5жmÛâäädtD‘ËÖÖ–ÀÀ@yûí·¹qã¬Y³†ððpÆŒƒ:t {÷î4hÐ@³îˆˆˆˆˆˆˆˆˆ• Ó˜FIJ2ˆA\å*oð†Ñ±DDD åàà@ZZ·nÝ¢X±bFÇ‘‡ ODEDDDDDD ™#¡=h@ŠSœ½ìe1‹UyBŽ;Ƹqãðòò¢Fü÷¿ÿ¥Y³flÛ¶ .0þ|ºwï®2ˆÈcæàà@ÇŽùâ‹/8sæ [·n%$$„5kÖиqcÊ—/ÏÛo¿ÍñãÇŽ*""""""""ùHa|ȇü…¿0 FÇ1”ƒƒ‹Åà$"ò°4CˆˆˆˆˆˆˆH!q–³Ld"³˜Eª°šÕt ƒÑ± ¥ääd–,YÂìٳٱcåË—'44”nݺáïïot<‘"§X±b4mÚ”¦M›ÎÁƒY²d óæÍcÊ”)4lØW^yEå,`,cqÁ…×yÜ`2“Ž$""b³Ù À7ptt48ˆ< Í"""""""RÀ]ãa„á‡ØÀç|Ψ òìØ±ƒW^y…råÊ1|øpžyæ¾ýö[N:Å„ ò}$##ƒùóçÈSO=…ƒƒÏ=÷ï¼óQQQYÆšL¦'ž'/öQИL&Ã΋‘û~ÜjÖ¬É?þñâââX·nåÊ•cèС”+WŽAƒ±k×.£#ŠˆˆˆˆˆˆˆˆÁ†2”ùÌç>aøÍm£#‰ˆˆä¹ÌBnܸapyX*„ˆˆˆˆˆˆˆP©¤ò ŸP |Æg|ÀœàCB1Š¯Ð¸}û6«W¯¦Aƒ4jÔˆ={öðÞ{ïqúôi¾þúkZµj…MÁx‹e„ ,Z´ˆ/¿ü’³gÏÏ´iÓX¿~}¾/³ErßOŠ mÚ´aÑ¢Eœ?ž?þ˜ýû÷Ó AêÔ©ÃüùóIOO7:¦ˆˆˆˆˆˆˆˆä%^b˘ÃúÑtô^‘ˆˆ-™3„X,ƒ“ˆÈÃ*ßV‘,"ˆ µø;g0ƒ9Á þÂ_°ÇÞèh…Æ­[·˜7o~~~tîÜ™råʱk×.¢££;v,îîîFG̵3f0gÎjÖ¬‰Ùl¦dÉ’ñ¿ÿýÏèh"O\É’%2dûöícÇŽT¨PRµjUæÏŸÏ­[·ŒŽ(""""""""èD'Ö²–•¬¤+]± /ÄŠˆHÑ¡BD >BDDDDDDD “œ¤=hIK*Q‰Ãf2“)II££*K—.¥F„††ÄñãÇYºt)FG{$)))¸ººf[^­Zµ,³C˜L&럙÷ÐÐPëó«V­¢aƘÍf¼½½5jÉÉÉY¶™¹ÞÉ“'yñÅqss³.ËÉ>î&'û½rå £FÂÇdzٌ§§' 22ò¾Û~P^€¨¨(Ú·o³³3ÎÎδiÓ†¨¨¨‡Ê M»víprrÂÕÕ•.]ºð믿Þ7碢¢h×®5O»ví²åÉ<†Ó§O‚³³3eÊ”¡oß¾\ºté¾Û‹‹Ëò÷õkײ,‹‹‹ËqÞü¤Aƒ,_¾œ£GÒ¸qc „¿¿?Ë—/7:šˆˆˆˆˆˆˆˆ ˜`Ö±Žïùžö´ç׌Ž$""’'T)øT)RH!Œ0jPƒƒdXÍj*PÁèh…JTTÁÁÁôìÙ“:uêpäÈfΜI¥J•ŒŽöXtìØ‘qãÆ‘””tßq™åŒŒ ë}æÌ™ÖçCBBèß¿?‰‰‰ìÞ½›ÄÄDFŒq×m :”Ñ£GsîÜ9Ö­[—ã}ÜMNöÛ¿J–,É®]»¸rå ëÖ­#&&†zõêåè˜ï•7&&†–-[Ò±cGbcc‰‹‹£oß¾„„„pæÌ™\ç°@PÍœ9“wß}___êÕ«G‡ ÁËË+WÛ¹s6‘%J0mÚ4|}}ï:öwÞ¡Aƒ´mÛ6˺¹•“ýnÞ¼™Ù³gSªT)jÕªÅW_}…OŽöq¯¼aaaŒ5Š×^{Í:¶o߾ܼy“>úˆéÓ§ç*gXXýë_yå•W²l/==]»v=0gXX£Gβ~hh(W¯^e„ |õÕWYÆ6Œ6mÚàääć~Hppð÷ãïïOzz:à¹çž`Æ ”/_Þú¸0ðóóãÿû#FŒ`Ĉ<ÿüó¼ýöÛüíoÃÎÎÎèx""""""""’GjS›ml£%- &˜ l 4¥Ž%""òÄh†‘‚O3„ˆˆˆˆˆˆˆäSÇ8FÚBMhÂQŽ2–±*ƒÌ~ëׯO§NذaéééT¬X1ÇE”{刈 gÏžÙ–·oßžM›6å:ç¦M›èܹs¶±™¥‰ˆˆ¸ëú]ºt!"""Ûò&MšdyìííM|||ŽöÕ¿¾øâ ëãÿûß¼ùæ›9Z·  dÏž=„‡‡óÉ'ŸP¿~}Ž?nt,ÉCU©Ê÷|O"‰4£ç8gt$‘'&³¢BD .BDDDDDDDò™Ë\fã¨IMH`;Û™Ï|ÊPÆèh…κuë¨W¯vvvüüóÏ >›Âÿv‰««+½zõbþüùÄÅÅa±X9rdŽÖMHH 44”§Ÿ~[[[L&&“éžãK”(ñX2çt¿K–,! €áÇSªT)š4iÂôéÓIKKËÑ~î•÷Ò¥Kx{{[÷›y/[¶,qqq¹ÎyñâEÊ–-›mùÝ–ýyŸ÷[ßÓÓ“‹/f[îææ–åqñâÅs\’éÓ§‹/&%%…ØØXN:E»vír´nAdccÈ#øù矱±±¡^½zlذÁèX""""""""’‡*Q‰øÛܦÍ8ã#‰ˆˆ<vvv+VL3„ˆ`…ÿ"""""""DÌaÏò,s˜Ãç|ÎüH­Pš5k:u¢K—.lÛ¶ ???£#ÂÑÑ‘©S§²bÅŠùå—qttdûöíX,222r\,x9ݯ««+áááœù„Ù³g“@bb"‹/æƒ>ÈQžñãÇÎÌ™3ùí·ßHHH`Ö¬Y„‡‡gÉó¸tèи¸8ºwïN‰%ûöó»÷ߟѣGÊž={ŒŽ#""""""""y¨4¥ÙÌf\q¥͈%ÖèH"""•ƒƒƒf)ÀT1ÈOüD L`a„±—½Ô¥®Ñ± µäädzôèA›6m˜4i’ÑqòÜ®]»¨Zµ*ýúõÃÝÝwwwLçÎùÛßþ–elxx8/½ôNNN >œÏ>û €ùóçc±X¨W¯ÎÎÎtéÒ…öíÛYgüÈüÙd2Ýs&{íãnrºßˆˆhÚ´)...Ô¯_‹Å¼yóî{n”×ÛÛ›eË–±páB*Uª„§§''NäÓO?%$$$×9}||X¿~=K—.¥bÅŠT¨PY³f±`Á‚lcïÆÏÏõë׳lÙ2|||¨X±"K—.eýúõøúúÞõ¸îu¼ `ccƒ‹‹ ¯¿þú}sf~ø!-Z´ gÏž\¿~Ýè8""""""""’‡JSš­lÅ‚â$'Ž$""òؘÍfBD 0BDDDDDDDòX )ŒcàŒ3ûÙÏXÆb‹­ÑÑ‹ÀÀ@Ö®]{×ç8ÀÓO?Mzz:ßÿ=˜Íf¼½½™5kV–ñÉÉÉŒ3???J”(««+-[¶dÍš5•í£>âúõëÌ™3ç_¸/ŒùòË/9qâ©©©¤¤¤ðÓO?ñúë¯g;Ý»w'66–ÔÔTNœ8A§Nððð`þüù\¸pÔÔT:D=ÈÈÈ ##ú~æã?/ÏÉ>î&§ûmÞ¼9ß|ó /^$55•ØØXÂÃÃqvv¾ï¹ÉI^???–.]ÊåË—¹ví‘‘‘téÒå¡rT¯^uëÖqíÚ5’““ùöÛo©V­Ú}3ÜÉßߟõë×síÚ5®]»Æúõëñ÷÷¿çq=hùýŽùòå4nÜOOÏæ*¬lll˜;w.—/_æã?6:Žä&“‰[·nñþûïãííMñâÅ©\¹2ÿþ÷¿³]½z58::âèèxßÿ¿yÜôöѹáÆF6ª"""…Žƒƒ‹Åè"òTÉCkYKUªò%_ò9Ÿ³…-T¦²Ñ±«7ÞxƒiÓ¦Ýõ¹O?ý”×^{ [[[Ž;F·nÝ5j¿ýö‹/fÒ¤IlݺÕ:¾ÿþ¤§§Á•+W8uêo¼ñŸ~úi®sY,¦M›Æ˜1cxê©§öðDŠ“ÉDdd$S¦LaÔ¨QFÇ1\éÒ¥3f S§NÕ#b5lØ0nܸADD‰‰‰üç?ÿaêÔ©,Z´È:f÷îÝ 0€‘#GÇ©S§xýõ×yùå—ùñÇ L/""""""E‰^Ã>:7ÜØÄ&ÊR– ‚ˆ!ÆèH"""ÌÁÁA3„ˆ`*„ˆˆˆˆˆˆˆä \ ýè@êQccC0Qøf©èÖ­GŽ!:::ËòK—.±lÙ2† À¤I“7n½{÷ÆÅÅ…€€¦NÊG}d]gÓ¦M„……Q¡Bììì(Uª:tàÛo¿Íu®7rýúuú÷ïÿh(RÄ4jÔˆ&MšP«V-££ä ýû÷'99™M›6Eò‰2eÊ0iÒ$|}}qtt¤iÓ¦|úé§L:Õ:fÊ”)üãÿ OŸ>”.]úöíËĉ™}šÊ•+c2™¬÷R¥Jo³páBðõõ¥jÕª 8•+W’‘‘‘ëLñññ”+WîÑNDŠâ#JPÂèXyÆÝÝ_|‘/¾ø€•+WR¶lY¬c\\\8wîYî·nÝ²Ž©X±"ß|ó —/_fáÂ…Ô¯_ŸI“&šëLÎÎÎ\½zõÑNDм«W¯âêêjt É'llü6{™2eøí·ß²-ÿí·ß([¶ì“ˆ%""""""’^Ã>~®¸² øàC0ÁDmt$‘\3›ÍX,£cˆÈCR!DDDDDDDä1ºÀBáU^e ÙË^êPÇèX†xã7˜1ciii|úé§Œ1"ËóAAA¬\¹2GÛ*^¼8Ï=÷C† aýúõ,Z´(×y|||8~ü8iii¹^W°ÎâbÄ~s3öÏw777Y¼xñLY°œ8q‚Ž;âîîží|Ƀ¥¥¥qüøq|||ŒŽ"HíÚµY½zu¶å«V­¢víÚ$¹;½†Í=W\ÙÄ&ªP…`‚‰"ÊèH"""¹¢BD 6BDDDDDDD“%,¡:Õ9Ä!¶°…éL§8ÅŽe˜5jP¥JÆÏ‘#GèÑ£G–çÇÏĉ™7o—.]âúõëlÞ¼™öíÛ[Ç4iÒ„ÿþ÷¿œ9s†ôôtΟ?Oxx8Mš4ÉužöíÛsùòe6oÞüÈÇVeddá23fÎ6“––FTTÆ cĈ|úé§'ÌúõëGóæÍ‰Í2;ä̦M›¸råJ–«Dä­·Þâ½÷ÞcÁ‚$$$À‚ øûßÿθq㌎'""""""b¥×°ÇGÖ²–jT£9Í9Ä!£#‰ˆˆä˜ !"› !"""""""(‰$^æezÒ“®tå iBî …Ño¼Á‡~ÈàÁƒ±··Ïò\õêÕY·n‹-¢bÅŠ”.]šI“&ñæ›oZÇLœ8‘+VðüóÏãììLãÆ¹uë_ýu®³x{{ĤI“ù¸¤`°µµÅËË‹~ýúñ¿ÿý©S§)_Ø·oÇÇÅÅÅè(NFFï¿ÿ>-Z´ |ùòFÇ‘¤AƒÌž=›iÓ¦Q¡B*T¨À´iÓ˜3gFDZÒk؇çˆ#kXCuª«"""ŠÙlÆb±CD’ !"""""""`=ë©A 6³™Õ¬æ ¾À '£cå-Z´Àl6óÚk¯Ýõù矞uëÖqõêURRRøî»ïhÞ¼¹õùfÍš±téR.^¼È78qâ“'OÆÕÕõ¡òLž<™;v0wî܇Z??Zµj 6Äl6ãííͨQ£HNNÎ2Æd2a2™8}ú4!!!8;;S¦LúöíË¥K—²m3::švíÚáää„««+]ºtá×_ÍQž+W®0jÔ(|||0›Íxzz2`À"##³Œ‹ŠŠ¢}ûö8;;ãììL›6mˆŠŠÊ–ûÎü&“‰ÐÐÐÜœ«€€Î;—myNr8p€V­Záè舋‹ mÛ¶åÛo¿Í¶­víÚY·Õ®]»,Ûºó8¶nÝ ÀÂ… ³,Ïíù1™LœsæÌ‡:O‘‘‘x{{gY–ÓǧgÏžŒ3†„„vïÞÍõë×iÓ¦M–mež·“'OZKmÚ´!&&€ôôt*UªDbb"Íš5 W¯^œ;wŽR¥JqíÚµ‡:?C‡eôèÑœ;wŽuëÖÝ÷<üùœ>HN®¯?_3§NbàÀôìÙ3Ë> ºŸþ™‘#G2nÜ8jÕªetɧœpbë¨NuZÒ’Ã6:’ˆˆÈ}™ÍfBD3¡r= IDAT 0SFaùDVD$˜0±ˆEô ‡ÑQ¤0™L,Z´ˆ=t=‰ˆÜéË/¿dÈ!Fǹ§Mlbƒ¸ÉM¾äK:¡ßŒög&“‰%JкukæÎ‹‹‹‹Ñ‘¬ÒÓÓiݺ5ÑÑÑ|÷ÝwT«VÍèHURR¾¾¾Ùfþ0™L¬X±‚ë²cÇŽÌÙ³g­Ë^~ùe^xáÞxã,ëÏ;—>ðËýÎÎÎüòË/”*UʺìÔ©SøøøX×íÛ·/5kÖd̘1YÖ={6`úôéYrçæí«;Çߺu‹ .°yófÆŒc-(eÊiŽ>}úжm[^~ùeë˜cÇŽQ¥J•,ÇT·nÝlç-<<œŸþ™¯¾ú €wÞy///†n3yòd~ùåf̘ñPçgË–-Ö‚InÏQN–ßén××½®™… Ò»wïBQ‰ŠŠ¢yóæÔ¬Y“ 6P¬X1£#‰ÑL&X´ôz^DäöíÛ@:u N""’;ú÷KD Ì×®‹›£J!…v´ãGØÊVªRÕèH"""wÎÔ©S9}ú´ÑQD œ¼|ÿà^ß7Õ !"""""""9”Fa„ц6Ô£QD© r\¿~åË—ç«2€­­-+W®¤råʳk×.£#=Vnnn$&&Þõ¹&MšdyìííM|||–e›6m¢sçÎÙÖ½s6Œû©_¿>:ubÆ ¤§§P±bÅ,¥€ˆˆëìwjß¾=›6mÊÑ~îÇd2a2™°µµÅËË‹±cÇòÕW_e)ƒä&Çwß}GóæÍ³ŒyöÙg³ÓÝÎ[—.]ˆˆˆ°>8p sæÌÉ2föìÙ <8×¹2d[ö¤Üíúº×5Ó²e˼ŠõDíØ±ƒàà`ªV­ÊŠ+T‘)A V± o¼iE+b‰5:’ˆˆÈ]i†‘‚M…‘8Æ1 $œpf0ƒ%,Áw£cÉCrrrbݺuÔ­[—   fÏžmt¤‡’@hh(O?ý4¶¶¶Ö"Ľ¸¹¹ey\¼xñl³7\¼x‘²eËf[÷nËîfÉ’%0|øpJ•*E“&M˜>}:iiiÖ1—.]ÂÛÛÛš7ó^¶lYâââr´ŸûÉÈȰޓ’’øàƒ2dH¶mç4ÇÅ‹qw¿ÿÿÞïuÞ<==¹xñ¢õ±ŸŸ%J”ààÁƒlÛ¶ j×®ë\™J”(‘ƒ³’{9½¾îuìO=õÔÉ•—fΜIóæÍiРk×®ÅÑÑÑèH""""""""R€¸àÂF6R–²DqFGÉÆÁÁA…‘L…‘˜Ï|^àl°aû£#ÉcàääÄÊ•+yóÍ7 ¥{÷îY¾¸_¼üòË8::²}ûv,‹µñ(ÜÝÝ9þ|¶å—.]ÊÑú®®®„‡‡sòäIbcc>|8 .¤wïÞYö‘˜˜˜¥¸‘yOIIy¤üV²dI À€?~|–çršã©§žzàµáîîžm¶€øøøle’2kÖ,fÍš•evÜäzÒrz}Ýëš¹Û²‚"!!®]»2dÈFÍòåËU‘‡âŠ+ëY .´¤%ç8gt$‘,°X,FÇ‘‡¤BˆˆˆˆˆˆˆÈ=\å*}èÃð ¯°ƒøágt,yŒllløàƒظq#{öìÁßߟ¯¾úê‘KyeçÎL˜0ooolmm¹0ЪU+V¬X‘mùæÍ›s´¾ÉdâÌ™3ÀïEž={²víZ6nÜhÓºuk¶nÝšmÝ~ø!ËL™Û{† ÆÊ•+ILLÌuŽfÍš‘ęV­šõq‹-X¾|y¶m}óÍ7´hÑ"˲îÝ»³råJذa}úôÉò|nÎÏ“”Óëë^×Ì·ß~ûÄ3>nÌ›7öîÝKDDï¿ÿ>66zUDDDDDDDDž;îlf3öØDç)¸¿LEDD ³ÙÌíÛ·¹yó¦ÑQDä!è“L‘»ØÃjQ‹ïøŽµ¬e:Ó±ÇÞèXò„´hÑ‚ƒÒ¹sgH£FˆŒŒ4:Ö5hЀ1cÆÇÍ›7‰‰‰aèС´Í°°0>ùäfÏžMBB‰‰‰,^¼˜>ø ÇÛ %::šÔÔT.\¸ÀÇœ¥ÆøñãYºt)—.]"99™5kÖлwïl³xxyy±k×.ÒÒÒØ¸q#Ï<óÌC—»»;;vdΜ9¹ÎñÞ{ï1qâD"""¸~ý:ä•W^aĈÖ1ãÇ'<<œ™3gòÛo¿‘À¬Y³ÏvLNNNÓ·o_:v숳³s–çss~ž¤œ^_¾f.]ºÄ×_ÍþóŸ<Ëú8ìÙ³‡† 2hÐ ºvíÊÁƒ 6:–ˆˆˆˆˆˆˆˆxðßaƒ ­hÅ%r6+³ˆˆÈ“æààÀ7 N""C…‘;ÜâS˜Bcã‹/ûÙO[ÚKò@É’%™1c?þø#666Ô«W8`t´{š?>‹…zõêáììL—.]hß¾=ufÌŸÿ<ÛÆÝ–ûøø°~ýz–.]JÅŠ©P¡³fÍbÁ‚wÝÆŸEDDààà@Ó¦Mqqq¡~ýúX,æÍ›gãííͲeËX¸p!•*UÂÓÓ“‰'òé§Ÿ’e{ááá¼ôÒK8991|øp>ûì³{îû^Ç™iذaŒ=“ÉÄÅ‹sœ#s昰°0ÜÝÝéСÝ»wÏRŽðóócýúõ,[¶ *V¬ÈÒ¥KY¿~=¾¾¾Ù² 872dÈlÏå4×ǛәTrs-äôúúó5S±bE,XÀ¼yó ÄÌ?ýô;v$00[[[öîÝËgŸ}†«««ÑÑDDDDDDDD¤)C¾å[®r•6´á WŒŽ$""b-„X,ƒ“ˆÈÃ0edddBD¤ 1ab‹èA££H!`2™X´h=zèz¹Ó—_~y×/ÈŠø°…-ç8éL*©FG‘",s†7nœDD† !""""""R¤¥’Êp†Óƒô¦7?ò#þøKò “ÉD§Nøá‡صk+Väµ×^ÃÓÓ“ÐÐPvîÜitD‘|aÍš5¬^½šòåËãááAhh(ƒæ­·Þ2:ðûl ¯¼ò žžž 6 ???"##Ùºu+:tÀdRPDäÿ³÷çñQÖ÷þÿÿ˜,d$Ê–„E!‚Ö E° w±jÅ¥ ZTzz<­Ý{ì¯êÑs[OÅj«(j«µ‚þê†Ö* Zi¡uƒObd3,ÖLÖùþ3%@‚$W&yܽÍk®¹æâyÍLðzgÞ¯ë%I’$Ij]Gr$/ñïð—r)5ÔI’ÔAÅ B¢ÑhÀI$Œ´ H’$I’”Õ¬æb.fËx‚'¸˜‹ƒŽ¤6lÔ¨QÌ™3‡òòræÎËý÷ßϘ1c2d—\r S§N¥   è˜R Î>ûlÎ>ûì cÔ³~ýzæÌ™ÃìÙ³ùàƒ6l7ÝtS§N¥gÏžAÇ“$I’$I’8–cy8Ó¸š«y˜‡í^.Ijuáp°Cˆ”¬ì"I’$Iêæ1/ñ%¶²•·xËb5YVVÓ¦McéÒ¥,Y²„ &p÷ÝwsÄGðÕ¯~•™3g²jÕª cJÒ§Ÿ~Ê]wÝŸqãÈËËãÖ[oeìØ±üýïgÙ²eüð‡?´D’$I’$ImÊhFó4OóOp=×G’ÔÅ;„X"%' B$I’$IJ 5ÜÌÍ|¯q6g³„% cXб”¤ŠŠŠ¸çž{())áñǧwïÞÜ|óÍôïߟãŽ;Ž[n¹…wß}7è˜R»ö÷¿ÿŸýìg{ì± 8[n¹…ÜÜ\žxâ JJJ¸ûî»ùÒ—¾tLI’$I’$©Q§r*ó8¿æ×ÜÊ­AÇ‘$u0ñ‚h4pI#-è’$I’$µ–lä|ƒE,â7ü†iL :’Ú‰p8̤I“˜4iÕÕÕ,\¸gžy†|0Q rî¹çrê©§ròÉ'Ó­[· #KIkëÖ­¼ñƼüòË<÷Üs¬^½šüü|Î=÷\~ñ‹_pÊ)§žžtLI’$I’$©Y.àf3›oòMºÐ…ïòÝ #I’:ˆp8 Ø!DJV„H’$I’:„…,ä|ƒL2y›·Áˆ #©JOOçÔSOåÔSOåž{îaéÒ¥<óÌ3Ì›7_ýêW¤¦¦RTTĸqã?~ø€?ü?ü—^z‰ÒÒRzôèÁˆ#(,,døðá 2„Áƒ“““䡨*))aÅŠüóŸÿL|?øà6mÚ@Ïž=1b#GŽäÊ+¯døðáŒ1‚H$prI’$I’$©í‹áyžg,c9•Sy“7½–$©ED";„HIÊY1’$I’¤va›¸˜‹ùã·ü–)L :’Ô":wį̂Q£5jT½õ udxøá‡Ù±côíÛ—Öë&2pà@ú÷ïOJJJ‡£$PVVFqq1ÅÅʼnÏXqq1}ôÛ·o¨×±æôÓO·c$I’$I’tˆt§;/ðcÃD&ò¯Ñ….AÇ’$µ3ápØ!R’² D’$I’”ôþÎß¹€ H#·y›á :’Ôê²³³9餓8餓ëb±Ÿ}ö+V¬¨w{ýõ×™={6;wîv™ 4ˆPPP@~~>}ûöM,çææ’žžÔ¡©UUUQRRÂÚµkY½z5k×®eíÚµ¬ZµŠO?ý”+V$®•™™É Aƒ8âˆ#˜0aÿöoÿÆ Aƒ4h}ûö% |4’$I’$IRû”K.¯ð cÃÅ\Ì3ôìÙ“ž={Ò£GRSS[ù(ÕÚÚZ6mÚDii)¥¥¥¬[·ŽuëÖ±fÍÖ¬YÃÚµkY³f ëׯ'‹žžNnnnâý8qb¢àcðàÁôéÓ'࣒$I’$I’:®A âyžg<ã™Æ4f3›^¤E’thX"%/ B$I’$II©†nâ&îà~À¸ÛIʼnèRsäää““ÃW¾ò•}«««cýúõ¬^½šÏ>ûŒ5kÖ$:H¼óÎ;<õÔS¬_¿žÚÚÚÄsB¡P¢0¤gÏžôîÝ›^½z%ÖõéÓ‡ììlºwïNVVVâ–’’Òš‡tjkkÙºu+ååå”——³uëVÊÊÊX¿~=¥¥¥lÚ´‰ 6°qãÆÄýÒÒÒD¡@jj*}úô¡_¿~äåå1f̘Är¼È§OŸ>¾’$I’$IRv'ð$Or.çÒ~üŒŸI’ÔN„Ãa¢ÑhÐ1$ B$I’$II§”R.æbÞáæ2— ¹0èHR»“’’Bnn.¹¹¹n‹Åê lܸ‘ 6$îoذ÷Þ{/qÓ¦M î§k×®tïÞ½Á[VVÙÙÙ¤¥¥ÑµkW:uêD—.]‡ÃD":wîLFF™™™¤§§Ó­[7RSSÉÊÊ"jÝ«ãÅb1ÊËË©­­eÛ¶mTWW³cÇ*++ÙµkD£QvîÜIUUÛ·o§ººº^¡ÇÞ·òòrvìØÑàß/¼‰ß ˽zõJãÄ rZûõ$I’$I’tèÉ™ÜÇ}\Ã5ô£WreБ$Ií@$¡¬¬,è’‚!’$I’¤¤²„%œÇyt¦3oñ…Iê°B¡P¢ð )jjj…{A4V ±bÅŠÄv555õŠ,š+;;{Ÿuñâ’=Å»jì]</âØ{ÛòòòfgÙ³x%---Ñ)¥{÷îôéÓ‡!C†$Šaö.މ¯ËÊÊ"-Í_íI’$I’$IÑU\Å'|Â4¦‘K.§qZБ$II.‰PRRt IÁo%I’$IIc.sù&ßä+|…'x‚,²‚Ž$©ÒÒÒèÑ£=zôøÂûŠF£TTT°k×.*++Ù±cÕÕÕlݺ•ºººzW0ª©©aûöíûì#þÜ=ýþ÷¿à²Ë.«·>##ƒÎ;ï³®]»Ö+ÌÈÎÎ&%%…îÝ»“žžNfff⹑H„p8ü…Ž[’$I’$I’nçvÖ²– ¹¿ðŽá˜ #I’’X8&CÒA° D’$I’ÔæÅˆqwð~ÂÕ\Í=ÜC:éAÇ’ p8L8n°óDZtéR~øÃÒýJ’$I’$IÒ¡"ÄlfSB gqoñùäK’”¤"‘AÇtR‚ I’$IÒþD‰2…)ÜÄMÌd&÷s¿Å ’$I’$I’$©ÃK'?òGçpÎâ,Ê):’$)I…Ãa B¤$eAˆ$I’$©Í*¡„“9™y‘?ógþƒÿ:’$I’$I’$IR›Ñn<ÇslbpUTI’”„"‘Ñh4è’‚!’$I’¤6é]ÞåDN¤œr±ˆñŒ:’$I’$I’$IR›ÓŸþÌcïðÿοG’”„ì"%/ B$I’$ImΓ<ÉÆpG±˜ÅÅQAG’$I’$I’$Ij³Š(âqçaæNî :Ž$)ÉØ!DJ^„H’$I’ÚŒ1f0ƒK¸„˹œyÌ#‹¬ cI’$I’$I’$µy™È fð~Às<tIR‰D"v‘’TZÐ$I’$Iˆå ®àižæ7ü†iL :’$I’$I’$IRRùßãc>ær.çMÞd#‚Ž$IJáp˜ÊÊJêêêHI±ß€”Lü‰•$I’$®”RÆ3žWx…?óg‹A$I’$I’$I’Ò=ÜÃHFr.ç² AÇ‘$%H$@4 8‰¤æ² D’$I’¨Oø„“8‰õ¬g‹Ǹ #I’$I’$I’$%­tÒyЧH' ¸€J*ƒŽ$Ijã,‘’WZÐ$I’$io={ödÖ¬YAÇP+(î]̯Oÿ5‡o;œkÿ|-©ø á/AÇ’Ôøÿ!Iû5 ˜?>ÅååAG‘¤6oÛ¶m\|ñÅAÇ$I’:œÃ8Œçyž9‘k¸†ßñ» #I’Ú°p8 @EEEÀI$5—!’$I’ÚœóÏ??èjOñ7pgp…£óäÎAG’$æÏŸÀ´iÓN"©MûÖ·˜0a\tQÐI$I’$I’uG1‡9œÍÙ e(?æÇAG’$µQñ!„HÉ'%è’$I’¤Žg&3¹˜‹¹†kxЧèŒÅ ’$I’$I’$I‡ÚiœÆÿò¿ÜÄM<Ã3AÇ‘$µQñ‚h4pIÍe‡I’$IR«©¡†ë¸Žx€»¸‹ë¸.èH’$I’$I’$IíÚu\Ç2–1…)¼ÍÛ ehБ$ImL8ì"%# B$I’$I­b;¸˜‹y×yš§™ÈÄ #I’$I’$I’$u¿âW,g9çqoó6YdI’Ô†Ä;„X"%Ÿ” H’$I’Ú¿5¬a4£ùÿàu^·D’$I’$I’$©¥“Î\沓\Â%ÔRt$IR/‰F£'‘Ô\„H’$I’ZÔ2–1šÑĈñoQDQБ$I’$I’$I’:œ>ôá)žb! ¹•[ƒŽ#IjCÂá0`‡)Y"I’$Ij1oñcKy,d!I’$I’$I’$©ÃÅ(f2“ÿæ¿yЧ‚Ž#Ij#222HII± DJB„H’$I’Zij<ËxÆ3†1¼ÆkÎáAG’$I’$I’$Iêð¾Å·¸š«™ÊT–±,è8’¤6"FƒŽ!©™,‘$I’$rñr!—r)Oñ"AG’$I’$I’$IÒçîå^ŽåX.à¶²5è8’¤6 ‰Ø!DJBiA$I-'z’.Iju3Ã3¹5r+?ˆþ€Vüíl:’¤v¦{÷$ÿuN<_—’W6°sçNªÊÊ‚Ž"I’¤fŠD"„Ãá cH’¸tÒù`$#™ÂžáB„‚Ž%I P8ö»+) Y"IR;vß}÷Ñ·oß cH’:ˆºP³›Í«^嚥×PT\Ä|æKR;S\\Ì©§žÊqÇt”/Ìóu)yM>øàÖdfE’$IÍôÙgŸqà 7C’¤6¡/}y’'Ïxf0ƒñ£ #I’‰DˆF£AÇÔL„H’ÔŽuîÜ™I“&C’ÔD‰r9—³ˆE<˳œStJR{´téRb±XÐ1 Ï×¥ä6jÔ(Fù3,I’”tfÍštI’Ú”“8‰éLçû|Ÿ"Š8•SƒŽ$I H$±Cˆ”„,‘$I’$}!›ÙÌD&òñ*¯2ŠQAG’$I’$I’$IR}‡ïð7þÆd&ówþN.¹AG’$ Û!DJB)A$I’$%¯ÏøŒS8…Jx“7-‘$I’$I’$IJ2!B<ÄCd“Í…\H5ÕAG’$À!Rr² D’$I’tP>åSNáê¨ã/ü…£8*èH’$I’$I’$I:]éÊ\æòïñS~tIRÂá°!R² D’$I’ÔlËXÆIœD6Ù¼Îëä“t$I’$I’$I’$}ÃÎ,fqwð4OG’ÔÊ"‘Ñh4蒚ɂI’$IíJ(" ã r´•ìâèÍGs$Gò*¯Ò“žAG’$I’ÔmeÌÑ®ÇKI’S’$I’s—1õóÿŠ):Ž$©E";„HIÈ‚I’$Ij%íabЋ¼¯‹àOü‰ntk±¿«=¼^’$I’šÆóÿæñõ’$I’Ô’îá2 ¸€ œ,IE8¶Cˆ”„,‘$I’$5Éøçqüÿ¯C˜pБ$I’$I’$I’tˆ… 3‡9¬d%ßá;AÇ‘$µ;„HÉÉ‚I’$IÒý†ßp9—óm¾ W5A'’$I’$I’$IRKÄ åQf1‹ßòÛ ãH’Z!Rr² D’$Ij£B¡¡Pˆ]»vñ­o}‹=zеkWÎ?ÿ|V®Ušp! IDAT\Ùà¶\wÝuôîÝ›´´´ÄãUUU̘1ƒc=–Î;Ó¹sgŽ=öX~þóŸS]]]o_555Ìœ9“‘#G’™™IçÎ;v,Ï?ÿ|½í{ì1ÆŽKVV 4ˆüàlݺµÞ¾~ýë_sâ‰'Ò«W/:uêDnn.^x!óçÏoò6£¶¶–Ûn»þýû“‘‘ÁСCyøá‡ܶ)Çðæ›o2yòdòòòHOOçðÃç´ÓNãå—_>`žP(ToyÏ[s³¿öÚk„B!rrrüeLee%yyy„B!^}õÕfÛSCïÿ€Ù¸–k¹Û™ÉLˆ5|| KC5å=oêëÕÔ÷î@?#’$I:´ÏÜx¦9çÕ{®kÎø/ÊñRCŸ¯†ŽÏñ’$I’¤d4‘‰ÜÈ\˵,cYÐq$I-,FƒŽ!©¹b’¤f!FlNlNÐ1ÔN±9sZîótÿý÷·Ø¾%µyŸ}ÄoãÆ‹UUUÅb±X¬ªª*vÚi§5ºm,‹ÕÕÕžño4ºMaaalëÖ­±X,›:uj£ÛÅ÷×”mæukl¿sçÎMlÛœcÙsß{ßB¡Pì™gži0ÇžÛÐ6MÉ>zôèûÅ/~±Ïkp÷ÝwÇ€ØñÇ߬׮Á÷1j‰ñ­}sîïx÷÷XSÞó=~°ï]C?#’’Ë’%KbK–,i±ýOš4)6iÒ¤Ûÿž<_—’Äb-8žoÏ|±ñLSk΢¹Û;^ª¯)Ÿ¯†Ž­±u=æxI’Z^SÇb-=þ”¤V3iÒî›tªbU±1±1±¡±¡±±AÇ‘$µ ÿþïÿŽyä‘AÇ’Jkþþžoj‡I’$©[ºt) .dûöí,X°€‚‚Ö®]ËŒ3öÙö­·Þâ…^`ûöíìÀ]wÝÅo¼AVV=ô7ndãÆÌž=›nݺ±`Áîºë.î¾ûn^~ùe233™9s&kÖ¬!ò׿þ•óÎ;€Ù³góøãÓ·o_æÎKii)»víâ¯ý+Ç<Ë–-ãþçxâ‰'¸ùæ›Y»v-UUUlÞ¼™çŸžÓN;­ÉÛŒW^y…矞­[·²zõjÎ=÷ÜÄ1Æ5çXÎ<óLþô§?Q^^NUUk׮嗿ü%±XŒÛo¿}¿yâïG|yÏÛÁdÿéO ÀwÜÁÎ;ë+++™>}:?þñ›üzÅ÷ÿïšyWo¿šÔ¤rSñMœ·á¼fíkšòžèõjî{×Ðψ$I’ZŽã™ÖÑ”1Ds·w¼T_S>_‡‚ã%I’$ImM:é<Á”RÊu\tIR ‡Ã vÝ•ÔÆµX Š$µSØ!D‡Tl*^qXJn|~EÎçž{®Þú§Ÿ~:ĆºÏ¶üã÷Ùψ#b@ìá‡Þç±|0ÄŽ=öØX,‹sÌ11 6kÖ¬Fs}ùË_ޱ7ÞxcŸÇ>ùä“}zb]üj·C‡ÕÕÕ5úw5$þþß÷À}±©±©±N±N±§bO5š³©Ç¶÷cM}Ï÷·Ïæ¾wûû‘”\ì"©MÀ!âxæà4ç¼zÏuMC8^úâã¥ý}¾;ÇK’Ô¶Ø!DR‡c‡"/Ä^ˆ…b¡Øoc¿ :Š$©…ÜsÏ=±ž={CJ*m¡CHèó%IM"ÄæpEí@(bΜ9\tQË|žf͚ŴiÓZdß’Z^( ¬¬Œ¬¬¬Äú²²2;ì°zWfˆo»e˲³³ëí'‰F)--¥Gõ+--¥W¯^D"víÚEçΩ¨¨`ãÆôìÙ³Á\]ºta×®]¤¦¦ÿº2il«‘¦§§SUUÅ_ÿúW.»ì2V®\Yogœq¿ýíoéÕ«W“¶iŽøkQQQA8N¬Åb¤¤¤ÔËÜœc™3g—^zé~¯’ºçcñZw°Ùž}öYÎ;ï<?üpV®\IZZGq%%%<òÈ#\qÅfmHçΩ¨ªà¢ñløYæ0‡¯ñµFs6õØö~¬©ïùþöÙœ÷nÏ}5ô3")¹,]º€¢¢¢ÙüÜ|îܹ-²ÿ=y¾.%±PæÌÏ·Žg¾Øx¦)çÕ{®kêÂñÒ/àóu0ÇæxI’Z_SÇb-=þ”¤V»¶Âï»Ôþ}—ïr?÷óï0”¡AÇ‘$b=ô×_=Û·o:Š”4Zó÷Í7Miñ¿Y’$IR«i‰ñÉ#µµµÔÖÖRWWG]]]½I(ÕÕÕŒ=š?þ˜E‹qÇwpá…’™™ÉK/½Äõ×_ßämÆž„à_\öXn½õVb±ÿöoÿÆÿýßÿQQQA,cÇŽñ‹d8÷Üs1b›7ofæÌ™Ìš5‹’’ øÆ7¾Ñü¿¸0æeÌcó,iLc“‘j'{(Þóæ¼w{rr“$IRÛåx¦yçÕ{jê¢9Û;^:t/I’$Ij/¦3Œà".¢‚ýU%IÉgÏ ùHJ„H’$ImÜ_þò—z÷_ýu Фçy䑼øâ‹û<6oÞ<† RïÏgŸ}¶Ñý°xñâÄÕEºÅ¥¥¥1zôh¾ÿýïóä“OòÞ{ïðòË/7k›–Мcùä“O˜>}:GydbÒ«¯¾Úä¿/> ¨¦¦æä…BÜtÓMÜyçLŸ>€ï}ï{¤§§7k_»ØEÚ‹iðUøÏyÿÉ&4ëù½{÷Øç*¶{~ãšòžïïõjîçP’$IÁp<Ó<Í=¯nIŽ—êkÊç«1Ž—$I’$µé¤óOPB ßá;AÇ‘$b‘H„ÚÚÚ/&"©í² D’$Ijã®»î:^ýuvìØÁÂ… W8qb“ž¿òé 7ÜÀï~÷;6mÚĦM›xä‘G¸ñÆëm3yòdn¼ñFî½÷^>ûì3*++yë­·8ÿüóø÷ÿw`÷ÕV|ðAV®\IEE•••|ôÑG<ðÀŒ=€“O>™Ç{Œµk×RSSCii)O=õÑh´ÉÛ´”æK~~>wÜq[¶l¡¼¼œ'Ÿ|’iÓ¦5ùïëÙ³'O>ùä!;¶¯ýë :”²²2Ö­[GÏž=¹úê«›µìä\Î¥æ¸8 î½ìÞFßÿÆ 6 Øý9[³f Û·oç…^h0KSßóý½^Íyï$I’Ç3ÍÓœóê–æx©¾¦|¾ãxI’$IR{R@ð³˜Åïù}Ðq$I‡P$Z~®†¤C,&IjbĿĿCí›3§å>O÷ß‹í[RËb@ìüóÏO,Çoyyy±Í›7ï³mC¢Ñhì+_ùÊ>ûˆßÆŽ«¬¬ŒÅb±XUUUì«_ýj£ÛÆ]wÝun³ç¶û{|Ú´iMÞæ`^·¦>ÖÔc¹ãŽ;|lÊ”) î·¡uW^yå~_׿f{ôÑGßvÛmûöR+ŠŠõ‰õ‰½[ýn“Þÿ†²¼øâ‹ >gêÔ©>ÿ@ïù^¯¦¾wzý$%—%K–Ä–,YÒbûŸ4iRlÒ¤I-¶ÿ=y¾.%1ˆÅZp<ß8ž9¸ñLsΫôÚŠí;úxiOMý|9^’¤¶¯©c±–JR«™4i÷M:Ä®]ËŒeÆþûgÐQ$I‡È‚ b@lÆ AG‘’Fkþþžoj‡I’$©{ôÑG¹êª«ÈÎΦK—.|ík_ã7Þà°ÃkÒó322xå•W˜>}:#FŒ ‰D1b3fÌàÏþ3:u ==?ýéOüüç?OlÛ¥KÆŽËsÏ=—ØçÝwßÍüùó¹ð éÛ·/éééD" ¹ñÆy÷ÝwøÛßþÆUW]Å€HOO§GŒ=šû￟_ÿú×MÞ¦%5õXn¼ñF~þóŸ3xð`2228p ·Ür ³gÏnòßu×]wñío›~ýú‘žž~ÈŽaðàÁtíÚ•k¯½¶ÉÏÛÊVNçtÖ°†×ycÒŽiÒûß3Ï<“‡zˆ£Ž:Šôôtòóó¹å–[˜5kÖ>Û6õ=?ÐëÕÔ÷N’$IÁq<Ó<Í9¯n y¼´·¦~¾âxI’$IR{t'wr$GrÅ+ÉKR{ïRQQpIÍú¼ZD’ÔD!BÌaqQÐQÔ„B!æÌ™ÃEµÌçiÖ¬YL›6­Eö-©å…B!wîÜÙÿž<_—’X(sæ@ çÛÇ3j+f¼$Ijßš:kéñ§$µšøØµ~ߥŽg+(¢ˆ+¹’™Ì :Ž$é zÿý÷9æ˜cøç?ÿÉ!C‚Ž#%…ÖüýAcóMí"I’$IIlåÊ•¼ð Àî‰NMa1ˆ$I’¤Žà`ÆK’$I’¤¦Ä à~ůxš§ƒŽ#Iú‚Âá0`‡)Ù¤@’$I’ö'~eáéˆWÞóµ9묳6lØ~· ;ðg /0†­ØýœŽøúI’$I-ÍñLpj¼ÔßI’$IjÜE\Ä+¼ÂU\EEPt$IÒAŠD"€!R²±Cˆ$I’$%±p8ÌgœÁâ#ê¨K¬«¦šläðÏÿ“$IÒ!tÄ0bDÓijjà’KZ?“$I’$I™2RS÷]ŸžW\ÑúyÔ¡Mf2—qWqØtIÒ„Ãa;„HIÆ‚I’$IJ2Ó™N*û~‘SC ò!'s2›Ù@2I’¤v¬¡É4¡Á€Ád’$I’$©!—^ µµû®¯©I“Z?:¼{¹—L2ù&ß$F,è8’¤ý°Cˆ”|,‘$I’¤$²„%,bµ4ðE»;…,g9§p ¥”¶r:I’¤vìÒK¡®®þºÔÔÝ…"’$I’$µ%ùù0j¤ì15,%e÷ºþý‹¥Ž«Ýx‚'˜Ï|~ͯƒŽ#IÚ B¤äcAˆ$I’$%‘Ì ôýnSGËXÆ‹¼ØJ©$I’:€œ3¦þdšº:¸è¢à2I’$I’Ԙɓww¶ŒKIñ¢ ÔñÏù1ßã{|ȇAÇ‘$5"FƒŽ!©,‘$I’¤$ñ)ŸòGþH5Õ >žF_âK<Çs\Á­O’$©ý›<ù_Ë©©0v,ôîXI’$I’5iRýû±\pA0Y¤Ïý”Ÿr,Ç2…)TQtIRì"% B$I’$)Iü/ÿK*©û¬¯;†cxŽçx‡w˜ÈÄÖŽ'I’Ôþ]xáîB¸= D$I’$IjKzô€ vcSSw/÷êt*upi¤ñ±‚ÜÌÍAÇ‘$5À‚)ùX"I’$II` [xëuIù|Hw$G2—¹‚H’$µ´ìl8ýt… %Î??èD’$I’$5î²Ëvw‰Åv/KmÀÁÜÉ f°€AÇ‘$í%FƒŽ!©Ò‚ I’$IíI]]7n¤´´€ž={Ò«W/RR¾X=þ½ÜK 5ÀîB1ŽäHnåV.äBB„¾pvI’$XùÙg“5o»Æ#-¡SÐ$I’$Ií^MM Û·o ¬¬¬ÁmêêêØºuk½u)pLZ!àÝþý©[º´ÞãÝ»woðû‹P(DVV]»v%-Í)f:´®á^æe¦0…÷yŸl²ƒŽ$IúœB¤äãÙº$I’$@ee%¥¥¥¬_¿ž 6PZZʆ X¿~ý>Ë¥¥¥ÔÕÕÕ{~JJ ={ö¤gÏžôéÓ‡Þ½{ï³Ü»woúôéCÏž=ÉÈȨ÷ü(Qîâ.j¨!Dˆ! ávnç<γD’$éÚ±c«V­bõêÕ¬]»–5kÖ°jÕ*Ö¬Y“¸¥VVR L}ùeþ“““CAAùùùäååѯ_? ÈËË#??Ÿ>}ú}X’$I’¤V°mÛ6¶lÙByy9;wîdçÎlݺ•íÛ·'î———³cÇvìØ‘¸_UUÅÎ;‰Åb”——'öU[[KEEžB÷#ŸÿyåÉ'¡ý„Ãa"‘iiitíÚ€¬¬,B¡]ºt¡S§Ndee‘™™I—.]ÈÌÌ$++‹.]ºÐ¥KºvíJ÷îÝë=vØa‡%ö¥Žç7ü†Œà?ùOåÑ ãH’>‡-‘’Œ!’$I’:¤;wÖ+ðØßrü ˜¸Î;×+à0`'žxâ>…@£û...N,ïÚµ«Þþ³²²ûèÝ»7ÛÎÞÆ–+·Ð{ko¦|4…s«Ï%§w»úì¢K—.­öšI’$%³êêj>û쳋<â÷÷<ïëÚµ+0xð`ÆŸ¸¿5'‡ÿ_y9ßÜëùo¾ù&øÃX¿~}b?äçç'nýúõK,Ç÷—™™ÄK"I’$IjĦM›Ø°a7ndýúõlٲ倷ššš÷ÕµkWºtéBçÎÉÎÎNHdff2`ÀÒÓÓ-°ÈÈÈ sçΤ¤¤Ð½{w ñ®ݺu#55µþÊX €OBõ/0U[[˶mÛÜÏžmݺ•ºº:víÚEeeeƒ,Û·o§ººšòòr6lØ(€)++cçÎìÚµ+ÑádoééévØa‰[vvv½ûñ[ü"[½zõ¢G îKÉ¥=x„G8ƒ38‹³¸”KƒŽ$Ibw‡/Z*©uY"I’$©Ý¨¨¨`ݺu”””PVVÖèrIIÉ>Eáp˜ììlrssÉÉÉ¡ÿþŒ=šœœœzëãËM5tèÐ&åÞ_Þ/l$ÿ|jž­áÎuwòóºŸï“»¡Œ{/÷éÓ§Ñ/‰$I’’]ü©w>‡üûã÷óóóIOO?4/”$I’$uPuuu¬[·ŽU«V±víÚÄE›Ö­[—(üX¿~=7n¤ºº:ñ¼ÔÔÔ Ž8âˆ×ïYô/âÐn[·n­W,ÒXqÍæÍ›ùøãë­‹é:uêDÏž=ÉÉÉI‰äææÒ«W/úôéC^^ýû÷÷{‘$p§ñïŸÿ7†1Pt$Iêð"‘ˆB¤$cAˆ$I’¤6->±oëÖ­cíÚµTUUÕ{n|b]¼0¢°°°Á‚‰‚‚‚@[’G""‘¹¹¹5¾á½PYYÉæÍ›÷ûz,_¾œ’’JKK÷¹"Yvvö~‹FâËN:”$ImI4¥¤¤dŸ"øòG}TïJ£ñsžxQÅ„ êÝïׯ߾WL=„:uêDnnî~ÏïâÅÌ ¯¼ù曬\¹²^'¹ììì‹Eâ÷û÷ïï$I’$IZEEÅÅŬ^½šÕ«W³fÍV¯^ͪU«X½z5Ÿ}öY¢Ð#%%…^½zÑ«W¯DQÁСCÉÍÍMÄ;y÷êÕ+à#k?ºwï~ÐE27ndãÆ‰"žÒÒRJJJذa%%%,Y²„ÒÒR6nÜH]]°û‚yyy‰ŽñNñ®$‰ÊCÔAø¿à Þàr.g H¥å~g#I:°p8lAˆ”d,‘$I’ÔªšRÐ_Þ» ¡S§N~øáõ:b 8°Á‚†‚‚ÒÒÚß'###1¹°°°ð€Û¨ féÒ¥”••±fÍš}Úµï]PÓØr~~>ݺuk©C–$I@YYYƒ…ñûëׯ'‹õ»iäääPXXÈ´iÓ…Gyd Å¾M‰DE‰¿.{¿&Ë–-cþüù¬\¹21É%##ƒ¾}û6Z02hÐ ¯L+I’$)éUUU±víÚÄØhùò剱Ҟc¤=ÇŽGuÔ>!ÛëwíY¼€çè£>à¶ýžá¥—^JÜ‹_€aذa&>#C† !33³%IŸ æ·ü–QŒâ—ü’ïñ½ #IR‡‰D¨¬¬$‹ …‚Ž#© ÙH’$IúÂ***šTàQVVƆ _ÈÀî/eö.0h¬“‡­½›/^<ÓzãW«Ž¯ßSCïccË999þâH’¤$^ ºwWøÄŒÕ«W×+ŽOÄ8p cƌ٧¸¡#KdggSTTÔh—‘ÊÊJ>ûì³_ßùóç³bÅ ¶nÝšØ~Ï Q uqB”$I’¤¶bûöí,[¶Œ>ø€>ø€eË–ññdzvíÚÄäÄüü| Ä Aƒ8ýôÓË^Ĩƒ;ÐxzÛ¶m¬^½š+V°bÅ >þøcV¬XÁÂ… ë}Æòòò{ÅÅÅÌ›7oŸ ‰°ïg¯±åüü|ÒÓÓ[ê%Iêâ]Âö.$ˆ/úé§Äb1`wqBß¾}…&L`Ú´i‰ûƒö ­ˆD",iì}^ºt)óæÍcåÊ•‰ß½ß罋G|Ÿ%I’$íOII K—.MÜþú׿²eËÒÒÒ(((`ذaL™2…ÂÂB† ÆÐ¡Cd¯V‰D>|8ÇïÛ¡"þù]¾|9Ë–-ãÏþ3wÞy'•••dffrÌ1Ç$:“5éûýK )<Â#Ã1ÜÊ­ÜÆmAG’¤)‰» B¼À§”,‘$I’’Xee%›7o>`GII 7n¤¶¶6ñÜŒŒ ;ì°zëØà„û^½zy¥f}!ñŽ Mq î4K—.¥¬¬Œ5kÖ°}ûözÏÝ»;McËyyytïÞ½%U’¤¤±¿ÎÅÅŬ^½ššššÄö{vŽ(**²sD;’«¾{i IDAT˜¬Òýu‚™?þ;Áì]øàÞyç/^Ìüùó¹çž{¨««#77—N8Q£F1vìXŠŠŠÀpwp-×r:§ó¾t$Iêpâ!Ñh4à$’šÊ3LI’$©©¨¨hRGYYëׯO\‘vO†ß{â{aaaƒâûôéãUµÔ&5å*Öqúy)..æÍ7ßL¬ßSC?/-çää8ÁU’”Tö7ݺu¬X±‚­[·&¶UÆÿúˆíÛ·×Û_CÝEâ÷ûõëg¡®$I’ÔÖ®]Ë3Ï<Ã3Ï<Ã믿Njj*ãÆã¿þë¿7n#FŒp*íeèС :”k¯½¶^7W^y…iÓ¦Q[[ËØ±c9ï¼ó8ï¼óèÛ·oБ•K.÷p—qgs6grfБ$©Ãˆw‰F£'‘ÔT„H’$IŸkJ÷€øòÞÝ2228ì°ÃêMø8p`ƒÁ{õêEZš§âR[¿ÊuS¨ ÐÒ¥K)++cíÚµlÛ¶­Þs÷îÔØr^^žWo—¤v ªªŠM›65ZðñÉ'ŸP^^žØ~Ï. ñNq{Þ·¸PíIFFFâ³Ý˜xÑîÞ?CÅÅÅÌŸ?ŸÕ«WSSS@zz:=zôh°ÓÈž÷%I’$XYY¿ûÝïøýïÏ’%KÈÌÌ䬳Îâ±ÇãÌ3Ϥ[·nAG”’FJJ Ç{,Ç{,7Þx#Û¶mãÅ_äé§ŸæÇ?þ1×]w'œp—]v“'O&+++èȸ”Ky–g¹†kxŸ÷9 ;¨HRk°Cˆ”|œ…&I’¤v­¢¢¢Ieee¬_¿žX,–xn8ÞgBvaaaƒµsrr…B©¤ E"‘N^Œ;пKÅÅżùæ›Mþw©±eÿ]’¤`Äÿ-ohÂúºuëX¹r%uuutêÔ‰¼¼¼Ä¤ô &0yòäÄ„õAƒY (í%^´ÛXÇ·êêjJKK?{þ,.Z´ˆ’’’zçX{]5T0Ò¯_?233[ó%I’¤6eÑ¢EÜÿý<ù䓤§§3iÒ$n¾ùf¾úÕ¯’‘‘t<©]èÖ­—\r —\r Ñh”ùóçóÇ?þ‘Ÿüä'üð‡?dÒ¤IL›61cƵÕÝÇ} g8×s=òhÐq$©C° DJ>„H’$)éèJüñå¦\‰?~•e¯Ä/©µD""‘¹¹¹íwÛ¦t.Z¾|y“;5¶lç"Ijšøyhc«V­bçΉíãç›9992qâÄz“Îû÷ïOJJJ€G$µ?éééäææî÷\+&ºŠìý³¼téRV¯^ÍŽ;ÛÇ‹mê.2pà@úõëGjjjk¢$I’Ôâ¢Ñ(?ü0÷Þ{/Ë–-£¨¨ˆ™3gr饗ҵk× ãIíZ8æœsÎáœsÎá®»îâñÇgÖ¬YœtÒIríµ×2uêÔS•E³™ÍœÁD&rI’Ú½ŒŒ B¡Ñh4è(’šÈÙ’$IjÊÊÊXà±nÝ:Ö¬YCuuu½çÆ'çÄ'7ýìÝy|SUÞ?ðOº¤I÷”.Ié%hÊ"-`a) ‹30Q´2¢¢0Zü3 ó<*ó(Ê8ŽâÌð8²ª£)*ÐÊb[e tO›Òt¥{ïï&yš6iRhzÓöóæÕ7É]¾'7¹9÷Þó=Gc±Ñsxx8ÜÝÝE*!QדØBCC­ö‚Ý–­c©V«ÅÁƒí:–Z› ƒT*uT‘‰ˆDÓØØˆ²²2‹£z ÓüíGÐh4f Å£££áéé)b‰ˆÈ™Lfst7c½ªý1A«Õ"-- ¹¹¹¦d\©TŠXM1N9»ÆÆFüíoßÿüg $''cË–-6;¶!"ÇðõõŲe˰lÙ2œ:u ›6mÂÊ•+ñ_ÿõ_HMMŲeËúÅõú©˜ŠÇð–c9îÁ=PB)vHDD}šD"‡‡G!êE˜BDDDDaO¯öÆi{zµ‰‰±Ø@9$$„=±ý‡B¡€B¡°k^[£-effÂ`0 °°•••f˶mÉÚôÀáïïu™Á`èäÑvº³ÆÝ‰‰‰f» Äã9Ì”)SpèÐ!±Ãè÷Œõ*kI¹MMMÐëõ“ÈÒÒÒLÓFí“ÈÚ'DEEÁËË«§ŠGDDDDÔÁ¾}û°jÕ*ã·¿ý-~÷»ßA©d£kgs«çŒ]]¾»ÏQyÎ{óFÑ£G㥗^Â믿ŽÕ«Wã¯ý+Þxã $%%‰žÃ½7†4¤ û±_ìpˆˆú<¹\ÎBˆz&„‘ÝêêêìJð0N·%“É:4V«Õ«T*H$‘Ji]]]T*Fމ£GöÈ6Û¾‚ Øœ×Ò<ÆuØZžº_vv6žzê)œ:u 555¬ï±÷Ÿ˜Ÿ“žÞ¶£·gïú¿úê+¼ýöÛ8zô(ZZZ‹'Ÿ|=ôPår¹Íž±lÿµZ-ÒÓÓa0 ÓéÌÊnéøomZ©TÂÅÅÅ‘Å&¢>ª¾¾ÞÔs¿¥ýóòòL¿ÅÀ†ÞÆFØ–>"##EO:fݳsÎXwé®÷¤µµõ#융8Åv+ûTŒz•»»»i$7k½%wvlÊÌÌDnn.jkkMó·=6YmÄŽMDDDDÔ÷\»v O>ù$vî܉E‹áÕW_ExxxmßRÝßßC‡ÅÊ•+ñÀôX,]Õ•{ÝåVÏ--ßÙyâÍnÏÚ9ž£Ïy퉡· ÅúõëñÌ3Ï`õêÕ˜={6-Z„¿üå/;<‡ñ†7>ÂG˜ˆ‰Ø‚-x‰QŸ&—Ë9BQ/„"""¢~ÎVñÆéüü|TWW›-Û¾‡ø˜˜ÄÇÇwhà???‘JØ}>þøcŒ;çÎÃÅ‹ëðm ‚pË ¶Œë ž·dÉ<ÿüóøüóÏqìØ1L™2¥Ëëè©ý'æç¤§·íèíÙ»þ{ï½#FŒÀ_|áÇãâÅ‹X¾|9òóóñ‡?üÁañÝ ¹\¹\ÞiƒG£††\»v­Óß”œœ»Gˆ²6 77^Ö ê:ë…¿í´Qû^ø5M¯ì…ŸuOçØNwl»«ó>|øfÂêõneŸ:k½J&“ÙLÀµ6z‘V«EZZZ§£µOáèEDDDDÔU.\@RRªªª°oß>QFh.ÚÜÜŒ’’>|O=õJJJðÔSOõx\öèŽ{]u«çŒ]]þf·gí¯'Ïyûúý±ˆˆlß¾=ô}ôQŒ=À°aÃÄÍa&`žÆÓX˜ŒÉGÏ%¯õ72™Œ !D½ˆDèkiÐDD&;±Ày{"¡ÞC"‘`çÎëÙfÓ¦MHIIqȺɹ »FðÈÏÏGSS“Ù²Æ:l5Æ ‡»»»H%Çĉ±bÅ |÷ÝwpuuÅ«¯¾Ú#Ûµ·Q^gó9k/À}»»;êëëíêÅ×öŸ˜Ÿ“žÞ¶£·gÏú% Ξ=‹#F˜ž»|ù2âãã¡Ó雳±÷7«  fËÚû›©T*R ©»eff€Í䤛e¬›ïÚµË!ëo‹õõŒÇk mC»»»#00Ðjchãt_Àº§mÎVwq†÷ÄÎK{7[_®W566¢¬¬Ìê1òòå˨¨¨0Íß6)ÎÒ12::žžž"–ˆˆˆH|öž‹9úü“Hl§OŸÆäÉ“qÇwà“O>A`` h±X;HKKCJJ ´Z­QÙÖ•{ÎÌQç‰Îpþé 1ô„ÒÒRÌ;ÙÙÙ8räFŽ)vHs×q'îD$"ñ¾€}7釈HLÇǼyóðÒK/‰ ‘ÓëÉëÖÚ›º8|ËDDDDtËêëëQTT„ììl¤¥¥aë֭ذaV¯^Å‹#)) qqq …««+0|øpL˜0‹/ƺuë°{÷nhµZ( h4¤¤¤`óæÍØ¿?222PXXˆææf”——#;;éé騵k6lØ€µk×"%%IIIHHH@LLL¿K¹té~þùgÌœ9<ò¶lÙ‚æææóI$H$üôÓO7n<==1aÂ\ºt ÙÙÙ;v,¼½½1iÒ$äååu[|ÆŽŒÛ—H$xôÑGÍæÉÏÏÇìÙ³áããƒ<øàƒ¸víZ‡ueeeá¾ûîƒ|||0mÚ4deeÙGVVf̘aZvÆŒ¦e9bŸQMM |||PTTdzÎÍÍÍ4ß‘#GÌÊÖå0®óòå˘;w. E‡øºZζënnn6•eõêÕV×ç,ûÏZYíYßþýû™L†¨¨(<ûì³F€ììl̘1ÞÞÞðóóÃý÷ßoõ»àÈ}hm{¶ö+œ={S§N…——|}}1}út|ñÅV×;xð`SL ¦çA0k´ @CCC—cïÍ Ôj5°`Á¬X±k׮ůqàÀ¤§§ãòåËhhhÀõë×qùòe;v û÷ïÇk¯½† ˜.¬dffb÷îÝHMMŬY³0a 4Ëå4h””„Çk׮ņ °{÷n¤§§#;;ƒAäw„¨o©¯¯7õr¿uëV¬[·?þ¸©þêããcª»N™2«W¯ÆP\\Œ˜˜¤¤¤àƒ>À¡C‡pùòeÔÕÕ¡¨¨صk^{í5¬X±Ât,è+É ¬{Ú_wéîíØ[§i¯»Þkõ{⯬¬Ä³Ï>‹˜˜Èd2¨T*,Y²'OžìRœ–âéÊûlO}ª»ë„}¹^%•JM£´-X°©©©Ø°avíÚ…ŒŒ  SéСCذa’““­V‹Ý»wãÙgŸÅ”)S0|øpxyy! qqq¦:‘ñšAzz:´Z-Z[[Å.69Xqq1¦M›†Ñ£GãСC¢&ƒtf̘1f÷Œì=w³çZîÍž—vvÀÞ{]½¶méõ®œ·Y[¾íkmÏ­Ås+çÎÖâoÿ'“ÉLót×}k×êíÙÆÕ«WÍâ»téjjjÌž»zõj—cê.ÁÁÁ8|ø04 ¦M›†’’Ñbq4Oxâ#|„¯ð>ćb‡CDÔgÉårŽBÔ›DDÔ% ìvŠõ„;÷yÚ¸q£ÃÖM·îúõëBaa¡‘‘!ìß¿_زe‹ðÚk¯ O?ý´œœ,$&& Æ T*•ÀìO&“ *•JÐh4ÂÌ™3…ääd!55Uxë­·„-[¶‡²²²„ÂÂB¡µµUì¢ö «W¯~÷»ß™ÇÇÇ ûöí³8/árrr„ŠŠ aùòåÂØ±c…yóæ™žûío+ÌŸ?ß®mÛ[mïl>ÂÔ©S…Ï?ÿ\¨®®òòò„ K–,1›ïâÅ‹‚R©Þ}÷]¡´´T(++¶mÛ&ÄÄÄùùùnÿâÅ‹BXX˜ðþûï %%%BII‰ðÞ{ï .^¼(‚ ddd¾¾¾Bss³i¹>úH8p ðæ›oš­ï¶Ûn222l–ãþûï¿¥r¦L™"?~\¸~ýºðÙgŸuú^ÚSζ붗ØûÏR ]}7nÜ(ÔÖÖ ÅÅÅÂâÅ‹…‡zÈlžK—. ááá¦÷N¯× Û¶mî¾ûî[ÞvWö¡µ²Ú³_ÏŸ?/ :T8tèP[[+dgg &Lè°¾¶322„¨¨(áܹsÆ$‚°råJá¾ûî³9ÙÖ•ßY‰DrÓ¿³---bµßÉÈÈèðûÐ,X ,X°Àaëo«/Ô×ËËË…ŒŒ a×®]Â[o½%¤¦¦ ,4M‡ï—L&bbb„ÄÄDÓ÷jãÆ¦ïTuuµØÅq¬{Ú_wéîíØS§±VþîxO,­ÇÞøgÏž-¬]»VÐétB}}½ðÃ?ñññÖSìÑ•:¸=õ)GÕ Ûc½Êœñx½ÿ~aãÆ¦ãu||¼#¸¸¸˜Ž×R©Tˆ‰‰âãã… ˜êA»ví222„ŠŠ ±‹CDDtKì=sôù'‘˜æÏŸ/ <ØiÎÅ­§:tH:t¨Åùm»Ùs-·»¯©×Ù•{·zmÛøœ=çm-ßÕíu×¹sûÇ ÂèÑ£…wß}W„îÝGÖ®ÕÛ»sçÎ £G6[¶µµU|·Ývà£>ž={°oß¾óGµøÅ/~(**ÂÀÍž+((€F£±«‡{‡³îl>‰D‚½{÷böìÙ¦ç®\¹‚„„šž{ðÁ1bÄ<ÿüófËðÁ8{ö,6lØ`uû>ø F+V˜=¿~ýzœ>}Û¶mƒ P©TØ»w/ÆHLLÄêÕ«±fÍ?~À^±P\\lÖ[”¥rœ?“'O¾érH$|ýõט8q¢Õ²uµœm×mï)—ØûÏR ·²>ƒÁ€Áƒ›õ@–œœŒ¸¸¸ïÝG}„‡~ø¦·ÝÕ}h­¬öì×E‹aúôéHNN6ÍsþüyÜvÛmfë3®?''÷Ýwvî܉1cÆXŒ%//GÅÛo¿+W®àûï¿Ç Aƒì. ݺ††\»vÍæïwQQôz}‡^ú …ÍßïÐÐPDDDÀÍÍM¤RöŽr×X7ßµk—CÖß–³××uÛââbhµZhµZ³Çyyyfß…B˜˜ÄÄÄ@¥R!44Ôì±J¥º©œúÖ=»Vwqôv,ÕiŒÛn_þîxO,­ÇÞø}||››‹€€³mÄÄÄX¬§Ø«+up{êSŽª¬WÝŠÆÆF˜çÛN_ºt •••¦ù×0¬÷ÃÃÃûÝÈ¢DDÔ{Ø{.æèóO"±hµZÄÆÆbÏž=˜5k–Øá0?OiiiAII >ŒçŸëÖ­ÃâÅ‹;]ÞÒ¹›=×r»ûšºq]¹‡p«×¶ÏÙsÞÖÙòÏÚ:‡ì®sgxâ‰'PQQíÛ·è¾}ÔÙµú®lã®»î‡~ˆ‘#G>ÿüs¼þúë8|øp§qô¤={ö`þüù¸t颣£ÅÇaÐ 4PA…/ñ%$àuG"¢î4uêTDFFâ½÷Þ;"§×“׬µ7eBQ1!„ºBz«W¯";;z½:%%%Ðëõ())N§ƒ^¯‡^¯Gkk«i©TŠ   C©T"88ØêtPP\]]E,!uæÀX·nÒÓÓMÏÕÖÖ"<<999P*•fóK$TUUÁÇÇÐÚÚ WW×Ϲ¹¹™}f¬é®FyåååP(¦çZZZàîînƒR©Ä‰'i¶|II &Mš„œœ«Û·¶ì•+W0~üx–,Y‚ØØXüþ÷¿Gaa!æÌ™ƒS§NáÞ{ïÅæÍ›õë×ãܹsøè£l–£¡¡r¹ü¦Ë!‘HP[[ OOO«e»™r×Ý] !ŽÞ–bè©õét:¨Tª›ÞvW÷aWbk¿_•J%~øá„††Ú\¿V«Ebb"6oÞŒI“&u:odd$æÍ›‡çž{®Ã1…œKKK ôz=JKK¡ÓéPZZjuZ¯×›%€º¸¸ ((AAAP*• APPBBB T*µZ¨¨(ñ Ø 0!¤ûÔÖÖâøñãÈÏÏG~~>rssQPP€üü|äåå™ Œ°°0„‡‡#22ááᦿÈÈH¨T*¸¸¸ˆV–¾„uÏ®Õ5Ĩ#uå¹®Æji=öÆ?uêT\¿~/¾ø"­&!ÞLBÈ­ÔÁ-Õ§Q'4ÆÊz•ã”——›~#òòòŸŸ‚‚äææ"//EEE¦DAWWW(•JDEE!<<Üô…‘#GvاDDD=‰ !ÔßmÚ´ Ï?ÿ<®]»æ4÷¦,u ¡R©°uëV$&&Ú½Žöç¶®å:â|±«÷nõÚ¶ñ9{ÎÛ:[þVBº²ÞÎÖ÷Ïþ/¿ü2N:oooݳl]«ïÊ66lØ€óçÏãoûà¾ûîÃO<ûÓ8zRss3 €7Þx>ú¨Øá8Ô œ@<âñwü¢o—•ˆ¨§Íž=>>>øÇ?þ!v(DNÏBØ-%‘’’’••eõu///Œ=#FŒÀðáÃ1bÄÜvÛmLôè#6oÞŒ¥K—š=çåå…ùóçã£>ÂêÕ«;,cl|ÀÔ8²ýs=›ÝöFp£Nû®]»fµ!²\.ïtýeee\©T*”••™OŸ>7nÄïÿ{lß¾‹-üú׿Ǝ;ð /àÀøíokW9<<C&“¡¾¾^ì0ˆÈNL!"""²ÃñãÇ‘››k6:Hûé‚‚œ>}ÚjOàm{ÿ¶6íáá!b)ÉN‡Ï>û û÷ïÇ#<ÒáõØØX‹òz«ÀÀ@ääät¸q`ï²ÅÅÅ.š›Ýt™:u*–.]Šºº:üóŸÿÄgŸ}˜7o~ùË_bÙ²eøþûï±gÏQÊaϺí)§º»Üö®/99C‡Ezz:ÂÂÂL½Q·ïÕ-00:®C/[퇑ïʶ»‹½ûuÀ€(++³9B¬]»±±±˜9s&öíÛ‡±cÇvwØÔÃ,Ö,M[9lèСÖØ[6õ¤U«V!))ÉldÜÜ\Óˆ!»wïÆ¶mÛLó+•JSˆ0¥RÉQBnëžânÇÞ:MO³7~???¬_¿ëׯGYY>Œ·Þz ÇŽÃÇÜ#qÚSŸræ:!YWQQÑáw¢íãÂÂB455¸q-D©T"..ÎìwÂø»1dÈ‘KCDDDÔ¿ 8hhhpÚûRþþþX²d òòò°fÍlÙ²Åôš½çnö\ËuÄ9‡3ßCèŽ8w®­­Å¼yó°nÝ: >ÜìµîØG¶®Õwe!•Y IDATAAAˆÇŽ;ðÓO?áé§ŸýºA{õõõ(((@XX˜Ø¡ôˆ—ñ2>ŧx# iÀ¹öQo%—Ë¡×ëŃˆìÄ„"""";øúúâŽ;îÀwÜasÞºº:£¨¨ƒ¡Ãô©S§LÓƒÁlY™L…BÐÐP¨T*›Óäx[¶lÁÃ?ŒM›6Y|]­Vã›o¾Á=÷ÜÓÑuÔœùË_âÈ‘#¸ÿþûÍž?vìV¬X~øÁ겉‰‰øä“O°råJ³ç÷ìÙc6¤»B¡À¨Q£ðÎ;ï˜@7z›U*•X¿~=âââàïï/J9l±·œ]%öþ»•õ}ûí·Ø±c‡Ù>»~ýz‡õM:{÷îÅŠ+Ìž?|ø°ÃËb‹½ûuâĉHKKÃâÅ‹MÏ={ .ì04ýÂ… ÿøÇ?0wî\9r±±±¶ÝÓ=¶“¹ºº:‹¿×–¦KJJÌ’<Œ¿ÛÆßåÐÐP¨Õj‹¿Ýl(OÎÈÕÕ·ß~;n¿ýv«ó ¡¸¸Z­Z­EEEÈÎÎÆ››kê ÒÝÝ ELL bbb R©LÓdëž7t÷ï}w×i¬qT{ã—H$ÈÏÏGXXñ«_ý S¦LéÐÉQqÚ[Ÿrdõª›ÓØØˆ²²²ÇzããË—/£¢¢Â4¿L&3Ûo¿ývLž<ÙìXÝm½Q÷›6m°sçN³ëœÎè‰'žÀàÁƒQ^^Ž€€öŸ»Ùs-×祎º‡àH]9O¼ÕsgK–-[†±cÇbÉ’%f1 ‚Ð-ûÈÖµú®n㡇Âÿ÷£ººgΜéjqnÇŽhjj´iÓÄ¥GxÀ[±ã0›° ãq±C""êär9êêêăˆìÄ„"""¢n&—ËM àl±§jff¦]P;K qº¡¿{‹>øÀ¬gìö–.]ŠÍ›7;E£¼â»ï¾C\\¾þúk,]ºùùù]ZÇÚµk1kÖ,´´´`Ò¤IJ¥8zô(–-[†wÞy§Óe׬YƒI“&Á×׳fÍ‚D"Áþýû±~ýz9rÄlÞ3f`íÚµ;.\¸K–,ÁÚµk»ww–Ö®”³+ÄÞ·²¾ñãÇãùçŸÇÿûÿ¡¡¡ÈËËßþô'‹ë›8q"|||””WWW¤¥¥á•W^qxYl±w¿¾øâ‹¸ÿþûŠ»ï¾—/_Æ#<‚§žzÊ꺱~ýz̘1ééé 1½‰D‚ôôôn/Sf+9Ó8]TTdÖ¸蘜©R© Ñh˜œIý–±¾©V«-¾ÞÔÔ½^o±qZZšiÚ¨m#â¶É"ÆÇQQQðòòê©â9Ö=ó{ßÝukºã=¹•øàÑGÅo¼Áƒ£¢¢6lèÐàÈQqÚ[ŸrTõ*ë C‡$¶Û&÷I¥R 0Àt|NLLDrr²éñ Aƒn)yŸˆˆˆˆÄ§R©ððÃãùçŸÇ´iÓ,vHV")) ~ø!V­ZÀþs7{®å:â¼ÔQ÷©+牷zîÜÞÆqöìYœ8qÂâëݹ¬]«ïê6fΜ‰Ç{ Ë–-sºdxN‡ÔÔT,]ºÔÔ![pîªÿüKD"aØ!õz2™ õõõb‡ADöˆˆ¨K @Ø)ì; ê#;w:îó´qãF‡­›z^CCƒPXX(dee ‡¶lÙ"¼õÖ[Bjjªœœ,Ìœ9Sˆbbb777€ÙŸB¡† &ÄÇÇ ,ž~úiaÍš5ÂÆ…ýû÷ ÇŽ._¾,466Š]T§ÑöýÛ³gO‡×?ÿüs³yÚ/ci==g+[víÚ%DGG R©T\عs§Åõeee Ó§O¼¼¼oooaêÔ©BvvvîCkóÛûùýöÛo…øøxA&“ áá᫯¾jöºŸŸŸÙú Ͷ¹mÛ6Ó¼ãÆÆoWÜý]yy¹••%;vLصk—ðÖ[o kÖ¬RRRÌ~¥Ri§¿3gÎRRR„5kÖo½õ–°k×.áØ±cBVV–PUU%v1ÉBFF†ÃÖ¿`ÁaÁ‚[[ý¥¾^WW'\¾|ÙT‡}íµ×„””!11Q6l˜àííÝá;«ÑhLß××^{Mزe‹pèÐ!áòåËBss³ØErÖ=»§îÒukÛèŽ÷äVâOKKæÌ™# 0@J¥Btt´ðì³Ïvø}³gw¼ÏöÖ§º»N(ý·^eëëååeók¬õåc,Q[öž‹9úü“HLÂàÁƒ…;ï¼S(//5[ç¢ß~û­éu½^ߥëѶ®å B÷œ—Þ̹Qw]ÛîŽsήœÏÞʹ³¥ç<<<:¼íç¹Ù}Ô•kõ]ÙFSS“%ÙŒ¡'•—— #GކÚ/¯u× õ‚ZP ÷ ÷ ­B«Øáõz/¼ð‚0jÔ(±Ã êzòú`¹½©ä?/‘$`'vâ< v(ÔH$ìܹ<à˜ÏÓ¦M›’’âu“ó³·wôüü|TWW›-kì¹¹³QG ÂÃÃáëë+R ‰ˆ¨7khhÀµk×lŽ”UTT½^ææfÓ²°k”¬àà`¸¹q€Ô¾$33 Ñh²~cÝ|×®]Y[¬¯ÿK½×·î¬÷zK£( ‘KDDtë:…©í´Ga"""²½çbŽ>ÿ$Û•+WpÏ=÷ÀÛÛû÷ïGll¬Ø!õ »víÂÁƒ±uëV±C19þÛÆÏWLLŒÅÏ]HH‡R'""º ÆßÚÎ’ ƒÅ‘Ž?Ž¢¢"èt:‚À< mˆqzÈ!ðññé©â‘Œû×ZÂÇÕ«WÑÚÚ àF’ÑÀMû311)))¦ÇƒæèjDDDDDdF¥RaÑ¢EX´h ¹¹çÏŸGff&233ñå—_bݺuhmm…† ‚aÆA­Vcذaˆ‹‹c#zr ƒÙÙÙÈÉÉ1ýúôi\»v L"Ìš5 qqq¦Æ´$¾P„⼂§ñ4æa4°<â.ÝÀBˆz&„õ3r¹ÜfOÑFuuu6ä×jµHOO‡Á`0kü_c~{H”J%‡†ïEl}.ÚO·eés¡V«-~.ØÃ8‘sP(Ðh4Ðh,ß(¯¯¯7J×~´‘œœ\¸pÕÕÕfë³4ºˆñqdd$=»‰1ÜRBOQQ®^½Šëׯ›æ7Ž£R© V«‘””d¶¢¢¢Xo'""""¢[âææµZ µZÅ‹ªªª••…ü?þø#²²²ðé§Ÿ¢¼¼ T*1|øp 2±±±2dŒèèhŽMݪ©© W®\ÁÅ‹M.\À?þˆ’’@@@FŒµZùóçãŽ;îÀðáÃáëë+rôdË2,ÿð/,ÅRœÂ)¸ƒÇ""kŒ !!„¨w`BY%—Ë!—Ëjµ QCC®]»Öir@NNŽÅ‘ <<<`WIpp0ÜÜx*ÓÝlcœÎÏÏ7kÐ t9ÆØ Vû}Î"DDD}L&³™pl0,Ž@¡Õj‘––†ÜÜ\SýÐÝÝGiû¸¿kllDYY™Õ„Ë—/£¢¢Â4ÛÑ[Œõµ¶ïqtt4<==E,õW¾¾¾?~<Æoö|QQ²²²pîÜ9äääàìÙ³øøãQZZ àFrIdd$bccMÆŽ"""x=š,ª¬¬D~~>rss¡ÕjqáÂ\ºt /^Dnn.š››ÁÁÁˆÅСC1mÚ4Sâ¯Iô^Hð.ÞÅHŒÄëx/à±C""rZÆQ®8BQïÀVTDDDDDÔ-<<<ŠÐÐP¨Õj›óZK>Ðjµ8xð ÐØØh¶¬±Wi[ $aaaJ¥Ž*²Ó³õ§óòòL78ŒÚ¿ÇÆâ{Î؈ˆˆÈ&…BaÌ’¦¦&èõz³Ñ+ŒÓÇ7=6j›Ü`)a$22ÞÞÞ=U<‡0ŽÈg)‘¦¸¸W¯^Ekk+@*•bÀ€¦÷!11ÉÉɦǃ ‚¿¿¿È%""""""êã=‡©S§š=_UU…‹/šñ_ºt رcôz½i>???„‡‡#22áááGDD"##•JejìH}C]]t:)á#??ßôwõêUäç磪ªÊ4PP)™(!!ƒFll,Ì„¢>*±XóŸ³1Ã0L숈œ’»»;ÜÜܘBÔK0!„ˆˆˆˆˆDalh[£Wdffš’Úö‚ t½"t`(TÊŽ $ÆÇά®®ƒÁf‚GQQJJJL ïCÛrªT*ÄÄÄXL¨ «««ˆ%%""¢þÆÝÝÝÔÐÇÚÈtõõõ¦ÄáöI™™™ÈËËCMMi~c‚«¥ÑEŒ=ÆŠUç1Öo­%|äææ¢¶¶à`·TЬÈHÔ  µZ¤¤$³òDEEÁÅÅE”²õ4___h4‹ç555ÈÍÍE^^ž) 77?ýô¾üòËPùùù™F'7^ Ahh¨ÙsLI}}=ÊËËQRR‚ââb”––šîƒ´®m²‡T*EXX˜))èÎ;ï4%G‘éíIÐÍY…UøãQ<Št¤Ã¼¦BDd‰L&C}}½Øa‘˜BDDDDDNO.—›ñÙbLš°”$‘瞇);0àŨO¯·+iÂÚ´R©ì–Fw¶’]ÚN ³eñ¶O­V[Œ[¥RA"‘Ür¼DDDDb‘Éd6ë„Æ$áö Z­iiiÈÍÍEKK €Ž£jXmÄÞæ¶QVVfqTãã¶õºö£h4³x¢”Jx=ó ænß,Züñ“?ˆˆˆˆˆˆ,òöö†Z­¶:B¥ (..F~~>JJJ Óé ÓéPZZŠÂÂBddd˜ž»~ýºÙ²žžž€B¡@@@€Õ?x{{ÃËË ^^^P(ðòòê·#š766¢¶¶ƒµµµ¨­­EMM ***P^^ÞéŸÁ`°¸Œ÷i‚ƒƒqÇw 88J¥J¥!!!ˆˆˆ€R©ä}²È nxï#qxïb9–‹‘S’Ëå!„¨—`Bõ)r¹r¹¼CïÒßàÌÇ|Ä {wíE$"ÑÐЀÒÒRÓÍžöÓ………8}ú4ôz=JKK!‚i}R©AAA¦› AAA R©4M@II ôz=t:iÚx“I¯×›õD&‘HŒ   Ó͋ѣG›¦ƒƒƒÍ¦=<˜?ˆ‹víÆëžuQÞÞÞ2d† b÷2Ƥ‰šš³‘0¬=®­­5%j\»v 0ªªªÐÒÒbJÀè %¨G<1&¹Ï·Û&º´O|i;rŠñ1Gð 1ýÀìÁr,ÇgøLìpˆˆœŽ\.ï±zÝ&„QŸÕ€<'ð!>ÄñG¬ÁHps7\]]MÃßqÇ6çoœ3 Ý»÷¦¶GDDDDâðó󃟟†nöü œÀ8Œƒö‚^z/øùùõüˆmwÝœ:üæ7ÀĉÀºuÀŠ=Y%‘HL£Wö”––³Ñ.ÛjllÄ#<øàƒ •J-ÎçëëË«¨ßñ€ÞÇûH@¶a’‘,vHDDNE.—s„¢^‚ !DDDDDÔ'é¡Ç,ÀüpÛg IDATø{°³1»G·oí¦ õN:èJ‰ò`¹x |þ9ðç?+WÀÆ€§§x1‘h\]];M@ñòò„„„ôTHD½Æ8ŒÃxÏâYLÅT„€ß""#™LÆBˆz ± """""ên§qqˆC! ñ¾ëñd""""ê{tÐÁþCÄd#‰HM>û ˆ‹~úI쨈ˆˆˆˆˆˆˆzWñ*üà‡à(¬DDmq„¢Þƒ !DDDDDÔ§ìÀ$ ·á6œÄI¨¡;$""""êtÐA ¥Øa˜›18sðõÆ>þX숈ˆˆˆˆˆˆˆz/xá=¼‡]Ø…=Ø#v8DDNƒ !D½Bˆˆˆˆˆ¨O `-ÖbáA<ˆOñ)°>D:QW” ÄùB <8zX²xà`Å  ©I쨈ˆˆˆˆˆˆˆzɘŒd$ãiûÏ?&ƒQOÐA½+!Ä() 8v (,â‌ ±#""""""""rj/âE„"Ïà±C!"êîîî8~ü8²²²pþüy¢²²fÉ 0lØ0‘¢$"k˜BDDDDD½Â9œÃݸe(ÃqÇTL;$""""ê'zuBŒœ:  L˜lÛ&vDDDDDDDDDNËxoã_øöc¿Øá9œ§§'|ðA¸»»w:ŸŸŸÆßCQ‘½˜BDDDDDNï3|† ˜€Dà;|‡Ûq»Ø!Q?R‚¸ÀA;”›7`ðï+V=¬^ ´¶Š‘Sš‚)Xˆ…x O¡5b‡CDäpK—.ESS“Õ×ÝÝÝ1wî\¸¹¹õ`TDd&„‘SÛ€ HB~…_á0÷îFxDDDDÔ+é C á†^~£ËÍ xíµ#„üå/ÀÌ™@E…ØQ9¥7ñ&ªQ—ñ²Ø¡9ܘ1c0lØ0H$‹¯777cΜ9=Ùƒ !DDDDDä”Ѐ%X‚UX…Wð 6aÜÑùð¤DDDDDŽP‚(¡;Œîó›ß‡gÎcÆ?ý$vDDDDDDDDDN'!x¯àM¼‰38#v8DD—’’ËMË¥R){8""²BˆˆˆˆˆÈ锡 S1ŸàìÅ^¤"U숈ˆˆ¨ÓA×·Bà ;Ø¿_숈ˆˆˆˆˆˆˆœN R0cð8G+ZŇˆÈ¡’““-&„¸ººbÚ´iðôô!*"²… !DDDDDäT~ÄÑ(D!¾Ç÷˜‰™b‡DDDDDý\ŸL€ÐPàèQ`þ|`Î`íZ@ÄŽŠˆˆˆˆˆˆˆÈi¸À±§q›°Iìpˆˆ* ÷ß?ÜÝÝÍžsçÎ)*"²… !DDDDDä4>ÇçH@b ¾Ãw†ab‡DDDDDÔwBÀÃøààï^yX¸¸~]쨈ˆˆˆˆˆˆˆœÆp Ç3x©HEŠÄ‡ˆÈ¡{ì1455ux~ÆŒ"DCDö`B9… Ø€™˜‰X€¯ð‚$vHDDDDDn$„„ Dì0+%HK¾ú ˆ®^;"""""""""§±k1ð;üNìPˆˆêÞ{ïEDD„鱋‹ (bTDÔ&„‘¨ш‡ñ0Va^Á+ØŒÍB*vXDDDDDJQÚwGiëž{€ï¾š›qãnL<á‰wðþ‰âS|*v8DD#‘Hðè£ÂÍÍ À„yóæ‰u† !DDDDD$šk¸†_â—øcö ©b‡DDDDDd殡 M}„£Aƒ€o¿ÆŒ&O¶o;"""""""""§0Ó1óð$žD-jŇˆÈa~øa´¶¶š››‘””$rDDÔ&„‘(²…Ñ<äá{|$ð9tÐ?F1òñöíRSV¯þs󈈈ˆˆˆˆ¨?{oÃ^Å«b‡BDä0aaa˜2e @­V#::Z䈈¨3nb@DDDDDýÏgø ±£0 ÿ¿ˆ@±C"""""²¨_&„€D¬] „‡O<\¾ lÙxzŠ‘hTPáe¼Œçða†a˜Ø!uª¶¶¨ªªBKK‹Ùkƒ¡Ãü---¨ªªÂ¸qãðÅ_@£Ñ --ÍôºL&ƒ\.ï°œ——¤R©Ùs~~~pqqB¡è¦Ò‘%L!""""¢õ6ÞÆ³xá!¼‹w!…ÔöBDDDDD"ÑA)¤@€Ø¡ˆcéR`èP`î\`òd`ï^@ÙÏ’cˆˆˆˆˆˆˆˆÚxOb;¶c–á(ŽB‰Ø!QQ]]ÊÊJÓ_EE…ÙcƒÁ€ÊÊJTUU¡©© •••hmm…Á`0%r466¢¶¶õõõ¨««»å˜¶nÝŠ­[·Þòz\]]áëë ©T ///Sb‰1‘Ä××®®®P(ðññŸŸüüüàïïoš6þ) S² 1!„ˆˆˆˆˆzH Z°+ñ6ÞÆñG¬ÅZ±C"""""²IBÒ¿oì'$ß~ $%qqÀ¾}€F#vTDDDDDDDD¢p 6b#â‡ñ!Á#b‡DDN¨±±z½¥¥¥ÐétÐëõ(++ƒN§Cii©éµòòrS¢Gkkk‡õ¸¹¹™%BøûûÃ××nnnˆˆˆ€««+üýýM îîîðöö6%\xzzÂÃÃÞÞÞpww7[·ÜÜ:6%÷÷÷‡DbùšxMM ššš:<_QQA:<×ÒÒ‚ÊÊJ477£ºººCŠq“êêj477ãêÕ«fI0•••¸~ýºÅX|}}MïMpp0”J%¥R‰àà`!88!!!ðöö¶º¿ˆz3&„‘ÃÕ¢ ±_âKü/þ ±P숈ˆˆˆìR‚(Á10xð¤ùó‰ü˜=[쨈ˆˆˆˆˆˆˆD1#±ËñžÃLÌD0‚ʼnˆzN§CAA ——‡üü|Óc½^’’TTT˜-#“É„S¢‚Z­Æ€L#^´ý3ŽŒáåå%R)-S(=¾ÍÆÆF‹#¥GP©¨¨@ii)JJJ™™ ½^ââbTWW›­G.—›’E”J%"þ?{wVU¹°ü»A14y ‡œgœõ82rVN‰VVh™yÌl°<™9å<–#N嫉V jET$GPß¾î_&*¸@îÏuíKöÚÏ^ë^kï«6‹uï§BÜÜÜpss£B… ¸»»S®\9¬¬¬žú>Š< BDDDDD$OãèÄiNN8ilt$‘»?Cˆ%KÂæÍ0ltë}cÆJDDDDDDDÄïñ«XE!Ìg¾ÑqD$—¤§§sæÌ¢££9yò¤¹ìqæÌâââøã?HMM5wqq1— êÖ­›iFŠ2eÊP¦LÊ–-‹½½½{U°YYY™å£HIIÉ2+Kbb"ñññœ?žC‡±aÃÎ;gžõÄd2áêꊻ»;nnn¸»»S¡B<<<ðôôÄÓÓ[[Û¼ØM‘ǦBˆˆˆˆˆˆä™(¢è@ŠSœƒÄ£#‰ˆˆˆˆ<’¨HE£cäE‹ÂܹPµ*¼öÄÄÀÌ™P¤ˆÑÉDDDDDDDDž*{ì #Œît§?ý ÀèH"’CiiiÄÆÆrâÄ ¢££‰ŽŽ6ÿ|êÔ)nß¾ @©R¥pwwÇÝÝZµjѺuksAà~ ÄÚÚÚད±±±1¿NçîÝ»ÄÇÇgšé%66–¸¸88ÀÊ•+9þûŒzõê±qãFY¾|9C† QDä1”.]šnݺñÉ'ŸáC‡xå•WØ»w/­ZµÂÙÙ™~ýú±råJnܸat\ÉCš!DDDDDDrì7èK_¶°…E,¢/}Ž$""""’g4CH.xáØ·Ú´† aóf¨ZÕèT""""""""yÆ +>æcZÓšMl¢-mŽ$òT;wŽ… 2wî\Nžúè#ŒŽ)¹D…y¨(¢hHC®r•}ì£ MŒŽ$""""’çâ‰Ç£c<\\`÷nðñ¦MaÓ&£‰ˆˆˆˆˆˆˆä©ù˜sœc³ŒŽ"’gNžXXX°|ùrâââ˜>}:µjÕzê™L&S–[É’%iРË—/êyòƒûÇ!¿2*_~?.¹ÅÉɉQ£FqèÐ!8@³f͘Щüç?F'É3¨À8Æ1‰IœáŒÑqDrUDD=zô Zµj¬]»–Ñ£Gsúôi¾ÿþ{ºu놕••aÙ222ÌÿfddpçÎŽ9°aÃ9r$³gÏ6,›Qî“üʨ|ùý¸ä…zõêñÙgŸqîÜ9æÍ›Ç¥K—hÕªuëÖeñâŤ¥¥Qƒ !"""""ò@_ò%è@7º±‰M8âht$‘§â·¸ÊUÍ’Û,-áË/áµ× (¦M3:‘ˆˆˆˆˆˆˆHž !7ÜËX££ˆäŠ#GŽÐ³gOêׯOtt4óæÍãĉL˜0777£ãeËÒÒ’òåË3`À/^ÌÌ™3Ž$b¸bŊѯ_?víÚÅ¡C‡ðööfРAxzzòå—_’žžntDy*„ˆˆˆˆˆHé¤3žñ aoñó˜‡Æ}ƒ‡ˆˆˆˆÈÓO<€fÉ &|ø!ÌšãÇCHÂob‘gŸ5ÖÌf6+XÁf6Gä±>}šž={R»vmbccÙºu+¿þú+ ÀÒÒÒèx9æëë˹sç2-3™L˜L¦,c³[~ÙÙ³géܹ3ööö¸¸¸Ð¯_?’’’ºýûÏ?vìmÚ´ÁÁÁâŋӾ}{~ûí·,ã=J»ví(^¼8´nÝšcÇŽ=0óÃÄÆÆH‰%þv»ëׯçÅ_ÄÆÆ‚ƒƒ¹víZ¦1ÉÉÉS¹relll([¶,ƒ """"Ó¸#GŽÐ¾}{ìíí±··§M›69r$Ïó>|˜V­Zagg‡ƒƒmÛ¶eË–-<>žžžæcÛ¸qcóòÇyäô5Îêԩ 8~ü8Í›7gذaÔ©S‡Í›õÿ­‚B…Éä7èJWÂã[¾%”P£#‰ˆˆˆˆm{òäIÚ¶mK÷î݉‰‰áÔ©SüóŸÿ¤W¯^›ûïôîÝ›nݺqòäÉn sçÎ 8K—.ñÓO?qéÒ%FŽ™iÌÀqttdß¾}$''³qãF¢££©_¿¾yLtt4-[¶¤cÇŽÄÄÄpúôiúõëGçΉ‹‹ËÓ|ǧW¯^„„„˜˜ÈO?ýÄ7hÓ¦ÍϲeËððð 22’={ö˜—?Î{$'¯q~S©R%¾þúkŽ=JµjÕhÛ¶-ù:³ÜSpjy"""""’çH #9Å)¶³F42:’ˆˆˆˆˆ!â‰Ç„ \ŒŽòlëÛœ!0®\eË X1£S‰ˆˆˆˆˆˆˆäªÙ̦5#ŒBŒŽ#’#QQQôïߟ'NÂo¼µµµÑ±Yzz: lß¾¦NúÄë6l˜¹XP¼xq¦L™‚¿¿ŽŸ?aÂs±îL®^½Jhh(óçÏ 44”×^{—^zÉ<®wïÞôéÓç±rѯ_¿¿Ý.d.:ØÚÚ2kÖ,<==3­kûöíÌ›7R¥JP·n].\HåÊ•ÍcBCC fÈ!æeýúõãöíÛL›6°°°<ËÊ[o½E‹-¨Q£sçÎå¹çžËöØ;vŒîÝ»³lÙ2¼½½³ó(ròçWÕªUcÕªUìܹ“‘#GR³fM>øàFõX3ÓHÞÓ !"""""@4Ñ4¡ I$±‡=*ƒˆˆˆˆH¡O<%)‰5ïœN‹°};ìÝ mÛBr²Ñ‰DDDDDDDDrU*B™ÈÎG䡦OŸN½zõ(Y²$ÇŽ#44´À•AL&&“ KKKÊ—/ϸqãX¸p! xâuûùùeºïááÁùóçsüüû%…? dëÖ­æûÛ¶m£K—.YÆ=Éì,mÛ¶}èv³S²dI.]º”iYÆ éÔ©›7o&íÿf®T©R¦²Fxxx¦RÄ}íÛ·gÛ¶myšoÇŽdZV­Zµlgõ8uê;vdÞ¼yøúúþí¶r*'¯q~×¼ys~þùgFŽÉk¯½F`` É:Ÿ/©""""""ìc iH)J±}T£šÑ‘DDDDD •@®¸£ððõ…Ý»!:üýᣉˆˆˆˆˆˆˆäªqŒÃ 7Í"ùZjj*dܸq¼÷Þ{lß¾Š+ë±ddd˜o—/_æƒ> ((ˆÓ§O?ñºK–,™é¾µµu¶Eƒ)]ºt–eeË–%11Ñ|ÿâÅ‹¸ºf=GÝssÊÅ%ëŒØÝnbb"ƒÆÍÍ KKKs±æ¯V¬X¯¯/ǧT©RøùùÆ;wÌc’’’ððð0¯ãþÍÕÕ5Û×!7ó]¼x''§‡€6mÚpýúunܸ‘£ñ9‘“׸ °¶¶æÃ?dçÎ8p€† mt,ù BDDDDD ¹•¬ÄšÐ„ìÄg£#‰ˆˆˆˆ.\ÈúÇ'ÉC5kž=pí4l'OHDDDDDDD$×XcÍÇ|Ìr–³…-FÇÉ"55•Î;³~ýz¾ûî;BBB°°x6.3vttdРA 4ˆwß}7Ëã&“‰ÔÔÔLËþ:ãDnJHHȲìüùó”)SÆ|ßÉɉøøø,ã²[–SÙíÓ_·Û¿ìììØ³g)))æRÍ_•(Q‚3fpòäIbbb>|8K—.¥OŸ>™öáÒ¥K™Ê9÷o7oÞÌÓ|¥K—æâÅ‹?(@hh(ßÿ=ƒfÿþýÙŽyÔ÷HN^イI“&DDD`ggGãÆùí·ßŒŽ$òlü—ZDDDDDKaô¢A±’•£˜Ñ‘DDDDDò…xâ5Cˆ<<àÇÁÁš4ÈH£‰ˆˆˆˆˆˆˆäšÖ´¦#Å(RI}øDž’ôôtzôèÁþýûÙ¾};mÚ´1:Rž6lëÖ­1$Š~ IDATËr!¿««+±±±™–ýðÃy–cË–¬¥°5kÖЪU+óýV­Z±víÚ=7§ÂÃúݽ{÷2qâD<<<°´´ȶ¼a2™ˆ‹‹î?zõêÅ÷ßÏÖ­[ÍcZ·nÍ®]»²<÷ÇÄÇÇ'Oó5kÖ,Ëú>L5²ŒíÓ§õêÕcÑ¢EtíÚ•'Ndó¨ï‘œ¼ÆMùòåÙ¹s'žžžpêÔ)£#ÉÿQ!DDDDD¤J'á ç5^c³# ýz """"b¦Bˆ\\`çN¨Rüý!"ÂèD""""""""¹æ>!Ž8>æc££ˆ˜Mš4‰mÛ¶±iÓ¦l/ÔV899ѱcG¾þúëLË[¶lÉ›o¾I\\ׯ_gË–-Ì™3'Ïr|ñÅ,Y²„¤¤$ùꫯ #44Ô<&44”éÓ§3oÞ<IJJâÛo¿eîܹ½Ý>ø€åË—ÿív5jDHH§OŸæöíÛDGG3tèÐl×7xð`Ž=Jjj* |ôÑG´hÑ"Ó>¼û\¹’¤¤$®]»Æwß}GŸ>}²©%7óM˜0I“&Î7ˆŒŒä¥—^bäÈ‘<>-Z´`ÆŒ´k×.Ë úÉÉk\/^œ7âââB=²Ìš"ÆÐ_"""""…Ì nÐ….ÌcKXÂHüË®ˆˆˆˆHaO<.¸£ðrt„-[ ~}hÑvï6:‘ˆˆˆˆˆˆˆH®¨@B!”PÎpÆè8"ìß¿Ÿ÷ߟ°°04h`tœ'f2™2ýûWÆ ãõ×_Çd2qñâE¦OŸŽ••>>>¸ººòé§Ÿš‹^σÖý°mþÕâÅ‹ùöÛo©T©•*UbÕªUlÞ¼ó˜Ê•+³iÓ&V®\i·dɾùæ,,íòïû¹–,YÂW_}…‡‡Ç·»`ÁRRR¨_¿>öööÒ¾}û,ûN±bÅhÚ´)4lØ””¾ùæóV­ZÅÒ¥K©R¥ eË–eÒ¤IÌž=›Î;çi>ooo.\Hhh(NNNtèÐ=zd*8::fzÞ¹sçèÝ»7ÑÑѸºº²hÑ"óØœ¾GîËÉk\P988°bÅ ¢££™8q¢ÑqBDDDDD •xâiJSö±pÂéIOò4hЀï¿ÿ>ÛÇ>Œ››iiiìÞ½___lllððð૯¾Ê4þÚµk„„„PµjUlmm)Q¢-[¶ä»ï¾Ëóýx“ÉDzz:ï¿ÿ>X[[ãååÅ'Ÿ|’eì† hРvvvØÙÙýí1‘¼•@B¦B Ëç×|ÅÖÖ¬6m ukX¿ÞèD""""""""¹b<ãqÃqŒËÕõê–<ŽñãÇÓ¸qc‚‚‚ŒŽ’+222Ì·ì4lØÐü¸““poæÅ‹sá®_¿Îúõëqwwϲž­ûaÛü+6lØÀÕ«W¹~ý:7n¤zõêYÆÕ¬Y“7rýúu®^½Ê† HIIÁÝÝ=§‡#S¾5j°eË®]»öÀí:;;³`ÁHMM%**Šž={fÙ¿€€Ö¬YÃÅ‹IMM%&&†3f`ooŸi}U«VeåÊ•\¹r…ëׯA```žçƒ{¯õž={¸uë±±±Œ?>ÓãW®\Éô¼råÊez-ûõëg›Ó÷È}9} *OOOBCC™5kqqqFÇ)ôT)$ŽqŒ4à WØÇ>^äECóŒ=šY³feûØìÙ³2d–––ü÷¿ÿ¥{÷îsá–/_Îäɓٵk—yüÀIKK#<<œäädN:ÅèÑ£™={öSÚ›¿7lØ0nݺExx8—.]bîܹ̜9“eË–™ÇüôÓO 4ˆQ£FqúôiN:ň#èß¿?00½ˆˆˆHás…+ÜâV¦BHaúüš¯XYÁ·ßB߾У¬\it"‘'f5ó1ËXÆväÚzuKÕÑ£GÙµk—¾å?Ÿ2™L„……qùòeRSS9t课ú*Ç7:šC‡ÅÑÑ‘yóæ¥ÐS!DDDDD¤ØÉN^äEÊR–}ì£*UŽD÷îÝùí·ß8zôh¦åIII¬ZµÊüí#“'OfüøñôéÓ|}}™9s&Ó¦M3?gÛ¶m„††R±bEŠ-J©R¥èС[¶lyªûô ...Lž<OOOìììhÚ´)³gÏfæÌ™æ1S§Nå½÷Þ£oß¾”)SgggúõëǤI“øðà L/"""Rø$©R˜>¿æ;EŠÀW_ÁСл7ÌŸot"‘'ÖšÖt¤##ÉîäÊ:uKÕÚµk)_¾<~~~FG)L&S¦æ»ï¾cÆ T¨PgggÌ+¯¼Âرcó2¦Ë•õé–<ªcÇŽñ /`a¡Ë‰Ÿ†ŒŒ ó-'Ú·oOxx8×®]#99™ƒ2pàÀLcL&ÓßÞäézÔ׸ «_¿>¿ÿþ{¡ÙßüJÿy†…FozDËYN1Š)“   V¯^ÍÅ‹HOOç³Ï>cĈæ1gÏžÅËË+Ó ‹R¥Jqþüyó˜¥K—’˜˜ˆ§§'Õ«WçŸÿü'ëÖ­Ë7¿pzxxdYV²dI._¾l¾Ÿ`>ûg...ÄÇÇçe<ù‹xâ)B‘,eêÂòù5_›4 ¦LÑ£aút£Óˆˆˆˆˆˆˆˆ<‘ÊTf c˜ÈD.r1WÖ©sXò(’’’(]º´Ñ1ä ü¹€ÝM$/•)S†””®]»ft”BM…‘gP:é e(¯ñó1a„a‘?þ;99ѵkW¾øâ Ö­[‡««+¾¾¾æ1œ;w.ËI‹ôôtó˜J•*±fÍ®\¹ÂÒ¥Kiذ!“'OfðàÁO}Ÿ²““oSqqqáÂ… Y–_¸pWW×¼ˆ%""""O¿æ{ãÆÝ›!dìX?Þè4""""""""Oä-Þ¢Åx‡wre}:‡%ÂÉɉÄÄD£cÈ#HKK#((Èè¹*((ˆ´´4£cÈc¸pá666ØÛÛ¥PËW„‰ˆˆˆˆÈ¹Îu:Ó™…,d kÎp£#ý­Ñ£GóùçŸsçÎfÏžÍÈ‘#3=Þ¼ysÖ­[—£uY[[S§N‚‚‚Ø´iË–-Ë‹ÈyÂÇLJ 6dY¾~ýz||| H$"""Rx%€+Ù—rõù5Ÿ>æÌiÓ $ôMw"""""""R@§8ð_ò%‡9œ+ëÔ9,É©êÕ«sðàAîÞ½ktÉ¡üüüŒŽ‘«š4iÂx}ùOAõêÕ1™LFG)ÔTy†œç¿æsíÚÁš5°x1¼òŠJ!"""""""Rà¸âÊ8Æ1…)ÄûXëÐ9,yï¼ó­[·¦mÛ¶8pÀè8òIII”(Q"GcK–,É¥K—2-kذ!:ubóæÍ¤¥¥P©R¥L3uÀ½™D¾øâ óýO>ù„ýë_Y¶áçç—龇‡çÏŸÏ´lÇŽdZV­Zµ,Û,Q¢III9Ú71ÎÕ«WiÓ¦ ‰‰‰¬X±kkk£# *„ˆˆˆˆˆHi¤ñ*¯òoñ ŸFðã}FF7nÜ`õêÕ888GDDDD„‹\$´l !úüZ´m{¯²d ôëÿ÷GM‘‚âu^§<åy‹·ëù:‡%Â+Vàç燿¿?ëÖ­3:’dãÚµkXZZfYž˜˜ÈàÁƒqssÃÒÒ“É„ÉdÊ2nÅŠøúú2|øpJ•*…ŸŸaaaܹs'Ó¸¾}û²|ùrnÞ¼ILL §N¢]»vYÖW²dÉL÷­­­³=.^¼ˆ““ÓC÷ÍÒÒ’ää䇎ãüñÇömÛððð0:’üŸ‚wŘˆˆˆˆH!wk´£KXÂzÖ3ŒaFGyfÄm!D ˆ6m`Ý:X»ú÷W)DDDDDDDD +¬øYÌbö°Çè8RˆXYY±råJzöìI·nݘ2e w5 o¾booožÙãÏú÷ï{öì!%%…ŒŒŒ,Å ¸7 ÇŒ38yò$111 >œ¥K—Ò§OŸLãÊ”)Ë/¾ÈÒ¥KùüóÏ5jT¶“œ(]º4/^|踴´´Ï~"OßîÝ»©W¯)))ìÛ·5jIþD…‘äçðÃ(¢ØÍnÚ‘õDDDDDäñ©òŒhÕ 6m‚ àÿP)DDDDDDDD ”®t¥%-Íhî¢ òåé±²²â«¯¾â£>"44”æÍ›súôi£cÉÿ)]ºt¶³hìÝ»—‰'âááažAäæÍ›YÆ™L&âââprr¢W¯^|ÿý÷lݺ5ËØòé§Ÿ²nÝ: ðØ™›5kFxxx¦e‡ÎR(HNN¦téҽɩ©©Œ;5jÄÞ½{©T©’ѱä/T) ŽqŒF4â:×ù‘ñÁÇèH"""""Ïœxâ±ÆGŽ"OªiSظñÞ­o_•BDDDDDDD¤@™ÉL"‰ä¾1:ŠBcÆŒáçŸæêÕ«Ô¬Y“ÐÐPRSSŽUèyyy™ Ö¨Q#BBB8}ú4·oß&::š¡C‡f»ŽÁƒsôèQRSSIHHà£>¢E‹YÆuèÐÓ§OÓ£Glmm;ó„ ˜4iáááܸqƒÈÈH^zé%FŽ™i\\\^^^½É};vìÀÇLJ/¾ø‚Ï?ÿœU«Vaooot,Ɇ !"""""À>öÑ”¦”¥,{Ù‹'žFGy&%€ .˜0ErƒŸ¬_¯Ò¯¤§HDDDDDDD$GjPƒWy•7xƒd²Î ’×jÕªÅO?ýÄo¼Á´iÓ¨]»6«W¯&##Ãèh…V@@?ÿüs–å , %%…úõëcooO`` íÛ·îÍ r_xx8ÅŠ£iÓ¦888аaCRRRøæ›¬Å3 1bD–Çî¯óÏë~Ðrooo.\Hhh(NNNtèÐ=zd)¬üòË/´lÙ2§‡BòбcÇ $ €êÕ«säÈ‚‚‚ŒŽ%C…‘|n5« €Æ4f;(C£#‰ˆˆˆˆ<³HÀW£cHnjÞ¾û6l€þîÞ5:‘ˆˆˆˆˆˆˆH޼Ç{¤‘ƦE )kkk&L˜À±cǨ[·.Ý»wç…^`ãÆ*† 00… fYîììÌ‚ HHH 55•¨¨(zöìIFFF¦×) €5kÖpñâERSS‰‰‰aÆŒÙÎú°zõjš4iBÙ²e³7ÆÇLJß~ûÅ‹sâÄ ‚‚‚(R¤ˆÑåèŠ2‘|&•TúÒ—™Ìd‹%ÔèH"""""…† !ϸ֭aéRX°ÆŒ1:ˆˆˆˆˆˆˆÈC¡³˜Å:Ö±…-FÇÀÇLJo¿ý–'NЫW/>ÿüs*W®LëÖ­Y¾|9©©©FG|fYZZ2wîÜ<[FF·oßfæÌ™y¶¿úòË/±´´|jÛ+ÌöïßOPPåÊ•ãÕW_¥\¹rlß¾гgOA (BDDDDDò‘Ë\¦­ØÌf¶²•¾ô5:’ˆˆˆˆH¡O<.¸CòR—.÷f ùì3ø×¿ŒN#"""""""òPþøÓ….ÌîGÄÌÃÃiÓ¦qöìYV®\‰••}ûö¥|ùòŒ3†C‡Q¤ÐKHH`æÌ™ÔªU‹ °oß>&NœÈüÁòåËñ÷÷7:¢Eíh‡#ŽüÄO¸ãnt$‘BçÈ C…¢gO¸y^~¬¬`£‰ˆˆˆˆˆˆˆà^æe^åUjSÛèH"9V½zuªW¯Îøñãùã?X·n›6mâÍ7ßäêÕ«”-[–€€sA¤bÅŠFGÉW®^½Ê?üÀŽ;رc‘‘‘)R„zõêÑ¿ºté‚&“Éè¨ò”©"""""b0Â&˜‘Œd&3±ÀÂèH"""""…Z<ñØcvFG‘§môh¸uëÞ¿%JÀ€F'ÉÖðŸL0ÛÙnt‘ÇR¾|y† ưaÃHKKãàÁƒìܹ“;v0|øpnݺE•*UhÞ¼9~~~øúúâåå¥ Ý¥PILL$""‚½{÷²cǃädèÚ¶mƒFŒN%"""""""’Å¿ù7Ïñs˜Ã(FG$ÏXYY™/Œ1b—/_6D"""˜:u*.\ÀÅÅooo¼½½©U«ÞÞÞÔ¬Y[[[#wC ¡ôôtNž|Ø\:¸téR¦çXYYe[qrr¢D‰8::R¢D‰,?;88<ÝÎ×RRR¸rå ÉÉÉæÛ•+W¸|ù²ùþŸK \¸pëׯgZ­­­ùø»¸¸àããCçÎͳ½T¨P²eËR´hQƒöTäñ¨"""""’‹ÒIg(C™Ç<æ0‡ ‚ŒŽ$"""""9 Bˆü-7·ÿ?SH÷î°aè‚"""""""’O´£­iÍÆpƒX`at$‘gš³³3ÎÎÎøøø’““IMMÍ6ËŸ‰‹‹ ÎÎÎT®\ggg\]]qrr2ÏÐâììŒÝ_/‘‚L…‘\rƒô¢»ØÅ:ÖÑžöFG‘Š'&œq6:ŠäWÕªÁÆÐ¬  óçƒÉdt*f0ƒ:Ôa KèG?£ãˆzE‹¥\¹r”+WŽ:uêäè97oÞÌR†¸ÿóý[rr2ׯ_çÖ­[$$$pûöms©âÊ•+¤§§“œœl.bäÖÖÖØÚÚR¬X±Le–%J˜Ë,E‹¥råÊYfIyÐ *"r !"""""¹ ‰$:щÿò_¶²•F42:’ˆˆˆˆˆ<‚(E)¬°2:Šägÿó?°n´m åËÃHDDDDDDD€ÔàŸü“ñŒ§+]±ÅÖèH"òˆlmm±µµ¥lÙ²¹¶ÎÔÔTnÞ¼iž™ãÏnݺEJJJ–ç\¾|ùë³³³cóæÍÌŸ?ŸÕ«W`aa‘¥ qf“û3•ˆHÞQ!DDDDDä âmhÃî°—½xáet$yD $àŠ«Ñ1¤ ð÷‡¯¿†~ýÀÕF2:‘ˆˆˆˆˆˆˆïó>ËXÆLfòoGDòkkk¬­­)Y²d®­311‘÷Þ{///•=Dò £ˆˆˆˆˆd9HCâ€ûا2ˆˆˆˆˆHO¼ !’s}ûÂûïCp0¬Zetœqf,cù9Ïy£ãˆÈ3ªZµjdddpüøq££ˆ*„ˆˆˆˆˆ<¶p o¼ÙÎv\p1:’ˆˆˆˆˆ<&B䑽ù&Œqo¦={ŒN#"""""""Àk¼FiJó.ïEDžQUªT¡hÑ¢ü÷¿ÿ5:Šˆ BˆˆˆˆˆÈcYÀÚÑŽÎtf#qÀÁèH"""""òT‘Ç2s&´o:@d¤ÑiDDDDDDDD(F1Þç}æ1(¢ŒŽ#"Ï KKKªT©¢BˆH>¡BˆˆˆˆˆÈ# #ŒA b(Cù†o(JQ£#‰ˆˆˆˆÈŠ'^³þÉ£³°€… ÁÛÚµƒ³gN$"""""""Â?ø>ø0†1FG‘gÔsÏ=Çïÿ½;‹ªÜÿþö}uaS™ÅÜÐRPÓDMa¸[™eš7Û35«[i«Ke–Z7ÍPÛõº–7÷BMRÑaQ6A‘õùýá¹ 0£0g`>ï^óRΜ9çóŒ‡éžÃsXƒ5X…UA&u,"""""ºCyÈã !t{lm;7·[E!EER'""""""""'ƒ «° ¿á7ìÆn©ãQ;È‚"#Á‚"""""T S0ë±[°ó0OêHDDDDDÔBŠP„ T° „nŸ³3°kP\ LœTTHˆˆˆˆˆˆˆˆLÜ` ÆDLÄsxÕ¨–:µ3HMMEmm­ÔQˆL BˆˆˆˆˆšQˆBD ñˆGâ0 “¤ŽDDDDDDw ¹¸ŒË¨D¥úgèŒÎRÆ¢¶ÎÇر8q˜=BêDDDDDDDDdâÞÃ{PB‰õX/u"jg‚‚‚P^^ŽÌÌL©£™< ©³ld#‘(@~Ãoèƒ>RG"""""¢;4ƒ‘Žt€œà g8Á oâMtAxÂÑC1rÈ¥ KmKŸ>ÀöíÀ¸q@` °x±Ô‰ˆˆˆˆˆÈ„\¼x¯¾úªÆºÿüóOÀ¤Iÿ»á™™™Þzë-øùù<#– <…§°K1“á g©#Q;8wî|}}¥ CdâXBDDDD&í ®À N°MƒçÎà " '8á(Ž¢ ºHˆˆˆˆˆZZô@2 pý¿ÿÀøÿ½l^‰J<ŠG±¤ŒJmQD°v-0g PS¦HˆˆˆˆˆˆLDii)¶nݪõ¹ôôtŸ_yå$""c°Kð5¾Ær,Ç;xGê8DÔN¸¹¹¡cÇŽHIIÁ˜1c¤ŽCdÒ̤@DDDD$•2”¡7zc4F£•ÏíÇ~ ÁøÁqÅ DDDDDíÈ}¸O]øQW-jQùßÿà1} ˆˆŒ+\±Kð!>ÄE\”:µ#AAAHII‘:‘ÉcA™¬ð®â*ã0¦c:jqkúìñ#"‰]ظHœ”ˆˆˆˆˆZÒ½¸U¨jôys˜£?ú#¡LEíÎGÝš-dÂà"[‘aLŸ>–––>oii‰GyÄ€‰ˆÈ<…§à _,Á©£Q;„sçÎIƒÈä± „ˆˆˆˆLÒ\Á;xÕÿýï;|‡ù˜ÕX‡ðÇãøßÁ¶RG%""""¢Öýá‡FŸ¯E-¿§;gflÞ x{ß* )*’:™€)S¦ ºººÑ竪ªððÃ0KXbV ±8Š£RÇ!¢v" ©©©RÇ 2y,!""""“ô^Ó¸#p-jñ>ÃKx ˱«±f<]&""""j—ÌaŽ0„i=ç—A_øb&HŒÚGGàßÿŠ‹˜ ‰AYDDDDDD-ÁÏÏ}ûö…™™–>¯L†þýû# @‚dD$µð†a^À RG!¢v" YYY(++“: ‘Iã7""""29)HÁøB£ nâ&:¢£DɈˆˆˆˆÈPîÃ}0‡yƒåf0Ã+x…âÔr¼½ŸžzJê4DDDDDdf̘¡µ ÄÜÜ3fÌ ‹÷ðá~ÆÏRG!¢vÀßßB(•J©£™4~£EDDDD&ç¼Ðäà®Ù˜=ØcÀDDDDDDdhÃ1¼A‘88Á Ó0M‚DÔ®…„ÿú°aðñÇR§!""""¢v.&&µµµ –×ÔÔ ::Z‚DDd,b ¢—ðªÁ™L‰èÎøùùÁÌÌ .\: ‘IcA™”ƒ8ˆØ¡uà—Š€Àƒx‰H4`2"""""2¤þè;Øi,³„%žÃs°…­D©¨]‹ŠÞ|xæ`Ç©ÓQ;æáá¡C‡ÂÜü3cš››ãÞ{ï…———„ɈÈ,Çr¤! ±Qê(DÔÆÙÚÚÂÛÛ©©©RG!2i,!""""“! ° a‹f×-G9Va•R‘,`!¢1{ 9Ìñ$ž”0µ{‹Ó¦Ýzœ9#u"""""jǦOŸ®Ó2"2=rÈñžÀ2,CʤŽCDmœ¿¿?g!’ BˆˆˆˆÈd|‡ïˆÄF§¾µ„% ÁX÷ßÿˆˆˆˆˆ¨ýº÷Á·î–j KÌÁ¸Ã]âTÔî}þ9Ы0q"PT$u"""""j§¢££afö¿¡afffxðÁ%LDDÆd)–¢ eøJ…ˆÚ¸€€ÎB$1„‘I¨B^Ä‹wÿU±€,a‰ ˜€ƒ8ˆ$$aæÀv$%"""""CŽá¨B 5øþ!q"2 VVÀ?ååÀ¤I@MÔ‰ˆˆˆˆˆ¨rrr˜1c`aa DFFÂÅÅEêXDd$: ^À xï"yRÇ!¢6Œ3„I!DDDDdÖb-2‘‰ZÔ¸UÐ ¯âUd#ßá;„!LʘDDDDDd@!-lá!( 8™ŒÎŸ–,‘: µSÓ¦MCMM jjj0uêT©ã‘‘yÏÁ.ø'þ)u"jÕ•…²²2©£™, ©QûURR‚êêêFŸ¯¬¬Ô躺º6¹=;;;X[[ë£ÅX†e¨E-,a‰jTã>܇X€HDj5„ˆˆˆˆˆÚ®²²2TVV6úü7PQQà=¬`îõ¹(¬)l°®µµ5ìì8{ µ‚þýu뀙3>}€‡–:µ3&L€ `üøñ§!"cc [,ÅRÌÃ<,À @êHDÔùûûC¥R‰»îºKê8D&‰!DDDDíTii)nÞ¼‰ëׯ£¬¬ (**Byy9nÞ¼‰¢¢"ܼy7nÜ@qq1***PZZª.⨭­Eqq1àæÍ›(//£¶¶ÕÕÕ()) 9˜J ŽŽŽ°°°€™™œ666°µ½u§ßÂy…(|¤7, Ø§€"NN%°Ãf¸P¯ëââkkkØÛÛÃÉÉ 666ppp€ƒƒlllàää{{{XYYIÖV""""¢¶@ÕŸ(++Cyy9®_¿®ÑG)--Eyy9JJJPRR‚òòrþHÝþ†¶e¥¥¥¨ªªÒXvÛ¦ Œxt„^/srr‚¹¹9,--áààà} 8::j]æèèõßmmmáàà'''ØÚÚªû#ª¿;;;ÃÌŒEìíÖôéÀ±cÀ#·ŠDˆˆˆˆˆˆôPTT„ôôtddd ==]ýÈÈÈ@FF†ú;>tëÖ Ýºuƒ¯¯¯ú¡úÙÅÅEâ–‘Å£X…Ux¯b¶I‡ˆÚ ???˜™™áÂ… ,!’ BˆˆˆˆŒTUU põêU ¸¸EEEþY÷Q\\ÜäÌ4ŠêHRÍÂaff¹\™L+++ØÛÛ`iiÙhñE]ºÜIW5+HýÙB´)**‚¢ÁrmE*u—‚®_uE—#]p³ð& ¯$•••(..VÉ¢¢¢7nÜh2‡œáââÒàOÕ£þrwwwtìØnnnMn›ˆˆˆˆÈ˜ ??(**Baa¡FߣîÏuÿ®*.oLÝ¢kmØVVVP( ÑGQõATËd2™Ö+u‹4´©û¼ª¸ñÚ×U?_ªR·¡ê›h[vãÆ äççãúõë(//GYY™Æß›R·¿áêêÚìß;t耎;¢C‡Mn—ŒÄGgÎQQÀŸüw#""""¢:®^½ª.î¨_𑞞®Ñ÷ððPyDDD¨ @hlãèÑ£ˆEnn®úµÎÎÎ"u Fºuëwwwƒ·ˆZŸ9Ìñ6ÞÆx‡q¡•:µ1¶¶¶ðööFjjªÔQˆL Bˆˆˆˆ ¤²²¹¹¹ÈÊÊÒ(ôÈËËÓø¹  W®\Ñ:€ª±BbÕr[[[8::ÂÞÞÖÖÖpqq­­­zjh“3K÷U¯_¿Ž›7o¢´´¥¥¥¨¨¨Pßḱ¢œ .4[˜caawwwtèÐAýèÔ©“úïªç<==ѹsgtîܹeß""""2iååå¸|ù2rss‘ŸŸÜÜ\u?DÛÏ555¯·±±ÑZ„àåå¥þ»êOggçF ?¨¡Æ EŠ‹‹µÞ\¹r)))ËoÞ¼©±MsssuaHÇŽѹsg­?{zzÂÛÛ[k¡?€¥%ðÝwÀÀ@L °g`Á¯oˆˆˆˆˆLEaa!”J%²³³‘““¥R©~\¸pAã;CWWW( ( Œ3žžžðòò‚B¡@÷îÝÕ³Uꪲ²—/_n°ÿäädìܹêk666ê}©u÷¯ºÑµ=0Ã1/ãeüŽß¥ŽCDmP@@.\¸ u "“ÅoˆˆˆˆîPuu5òòò••…ÜÜ\\¾|yyy¸téòòòÔƒ­®\¹¢ñ:{{{¸»»£S§Nê9~~~ TgggXYYIÔJÓãää'''têÔ鎶SZZŠââbõ€:ÕÝ•UE@ùùù8{ö¬FQPÝ»[YY¡sçÎðññ‡‡‡úOoooŸyW&""""*((@vv6.]º„ììldeeáòåËË®]»¦ñÕLvª   F \]]M·¸ÜT};¡šùPÕÿÈËËS÷AT?Ÿ´}¤¦¦âúõëêuë|„‡‡cΜ9ꢋ   ØÛÛ·h6Õ ªY:ëSŒh+VIHHÐ(±¶¶†···Öb…B___˜™™µh~"j9˱ƒ0;±÷ã~©ãQ€””©c™,„5£ªª —.]RO¡œ‘‘´´4õß³²²4fpssSß]ÕÓÓ}ûöÕ¸ïããƒ:ðΫ&ÂÁÁðööÖù5EEEÈÍÍU©f–ÉÉÉÁÉ“'‘““ƒ¬¬,»ÿÚÙÙ5˜¾»îŸœi„ˆˆˆ¨í+))R©DZZZƒ‡R©Dyy¹z]'''øøøÀÛÛ^^^ §§§z ¿j`¿g!hWlllàéé OOOÖ¯®®F~~~ƒ¢Ë—/###GŽÁ¥K—PRR¢~­­-är¹úî¯u …Bï»ÑR}ûëÖÓ§={³fIˆˆˆˆˆˆtÐTÁÇùóç5úTÚ >TÅr¹vvv¶¤¡æ FªªªŸŸß XDU0’™™©þµ¹‚‘nݺÁÜÜÜÍ#¢:îÆÝxâ¼€1 -%"=øùùa÷îÝRÇ 2Yü¿6nÝEõüùóHIIÁùóçqþüyuÑGvv¶úÎ6¶¶¶ê÷={öÄØ±cÑ­[7õR½¼¼xÇ\ºc...pqqAPPP“ë]»vM]rùòe¤§§#==ÿý7vî܉¬¬,­Ç®\.G`` ѽ{wtëÖwd""""27nÜÀ¹sç’’‚ääduß$-- êõ¼¼¼Ôƒð|ðA( tíÚU=»CKß1”Ú' uIHHH£ë•––ªg™QÝ$!-- ÇÇwß}‡œœõº:tP›Ý»wGÏž=„ÀÀ@£Ød”¦Myónˆôí+u""""""“W·à£~ÑGJJ JKK–––èСƒºÈ¡nÁ‡B¡@—.]`ii)qkZ–¥¥¥ú;ÒÆú•õß?ÕãàÁƒHKKSßàÂÊÊ >>>Z‹EX0Bdïâ]ô@ü ÿÂcxLê8DÔ†øúú"++ •••°²²’:‘ÉaA™”ÌÌLuá‡êqþüydff¢¶¶æææðõõE÷îÝÑ»woŒ?^c¦β@ÆÄÍÍ nnnÖú|UU•ºPD5ÃMzz:’’’°}ûväçç¸uáîÝ»«ªb‘ÀÀ@¸¸¸²IDDDD&ãêÕ«HNNÆÙ³gqîÜ9õŸBÀÒÒþþþ ÂСC1sæL™XˆN†äàà€=z GZŸ¿yó¦ÖÙk¾ÿþ{¬X±UUUÉdèÖ­‚‚‚гgO¢GèÙ³'ÜÝÝ Ü"#÷þû@RðÐCÀñ〳³Ô‰ˆˆˆˆˆÚ5m ª¢sçΡ¬¬ @ó]»våLœZ¸ºº"$$Dï‚‘ãÇ#==7nÜpëýïÒ¥‹Öb¾ÿD-Cfc6–b)b{ð¦3D¤¹\Žšš\ºt ~~~RÇ!29< &""¢v©²²©©©8~ü8Ž?Žäädœ:uJ=¾îtÌ3gÎDpp0 zôèÁ;–R»aii©0¨MQQ.^¼¨¾°~æÌìÝ»«V­R¹áéé‰àà`ôìÙS}±>((ˆw`""""ÒCvv¶FßäÌ™38{ö,„°¶¶†ŸŸ‚ƒƒ5ú&ÁÁÁ,ú 6ÃÆÆ={öDÏž=|ÇŽCqq1ìíí1pà@Lœ8ƒ  AƒÐ±cG©ãµk6660`  ^&„@JJ Ž9‚#GŽ`÷îÝøàƒP[[ ¹\ŽÁƒcذa5j …„鉈ˆˆî\mm-Nœ8={öàÀ8räJJJàêêŠÐÐPÌ›7C† ÁÀacc#u\¢6ÉÁÁaaa S/+//Gbb"8€Ã‡céÒ¥(**‚££#BCC1tèPŒ3ýû÷o7…X´8tˆ‰Nž8ã)µ3ª‚úƒõUŒŒ ÔÔÔ¸õ=EÝððpõ`}…B¹\ÞþútÇt-©ü%$$àÂ… (..ÖØ–¶b…BÀÀ@Þ „Ú {Øc)–b`>æÃþRG"¢6@.—C©TJƒÈ$± „ˆˆˆŒŽR©óKh IDATD||<âââð믿¢¨¨]»vŰaÃðÖ[oaðàÁèÝ»7,,x*C$5™L†   á‘G\¿~GÅ‘#GpôèQ<÷Üs(++ƒ¿¿?"""#FÀÉÉIâôDDDDÍ»víâââ°{÷nìÙ³W®\··7FŒwß}C† AÏž=y‡Q¢Vdkk‹¡C‡bèСþ7{¨jvžµk×bñâÅèܹ3ÆŒƒÈÈHŒ5 nnn'offÀ7ßýû3g;vàFDDDDmHEE²²²´ÎРT*‘žžŽÚÚZÚ >êºgÁµ] F´+%$$àâÅ‹(**ÒØ–¶b…BîÝ»ÃÑÑÑPÍ"ºcãq¬Á,Á|‹o¥ŽCDm€\.Ç¡C‡¤ŽAd’8Š’ˆˆˆ$W[[‹ßÿß}÷öîÝ‹ .ÀÁÁ÷Þ{/^ýuDDD ((Hê˜D¤#'''uápëî^‡F\\âââðÙgŸÁÌÌ ƒFdd$~øaÎBDDDF%33[¶lÁŽ;pìØ1Èd2„……aáÂ…ˆŒŒDŸ>}¤ŽHdÒÌÌÌЫW/ôêÕ O>ù$àÔ©Sؽ{7vïÞiÓ¦A{î¹&LÀäɓѵkW‰Sß77 6¸÷^à½÷€_”:‘Zý‚úEº|Ôüüüàââ"qkˆZž­­­ú8צ±ß¡3gÎ !!¡Éß¡ºþ‘&b"BŠ—ñ2öc¿ÔqˆÈÈÉåräç磴´RÇ!2),!"""ƒºxñ"6nÜˆØØX(•JâñÇGLL g!2:t@LL bbbP]]}ûö!66Ë–-ÃsÏ=‡aÆaêÔ©˜ŠQ£FA&“IWwÏ?> Lšœ< xxHˆˆˆˆˆÚÆf7P}äææj VWÍnŒñãÇsv¢fØØØè]0¢ú¹~ÁHýYvê?\]] Ù42ïâ]„! »±‘ˆ”:1¹\HOOG¯^½$NCdZXBDDD‡Õ«Wc÷îÝðòòBLL &OžŒþýûKˆ$daaÑ£GcôèÑøì³Ï°{÷nÄÆÆâé§ŸÆ /¼€Y³faÁ‚ê DDDD-¡²²ÿú׿°råJ$''cذaX³f zè!Þi‘¨±²²Â„ 0a©g=z4‚ƒƒ±páBLŸ>½m€ÉdÀÆ@H0u*˜›KŠˆˆˆˆŒ\s999êuU …aaaÏàää$aKˆÚ§æ F***••Õà÷VU0’žžŽÚÚZõ¶+ñôô„§§gÛº1…P„â<€EX„Ñ 3˜I‰ˆŒ”\.‡L&CZZ Bˆ Œ!DDDÔªvî܉7ß|üñFމíÛ·c„ °°àii²¶¶Æ<€xW¯^ņ °víZ|òÉ'˜:u*/^ ???©cQVSSƒýë_xã7››‹©S§âÛo¿EïÞ½¥ŽFDàââ‚Ù³gcöìÙHJJÂÊ•+1oÞ<üóŸÿÄÒ¥K1cÆ ˜{…«+  ¼ó°x±Ô‰ˆˆˆˆHb………Äë>.^¼ˆ¢¢"õºu >ÂÃÃáéé©<Þ½{w8::JØR5jâãã öú;Ý_koÏÔY[[7Y0RYY‰Ë—/7(QŒddd ¦¦@ã#ªÏÕ@^¢ú–c9z¡¶b+&c²ÔqˆÈHÙÙÙ¡S§NHKK“: ‘ÉáHL"""j)))xæ™g°gÏ„‡‡ãرc¸ûî»%É¢ËE+Õ4»­ÃûiMª÷òvÛÑïÁfjKûníýIù^ÖçîîŽ_|Ï?ÿ<¾ÿþ{,]º=zôÀܹsñæ›oò.dDDD¤·'N`îܹ8yò$bbbðúë¯K2 û'†Ç~HÛÚŸ¡ôîÝ7nÄ믿Ž÷ßsçÎÅêÕ«±víZ 4HêxM»ûn`Å à…€ðpÀØóÑ),,Ô:Ð[©TâÂ… (..V¯[¿àcΜ9êÞppp0Xîúý_¸»»£_¿~ˆŠŠÂ”)SZt¦¾ºûkëýÕl-ùú¦úÆ·»¿Æú‹wš¿­ûõ×_±fÍüç?ÿAMM 0þ|Ìœ9³UŠ-¬¬¬t*ÑöR¿`ÄÚÚÞÞÞZ‹E |}}afÆÙ!LQ 1Sñ*^E¢`…60Ó*IB.—³ „H,!""¢%„ÀêÕ«ñâ‹/¢W¯^8pà† "u, ‘õ/xò.'ºBÝû%e&Cﻵ÷gŒÿ¾fffˆŽŽFTT6oÞŒçŸ?ýô6oÞŒ¡C‡JˆˆˆÚ!Þ}÷]¼úê« ÃÉ“',y&öOÚ&öCÚ.]º`ÕªU˜?>æÍ›‡!C†`ñâÅXºt©qrùÇ?€øx`êT௿ÞÉ™ˆˆˆ¨ÍR|h+ú8þê}¤¤¤ ´´T½nS …¶¶¶¶äÎØÚÚB.—C.—ãá‡ÆçŸŽˆˆüõ×_pvv–:Q‹Ú´iz÷î àÖ •_}õÂÂÂ$)iŽ¥¥%¼¼¼àåå…ÏëS0bee­Å" …ݺuƒ¹¹¹¡›H-¤ºá <7ñ&fb&ì`}cÇŽ…££#1vìXœ>}ºÙ¶©¶¥ËrÕ²äädŒ3NNNpppÀ¸qãpöìÙÛ8sæ ÆŽ 8;;câĉÈÌÌÔšã—_~AXXlllàëë‹… jܱJ×÷`ܸqê÷`̘1:½Ñu{ºd×÷ýÐeßMk·ÓV]ŽŸS§N!""öööprrBdd$öîÝÛèvýýýÕ™Œa¦èر#~ùå,]ºsçÎÅÇ,u$"""2RµµµˆŽŽÆþýû±oß>ÌŸ?ß(ŠAÚsÿhþœS—óà–<—g?¤åû!MõšËp'Ç­!Èd2,X°ûöíï¿þŠI“&÷Ý…;uÖ­6n¶m“: ‘É*,,ÄñãDZ}ûv¬X±O<ñÆÀÁÁnnn0`¦NŠU«VáСC€ððp|ðÁˆÇÅ‹QUU…k×®!11Û¶mÃòåË1g΄‡‡#88¸Mƒh3gÎDDDàý÷ß×X~'}Ò¦ìß¿_£ï¡RZZ GGGdgg«—YXX¨×Û¿¿^ÙT¯»téþïÿþŽŽŽèܹ3¦M›†«W¯6›³©ïøtÙfc¯¯û\ݾqc}B]û­úä¯ÿ°±±Q¯s»ýs]ú¹º¶E—ïÑtÉ)„Pƒ¨¸»»£¢¢¢Ùö#UÁHHH¢££ñÒK/aݺuZ?»6oÞ¬.dËÉÉÁŽ;°páBŒ5 ~~~°³³ƒŸŸ† ‚I“&áå—_ÆçŸŽ„„(•Jua ¯ÅXŒR”b5VK…ˆŒT—.]½†MD­H‘^ ¶Š­RÇ v€ØºµõާuëÖµÚ¶ë{þùç…ƒƒƒ8uê”Áöy»š; F%:$nܸ!víÚ¥~MRR’8p ÆúµµµÂßß_üõ×_·µßÔÔTáãã#6lØ òòòD^^žøâ‹/„···HMM½íöh[@ôêÕK|ûí·¢  @½¯®]»Š´´4õz.\]ºtQgÊÏÏ›6mƒnt»ëÖ­eee"''G̘1CÌœ9S¯÷ÀÃÃC¬]»V\¹rEˆM›6 …B!.]º¤÷{ ÏötÉ®Ïû¡ï¾;Öôi«.ÇOJJŠ ñññ¢¬¬Lœ9sF :´Áöêþœ˜˜(|}}ERRR“™¤òöÛo KKKqøða©£h}ëADD-*11Q$&&¶Úö£££E´>¿ y¾nŠÞÿ}aee%Ž9"u”&µ§þ‰>眵©¥Ïå›k+û!ú÷Cë'èšáNŽ[C:tè°´´~ø¡ÔQš÷øãB¸º ‘™)u""¢6A×¾Xk÷?©í¸víšHLLÛ¶mË—/sæÌááá¢gÏžÂÎÎN„•••P(",,LDGG‹—^zI¬[·NÄÇÇ‹‹/Šêêj©›bpÍõ5þüóOq×]w©n­ïÌT…“““Æ¿ÅW_}%¼½½ÅG}¤±nPPÆg€>Ùˆˆˆ±{÷nQRR"233ÅĉŬY³šmCcíÐg›½^ßýÝnŸ»©ïž„¢¢¢B 8P¬]»VÑ2ýó¦ú¹º´E—kw’óÙgŸãÆk¶-í•¶ÏÑûï¿_„„„{{{õ稥¥¥ðôô!!!Z?G«ªª¤n !–Š¥ÂE¸ˆ«âªÔQˆÈíܹS%%%RG!2C^?hl¼) BˆˆôÄ‚jIí¥ äòåËÂÊÊªÍ hÓe Ëo¿ýÖèóýúõÓ¤²k×.qß}÷Ýö~§N*V®\Ù`ù| ¦M›vÛÛmìlll¬Ö}Õ½ð9mÚ4­™¾üòËfß?!n]ÔsssÓ9ëÔ©SÅŠ+,ß°aƒX°`A³û«¿Ý;Ùž¶ìú¼ú컹cMmûÓåø™çœµ©¥ÏåUûké}iÛ®)ôCšê'è“áv[C[ºt©pwweeeRGiZi©B„‡ QS#u"""£Ç‚ª«²²Rdeei-øP(¢AÁGxx¸˜>}: >tÐ\Ÿ·´´TØÛÛ«n­ïÌTjkkEçÎ5n1räH/BCCÕËRSSEçÎEmmíme ~úé'eçÎ^^^Ͷ¡±vè³Í–(©OŸ>wsÛ›;w®˜-nܸ!uSLB‰(D'ñ²xYê(Dd„N:%ˆ³gÏJ…È`Œ¡ Döß'‰ˆHG2Ȱ[1 “¤ŽBí€L&ÃÖ­[1iRëOŸþ9æÌ™Ó*Û®ëã?ƲeË““KKËVßß’ÉdhêH&“¡¬¬ vvvZŸ_µjRRRð駟Ƈ§žz ãÆ»­ýzxxàØ±cèÖ­›Æò´´4„††"''ç¶¶«m¹L&CAAÜÝÝì+,,L=vc™rssáééÙäû×Üþõyòòò0bÄ$''ëµ/CmOÛû¡Ï¾›;Öî$[ýãÇÃÃ'Nœ€——W³ÛW*•Çúõë1bijIa÷îÝ;v,òòòЩS'©ãhR}¶oÛ&m"¢væøñã€VÙ¾êÜ|›>¿ u¾nŠvî܉ & ??¿Á¹¯±iOý}Î9kSKŸË7¶¬5öÕÞû!ÍõôÉp»Ç­¡åçç£sçÎØ¹s'ÆŽ+uœ¦?„†Ë— J†ˆˆÈ¨éÚkíþ'ŽR©DZZÒÓÓ‘‘‘ôôtõ#;;555[[[ÈårtëÖ ¾¾¾vëÖ žžž·¤íi®Ï[ZZ ”––h½ïÌêš5kðꫯ"++ <ðþüóOŒ9ëׯ‡\.LJ~ˆ¤¤$|õÕWê×é“M&“áÚµkpuuU/«¨¨€­­-jkk›Ì×X;ôÙ¦>}ãæž»í6µ½o¿ýo¼ñþüóO888h™þtK|ßÖÜ5 }sÊd2tëÖ QQQxá…àáá¡s>Ò”““ƒŒŒ õgxÝ?ÓÒÒP^^077‡——|}}ÕÕg¹\.‡B¡¸%íLJø‹±çq>ð‘:‘ÂÂB¸¹¹aïÞ½ˆˆˆ:‘AòúAcãM-Z}ÏDDDÔîedd@¡P´‰b]5uÁpÊ”)èÑ£Þÿ}äææ"--펆h½èé鉂‚‚ÛÞnc´ ˆóôôD~~~³™´-ËÏÏÇ+¯¼‚={ö 77Wýʼn>®^½ ___­ÏÙÚÚ¶ÚötÍ®Ïû¡o[ô¹8­O¶úÇOAA:tè Ó6ÇŒƒÒÒR”••ÝQ6CèÑ£ ==Ýø BˆˆˆH2—.]‚»»»Ñƒèª­ôOô9çl¬M-}.ßöCôÓ\?AŸ -}ܶ–Ž;ÂÍÍ RGi^H°t)ðÊ+À}÷}úHˆˆˆˆÈ(ÄÅÅaôèÑ –›››£OŸ>ˆŠŠÂðáÃ1hÐ tîÜY‚„¦-99~~~êŸ ñYdd$Ö­[‡W_}[¶lÁ”)S111ˆÅ+¯¼‚;v`îܹ¯Ó7[Ý °¶¶Ö©è¢)­±MmZ¢Ï­Í¹sçðÌ3Ï`ß¾}êb eúçõsõéƒ7wMãvr¦§§7¹MÒ§§'<==1hÐ ­Ïçååáèѣؿ?~ÿýw>|h°'·œ§ðVaÞÆÛøŸJ‡ˆŒˆ««+qéÒ%©£™„ÑóòòBff&jjj`nn.uœV×±cG„……!66gϞł “Én{{:t@NNNƒ ˆ999: ¦’Éd¨¨¨€µµµzÙµk×]?//¯Á—999èØ±£F¦ÜÜÜw¸¹zõjƒíMŸ>8xð |||`aa¡Î¥«: 99¹Á…ìÛ¥ëötÍ®ÏûÑÒmiŽ®Ç»»; š½[3,[¶ ¸ÿþûñóÏ?ãž{îiéØ-F©T||xç"""úOOO\»v %%%ptt”:N«2¦þ‰>çœMí¿%Ïå[b_-½½¶Úi®Ÿ O†–>n[Kqq1 ïè˜6¨W^€)S€ÄDà6 ›ˆˆˆˆÚ›‘#Gâ?ÿùz†ºw–ÿûï¿qâÄ ¬\¹ÖÖÖê™@êÞQ^õðôô„™™™ÔÍiwÖ­[‡x@ýó~g¦‹ˆˆ<öØc(//Ç·ß~‹]»v¢¢¢0zôh<ùä“8zô(~üñG×"›±h‰>w}eeeˆŠŠÂŠ+ЫW/çZ³O«k[t¹¦aèïéjkk‘““£1ÃSÝYB222PQQ°´´D×®]|–Ëår„……IÜ’öÃ6XŠ¥˜‹¹xÏÂþRG""#âããÂ"co•ˆˆˆîØøñãQPP€íÛ·KÅ`fΜ‰O>ù?ÿü3f̘¡Ók»HŽ~ø¡ÁòüáááÍn×ÃÙ™™Ë~ÿý÷F×ß»w¯Ö}Õ½JDD~úé§ëíÛ·¯Á²Ã‡ãõ×_‡¯¯¯ú"ê7´î»±÷`ôèÑØ¿ƒå@ÿþýµ¾¦)ºnO×ìú¼-Ý–æèzü > ëœ:u ={ölðÚÉ“'cÀ€ؼy3|ðA¤¦¦¶xî–²fÍÜ}÷Ýmg€ÄðáÃagg‡õë×KÅ Œ¥¢Ï9gcZú\`?¤¥ú!ÍõôÍp;Ç­¡­_¿ööö>|¸ÔQtcf|ý5“s«8„ˆˆˆˆ`nnŽaÆaæÌ™xíµ×ðå—_â×_…R©Dee%®]»†ÄÄDlÚ´ >ú(är9rrrðã?â‰'žÀ!CàããøùùaÈ!˜4i^~ùe|þùçHHH€R©DuuµÔMmsÖ®]‹}ûöáÙgŸU/»ÓïÌtáêꊾ}ûâ“O>AçÎÕ³~¸¹¹ÁÃÃ~ø! ×"[kÒ§˜CŸ>·®ž|òIÜsÏ=˜5kVƒL­Ù§Õµ-º\ÓÐ7gkÌÞÒžâøñãØ¾};V¬X'žxãÇÇ€àèè 2<ò6n܈ääd¸¸¸`âĉX½z5âããqñâEܸqJ¥¿þú+¾üòK¼öÚk˜9s&† f7·4¤Y˜øã5¼&u"22]ºtaA‘¡ ""Ò ÄV±UêÔN[·¶Þñ´nݺVÛv}=ö˜pwwiiiÛçíjîH—S¤ŠŠ áææ&-Z¤ó~}||ÄáÇEee¥Ø»w¯ðññBqþüyáíí-¾øâ ‘——'®\¹"Ö¯_/¼½½Ejjj³Û1c†x衇ĥK—DII‰Ø³g=z´Öv¡¡¡â›o¾ê}uíÚUãßîâÅ‹¢K—.bÆ âÊ•+âêÕ«bëÖ­¢OŸ> ¶;zôhñøã‹´´4QQQ!RSSÅŒ3´î¿±÷ --MÜu×]bûöí¢  @\¿~]ìØ±Cx{{‹Ÿ~ú©Ù÷ þ¾tÝž®Ùõy?ôiË휎×®ÇORR’ðóóñññ¢´´Tœ:uJôïß_|úé§Mn?66Vøûû‹ÜÜ\½³¶¶/¾øB˜™™‰½{÷JE»èè[""jQ‰‰‰"11±Õ¶-¢ ôùmÈóuS´xñbaoo/Μ9#u”&µ§þÉížsÖÕÒçòMµ•ýÝéÒOÐ÷ý¼ãÖNŸ>-ìííÅÒ¥K¥Ž¢¿Í›…É„0Ö¾‘Ät틵vÿ“Ú†k×®‰ÄÄD±mÛ6±|ùr±`Á-BBB„ƒƒƒ KKKáéé)BBBDtt´x饗ĺuëD||¼¸xñ¢¨ªª’º)’¨Û—(//iiiâÛo¿#GŽÁÁÁâüùóëßéwfºöwÞzë-aoo/¾ùæåß|ó°´´o½õVƒ×è“­±ºækì;¾;Y·±¾±¶õõésë²ì³Ï>wÝu—¸qã†ÖõZº~;mÑ嚆>9CCCEXXX³ÙMIýÏÓ9sæˆððpѳgOakk«þ<µ²² …B„‡‡‹éÓ§7ø<­®®–º)TÇv±]È„Lœ'¤ŽBDFä±Ç£F’:‘ÁòúAcãMYBD¤'„PKjO!eee¢_¿~¢k×®âìÙ³Û¯¾T’šºhÚÜ:BQUU%|}}Evv¶ÎûÞ¶m›ËåÂÊÊJøûû‹ŸþYý\RR’3fŒ°··öööb̘1"))I§íæçç‹)S¦ˆŽ; {{{1~üx‘™™©µ DZZš¸ÿþû…£££°··‘‘‘"99¹ÁvOŸ>-"##…½½½pppâÌ™3 ¶›——'¦OŸ.:uê$¬¬¬D¯^½ÄÖ­[µî¿©÷àüùó"**J8;; {{{1pà@ñÃ?4ÛþÆþ½tÙž>Ùu}?tÝ·®Çš.mÕõø9|ø° 666¢K—.âwÞÑxÞÙÙYcûYYYûÜ´i“N9 á‹/¾æææÆ=8‹!DD­‚!¤«ÊÊJ&¼¼¼´žïƒöØ?iîœS—6µô¹<û!ÚsëÚѧŸ Ïûy;Ç­¡œ9sFxzzŠaƵÝ{?,D×®BK„ˆˆÈè° „ZRÝÎ+W®/½ô’º`ÄÑÑQãÜÙÕÕUkÁÈéÓ§EYY™ÔMiqõûÖÖÖÂËËKŒ7NlܸQܼySëën·OªO_çĉÂÁÁ¡Áû^ZZ*ìììÄÉ“'o;[»Öw IDATc9tͧm=}¶Ùغõµ­¯k¿U×ý[[[78ê¯Óýsmï­>}ðæ®iè“sРA"44´ÙüíEee¥ÈÊÊÒZð¡P(„………ÆgªàcΜ9âµ×^cÁGV+jÅ 1HŒc¥ŽBDFdÙ²e"((HêDc !²ÿ>IDD:’A†­ØŠI˜$ujd2¶nÝŠI“ZçxúüóÏ1gΜVÙ¶6………¸ÿþûqúôi|ú駘:uªÁömhÛ¶mÃÎ;ñõ×_KE/2™ŒÓS›VVV†… â‹/¾Àk¯½†eË–I©qªÏömÛ¤ÍADÔÎ?~Ò*ÛW›o3Àç·¡Ï×MÑõë×1nÜ8üý÷ߨ°a¢¢¢¤ŽÔ*Újÿ„L›±·Û·oÇìٳѧOüûßÿ†£££Ô‘nOAЫðÀÀgŸI†ˆˆÈ¨èÚkíþ'™†ÂÂB(•Jdgg#''J¥RýHMMÅõë×Õ뺺ºB¡Ph<<==áåå…   ØÛÛKØ"2e•••¸|ù²ÖÏ2¥R‰ŒŒ ÔÔÔ¬­­áíí­õ³L¡PÀ××fff·ˆZR0 £ð~Ãp —:7âé§ŸFYY™ÔQˆ Â×ojÑê{&"""“áêêŠýû÷ã­·ÞÂŒ3ðÕW_aåÊ•–:Z‹‘Éd8vìV¬Xõë×K‡È¤ìرÿøÇ?PXXˆØØX<üðÃRG""""#çää„„„¼øâ‹x衇pÿý÷ãÓO?E—.]¤ŽÖ"Ø?¡¶ÈXÛœœ¼ôÒKØ´i¦OŸŽuëÖÁÖÖVêX·¯C`Õ*`òd * 5JêDDDDD&ÉÕÕ!!! j¬`$!!/^DQQ‘ƶ`C5‹ˆÚUÁGclu >lllÔŸ= …áááŸGr¹2™Lâ‘!…##1/ãeÁÈÀ"S×¥Kܸq×®]ƒ›››ÔqˆL Bˆˆˆ¨EYZZbÙ²e1b,X€þýûcÖ¬Yxå•Wàëë+u¼1dÈÌ›7ýúõ“:Š^TÞ8Kµ5{÷îÅo¼#GŽ`úôéX±b<<<¤ŽEDDDm„µµ5V­Z…ˆˆ<ýôÓèÕ«.\ˆ… ÂÙÙYêxw¬­öOÈ´Óq[TT„?ü+W®DÇŽ±k×.DFFJ«e<üð­ çÌþþàA""""££KÁˆ¶ÁÙ P*•(,,ÔØ–¶b…B€€899ªYDdd***••¥þ ©ÿ¹’žžŽÚÚZÚ >ê~®°àƒ´YŽå¸wc'vb<ÆK‡ˆ$¦º)×¥K—XBd ,!""¢Vqï½÷âĉøòË/ñÎ;ïàË/¿Ä¤I“0þ| 4Hêx·­-R´åìdz***°mÛ6¬^½‰‰‰ˆŒŒÄÑ£Gq÷ÝwKˆˆˆÚ¨qãÆaĈX¹r%Þÿ}¬Y³O>ù$æÏŸOOO©ãÝžãS[d,Çmvv6>þøc|öÙgÉdxå•WðÌ3Ï´íYA´ùä 8X¼X¹Rê4DDDD¤'WWW¸ºº"88Xëóååå ŠE”J%:¤ð]w[ÚŠE üýýÛÅMˆLUý‚úEº|Ôý| Ò× Àƒx‹°ã0f0“:I¨nAHŸ>}$NCdXBDDD­ÆÜܳgÏÆ¬Y³°eˬZµ ƒFHH¦OŸŽèèh^P"" üñ¶nÝŠÍ›7£°°>ø Ö®]‹HˆˆˆÚ;;;,Z´óæÍÃêÕ«ñÉ'Ÿàý÷ßGTT¦M›†ˆˆXXð’)Q{U]]¸¸8lÚ´ ?üðÜÜÜðì³Ïâé§Ÿn¿ƒß<<€>yˆŠ†•:µ [[[õ@nmnÞ¼‰ììì#gΜABBÒÒÒÔEÛ®®®Z‹E üüüàââbȦQý.«Š>šú]®[ðÁßejMoãm#[°Ó0Mê8D$!{{{¸ººâÒ¥KRG!2üv“ˆˆˆZ……f̘3fàðáÃøüóÏñÚk¯áÙgŸÅ°aÇzîîîRG%" üý÷߈Ell,”J%0wî\Ì™3‡EcDDDÔ*œ±dɼøâ‹Ø²e 6lØ€ûï¿:uÂÃ?ŒiÓ¦aàÀRÇ$¢òÇà›o¾All,òóóŠÏ>û S¦LµµµÔñZߌÀ?³gý´·YPˆˆˆˆ¨Q666MŒ46«€R©DBBB“³ Ô}pV¢;ÓØl?u‹>TêÎöŒñãÇs¶2 ÝÑ30K°ш†5Làš 5ªk×®,!2 „‘A…††"447oÞÄîÝ»«¾çÈ‘#1nÜ8DDD {÷îRG%¢VRUU…Ç#..?ÿü3Μ9ƒ®]»bÒ¤Iˆ‰‰AHHˆÔ‰ˆˆÈDX[[ã‘GÁ#<¥R‰o¾ù›7oÆêÕ«„¨¨(DFFbРA077—:.騦¦GÅ®]»ðý÷ß#%%˜?>¦M›¹\.uDÃûôS 8X¶ X±Bê4DDDDd$¬­­õ*Q L×§`DU,"—Ë!“É Ù<"£QXX¨ñûSÿQXX¨^WUð¡P(¦ñ{''' [BÔ´7ð¾Å·Xõ˜‡yRÇ!" ùøø° „È€XBDDD’°±±Áĉ1qâD”––â—_~Á÷ßÅ‹ãé§ŸF·n݈ˆŒ9®®®RG&¢;’’‚øøxÄÅÅá·ß~Cii)üýý‰uëÖ!44”_‘¤ –,Y‚%K–àØ±cزe ¶lÙ‚·Þz nnn5jÆŽ‹1cÆ S§NRÇ%¢zòòò°gÏìÞ½qqq(,,„\.Çøñãñõ×_ãî»ï–:¢´¼¼€wßž|ø¿ÿBC¥NDDDDDm@s#•••¸|ùrƒbUÁHFFjjjh/Q‹( ŒP›VXX¨õ÷@©TâÂ… (..V¯[·à#<<\ã÷ 00¶„èÎxÃOâI¼703àG©#‘D¼¼¼ T*¥ŽAd2XBDDD’sppÀ”)S0eÊTWWãØ±cˆ‹‹C\\6nÜ0`† †ÁƒcðàÁððð855¦¶¶ÉÉÉ8räŽ9‚_ýpvvÆ}÷݇÷Þ{~DDDD$µ{î¹÷ÜsV­Z…sçÎa×®]سgæÌ™ƒªª*ôïß#FŒÀ!CŠ:H™ÈäàðáÃ8xð ~ûí7œ8q–––6l–,Y‚±cÇ"00Pê˜Æeölà»ïnýyâ`c#u"¢ÿgïÎãª*ð7Ž@vÙE\@Ü…ÒÔé×¢–æRfjæ’éhfe9•-j¥ÕT:N53šÅ¤-j3eÙÔ”8Sie¥¥æ.€Š,*«ì üþ¸sïp媨ÀayÞ½î‹Ë¹‡sŸ„÷ÀyÎWDDDš8''§ZFl$naÄÙÙ™öíÛÛ,‹DDD†½½}C…¹ða«ôqèÐ!òóó-ëž[ø˜9s¦åû9**ŠÖ­[¸'"õo XÍj^åU°Àè8"b€€¾ûî;£cˆ´*„ˆˆˆH£âààÀÀ8p ‹/&''‡-[¶ÏçŸÎ+¯¼Bee%aaa 0€þýûsÍ5×лwoôÒFĹ¹¹lß¾íÛ·óý÷ßóÃ?——GëÖ­‰eêÔ© :”~ýúéÿSir¢¢¢ˆŠŠbÞ¼y²e˾øâ ËñIUUQQQ 8ÐRéܹ³Ñ±EšC‡ñí·ß²mÛ6¾ûî;°³³#::šk¯½–§Ÿ~šn¸A'Ö\ˆüõ¯Ð³',] Ï)((Lß“~~~–ïŸê…ˆˆBCCõ·/‘Z˜Ç<^ã5–³œgÐ)DZ¢ÀÀ@rrr(--ÅÙÙÙè8"Íž^¡ŠˆˆH“âééÉСC:t(UUU$$$°}ûv6mÚÄúõëÉÏϧ¢¢{{{""",E‘ž={Ò£G:tè ‘Ò"QRRBBB{öìaÏž=ìÞ½›]»vqúôiBCCéÙ³'cÆŒáꫯ¦ÿþøûûœZDDD¤aùøøpË-·pË-·PVVÆŽ;øî»ïøùçŸùûßÿÎÒ¥K©¬¬ÄÛÛÛª Ò»wo"##qtt4x/DŒS^^Nbb"»ví²*€äææbooO—.]èÓ§¿ÿýï0`111899»é›3Þyî½þóÓä‘FÊÇLJ˜˜˜K*Œ¤¥¥±ÿ~8@QQ`:Ñ?$$ÄfYD'ú7}çû>HOO'!!ÂÂB æ÷ "õà /á–²”û¹ôwd‘–& €ªª*233 5:ŽH³§W°"""Ò¤•••±}ûv–/_ί¿þÊõ×_ϼyóèÛ·/;wîdÿþýìÛ·÷ߟ… RYYi¹BPtt4ݺu³ü‚¯{÷î½K" *''‡}ûö±ÿ~Ë/È÷íÛGbb"gÏžÅÑÑ‘Î;ÃàÁƒ‰ŽŽ¦_¿~*ˆˆˆˆØàääÄ€0`€eYYY‡bçÎìܹ“~ø¿üå/ãàà@hh(VÇ'={öÔë-iVòòò8|ø°åxÃ|ü±oß>JJJppp K—.ÄÄÄ0zôhbbbèÝ»·¦~Ö{{xí5èß6l€ñãN$""""rÙ.µ0b. ÄÇÇ_p2„ #Ëù&Ť¥¥]pRL·nݬ š#ÒpäAV°‚—y™?ð£ãˆH  ==]…‘ #i’rrrxíµ×xýõ×ÉÉÉaüøñ¼ýöÛôéÓDzNPP#GŽ´¼æÌˉ‰ûŒƒRZZ @Û¶m‰ŒŒ¤K—.„‡‡Ó¡CÂÃà #((H“E¤É)))!%%…””Ž=JJJ GŽáàÁƒŽ;FEEÎÎδoßÞòy‰‰±úZ¨ð!Òx´¦5ñó8s™K{ÚIDù‚¼'itf—ˆˆˆ4)ùùù,_¾œåË—ÓªU+î½÷^î¿ÿ~K³üB<<<èß¿?ýû÷·Z^YYÉÑ£G9xð ¥,rèÐ!¾ùæŽ;FYY`:‘+$$„:V£,„««k½ì·Èùdgg“žžÎ±cǬJæ·Õ®½¼¼ £cÇŽ 6Œ¹sçZNÆjÓ¦{!"""Ò²8::Ò«W/zõêeµ¼²²’””Ø¿?‰‰‰ìÝ»—O>ù„´´4ªªªðõõµ*ˆ˜ï‡††Œ‡‡‡»%Í\~~>©©©;vŒääd«òGrr2999ØÙÙYNÎéÒ¥ Æ #::š¨¨(ÂÂÂt¡…ÆäÅaãFxæxå£ÓˆˆˆˆˆâR #æ²HRRñññ:tˆüü|«mÙ*‹EëÖ­j·•²²2RSS­>ç>þQQÁ@œ“.ÿðÁÀ­¦¶è¸R¤i¹—{YÆ2^à^åU£ãˆHrqqÁÛÛ›ôôt££ˆ´*„ˆˆˆH“PXXÈêÕ«Yºt)ÅÅÅÜwß}<þøãure\{{{Ë T7ÝtSÇm NKKc÷îÝ$$$PXXhY×ÅÅ…   Ë/wm½ Õ ZrQÅÅŤ§§[~9nëmjjªÕÌßôìÙ“[o½µÆDDDD¤ñ²···¼~>|¸Õcæ“'Î=6Ù²e‹å5¢ÙùŽKª¿. Õ$8±ÈÉɱ:1çÜã#GŽ››kY¿ú±Gll,ãÆ³|ï¶ä“œš__X²æÌ)Sàœ’šˆˆˆˆˆÔ¾0rnÙ!>>žÃ‡“——gµ-[e‘ˆˆ"##qwwo¨ÝªSÕga«ôqôèQΞ= XOFDD0xð`ÚûûÓóË/ñ¯ñ\i) lšfص«Á{&"Wʰ€¹Ìeó'ÜèH"Ò€5!D¤è/~"""Ò¨óÚk¯ñÒK/QZZÊܹs™7oÞÞÞ –áB¿è­ªª"==ÔÔT2228q⤦¦’™™É×_MFF'Ož´\ÍÀÓÓÚ¶m‹ŸŸmÚ´¡mÛ¶øûûãççg¹™—©@Ò´UVVrúôi«ÛÉ“'­Þ?uê”Õ²’’ËÇ;;;Ó®];‚ƒƒ  k×®\ýõ´oßžÀÀ@Ú·oOppp¤DDDD¤qrrr²œ,aKnn®ezƒùåĉ¤¥¥ñõ×_“žžÎÉ“'-ëÛÙÙѶm[Ë1I»ví,Ç#mÛ¶% Àò˜ù­4æc óÛŒŒ Ëû'Ož$33ÓêñêÇ«þþþLPP±±±HHHÁÁÁøøø¸wR§fÌ€¸8¸ÿ~øæ°³3:‘ˆˆˆˆH“R›Âˆ­’D|| #FèXE¤ ›Ît^â%–²”7xÃè8"Ò€Ti *„ˆˆˆH£µiÓ&æÎËÉ“'™>}: , ]»vFDzbggGPPÐE'/”——“™™i)Šœ:uŠÌÌL²²²8}ú4™™™ìÝ»×R(**²úxGGG¼¼¼ðööÆËË «÷mÝ÷ððÀÛÛÜÜÜðôô¤U«Võùéh–Š‹‹)))!77—’’ ÈËË#''‡ÜÜ\òòò,oÏw¿ú•tÍ|}}-'ÖùùùBLLŒUÈ\iÛ¶­{.""""M‰··7ÞÞÞtïÞý¼ë”––’––f)ŠœnžîDÐo¿ý¶ÆDPsaäܲHDD:uºì ‡[ø8·ôQ›ÂGL«wq1M/œ4 þýoX±F†Îá¾ûà·¿ýnA¤ÉqÄ‘§yšéLç!’H£#‰HÑ„‘†£Bˆˆˆˆ4: <ôÐC|ùå—Œ;–—_~™ÐÐP£c]GGG‚ƒƒ ®ÕúEEE–±N:Evv¶UÁ ''‡¼¼>>¸¸¸àêꊓ“­[·ÀÝÝGGGìíí-¿H6¯XM.V:ñöö¶}ÕŸó(++£°°ð¼›ËçÞÏËË£²²’ŠŠ Μ9@aa!eee–«-åååQZZJAAgΜ¡´´”üü| )--µYä8wl•q:tèP£ÀS}ò‹ŸŸz)."""" ËÙÙ™ððpÂÃÃ/ºnEEEIvÕ æûÇŽcÏž=VËÊËËÏ»Ýê¥uooo\]]quuµºïììl)˜!ª/óòòÂÞÞÞj™««+...ç}ÞKhQý ­ç*))¡¸¸0»•––RYYI^^žÕ²ªª*Ë1…yYqq1ÅÅÅäææRTTd)ŸW/œ£££Í"NDD„e¹ù­yú‹¹ô¡ã¹ ¾}aÖ,øýïaäHhÀ©¬"""""-««ë'‚æçç“’’ÂÑ£GINNæèÑ£¤¤¤°sçN>üðCN矆ëÍàçëGXX:t°z@JJŠe[ÕßV¿(„ŸŸ:t C‡ôìÙ“‘#GnÙ–§§gýRìíað`Óm÷nøóŸáñÇaɸçxàhß¾þsˆH™Ä$^äEžåYÞá£ãˆH `Û¶mFÇiôW i4rrrX´hþóŸéÙ³'[·neàÀFÇ2„››¡¡¡—U„)//'77—3gΗ—GII ………äçç[&\PRRb)?”””——Gvv6¥¥¥V'8Ù*W˜Ofj¬<<.©ôôtúõ뇛›[gi’¦N…7ß„‡†mÛ@¤iÔNp‚/ù’u¬;ï:®®®DGG7`ªzæì S¦˜nÛ¶Á‹/š¦…tì÷ß3f€. 'Ò(ÙaÇbs+·ò#?r5º…Hs@YYÙÙÙ´iÓÆè8"Íš.»-"""†ùàƒèÑ£{öì!>>žW_}Ue©weee€iÒ ˆü—¼ú*lß~ht¹ˆ5¬ÁoF2Òè(Æ46m‚ÄD>,€öíMCŽ7:ˆØ0ŠQô£‹Ylti€ébÁ"R¿T‘wòäIn¿ývÆϘ1cسg7ÜpƒÑ±DDDDDD¤…(--ÅÞÞ Ñ±Ò»7L˜óçËS"""""Ò8­a wqÎ8ÅX;Ê’?n*¸wìãÆ™ ï"Ò¨,f1ŸñßðÑQD¤žµk×€ÌÌLƒ“ˆ4*„ˆˆˆHƒÚ²e Ý»wççŸfóæÍ¬\¹£c‰ˆˆˆˆˆH RVV†³s ?aFä|–,´4XµÊè$"""""rÛØF LcšÑQ¶máÑGáÈx÷]8z®¹bcaÍ8{Öè„" e(×qOò¤ÑQD¤žùúúbooOVV–ÑQDš=BDDD¤ATUU±dÉnºé&®¿þzöìÙÃ7Þht,iJKKqrr2:†Hã÷ß‹C^žÑiDDDDDĆ8âèK_zÓÛè(“ÜqüðlÝ pÏ=Ð¥ ¼ø"äæP¤Å{–gÙÊV¶°Åè("Rìíí±T± IDATñööæôéÓFGiöT‘z—ŸŸÏرcY´hK–,aݺuš """""""†Ñ„‘‹xâ ¨ª‚?üÁè$"""""rŽB ù€4¤6 ‚  1FŒ€gŸ…`î\Ó1Ä q7ñ8SE•ÑqD¤ùùùiBˆHP!DDDDêÕ®]»èÛ·/?üð_}õ>ú(vvvFÇ‘LBD.ÂÛ,€åËáØ1£ÓˆˆˆˆˆH5Ø@)¥L`‚ÑQšŽŽaÅ HKƒgžM“CFŽ„o¿5:H‹´„%ì`Ÿñ™ÑQD¤µiÓF…‘ BˆˆˆˆÔ›õë×sÍ5ׯÏ?ÿÌÀŽ$""""""¢ !"µqÿý‹DDDDDDª‰#Ž[¹?üŒŽÒôxzš¦ƒ> ëÖAV–iŠHl,¬YF'i1bˆa£xœÇ©¤Òè8"ROTi*„ˆˆˆH½øãÿÈĉ¹÷Þ{ùâ‹/ð÷÷7:’ˆHýIKƒ9s`Ö¬ÿÝvî4ݪ/›3Ç´®ˆˆˆˆJBDjÁÉ ž}Þ~~ùÅè4"""""âÛØÆ4¦¥ist„;î€ï¾ƒ; :¦O‡ÐPS)>'Çè„"-Âs<Ç>öñ!EDê‰ !" C…©SUUU,Z´ˆyóæñÔSO±|ùrZµjet,‘úuü8üùÏðæ›gº?nº™ßóMÓ:ÇVDDD¤ÅÓ„‘Zš0úõƒÇ7:‰ˆˆˆˆˆãoĆ¥ùˆ‰1M9x¦L+LÅY³ 1Ñèt"ÍZwº3Žq,d¡¦„ˆ4S*„ˆ4 BDDD¤Î”••1iÒ$žþyÞyç-Zdt$‘†qõÕ gÏBy¹íÛÙ³dZWDDDD UVV¦ !"µag/¼_|›7FDDDD¤E«¤’µ¬ån _ 7ÿ;Ï=g:ŠŽ†‘#!>Þèt"ÍÖb“H"Ø`t©*„ˆ4 BDDD¤N0bÄ6mÚħŸ~ÊĉŽ$"ÒpììLWrt<ÿ:NN0mši]1Tii©&„ˆÔֵ׈0>Têj"""""Fù‚/8Îq&3Ùè(Í›‡Ì II°q#””À!з¯i’Hy¹Ñ Eš•.taXÈBÎrÖè8"RÇTi*„ˆˆˆÈ+..fĈìÙ³‡¯¾úŠ!C4¢XDZ I“.üG€²2˜0¡áòˆˆˆˆÈyiBˆÈ%záøõWøà£“ˆˆˆˆˆ´XqÄq-×I¤ÑQZ{{ÓtÍ›aÇèÞ¦O‡ÐPX´tr«HYÈB’Hb댎""u¬M›6äææRQQat‘fM…¹"eeeÜqÇìÞ½›Ï?ÿœ¾}ûIDÄ]»BTÔùŠ2ý±@DDDD § !"—¨[7SÁýé§á¬®Ö)""""ÒÐ²Éæ>aÓŒŽÒ2Åʦƒ;³fÁ«¯Bp°izüþýF§iò:Ó™;¹“Å,¦4.ÒœøùùQUUENNŽÑQDš5BDDDä²={–É“'³uëV¾üòKúôéct$cM™ŽŽ5—;:ÂÔ© ŸGDDDDlÒ„‘˰p!$%ÁúõF'iqÞáqd,cŽÒ²š¦ƒ= +VÀO?A0dlÚUUF'i²žæi’Iæ}Þ7:ŠˆÔ¡6mÚ¥ÉZ"õJ…¹,UUUÌœ9“O?ý”M›6qÕUWIDÄx'‚­Q§0~|Ãç›4!Dä2tîl:æY´ÈöqˆˆˆˆˆÔ›8âÏxÜq7:Џ»ÃÌ™°olÜhZ6jôé«VAq±±ùDš Ntb“x†g4%D¤Q!D¤a¨""""—eöìÙ¼÷Þ{lܸ‘k¯½Öè8""C‡ vvÿ[fgW]ááÆå+š"r™ž~’“á½÷ŒN"""""Òbìa»ØÅ4¦EÎeo#GÂæÍðóÏЯÌ aa¦2ýéÓF'iRžäIŽr”wxÇè("RGTi*„ˆˆˆÈ%{å•WX½z5ëׯgÈ!FÇi\¦L1ýÀÌÞÞ´LDDDD M¹L;šŽož}VSBDDDDDÈjVÓ…. `€ÑQäBúô•+M%úÙ³áµ× 8Øt µw¯ÑéDš„Žtd2“yŽç(§Üè8"Rœiݺµ !"õL…¹$[¶lá±ÇãÅ_dÔ¨QFÇi|Ư¹lìØ†Ï!""""ç¥ !"Wà‰' %Ö­3:‰ˆˆˆˆH³WFïó>Ó˜†vÿ1^@€i:ȉ°jìÜ =zÀ A°iTUP¤Q{š§9ÎqÖ²Öè("RGÚ´i£BˆH=S!DDDDj-%%… &0nÜ8~÷»ßGD¤qjÛþïÿ U+Ót뮃víŒN%""""ÕhBˆÈˆˆ€ `éR¨¬4:ˆˆˆˆH³ö1“Cwq—ÑQäR9;ÿo:ÈæÍàã£GCd$¬XEEF'i”:ЩLåž¡Œ2£ãˆHhÓ¦ ÙÙÙFÇiÖT‘Z)((`Ô¨QóÆoGD¤q›<ùWxš4ÉØ,""""Rƒ&„ˆ\¡ 1>ùÄè$"""""ÍZqÜÄM„bt¹\vv0x°i:È®]pýõðøã=fš$""VžäIÒIçmÞ6:ŠˆÔΜ9ct ‘fÍÁè"""ÒøUUU1}útÒÒÒøé§Ÿpss3:’ˆÈ%+(( ¼¼ü’?®¤¤„âââKúû¾}éЪGûô¡2)é’>ÞÕÕ—Kú'''Z·n}É'"""ÒÒhBˆÈêÚF2M ¹õV£Óˆˆˆˆˆ4K'8Á—|É:ÖEêJÏž°r%<û,ÄÅÁŸþË—›&‡<ò\}µÑ E…PB™Æ4žã9¦2'ta‘¦ÌÝÝ‚‚£cˆ4k*„ˆˆˆÈE­X±‚>úˆÍ›7nt©c¹¹¹TUUQQQa¹*Cqq1%%%äççsöìY*++ÉËËL'Ðýw”uõ¢ENNŽe»UUUäææZ=WõíšUÿ°]À0g4«þüfyyyTVV^ÚÎ×£÷ÿûöÎÞ½ ÍQ½½=^^^VËÜÜܬN†´³³ÃÛÛÛj[«÷]\\puu½à:æ÷qww¯ñü^^^ØÛÛÓªU+<==kl×ÃÛEDDD.…&„ˆÔ L'+mÞ C†FDDDD¤Ùy›·ñÆ›‘Œ4:ŠÔ5xôQxè!X¿^z úõƒaî\3þ{á1‘–ê žàoü8â˜Å,£ãˆÈЄ‘ú§Bˆˆˆˆ\о}ûX°`‹-âÿþïÿŒŽ#Ò,åææRYYInn.gÏž%??Ÿòòr ,Ås‘¢°°²²2K Ã\Ö¨¾ €3gÎPQQaUÊ(++£°°À²Ëu±úÍ<==iUíÖÕ?άC‡]Ç\0sppÀÃÃÃjwwwÏ›ÙV¢¶¼½½±³³»¤±ûo9eGµÏGmØ*ÒÔÖÅJ1æï«êÌß+fÕ‹AfçNW±µÎéÓ§­Ö1/›Õ¦Pt9ªOE©þ=`«|âíí½½=ÞÞÞ–Ò‰ùqgggÜÜÜ,åó÷µy›æïåêÛ‘¦«´´T…‘+uÕU¦"ÈóÏ«"""""RÖ²–ILÂM¸l¶œaÊÓmÛ6xñE?""à`Æ Ðdxi¡Báîa K¸›»õ³P¤ sww'55Õè"Íš !"""r^¥¥¥Lœ8‘¾}ûòè£GÄæ“Ø (..æÌ™3äççS\\Laa!¹¹¹STTDnn.EEE‘ŸŸ_£ÌQTTDiii2GmÔödõððpììì.iê‚ùc«&Ì϶O²©æi1Õ‹+æÿoàʦ٘‹6$%%]´tU+•´nÝ'''<==qssÃÍÍ oooË:ÞÞÞ´nÝ777<<<ðððÀÍÍÖ­[×(W‰ˆˆHÝ*++³š’&"—éÑGað`عbbŒN#""""Òlle+ $ð¾e¹4{ƒ™n‡Ák¯™¦2.\S§Â#@HˆÑ EÜ“<Éßøoñ³™mt¹Lš"RÿT‘ózì±Ç8zô(»ví²ºz¿HcV\\L~~>yyy–›¹¨Q\\Lnn.………–õΜ9Cqq1äååYÖËÉɱ:ý|.v¢w@@€e‚¹„Q›Éçžh.ÒT/ùûû˜ä“SlMá©í$ó6Ž;vÞâØ…˜føøøX~¶xyyáî«+üäíí§§'^^^xzzZJ`"""¢ !"uæÆ¡OX¶ Þ}×è4"""""ÍFqô¥/½émtih;Êðä“ðÖ[¦rÈ_þ·Þ óæAÿþF'i0A1é<ÇsÜÍݸ¢¿u‰4Eîîî– BŠHýP!DDDDlúÏþÊ+xûí· 3:Ž´æ“©Í… [·Ú<~>...øøøX®Þïããcy?00èèh«ÇÎwßü¾‡‡zI-Òyxx 3§úÏ´ê?ã.v?--}ûöÕx,//ÊÊJ›ÏUýçXõÛ¹?ãÎ÷x`` vvvõþ9©oš"R‡z¦O‡çŸ‡ÐP£Óˆˆˆˆˆ4y…òwþÎR–EŒÔ¶­i*ãÃÃÇÃ+¯À5ט¦3>ø Lœú;¥´ XÀ›ÿýï~î7:Žˆ\wwwM©gzU("""5”””0kÖ,FÍäÉ“Ž#MLQQYYYdgg[nYYY–eYYYäää——G~~¾å~^^6·Yý*÷æ+Ý{yyL·nÝðöö¶Z~îý†8¡[Där¹ººâêêJPPPl¯ªªŠÜÜ\«IIÕ''åçç“››Knn.ùùùdeeÕX'//Ïæ¶ÍSÌ?[Í÷}}}-·6mÚЦM«÷}}}qss«“ý© š"R‡î¼žxÂtÕÚ?üÁè4"""""MÞzÖSJ)˜`ti œœàŽ;L·mÛàO‚{îÅ‹aæL˜5 ¼½N)Ro ä·ü–¥,e:Ó5%D¤ òððP!D¤ž©""""5,Y²„ŒŒ þýïE T\\\£Ôqn¹ãÜDz³³mNèðññ±:9Ø××—ÀÀ@<==ñññ±Yâ0ß÷Ö/0ED.‰e²Ç•¨^*1—Eª—Frrr,ï§¥¥±wï^²³³9}ú4¹¹¹5¶çââbU±U ±õ˜‹‹Ë퇈ˆÈ¹*++©¨¨Ð„‘ºâè÷Ý/¼O> žžF'iÒâˆã6nÃ?££Hc3hév䈩òì³°d L›óæA‡F'©ó8«YͼÁƒ7:Š4f;ЦBH\,[fšÚ8|8<ö htB‘:@³˜Å ¼Àoù­¦„ˆ41”””P^^Ž£££ÑqDš%BDDDÄ¢ªªŠ™3gÒ³gOæÌ™ct©…üü|ÒÓÓ9uê§N:ïýŒŒŒWkwuuÅßߟÀÀ@Ú¶mKûö퉽`±ÃÁA/EDäâìííñóóÃÏïÒ®`WQQqÞòHVV–åß¶ƒZî[mÃÛÛ›€€Ú¶m‹¿¿¿å~Û¶m-ÿæ™ï{êêÕ""-Rii)€J„"uÉ×&O†?ÿ|ììŒN$""""Ò$ÅGA f°ÑQ¤)ðô4•òï¿>û žÞ4A$&Ætl6q"èï»ÒL<Ê£¬üïñÑqD主»PPP€ÁiDš'½â‹>úˆ­[·òã?ÒªU+£ã´h¹¹¹œ8q‚ÔÔTÒÓÓ9~ü8éé餦¦’‘‘a™êQRRbõqmÚ´±ºZz¯^½ð÷÷·:!Ö|ß|À%""ÒX888Xþݪ­‚‚2228yò¤¥iž„•™™É¯¿þj™„•••eõ±...–3&00ÐÐPiß¾=ÁÁÁxyyÕõ®Šˆˆ4!D¤ž<ð¬\ ññ0dˆÑiDDDDDšœJ*y‡w˜Æ4Z¡¿ÕÊ%hÕ FŽ4Ývî4M™>Ý4-dæLS9Ä××è”"W¤í˜Å,^äEf1KSBDš@…‘ú¤Bˆˆˆˆ¦+r?ùä“Lœ8‘ØØX£ã4[dffrìØ1KÁ#--­Fù£¨¨Èò1®®®DHH]ºt©q…ss D£ED¤¥qww§S§NtêÔé¢ë–——[Ê!çNÕÊÌÌdß¾}ÄÇÇ“ššj5yÄÍÍ !((Èòï²yY»ví4IKD¤‰Ð„‘z ¿ùiJˆ !"""""—ì_ü‹TR™ÊT££HSkÖÀâŦÒþŠðÊ+¦i!? QQF'¹ló™Ï_ù+oò&÷s¿ÑqD¤–̬=sæŒÁIDš/© """¬ZµŠ#GްiÓ&££4i™™™$''“œœLJJ 'Nœ°šî‘™™Iee¥eývíÚHpp0‘‘‘\ýõ–“Kƒƒƒiß¾½Úñ"""uÄÑÑÑRæèÝ»÷×ÍÎÎ&--ÍRÞ¬^âüõ×_-%3{{{hß¾½¥,Ò¾}{ÂÂÂ'<<ü’&ŸˆˆHýÑ„‘z4gŽé$£” 3:ˆˆˆˆH“G×r-¸øÅoD.*<^xžxÞz þøGX½†‡¹sað`£Š\²ø-¿e)K™Á \p1:’ˆÔ‚yBˆ !"õG…¡  €gžy†û;§QËË˳”=ÌÅê7ód‚ƒƒ &$$„ßüæ75ŠAAA:IDD¤‘òõõÅ××—îÝ»ŸwÒÒRKéóĉ¤§§[¦€íÚµ‹O?ý”ÔÔT***Ó¤s9ÄÖÍÓÓ³¡vOD¤EÓ„‘z4f ˜N2zî9£Óˆˆˆˆˆ4Yd±‰M¬d¥ÑQ¤¹ñð0@xþùOøÓŸLSûô{ï…)SÀE'ÕKÓññoG³™mt©ó„‚‚ƒ“ˆ4_*„ˆˆˆ«W¯¦  €'žxÂè(†+++#55•¤¤$«[ZZééé$%%YÖõññ!""‚ˆˆFŒa¹Ahh(z©%""Òœ9;;FØ®~]QQÁÉ“'-¯#Ì·ðÏþ“””Ëô0 ²z]Add¤å—¥""re4!D¤98ÀŒð׿ÂÓOƒþ?©•wyG¹ÛŽ"Í•½=ŒiºíÜ +VÀý÷ÃÂ…0k–©0Ò¦Ñ)E.*@îážçy¦3'ô»‘ÆNBDêŸÎRiá***øãÿÈŒ3ðóó3:NƒÉÉÉ!))‰}ûö±ÿ~ËýÄÄDΞ= €‹‹‹å„Ìnݺ1räH”)"""—ÄÁÁ   ‚‚‚ˆ‰‰©ñøùʨ;wîdÓ¦M¤§§[Ö ¤[·nDDDm¹Ñ»$"ÒäiBˆH=›1Ã4dÓ&¸]'³‰ˆˆˆˆÔFqŒg<îèïÒbb`ÍxñEX¹^}^xƃǃèh£Š\У<ÊjVó7þÆLfGD.ÂÉÉ '''M©G*„ˆˆˆ´p6l 55•xÀè(u®²²’””Ù¿?‰‰‰8p€••˜Zè‘‘‘tíÚ•I“&IDDáááxyy¼"""ÒÜ999]°Ô‘››KJJ Gޱ¼–ùùçŸyÿý÷-WÑñóó£k×®DEEItt4QQQtèÐ{{û†Ü‘&ABDêYp0 qq*„ˆˆˆˆˆÔÂ/üÂ.vñ¯EZšÀ@X´yÞ{–/‡îÝáÆáÁaݳ3:¥H !„p7w³„%ÜÍÝš"Ò8::R^^nt ‘fK…‘î•W^aìØ±tìØÑè(—­²²’dz{÷nöïßÏHLL$!!’’ÀtEí®]»Ò£GÆo9a2$$Äàô"WÆ®¿„­ªªºäíUÿ[ËŒbggg3GCe4òsaÄsWTTpß}÷±jÕª{N±æííMïÞ½éÝ»wÇŽ?NBB 8p€„„6mÚDFF®®®DFFE×®]éÚµ+½{÷¦cÇŽ*ŠˆH‹¦ !" àž{àÎ;áøqÐï^DDDDD.(Ž8ºÐ… 0:Š´Tîî0s¦iâã?ÿ úŒ½zÁ}÷ÁäÉàêjtJ+ X@q¬e-Ó™nt¹***ŒŽ!Òl©"""Ò‚ýôÓOüüóϼþúëFG©µââböîÝË®]»,·={öPPP€ƒƒáááDGGsÓM7ñàƒMdd$ÞÞÞFG©UUUç-I@í #¶¶w±eMCe4òsaÄsÏŸ?Ÿk¯½¶AŸSDj/$$„† bµ<77×2!Í\yçwHJJâìÙ³xxxгgOz÷îM¯^½èÓ§Ý»wÇÅÅÅ =iXš"ÒF__X»,0:ˆˆˆˆH£UFïó>¿ãwØÑ¸ÿ#-€½=ŒiºíÚù Ì O=Ó¦™¦†R€PB™Â–²”©LÅA§ÂŠ4jš"R¿ô¯ ˆˆH ¶fͺtéBÿþýŽbÓÙ³gIHH`çΖÛO?ýDYYîîîDFF͸q㈉‰¡oß¾¸¹¹[D¤YHNNfçÎ,[¶Ìè("r‰¼½½éׯýúõ³Z^^^ÎÁƒ­^[½ýöÛЪU+"##‰‰‰!&&†AƒÑ»woZµjeÐ^ˆˆÔMiNN0q"¬^ ?ü""""""FÙÈFrÉe SŒŽ"b­woX¹žyÆT yí5øãaÜ8˜?ºw7:¡Oò$kXû¼ËT¦GD.@…‘úeot1Fyy9ëׯgêÔÆsPœŸŸÏgŸ}Æ‚ ¸þúëñññ¡{÷îÌž=›_~ù…þýû³víZV›ýÈËËãá‡&""¹ûî»ùñÇHII±úZ>|˜‚‚«eÕשí×v÷îÝ :”Ö­[ãééɰaÃøâ‹/λŸ"-‰££#ݺucÊ”)¬X±‚mÛ¶‘——Gbb"ï¾û.Æ #99™ ‹7Üp ,àóÏ?·ùóJD¤)*++ÃÎÎGGG££ˆ4oÓ§Cr2|óÑIDDDDD­8⸉›BS¤‘j×-‚'`Õ*عzô€Aƒ`Ó&¨ö·H‘†ÖÜÅ]<˳TPat¹**ôÿ©H}Q!DDD¤…úüóÏÉÊÊâ®»î2,Cvv6ü1óæÍ#66___n¹å6nÜHDD/¿ü2»ví"//o¾ù†—_~™qãÆÑ¹sgìíõ2FäBÎ=Ñÿðáà 6Œ±cÇräÈK1àæ›oæðá×¼ý‹mïºë®cÇŽxzzZÔÿãÿÀËË‹ 6X–UTTÅŽ;¸îºëÎûœærKUU•å¶zõj«uf̘ÁìÙ³IOOgÇŽ”••ñÈ#ÔÈ>dÈFŽIRR)))Lš4‰Ñ£G“ššzYŸ‹ÚnoôèÑL:•ììl¶oßNvv6<ð€Õ:Gޱ|n“’’8rä·ß~;&L¸ìç6îfÏžÍ#ãðáÖ‰aaaìÙ³‡«®ºŠªª*:uê„»»;•••têÔ‰]»vfÉ~î×¶¨¨¨Æ×öàÁƒŒ?žùóçsêÔ)¶oßNaa!7ß|ó÷U¤%³··§K—.Œ?ž—_~™­[·’——Ç/¿üÂK/½DXX7ndøðáøøøpÕUWñ»ßýŽO>ù‰>㦠IDAT„œœ£ã‹ˆ\–ÒÒR/XŽ‘:Уôéï¾kt‘Fé'ØÌf¦1Íè("çì S¦ÀÞ½°y3øøÀèÑ +V@Q‘Ñ ¥…z‚'8ÊQÖ±Îè("rš"R¿ìªªTÓ¹vرžõŒcœÑQ¤°³³cýúõŒW?ßO«V­bæÌ™6»çž{8xð Û¶m«—ç¶¥²²’_~ù…øøxâããùúë¯)//'""‚Áƒ3pà@n¸á‚ƒƒ,“Hsp¾Ùª¿ÔŸ4iW]usçεZgÙ²eüòË/¬]»Öj{ç&œ»¬6Û«ªª"007Ò¿Ìc=ÆÂ… ùöÛoS©aРA¤§§_ô¤<[Ùª?¶qãFFmY–œœÌ Aƒ8qâ„Uöž={2þ|«뭷ؽ{7+V¬¸¤ W²½œœ:uêd5ébòäÉÄÆÆÖøÜþíocÚ´i—ýÜvvvüç?ÿ¹`馺Î;óý÷ßãççwÑum퇇‡GÅ××ײ,99™ˆˆ«}èÛ·/qqqôêÕ 0_~ùe¶lÙb•ýܯmbb"7ÜpƒÕ×vâĉ 6Ìj²Ibb"QQQçý¾‘Ú9yò$?üðß~û-ñññüòË/ôéÓ‡Áƒ3bÄ Ð ¥Ý;wS/Û7¿6¯^^¬/z½."õçÍ7ßäá‡&??ßè("ÍßË/ÃÒ¥žn:yHDD¤¨í±X}Šˆ,a ËYÎ NàŒ^/K”˜þ3¼ñ¸»Ã=÷Àý÷ƒþÞ/ l*Sùžï9ÀZÑÊè8"bCTTwÝuO=õ”ÑQDê\Cþþà|ç›êÒÚ"""-T||bÒ¤I´k×ŽØØX^ýuÂÃÃY·nÙÙÙ9r„•+W2eÊ•AD.Sõ©¶N|çÖ[o­±ü¶Ûn#>>þ’Ÿ¯6Û³³³ãæ›o¶œØâÄ òòòö–[naóæÍ—üœW²=²³³­–mÞ¼ÙæçÖÖ”‹K}¾ú‚yªËÊÊÂËË«VëÚÚk®¹†Q£Fñ¯ýË2%&<<¼Æ÷çÔ©SY¹r¥åý×^{yóæÕxŽs¿¶aaa5¾¶ÿþ÷¿¹ñÆ­–EFFª "Rüýý9r$/¼ð;vìàÔ©Slذ>}úðî»ïò›ßü†€€¦L™ÂÇLII‰Ñ‘EDΫ´´'''£cˆ´ 'B~>|þ¹ÑIDDDDDµ¬e2“U‘¦Ë<$%~÷;X»:v„qãàÇN'-ÈS>ÞjÙîÝ»‰ŽŽ¾ìmŠÈ¥óóócæÌ™lÛ¶””î½÷^Ö¬YC§N3f ?ýô“ÑE¤…Ó„‘6v,TUã‘–¨€þÁ?˜Æ4££ˆÔŸAƒLHL„[n LS$çÎ…cÇŒN'ÍPg:swð ÏhJˆH#äàà  !"õH…‘hïÞ½ôìÙóŠ·sðàAî¸ãú÷ïÏÉ“'yï½÷HIIaÑ¢E×AR©+ .dÙ²e¬^½š“'OrêÔ)Þ|óM–-[ÆÂ… ëu{ÇgÑ¢ELž<ÙjùwÞÉ‹/¾È°aÃjý¼íÛ·çûï¿§¼¼œ/¿ü’KξhÑ".\Èßÿþw²²²8sæ Ÿ~ú)wÞyçe}.j»½0þ|RRR(++ãðáÃÌž=Ûæö^yåÞzë-N:Evv66l`éÒ¥õ¾/ÕuéÒÅR訮¶û0cÆ öíÛGii)™™™¼üòË6 H#FŒ %%…;î¸77·ËÎüä“OòÌ3ÏOaa!{öìáž{îá°¬SQQ§§çe?‡ˆ\šÐÐP/^ÌÑ£Gy÷ÝwÉÈÈ _¿~Œ7ŽC‡ODZ¨²²2MiH>>0düãF'iÖ³žRJÏx££ˆÔ¿ÎMÓAÒÒà™gàáS'7¾ÿÞètÒÌ<ÅSàñ‘ÑQD䎎Ž*„ˆÔ#BDDDZ˜³gÏ’˜˜H·nÝ.{•ÿÏÞ}‡GUçíO*é„NBIB¯Ò4*E ,.U%®¢ðÊåAYA "MY,€¨´Ø@XH/ Bè-F’ïï<™%¤0©'$÷ëºæÊÌ™3ßsŸ2I&9ŸóÉÈ`òäÉ´lÙ’½{÷²råJ6mÚÄÀ‹­ëˆˆ8&«c†ÍfË·{F£FX½z5Ÿþ9!!!óÙgŸ±zõj6l˜ëxùMst<€¾}ûb³Ù¸ûM¿ë®»puu¥_¿~¯ïŒ3øÇ?þ··7£GfÖ¬YyfÌkzPPŸþ9K–,¡AƒÔ®]›I“&1sæLîºë®|—_”ñ-ZDrr2ûÛßðññaÀ€ôïß?Çx!!!¬^½šÏ>ûŒàà`êׯς øøã ½lG“«ÝqÇlÙ²%ÇtG×#** ºu놯¯/]ºt!99™?ü0ǘNNNøúúòØcåx® û¶U«V|ôÑGLœ8‘jÕªqçw‘­`娱c4nÜØ¡m "ÅÇÕÕ•Aƒ±yófV¬XÁîÝ»iÑ¢S¦L!#CWê‘Ò•’’¢!"¥íž{`Ý:¸tÉê$"""""–[ÈBîáªQÍê("¥ÇÏ/³;È¡Cðïgv ¹ùfèÐ-‚´4«J9МæÜ˽¼Â+ŒÕqDä*®®®¤é{½H‰QAˆˆˆHsèÐ!’’’ ]röìYúöíË”)S˜>}:Û¶m#,,¬˜SŠˆ£Œ1ÙnùiÕª«W¯&!!„„V¯^M«V­ò/¿iŽŽжm[âããst~ðòòâòåË´iÓÆáõˆˆ ::š””8@xxx¾óšÞ¨Q#>ûì3âââHHHà×_eÀ€×]~QÆ«Q£‹-âÔ©S¤¤¤ðçŸ2pàÀ\ÇkÑ¢ß|ó ÄÇdzvíZš7o^èeä8É2`À>úè£Ó];åË—söìYRRRˆŽŽfÆŒøøøäó‹/¾àÖ[o¥víÚ9ž+è¾íÒ¥ ?ýôIII9r„ñãÇg{~ùòåÜ{ï½m)ááálß¾×_É“'Ó¿Î;gu,©@Ô!DÄááž«W_wV›ÍFzz:¯¼ò AAA¸»»Ó¸qcÞ}÷Ýó®ZµŠÎ;ãåå…——;wæë¯¿.‰5)ûÙÏf63’‘×7¿ßo·oßN:uì'VþøãtêÔ‰J•*Ä‚ ²ÍÏ3ÏLLL =öC‡å·ß~+©5)’…,$@îàŽëÎ;vìXÞzë­\Ÿ›9s&ÿüç?qqqaß¾}Üwß}Œ7ŽÓ§O³lÙ2¦L™Â?ü`Ÿøðᤥ¥ÅÅ‹‰‰‰aìØ±Ìœ9³¸VM¤àBCaÙ2Ø·ÂÂ`òd¨W/³“È_Yî†WQ?[ßÄM„Æ+¼bu¹Š««« BDJ BDDD*˜óçÏãääDåÊ• üÚÿùŸÿáÈ‘#üðÃtèСÒ‰ˆHYòÚk¯ñã?–è2BCCéÚµ+mÛ¶-Ñådùàƒðòò*•e‰ÈõuìØ‘ï¿ÿžÃ‡3a«ãˆH¡!"0¾þ’“¯;kÍš5™2e 6ÄËË‹nݺ1sæLÞ|óMû<Ó¦McòäÉÜÿýT¯^5j0dÈ&MšÄÔ©SKrMDDDDD %tþÍ¿Áœ¹þE÷î»ï>öìÙî]»²M?wîŸþ9‘‘‘L™2…ñãÇ3xð`|}}éÔ©o¾ù&¯¿þºý5ëׯgâĉԯ_WWWªT©ÂwÞÉÚµk‹w%E £Axûm8~<³(dÅŠÌÎ!aaeuºVEþlý/ð+¿…Ž‘²ÂÕÕÕÞÙLDŠŸ BDDD*˜ .àëë‹“SÁ~ HNNfþüùL˜0   ’ '""eŠ‹‹ óæÍ+±ñ1¤¦¦fûóˆT<ÁÁÁüÏÿüóæÍ#%%Åê8"R¨CˆˆE €Ë—áÛo¯;ëˆ#rLëÒ¥ û÷ï·?Þºu+aaa9æ gË–-EŠ*""""RÖ²–XbÎp‡æwuuåÑGÍÑ%dþüù„……Q£F 6oÞLxxx¶yºvíÊöíÛí›7oÎSO=űcÇŠ¸"%È×7³;HLLfQȹsг'tè‹N$.ŠüÙºèIO¦0Åê("òœU"R‚T"""RÁÄÅŪ;Hll, „††–@*‘OÏž=­Ž RnÜzë­ÄÇÇsôèQ«£ˆHššª‚+BûöðÕW×5·‹‘øûûsáÂûãS§NÙO€»ZÍš59yòd‘¢Šˆˆˆˆ”„…,¤+]iHC‡_É_|ÁÙ³gHOOç½÷Þã±Ç³ÏsôèQ7nŒÍf³ßªT©Â‰'ìó,Y²„3gÎаaCš5kÆÈ‘#Y¹r%Ƙâ[A‘ââä”ÙdóføýwhÞ|êÕƒ‰áüy«Þ*úgëxøl´:Šˆ6›Íê"åš BDDD*˜ôôtœ¯ß‚øZ5jÔÀÅÅ…}ûö•@*©ˆŠëÃÞ¼yóhÔ¨Q±ŒUPYT/Êë¥bÛµk·ß~;>>>E>žŠKYÉq­ÒÈTÐedddä˜V÷iE©«ÊÜÀöîÝ‹««+5kÖ´:ŠˆT)))¸¹¹YC¤bêÛ¾þúº³9ÒÙ¶fÍšœ>}:ÇôÓ§OS«V­BÅ))ç8Ç*V1’‘z]µjÕ¸çž{˜3g+W®¤V­ZtêÔÉ>¯¯/ÇǓ햞žnŸ'88˜åË—Ç’%KèÒ¥ S¦Lᡇ*ž))íÛgvÙ¿† ƒwÞúõáá‡aï^«Ó•iý³u7ºq+·ò/þeu‘§‚‘ ÆÓÓ“¤¤¤¿Î××—»ï¾›—_~™„„„H&R8ß|ó ýúõ³dÙºj’Õˆ#xä‘G8{ö,ëׯ·: ãº ¾ýöÛÓÊâ>­(n½õVÆou )„øøx^~ùe €ÕqD¤P‡ õí GÂîÝEª]»v¬Zµ*Çô/¿ü’víÚy|‘ⴘŸâʽÜ[à׎;–Ù³gsåÊfΜÉã?žíùÛn»•+W:4–»»;7Ýt‘‘‘¬^½š¥K—8ˆ%‚ƒaêTøë/xåX·Z´Èì$euºVyÿlý/°†5üÆoVG)Q*©`<<-ËÎ;éß¿?>>>øøøÐ§OvîÜ™cY_~ù%·Ür •*U"((ˆqãÆŸmž¬q³–a³Ù²µ¤vtY7n$==îÝ»gïZ¹MÏš¶{÷núô郯¯/ÞÞÞôïߟ={öäc×®]ôë×oooüüü0`@ž'—æ6Èk]‹c_^ï¸É«_¿~ö±úõë—m¬«×ï‡~`É’%Ù¦$W~ëwøðálãœëv+Žu½:ßÑ£G¹ë®»ðññ¡fÍš 2„sçÎå˜7-- l6Ûuÿ¨êèqåèò¡`ÇõµŠëX»xñ"ãÆ#$$„J•*Q»vmFŒÁ¯¿þšmYW/óÚ÷KA¶M^y¯·Œ¼ÖÿÚiŽîÓ‚î«â~ßÇ~qd(úþ)È{{È!¬X±"Ïí.eËO?ýDÛ¶m‰eݺuZID*u±³3ôî],!7ß|3ï¿ÿ>o½õõë×§~ýú¼õÖ[,\¸N:CX‘ⱕ­lc#Yè1ÆŽË«¯¾Ê¨Q£r\ä E‹|óÍ7,]º”àà`ªW¯Î”)Sxâ‰'ìóLš4‰+VЦM|||¸õÖ[IOOç“O>)t&K99evY¿¶l–-á±Ç2;‰Lœ¹ü¯ErªŸ­ŸçyV²’?ùÓê("""%LjˆH`0KÍR«cH9˜¥KKîxš3gNŽiQQQ0§OŸ.ô¸/^4÷ÝwŸqrr2æÐ¡CE‰)%lß¾}¦I“&fýúõæòåËf×®]æÖ[o5×þ*˜ž={šM›6™ÄÄDóÍ7ߨç9pà€©U«–™={¶9}ú´9{ö¬ùè£LHHˆ9zôhŽqæÌ™c._¾lNœ8a† f†ž#W^¿ŠdYO<ñ„¹óÎ;7·é€iÙ²¥ùä“OÌÙ³gÍ©S§Ì¼yóL½zõLLLŒ}¾ƒšºuëš ˜S§N™3gΘ>úÈtéÒ%ÏqKkäµ®EÝ—Ž70uêÔ±o—¬íh8`Œ1&--Í4hÐÀœ?>[ÆãÇ›*Uª˜„„„¯s~ë·cÇÓ±cÇlógdd˜† šm۶庽Šk]¯Î׫W/³zõjoŽ9b `FŒ‘ë¾rTAŽ+G–_Ðã:¯LE=Öîºë.3qâDsòäI“œœl¶nÝjn¹å–\¿GǶÉ+ïõ–‘×r™–ßëÙWÅý¾-®ýR}WÔýãè{{ëÖ­9~.HÙsäÈiœMxxxŽŸ×óûï¿›ßÿ½„Òa"""Jlü«åöûºˆ”¼:˜§Ÿ~Úê"×GãæfÌ¥KV'‘ ÊÑÏb%ýùSD*ŽÇÌc¦±il2LF¡ÇHLL4•*U2±±±Å˜L¤œ9~ܘ—^2¦JcÜÝ:Ô˜]»¬N%Ë0¦µim›ÁVG©ÐJóÿo"¥­4ÿ~×ù¦*) „Hq²¢ äÈ‘#0›6m*ÒØfÉ’%¦aƦR¥JfìØ±æàÁƒESJÆàÁƒÍ¢E‹²MÛ»wo®'ì~ÿý÷¹ŽñüÃL›6-Çô ˜1cÆä»üóçÏ›*Uªä˜ž×IÓYVÓ¦MÍ{ï½çиy¸½dÉ’Ó§OŸžídå!C†˜·Þz+Ç| .tèäï’ܹ)Ž}éÈqóü#×í2}út3dÈûãçž{μûî»ÙæyõÕWÍ?ÿùÏçºÞúcLÛ¶m³ þÍ7ߘÛo¿=Ïù‹s]³ò­X±"Çx9^_”ýüŽ+G–_Ôã:kYE=Ö¼½½Í¹sç²Í] ‚kå·mò;v¬(qd_÷û¶¸ö‹£ûîZ…Ý?޼·OŸ>m7nœïòÅ:0cÆŒ1îîî¦Q£FfÙ²e&#£àÿWAˆˆUëÖ­Í‹/¾hu ‘ŠëÔ)cl6c¾þÚê$""RA© DDJSŠI1ÕL53ÕL-ô.\0/½ô’‰ŒŒ,Æd"åX|¼1sæÓ´iæçÏ=ŒùòKc ñ÷h)>1Ÿgãlöš½VG©°T"åYY(q*tk¹!Õ©S8P¤ql6ƒ b÷îݼñÆ,_¾œÆÓ¯_?¾úê+222Š)±Õwß}ÇwÜ‘mZ“&MÈü1»¼Ú¾FEE1hРÓû÷ïÏúõëó]¾¿¿?çÏŸw8¯£Ë:|ø0{÷î¥_¿~›=zä˜6`ÀÖ­[g¼~ýzî¾ûîóõéÓÇ¡e”Ô6ÈOQ÷¥#ÇMTTT®ÛeÀ€DEEÙ9’… f›çý÷ßgÔ¨QΕ%¿ÅÇgΜ9öÇï¾ûn¶¶è×*ÎuÍÒµk×lƒ‚‚8qâDž #¿ãÊ‘åõ¸ÎRÔc­K—.„‡‡³fÍÒÒÒÎõ{”£òÛ6e­½µ#ûª¸ß·Åµ_ »ï »yoûùùqNmàË”ôôt¾üòKúôéC“&MX±b3fÌ`×®]DDD`³Ù¬Ž("PJJ nnnVÇ©¸jÔ€fÍàÇ­N"""""Râ–³œ8âÊÐB½Þf³ÈŽ;xýõ׋9H9åí ‘‘°k¬[•*Ax8´isçBR’Õ ¥”EAC2iVG)*©`l6 6dß¾}Å2ž««+£G&&&†µk×âììLxx85kÖäá‡æ§Ÿ~*ÒI½RtgÏž¥ZµjÍëéé™ëôsç΄ÍfËv«U«‡¶ÏwæÌzè!êÔ©ƒ‹‹‹}¾‚ptYß|ó Íš5£~ýúÿZU«VÍ1­víÚœ9sÆþøìÙ³ÔªU+Ç|¹M+ÍmŸ¢îKGŽ›¼¶KíÚµ9{ö¬ýq£FðôôdÇŽlذ___ÚµkWèuÎkýî¿ÿ~–-[Fbb"ÑÑÑÄÄÄä[8TœëšÅßß?Ûcww÷"}/,èqåÈò r\秨ÇÚ§Ÿ~J§N=z4UªT¡k×®¼ýöÛ\¹rÅ¡åtÛäwìXÁ‘}UœïÛâÜ/ŽÌSœûÇ‘÷¶‹‹ /^ÌwH騲e cÇŽ¥^½zÜ}÷ݤ§§³dÉ:Ä£>Š«««ÕE¤KMMUAˆˆÕn»÷ ùL IDAT ¾ÿÞê"""""%n! éC(Ôë1\¾|™/¾ø__ßbN'RÎ99A°jüñtî cÇBPŒÇ[PJ‰3ÎŒg<‹YL 1VÇ)v*©€:vìȦM›ŠuL'''zôèÁªU«Ø»w/>ú(6làÖ[o%((ˆ§žzŠ~øÁá|¥øT­Z5×Ö ¢Zµjœ?cLŽ[bb¢}¾¡C‡âååÅO?ýDrr²}ž’XÖ7ß|“ëIþ6›”””lÓòëÎqêÔ©ÓNœ8AõêÕ³e:yòdŽùr» }inƒÂptlGŽ›jÕªåÚõâĉ9NJ9r$ ,`Á‚Ùºƒ$—#ªW¯Î-·ÜÂ’%K˜={6cÆŒÉ÷ôâ^×’PÇÕµ r\v|Gö©ŸŸ3fÌàСCDGG3zôh–,YÂàÁƒZNIl›²¦¸ß·Åµ_™§8÷#ïí´´4üüü 5¾Mjj*ßÿ=O>ù$õêÕ£C‡¬_¿žÈÈHöíÛÇúõ뉈ˆÀÅÅÅê¨""¤¤¤àîînu ‘Š­[7غââ¬N"""""RbŽqŒ(¢ÉH«£ˆH›60g> < @H ;wZNJÁ?øu¨Ãë¨Û’ˆˆ”?*©€ºwïÎÏ?ÿ\äËóÒ¸qc^~ùeöìÙömÛ2d«V­â¶Ûn£ZµjÜ{ï½Ì›7£G–Èò%»îÝ»•mÚöíÛiÞ¼¹ÃcôîÝ›~ø!Çô7fëò°yóf^~ùe‚‚‚ì'|æuœåu‚¾#ËJNNæûï¿Ïµ ¤V­Z9r$Û´ 6äº,€µk׿˜¶|ùrzõêeÜ«W/V¬X‘c¾o¿ý6Ç´ÒÚ…åèØŽ7=zôà‹/¾È1ÖòåËéÑ£G¶i¬\¹’3gΰfÍî¿ÿþBårÔðáÙ5k+W®dذaùÎ[ÜëZ r\9ª Çua8ºOm6±±±@f±Â Aƒøúë¯Y·n]¶×åõ~)ÎmSÐn>¥¥8߷Ź_™§¸Ýë½·/^¼˜kç')GŽaΜ9 0€jÕªqûí·óÍ7ß0lØ0¶oßÎîÝ»y饗hÔ¨‘ÕQED²Q‡‘2 {wÈÈ€b¾`‰ˆˆˆˆHYòP™Êô§¿ÕQD$KÍš0q"ÄÆÂܹ™+hÕ BC3;‰”³‹ŽÉ¹âÊÓ<Íû¼Ï1ŽYGDD¤x) f©Yju )'³tiÉOsæÌÉuúÑ£G `¾ýöÛ[vn¢££Íœ9sLDD„ñõõ5€ 1C‡5sæÌ1ÑÑÑ¥š§¢Ø±c‡iРY¿~½IHH0Û·o7íÚµ3ï½÷^¶ùòûÕ0&&Æ´jÕÊ|úé§æìÙ³æÒ¥KfÕªU&00ЬX±Â>_ï޽ͨQ£LLLŒIII10Æ Ëuì:uê˜Í›7›ÔÔT³víZS§N‡—µzõjãããcRSSsŒ;lØ0sß}÷™£Gšøøx³fÍÓ»wï\3ææ›o6ÿþ÷¿ÍÙ³gÍéÓ§ÍüùóM½zõLLLŒ}¾C‡™ºuëš ˜Ó§O›sçΙ¥K—š›nº)Ǹ¥µ òSûÒ‘ãfÿþý&00ÐÌ›7Ïœ:uʾýÍr,{äÈ‘¦W¯^æÁ,t®ë­_–””S¥JóüóÏçxíµ¯/îuÍ+_^Ç # r\9ºü‚×y)Žc 0½{÷6;wî4ÉÉÉæäÉ“æ¹çž3 È6^^ï—âØ6×[FAÖ¿ ³ÝWÅù¾-ÎýâÈ<ŹŒÉû½eëÖ­æÎ;ï¼î8R8Ç7Ë–-3‘‘‘¦yóæÆf³Ó£G3uêT³{÷îRÉñûï¿›ßÿ½Äƈˆ0%6þÕòú}]DJ–™?¾Õ1D¤eKcž|Òê""R9úY¬¤?ŠHù–a2LCÓÐŒ3㬎""ùÉÈ0fýzcî¼Ó›Í˜FŒyë-c._¶:™”€d“lM ¾7‹X 4ÿÿ&RÚJóïyoª‚‘RAˆ'« BŒ1¦aÆfüøñ%¶ìëIJJ2ëÖ­3/¼ð‚ 5îîî0AAAføðáföìÙfëÖ­æÊ•+–e,O6oÞln¹åS©R%S·n]óꫯf{>ë$ùÜN–ϲÿ~sï½÷???ãååe:vìh¾øâ‹lóœ:uÊ :ÔÔ¨Qø¹¹™–-[š¥K—æ:î²eËLpp°qss3 64+W®txY?þxޓųœ9sÆÜÿý¦zõêÆËËË„……™#GŽäš0111æÎ;ï4>>>ÆËËËôíÛ7×ZwîÜiúöík¼¼¼Œ···éÕ«—ÙµkWŽqKk䥸ö¥1×?nŒÉ<½OŸ>ÆËËËxyy™>}ú˜;väºÜ 6ÀüòË/…ÎåÈúcÌ•+WLPP9~üx®¯/©uÍ+[nÓ]c?® ²|c?®sS\ÇZTT”¹ûî»MÕªU››› 6ãÆ3—.]Ê6_^ï—Âl›¼òæ÷žÌoý ²Œü^Ÿßôâ|ß×~qdžâÜ?ÆäýÞÎ2þ|óöÛoçùzq\jjªÙºu«™={¶6l˜ 2€qww7·Þz«yñÅÍúõëMrrr©gSAˆˆ•»»»Y´h‘Õ1DäÑGéÜÙê""R© DDJÃæƒÁl3Û¬Ž""ŽÚ·Ï˜1cŒñð0¦zucž}Ö˜£G­N%Ål†™a<§9eNYE¤BQAˆ”ge¡ ÄöOŠˆˆƒlØXÊR2Ðê(RØl6–.]ÊÀ%s<Í;—ÈÈÈ\Ÿ{öÙgYºt)111Øl¶Y~A$%%ñË/¿ðã?²qãF~ûí7.]º„‡‡íÚµ£S§NtêÔ‰Ž;R&2‹u5jijÏ>ËC=T¤ql6úu¸|Z¶l_}õ‹-²:Šˆ£ë½·»wï΢E‹¨W¯^)'»±c8tè¿ýö¿þú+¿þú+üñIIIøúúÒ±cGºvíJ·nÝèÔ©–æÝ²e íÛ·/‘ñ³~7_¶lY‰Œµü~_‘’ãääÄ’%KJ쳸ˆ8hÑ"ˆŒ„‹ÁÝÝê4""R8úY¬¤?ŠHù6‚ìb¿ñ›ÕQD¤ NŸ†… áwàìY¸ë.xòIøÛ߬N&Å ‘D‚b£˜Â«ãˆT¥ùÿ7‘ÒVš?Èë|S—_²ˆˆˆ”IÆ ãµ×^cÓ¦M„††ZºwïN÷îÝíÓ¢££ù駟زe ¿þú+ï½÷)))øøøÐºukZ´hAóæÍiß¾=íÛ··üäD)=°:‚”Q6›_~ù…iÓ¦1þ|«ãˆH1qä½½xñb:tè bë¸rå û÷ïgË–-lÙ²…Ý»w³mÛ6Ξ=‹‹‹ 7¦}ûö 8ÐÐPÚ¶m‹“““Õ±EDŠMjj*ÆÜÜܬŽ"";CJ lß:YFDDDD¤Ø$Àç|Î4¦YED £F xöY7–,7ÞÈü {Ë-0v,Üs8;[R ÉOÆ0†7xƒgx?ü¬Ž$""Rd*© Z´hAëÖ­Y¼xq™(ÉMHH!!! 6 Èì"²}ûv¶mÛÆ¶mÛøã?X¼x1‰‰‰¸ººÒ¼ysû­iÓ¦4mÚ”ÆëDÉUV—u )BCC=z4mÛ¶µ:Šˆ£ë½·7lØÀ{ï½Wʩʮ””öïßÏÞ½{Ù»w/{öìa×®]ìÙ³‡+W®àééI«V­hÓ¦ ÷Ýwmڴᦛn¢R¥JVG)Q©©©¸«ˆõ5‚ªUá—_T"""""åÊR–’Fƒlu) 776,óöÓO0m !!ðøãðÐCàåeuJ)„Çyœ7xƒ9Ìáž±:ŽˆˆH‘© DDD¤:t(S¦Lá7ÞÀÛÛÛê8×åááAçÎéܹ³}Zzz:û÷ï·‰ìÞ½›>ø€Ã‡“žžŽ³³3ÁÁÁö"‘&MšØïW®\Ùµ«©¤|Ò~)ŸyoÏ;·’”=.\`ß¾}ìÞ½;[ñGLLL¶ß…š5kFŸ>}?~¢££‰‰‰áðáÃìß¿Ÿµk×Kzz:ÞÞÞg»Ùïûøø”ö*‰ˆˆHOLLL®·Ã‡“dv?«S§Žý÷Š»îº‹ÿ÷ÿþ!!!4iÒ„ªU«Z¼&""76u)cZ·c`ÇèÚÕê4"""""E’N:‹YÌHF⌳ÕqD¤4¸¹ADDæí§Ÿàwà`âDxøáÌ›.€Pfg<ïó>ŸñçïVÇ)4„ˆˆˆTpuêÔáþûïçÕW_åþûï¯ÐWI­V­ÕªUã–[nÉñÜ•+W8räˆý¤Í¬8ùå–,Y©S§²“U$H½zõ¨]»6uêÔ!00€€|$""Rޤ¤¤püøqŽ;Fll,'NœàÈ‘#;vÌþ;ùsçìóתUËþ»Bxxx¶"ÓºuëâêêjáÚˆˆ”oê"RÆÔ©U«ª DDDDDÊ…5¬áÇÎp«£ˆˆBC3o‡Á¼yðê«0e Œ ãÆAPÕ å!„p/÷2•© b6lVG)„ˆˆˆ“&M¢iÓ¦¼ûî»<ñÄVÇ)“\]]iР 4ÈõùÄÄÄWúމ‰aóæÍ,[¶ŒS§NÙ;ŒÔ¨Qƒ€€€lE"uëÖÍV8âïï_Z«'"""y¸pႽÐãøñãÄÆÆrìØ1Ž?ÎÑ£G9qâ§OŸ¶ÏïììLÍš5©[·.tíÚ•áÇg+úððð°pDD*6u)ƒZµ‚?ÿ´:…ˆˆˆˆH‘-d!ÝèFrÿ¢ˆT ÀÔ©ðüó°p!̘ï¾ ýúÁرУ‡Õ å*Ïó--S§NÙO½údÒ}ûöñÃ?pôèQí¯ñðð°‰Ô­[—€€{ñHÍš5©^½:5kÖTለˆH!\¸pS§NqæÌN:eïðqu¡ÇÑ£GIJJ²¿ÆÓÓÓþ3¹N:´lÙÒ~?ëk­Zµpvv¶pÍDD$?ê"Rµn ¿þju ‘"9Ç9¾â+æ2×ê("RVøúf€<þ8|ýuf‘HϞо=Œƒƒ:†[î&n¢½˜Æ4„ˆˆÈ K!"""ÀóÏ?Ï¢E‹˜8q"ÿû¿ÿkuœrÇÅÅ…ÀÀ@ó/))‰'NpüøqNœ8Att´ýþ¦M›8qâýõW¶n#þþþÔ®]ò¼_§Nø$""åÖ… 8~ü8.\°ÿ<Íí~ll¬ý ñY²~–P¯^=:wîLHHˆ}ZíÚµ©]»66›Ú…‹ˆÜÈÔ!D¤ jÝ,€Œ pr²:ˆˆˆˆH¡|ÄG¸âÊ=Ücu)kœœ ,,ó¶e ¼ý6<ø Œ‘‘™Å!UªX²BÏxnã66±‰[¸Åê8"""¦‚2¯xýÊ+¯ðÀ0räHþö·¿Y©Bòðð $$„<çIKKãÌ™3œ9s†“'Orúôé÷7oÞÌÉ“'9{ö,ÉÉÉÙ^_µjUjÔ¨aï0’Õm¤FÔªU‹ªU«RµjUªT©B•*UT@"""–HMMåüùóœ;wÎþõĉlß¾WWWN:•­ÃÇùóç³½¾R¥J9~ÖµhÑ"ÇϽêÕ«S½zu\\ô'‘Š@BDÊ –-áòe8|òù{ˆˆˆˆˆHYö0˜Áxãmu)ËÚ·‡E‹àå—aÎxç˜>î¿ƃ¦M­NX!u§;7s3¯ñ+Yiu‘ÓÙ"""b7tèP>ùä†Îü‡‡‡Õ‘$...ö«”·nÝúºó'%%å{¥ô½{÷òÝwßqáÂN:EFFF¶×WªT ÿ|oWw"ñ÷÷§Zµj:ÁJDDìrûYt½ÛÉ“'1ÆdÇÍÍÔÔTÜÝÝ©W¯Íš5£K—.¹vÆR7É:„ˆ”Ag~=xP!""""rCÚ¶³Ù̶:ŠˆÜ(‚ƒaêTxáøä˜1æÏ‡~ý`ìXèÑÃê„Î3<ð“´¤¥ÕqDDD D!"""bg³Ù˜?>­ZµâÅ_dúôéVG’bàáᇇ´oß>ßySSSíWb¿úªìW?>wî±±±lß¾Ý>=111ÇX¾¾¾T©R…jÕªÙ»\ÝyÄÏÏ???|}}ñ÷÷·ß÷óóÓÉY""eLJJ /^äÒ¥K\¼x‘ .d{|íϬŸçÏŸçÒ¥K9ÆóôôÌñs! €V­Zåú3#ë±››»víâÓO?eÑ¢E|ùå—}:=ôááátëÖÍêHRŠÜÜÜìÝG "999ß’¬û;wî´?wñâEr¯R¥Jöâ???*W®l¿uáHÖýÊ•+S¹rålÓ+éc""@fwެÂK—.G\\\¶‚Žkï_¸p!Ûk’““sÛÛÛ??¿l…õêÕ£M›6T­Z5×¢Ž*Uªé{t‹-hÑ¢&L`óæÍ|úé§¼÷Þ{Lž<™.]ºÁàÁƒ©Q£F¡—!""åWjj*...899YED®Ö°af‡‘L*©,e)Oó46Ô±XD ÉÉ ÂÂ2o[·Â[oÁcÁ„ ðÏfÞ¯VÍê”åš Oó4ò “˜DAVGq˜ BDDD$‡‘#GòÅ_0räH¶lÙ‚¿¿¿Õ‘¤Œ«T©èu¹^iþÚûY'.?~œ½{÷f›'·+ÏCfqËÕE$ÞÞÞxzzâíí¯¯/žžžxzzR¹re<==ñðð råÊxyyááᯯ/>>>xzzâåå…ŸŸŸN‘—õ}1!!¤¤$âãã¹téIII\¾|™¸¸8IJJ"..ŽË—/Û >âããIJJ"!!¸¸8û÷ÑÔÔÔ\—•UDwí×üýýsßåÖÙÉÊï‹NNN„††Ê믿Κ5køä“Oxþùçyúé§éÝ»7<ðaaa¸¸èÏ""’)%%E EÊ¢F2;„ˆˆˆˆˆÜ`¾à âˆcC¬Ž""åE»v°hL›sæÀÌ™0u* Ï> -ZX°ÜÌ`&0éLg&3­Ž#""â0!"""¹úðÃiß¾=ƒ bõêÕ8;;[IÊ!'''üýý‹TtdŒ±_í>·‚’¬«á_¾|™ÄÄDâãã‰ÍóäêüTªT üýýíE$YÅ&øøød+6ñööÆÕÕ\\\ì'Oûûûãä䄟Ÿ...øøøàêꊷ·w¡·ƒˆ”Ž„„®\¹Â¥K—HOOçâÅ‹dddpáÂ{1GZZñññ¤¦¦rùòå<‹6.^¼HRR‰‰‰\¸pÄÄDRRRò]~åÊ•ñðð°´yyyáéé‰ö"¶¬ÎJ×{\ÝyÉf+?W«sss#<<œððpX±b‹/æ¾ûî£fÍšŒ1‚‡zˆ«£ŠˆˆÅRSSqss³:†ˆ\«Q#ø÷¿­N!""""R` YH_ú@Á.š&"r]µkÃĉðÔSðñÇ™]CZµ‚;î€1càÎ;¡ý¯§,pÅ•'y’gx†xZÔ²:’ˆˆˆCT""""¹ªR¥ K—.¥k×®L™2… &XI$W6›­ÈE%W»ºpäzWåÏš/>>žÄÄDΜ9C\\IIIö“¿ÓÓÓ‰‹‹ÃãÐòÝÝÝíÅ&•*UÂËË 777{Q‰¯¯/ÎÎÎT®\'''*W®Œ³³3¾¾¾ö¢›ÍFåʕ̓¤½¼¼ìcöíuu!Ч§§®–,eZJJ ‰‰‰À 3²ŠÂ{d¾—³:c\¸p€øøxÒÒÒìEqqqö¯ééé\ºt‰+W®`_Vrr²½hãzÅY² ¾²Þ›Y~~~öŽ5jØŸË*ึ[QV¡ÇÕÝŠäú¼½½2dC† áØ±c,^¼˜Ù³g3uêTn¾ùf† ÆÐ¡Cñðð°:ªˆˆX@BDʨ† !&22@Ý9EDDDäK,ßò-Ÿò©ÕQD¤<óö†ÈHxè!øî;xûm¸ë.hÜy$ó9ýÏ£Ø<ȃLf2ïò.¯ðŠÕqDDD¢‚ÉS§N˜>}:cÆŒ¡cÇŽôíÛ×êH"%ÎËË ///ªW¯^ìcç×A ëäó¤¤$’““í'³çu{LLLž'°gM/ЬîYL{‘ `ïv’uÂ;ü·ƒJ–¬â•,W§ä5OnR²Ša²\)KV7–keØTÖú•YÇLAåUÀu¼\-ë8Ë’u|^-«£ ó\{,f߀½`êêy²Š/®ÎTØõ¿ZÖq–_•‹‹ !!!öc:ë8Ë:n³Žï¬Â©ü:IÙȳÏ>ËÓO?Íwß}Çܹs=z4ãÇ'""‚Ç{ŒV­ZYSDDJ‘:„ˆ”QuêÀ•+pú4ÔÒÕ7EDDDäÆðP™Êô£ŸÕQD¤"pr‚=2oÛ¶ÁìÙ0~<üë_0rdf×u+**O<ÃÞà žæiüð³:’ˆˆÈu© DDDDò5zôhþóŸÿ0tèP6nÜH³fͬŽ$rÃÊ:Q¼jÕª¥¶ÌÜNÀ¿úû«;.d€ŸWÇ…«OöÏê¸puqÁ¹sçì®#·,Y²ÆÉruAÀ&·‚—k]ݵâFsuAP–k‹®- ‚ÿAö€€{OÖ8yu´É#¯Ž6YÅy0‰899Ñ£GzôèAll,ï¿ÿ>ï¿ÿ>óæÍ£k×®<öØc 0 [šˆˆ”Oê"RFf~=vL!""""rC0>äC†1 wô9SDJY›60gLš”Y2k¼õ O? ºV‘<Îã¼ÁÌaÏðŒÕqDDD®K!"""r]sçÎ¥gÏžôéÓ‡M›6Q§N«#‰ˆƒ<<<ì'èߨ âââ0ÆØ_]Ä’×<×›žŸÜÆwDV—ü899áçWð«È\]üà(›ÍFåÊ•Êpíøy½V¤<¨S§&LàÅ_dݺuÌ™3‡AƒQ¿~}ÆŽË<£ ‘ˆˆ”ê"RFe]ÁôØ1hßÞÚ,"""""ØÀrá ·:ŠˆTd5kÂĉðÜs°t)¼ö´n ·ÜÏ> wÞ ÿwá6qœ~Œb3˜ÁÆP ]„NDDÊ6„ˆˆˆÈuyzzòÕW_ѵkWz÷îMTTµk×¶:–ˆT*L‘’àääDŸ>}èÓ§ÑÑÑÌ;——^z‰ &0bÄž|òIêÕ«guL)f©©©ê"Ryx@•*pü¸ÕIDDDDD²…t 7q“ÕQDDÀ݆ ƒ¡CáÛoáí·á®» aC=FOO«SÞPžäIÞå]±ˆH"­Ž#""’/'«ˆˆˆÈÁßߟ5kÖ‘‘Ahh(´:’ˆˆˆH± aêÔ©9r„I“&±|ùr‚ƒƒ ã矶:žˆˆ£””u)«2;„ˆˆˆˆˆ”q $ð9Ÿ3’EÅv IDAT‘VGÉÎfƒ=`Õ*Ø»úöÍìcÇBl¬Õ oµ¨ÅP†2i¤‘fu‘|© DDDDÈÆ©V­¡¡¡üöÛoVG)6¾¾¾Œ;–ƒòÁK—.]¸í¶ÛXµjÆ«#ŠˆH©CˆH¨!""""rCXÂÒHc0ƒ­Ž""’·Æ3;…>œYòùçР ¿übuºÂxÆóñ9Ÿ[EDD$_*‘©V­ß~û-íÚµ£k׮̟?ßêH""""ÅÊÍÍ¡C‡òǰqãFªW¯Î€hÕªŸ~ú© CDDn`ê"R†U­ çÎYBDDDD亲 À«£ˆˆ\_ðì³ óæevéÜBCáÓO!=Ýê„eV!Ü˽¼Ê«ô¿!)»T""""æííͪU«xê©§ˆŒŒdÔ¨Q$&&ZKDDD¤Ø…††²lÙ2¶mÛF³fÍ4h:t૯¾²:šˆˆ‚:„ˆ”aþþgu ‘|íg?ÿá?Œd¤ÕQDD ÆÍ † ƒ;`ãF€Áƒ¡I“ÌN"—/[°LzžçÙÁÖ²Öê("""yRAˆˆˆˆг³3“'OfåÊ•|þùç´mÛ–ŸþÙêX""""%¢eË–|úé§üùçŸ4hЀððpÚ´içŸ~ju4)u)Ãüýá«Sˆˆˆˆˆäë}Þ'@nçv«£ˆˆ^h(,[–Ù-¤xþùÌ‘±cáȫӕ)7q½èÅT¦ZEDD$O*‘" ãÏ?ÿ$88˜ÐÐPž{î9’““­Ž%"""R"Z´hÁ²eËØ¾};7fРAtéÒ…U«VYMDD !"e˜ BDDDD¤ŒK#øˆxgœ­Ž#"Rt fv9~&M‚åË!8ÂÂ`óf«Ó•ãÏüÈ&6YEDD$W*‘" dõêÕÌš5‹Y³fÑ¢E )"""åZ«V­X¶l?ÿü3•+W&<<œnݺ±a룉ˆH>Ô!D¤ SAˆˆˆˆˆ”!—¹ÌyÎg›¶†5œàÃfQ*‘âç—ÙäàAX²Ξ…[n`Ñ"HK³:¡¥ºÓ›¹™×x-ÛôXbùÉ Ã¢d"""™T""""ÅÂf³ñðógÏ:uêDxx8aaa8pÀêh""""%¦S§N¬^½šM›6áêêJ·nݸûî»Ù¿¿ÕÑDD*¼íÛ·sß}÷1pà@ÈÃ?ÌöíÛÙ²e ãÇgüøñL™2…7ß|“¤¤$«ãŠHåÊpùr…?ÉDDDDDʆ§yššÔdøš¯I'…,¤;Ýi@«ã‰ˆ” 77ˆˆ€ÿü6n„xàhܦM«Ðrx†gXÅ*v²“?ù“a #˜`F0‚C²:žˆˆTp.V‘ò%00O>ù„ÈÈHüqZ´hÁƒ>È„ ¨]»¶ÕñDDDDJÄÍ7ßLTTQQQ<õÔS´lÙ’‘#G2yòdjÔ¨au<‘ éÌ™3|þùçöÇ®®®öéY/HOO'-->}úЬY3KrŠÈÿñôc ) ||¬N#""""\q¤“ÎW|Å VP•ª¤“Îsø û÷ïgâĉÌ;—   žzê)Nœ8au<‘áìį̀Q£8pà>ú(ãǧ]»vüøãVG©P\\\¸ûî»íArãììÌÃ?\Š©D$Oîî™_SS­Í!""""$‘”cZÖÉ¿ò'ò(5¨Áw|WÚÑDD¬áë cÇfv Y±Ο‡ž=¡}{X´®\±:a±û’/©O}îâ.¶± À^(˜ÅNqÊŠx"""v*‘W©R%žzê)>ÌË/¿Ì'Ÿ|BHH<ò{÷îµ:žˆˆˆH‰ðööfÊ”)ìÚµ‹zõêѽ{wÂÂÂ8räˆÕÑDD*Œ{ï½÷º]š\JiD$_ê"""""eH2Éy>—AC%*Ñ€¥˜JD¤ prÊì²iüþ;´h>õëÃĉ™…"åÄv¶K,C:é¹Îã„“:„ˆˆˆåT""""¥ÆËË‹'žx‚èèhÞ|óM¢¢¢hÞ¼9}ûöeíÚµc¬Ž("""Rì4hÀW_}Å—_~É®]»hÕªo¿ý6VG)÷zõê…‡‡G®Ï¹¸¸NµjÕJ9•ˆä*«Cˆ BDDDD¤ È­CÈÕlØø‚/¨OýRJ$"Reu9p† ƒwÞÉ, yøaسçú¯¿r}vì(ù¬…ð"/I$Nùœf›Nº:„ˆˆˆåT""""¥ÎÝÝþóŸìÛ·•+WråÊúôéC³fÍxíµ×8qâ„ÕEDDDŠ]XX»víâÑGå©§ž¢[·nìqä"""Rhîîîôïß—Ï¥¥¥ñàƒZJDr¥!""""R†¤ÿ拾™ÍmÜVJiDDʸ  ˜:þú ¦O‡„–-¡gOXµ*ï×-[³gCh(üç?¥×Q6lÌf6çï8ãœë:ubêÔ©ìß¿ßê˜""""EÌwß}Çĉÿ?{wÅ•õüÛì;4²ï4¸;îˆË؈ˆÑ n‰ÆÉ8øÎ$ÑÄ$bfÓÌ›¼ÑIÆÑdœŒ&f3‰J&n¸Ðqƒ£h4?ÀDdYe“};¿?œ®¡¡ª¡¡AÎçyú¡»úvÕ©ê¢úÞª{êâÏþ3æÎ‹¬¬,}‡Åc•¨¨(=¸(+‘H0yòdŒ=ZÏQ1ÆT(“b9!„1ÆcŒ MhžÁ¶°Å×ø–°ÔcTŒ16ˆyz;v¹¹@L pý:ÐܬZ†èÁcÝ:`çNýÄÙ˜à(Žb¦©ŒÒ‚”£\‘1Æê8!„1ÆcžD"AHH>øàÜ»w/^ÄŒ3°{÷nŒ9~~~ظq#’’’„>Œ1Æcƒ‰¡¡!¶lÙ‚«W¯¢ªª 'NÄ_|¡ï°c족xñb<8n``€èèh=GÄëD9BH»Ñ|cŒ1ÆÓeBˆÀ_á+xÀCÏQ1ÆØCÀÊ ¸wOsû_™òâ‹À–-ý›°À)œB‚TF )F±£bŒ16ÔqBcŒ1ÆCCCÌš5 »wïF~~>Î;‡ððp9r!!!ð÷÷ÇË/¿Œ . ¹ãÝ$cŒ1ƸqãÆáÊ•+xê©§°|ùr¬Y³µµµú‹1Æ=;;;„„„ŒŒŒ°lÙ2=GÄëD™Â#„0ÆcŒ± ÿ½Îx‡0Sõ cŒ=Dª«÷ßï<:ˆ:ý+ðì³@[[ßÇ¥KXâ$Nb$FÂ[ŠP¤ç¨cŒ eœÂcŒ±AËÐÐsçÎÅÛo¿ÜÜ\\¾|¿üå/qìØ1Ì;öööX¼x1öìÙƒ;wîè;\ÆcŒ1QÌḬ̀{÷nüûßÿF||<¦OŸŽ[·né;,Æô”I ?þ8lmmõ c¬“ÆÆMLôcŒ1Æcøï!oà ,Á=GÃc‘÷Þûï9€îï¾ ¬[´¶öm\Z’Bг8 ?øà„ÆcúÅ·YbŒ1ÆØCA"‘`Ú´i˜6mvìØÌÌL( ( üñijÏ> ___„††B.—#44vvvú›±>WUU…BÓ§OãôéÓ€°°0„……A.—sG8ÆÀ–.]Š   ¬X±Ó¦MÃG}„¥K—ê;,ưêëë‘››‹¼¼<äççwzž›› ‰D‚K—.aÑ¢Eððð€§§'¼¼¼Tž›››ë{Ušªªü•JõcŒ1ÆØC¬¾¾ €ÊÊJQWW¨®®Fkk+ZZZpÿþ}@MM š››ÑÖÖ†ªÿÔÙÚÏîß¿åˆo€Ê<¨|VÓgP__¯6ne ÚªªªB›–w•700€­­-ZKZaòoìøÝìÀŽn?gll +++µï™››ÃÌÌLxmddkkk•2¶¶¶00øï}}-,,`jjªqþfffBûUùYXZZ¬­­addCCCØØØ,--abb‰D"\'m?ÆëÇŽý7¹ÃÐðÁH¡D¦©Kúhk>üðÁÈ"ŸÞéFMMM¨­­Emm-šššPQQ!jZkk+ª««Uæ¨þƵÿýQþn*)˵9·Áð°!Ö¿¶Ï*žU‰­ãï¥:bŽÃG”¿U€êïCûy)ù`ccCCCáwBùžT*¦YYYÁØØXí4Æc'„0Æcì¡$“Éèèh477ã»ï¾Ã™3gpæÌ¼ÿþû000ÀŒ3 —Ë1{ölL›6Ov²‡B[[®_¿ŽÓ§OãÔ©S¸|ù2ˆS¦LÁÓO? 8uê>øàH$LŸ> .DXX&Mš¤r±1Ƙþy{{#11üãñË_þ›7oÆë¯¿CCC}‡Æcýª©© ÈÏÏGNNòóó;%}”•• åÍÍÍáéé OOOxxx`êÔ©ððð@KK ŠŠŠ““ƒ¬¬,\¼xyyy*Ž:%‹xxxÀÛÛpww.¦2Æt¨ª 05}ð`Œ1Æ"ˆ•••*V•T•IÊ÷ª««ÑÒÒ‚ÊÊJ!aC™D¡ì°ZUU…––TUU [Õ%ch£}'Té’wMMMaaa¡¶ Ð9áÁÀÀ>>>Z%<´×¾ã«6Ú'Hˆ¥ÜÞwÝ…s3 ·‹;×UòIOfŠ‹‹»L˜©««Cãî°_QQ¡¶Œ¶ììì ‘H„À¶¶¶022‚­­­°-•ßµ2áÄÎÎFFF°±±ö åw«ì€Ü¾s±”ÀcGééh-.FMv6šîÞEka!ÚJJ`PZ ãòr˜TWì¦Æí“¿ü{ýü°ËÊ ÕÕÕ¨¯¯Žݱ³³ƒ±±1¬­­…cTûä¸ö¿[vvvÂo\ûß*åqOIå·é2ùíˆùý“¼Ø±LûÄÍö¿)íŠŠŠ„ß‘ŽI ÷ïßGss3*++»\®’T*…¹¹9lmmaccØÙÙ©¼¶±±^·ÏÖÖÆ ãÄÆëcœÂcŒ±‡ž±±1fÏžÙ³gãµ×^CYY Μ9ƒ>ø[·n…‰‰ ¦L™‚Y³f!$$³fÍâ‘Ø QZZŠóçÏC¡P >>wïÞ…““æÌ™ƒýû÷#""öööBù­[·¢¦¦çÎC||<öîÝ‹?þñpppÀ¼yó —Ëwww=®cŒ1%###lß¾#GŽÄï~÷;üðÃ8xð Ê±1Æ»ŠŠ dffâîÝ»(,,ìô<''­ÿ¹C ±±1àææ™L†Ù³g Ï]]]áææÖ©£‘˜å«[öÏ?ÿŒsçÎ!;;[¥³ŽT*UY^Çç^^^*ˆc"TU|.†1Æcœ2颺ºUUUBÂFUUîß¿ÚÚZÔÕÕ¡¢¢B¸ yMM ªªª„×ÕÕÕ*e»£ì€¯ì¤oggCCC•NúNNN055:«J¥ÒN[%‰J"‡²³~ûrÊeô4ƒ ÊÎÃíïz¯LLiW|eŒ29 øïH1’ÊÊÊÐÔÔ„ªª*´¶¶¢²²ÍÍͨ©©u'|àAkKKKX[[ÃÖÖVxmkk+tÖV&XZZÂÂÂB(«¼³}ûÉÜþfLÿêêêPRR‚²²2Ü»wOx”——wz]ZZŠ{÷î  ®®fm Oss¸š˜ÀÍÄÕÞÞXåì XXXÀÎÎNíÊ$Ãö£d0õ”I"ÊäRu£©TVV¢®®ÕÕÕ£¢¢¹¹¹*Ó”ulll0lØ0888`ذa°··Ç°aÄGû×ŽŽŽprr⛺2Ƙ¸FÌcŒ±!ÇÁÁ+V¬ÀŠ+………HJJBRR þú׿B"‘`Ô¨Q˜5k‚ƒƒ1wî\xyyé9rÆhmmÅ7‡øøxüðÃ000À´iÓ°aÃÈårLš4 ‰Dã<¬¬¬‰ÈÈH@ff& âââ°qãF¬_¿2™ ˆŒŒDHHˆÊºcŒõ¿_ýêW;v,–.]Š3f૯¾‚¿¿¿¾ÃbŒ±niJ¶ÈÌÌDff&òòòÐÜîní“-d2är¹J²…···ÎGJ’J¥J¥ ìr=Ô%­(ëÒí“VÚ¯‡¦Äm“V{èqBcŒ1ÆúACC*++QYY‰ªª*•¿*¯Õ=Wv¬W§cGööÞ===…ŽívvvB'UKKKáNäfff°µµ…¡¡¡p'sM#f0Ö}&ô(;WVV¢µµUUUhhh@]]*++QSS#t>Vv2V&Kݽ{·Sb•ònöš(ÿlmmagg§ö¹T*ÕX¦ýè6Œ±ÿjkkCII Š‹‹q÷î]”””àîÝ»(**ê4­ãÿ¨‘‘Q§Îþîîî7nœ |´I‚Gꦦ¦055ÕÙ¶W&¶O mŸÔ>i(77W%i¨ýÈXÀƒº•»»;œœœàææggg¸¸¸ÀÕÕNNNÂ{NNN|ž•16äqBcŒ1ƆglȨ®æ„ÆcŒ‰ÖÖÖ¦Ò¹°cGÃŽw(¿wï*++ÑØØØi^‰vvv:Œ»¸¸`äÈ‘j;˜·­À‚GÓ`¬e"“®;v+“²êêêT:kJàúé§Ÿ„çÊ„/"ê4_e2VûêÊ;ÔwœÖþÁÙ`WXXˆ¼¼<á‘““£òº¤¤Dåæ'æææBg|gggŒ7Nè¨ïââ'''888ÀÁÁ¡W×­Ùà&‘H„›ïh«ªªJ¨ÇuLF*((ÀÕ«WQXXˆ¢¢"Ô×× Ÿ344„““¼¼¼àéé OOOx{{ Ï===áââ¢ËÕdŒ±‡BcŒ1Æ:pttÄ’%K°dɆEþöÛoqùòe\¹r¯½öîÝ»###Œ;V%I$ @çwªeCSǤŒk×® I[¶lAdd$údÙæææËåËåT“QÞxã lÙ²EHF‘Ëå½NFaŒ1¦\¼x¿úÕ¯†÷Þ{kÖ¬ÑwXŒ±‡Pcc# 4Žî‘‘‘ÊÊJ¡¼™™™J2C`` ¢££…ĆáÇ?ÔõFccc¸¹¹ÁÍÍMcÒHWÛT¡PÏ•:nÓŽÏömʆ˜Š ÀÎNßQ0ÆcLOZ[[QRR"Üi\Ù °¸¸XcâGG–––6l˜Ðuذa?~¼Ðy[™ÌÑ1¹ƒëÔŒ ¶¶¶½N¾ª®®V›@RYY©r|ÉËËÃõë×…×êFÒ”(¢¼ƒ½££#ÜÝÝáèè'''¾†Ìú]II îܹƒôôtddd ''¹¹¹ÈÏÏG^^ž$)‘Hàââ"t¦†··7œáææ&$}ðï%ëkÊ㼟Ÿ_·e•£J)“EŠ‹‹…¤¦o¿ý‡BQQ‘hjjª’ âãã??? >þþþpttìëÕcŒ±>Å !Œ1Æcݰ±±Á¢E‹°hÑ"aZFF®^½Š+W®àêÕ«8pàêêê`ii‰   L™2'NÄøñã1jÔ(qµ‹u/33 …qqqP(hhh€L&CDD¶oߎ˜ššö{\¾¾¾Âè!­­­¸qãâââ÷Þ{†††˜6m"##!—Ë1iÒ$H$’~“1Ɔ333|þùçxõÕW±víZdffbÛ¶mú‹16ˆt5š…òuÇ fíG³ËåX½zµ  “Ét~çχ‘©©©°½4éjÔ•´´4¤§§£ººZ(ß>iD]âˆ,--ûcõë»w‡1Æ{è(“<Š‹‹QXXˆÒÒRܽ{ÅÅÅ*É¥¥¥ÂhzÀƒ9;;ÃÙÙYèdíïïßåûÍÌÌô¸¦Œ±ÁÀÆÆ666ðòòÒês ÝŽDTZZŠŸ~ú EEE(..FCCƒðy899ÁÑÑQè`¯ìlïèèWWW!‰ÄÉÉI׫ÍbEEEHOOÇ;w:=”çÌÌÌ “Éàãパ#GB.—ÃËËKHñðð€‰‰‰ž×„1í(ç£FÒX¦©© yyyÈÏÏï”uõêUdff ‰QÊD•ÇðáÃytÆØ À=cŒ1ÆzÀÏÏ~~~X±b ¥¥iii¸rå ®\¹‚³gÏâwÞASSLMM1fÌŒ?^åaÇw½òjjjpîÜ9ÄÇÇãÔ©SÈÍÍŰaÃð‹_ü»wïÆ#<}‡©ÂÐÐAAA ¶mÛPZZŠóçÏC¡P`÷îÝØ²e œ1{ölDDD 22’;2ÆX‘H$ضm±aÃTTT`×®]œ”ÇTTT¨M(P>ÏÉÉAkk+€#[888IÁÁÁF¢ðññž×jh077ï6i¤¢¢Bãw›””„ììlÔÕÕ å¥R©ÆQFd2¼¼¼øFLÿîÞÆŽÕwŒ1ÆÓBw펼¼<477«|F*• uQWWWŒ9Rx®ü«,Ãç8c…™™ÜÝÝáîî.ú3õõõ¨¨¨@aa¡pl¼{÷®0í»ï¾Caa!òóóÑÔÔ¤òYndzŽîÞ½‹ÔÔT¤¤¤ÓÒÒpÿþ}Î');°ÏŸ?ëׯ^{zzòo*’LLL„¾=ê´µµ!//¯S2Õ‰'pçÎ!±ÏÚÚ;v,0fÌÂolÂ@¸fÈcŒ1¦FFF7nƇuëÖxpÇÝ[·náæÍ›¸yó&nܸ'N ¬¬ àãã#$‡Œ7ð÷÷ç“w±¶¶6üðÃP(P(¸páÚÚÚ0a¬\¹r¹sçÎTû€££#¢¢¢…wß}WeýÖ­['¬Ÿ\.”ëÇcƒÁ3Ï<©Tеk×¢²²û÷ïçc-c¹®’Ôuºj߉@&“A.—«t"ðöö†¡¡¡׈iK*•B*•"00PcMó”#¶O RÎSÓ(#œIxQn IDATÄúEa!àêªï(cŒ1öÊúdNNòòò——‡‚‚áyaa¡ÐîH$pqq‡‡ÜÝÝ€ÐÐPa”AåHz^+Æë?æææ077‡››‚‚‚º,[VV†ââba4¥‚‚á.öׯ_G\\œÊH®ÆÆÆpuu…§§'<==áîî.<÷öö†ŸŸßœpºÿ>®_¿Ž””•䊊 €““ÆŒƒ©S§â駟ƈ#àïïwwwNú`LKðöö†··7æÏŸ¯ò!??wîÜÁíÛ·‘ššŠÔÔT?~¥¥¥{{{Œ3FH3f &Mš+++}¬clˆãÞŒ1Æc}ÄØØXHY½zµ0½¢¢©©©¸ví®]»†ÿûßxýõ×ÑÚÚ cccxzz" ÂßÑ£GÃÂÂBkÃzªýqqq(,,FÐxÿý÷ª4 „ÑCbbbTF@9xð vìØ!Œ€"—Ëä(Œ16X­Zµ ¶¶¶ˆŠŠB}}=>ÿüsN alª¯¯W›è¡|~ûömá΀êÝuÕîáëëËm‰!J*• õsuš››QZZªv»víâãã‘¶¶6€©©©Ð¡OÝÝI•Ïë‘– ´à}ˆ1ÆëWʤM¥öíŽÀÀ@Èår•z ,--õ¸&Œ16¸988ÀÁÁ¡Ë?455¡¬¬Lm;þòåË8zô¨ÊÍÚßø¡ãƒo20´´´àçŸú |ûí·¸qãZ[[akk  <<˜Î;‡ï¿ÿW¯^Å7ÐÔÔ[[[Lž<¡¡¡øýï)S¦ð÷Àئ®Ïð Þ}õêUáqüøqTWWÃÔÔ&LÀ”)S0mÚ4Ì›7îîîzŠž1ö°â^Œ1Æc€¹¹9&Nœˆ‰'ªLonnF^^žÊÝΜ9ƒ]»v¡¾¾@ç»»(G1b¬­­õ±:CNVV P(pæÌTUUA&“A.—#&&aaaÜé î„èèhÔ××ãÛo¿…B¡€B¡ÀÛo¿ Ìœ9r¹‘‘‘ÐwÈŒ16èÌž=§N¢E‹°bÅ 6;;ééé¸}û6ÒÓÓ‘žžŽØØXäää ­­ ‰>|8†???øúúB&“Á××R©Tk6¸uLf¸víšÌðÊ+¯àÑGÅèÑ£õæ€fnn¹\¹\@5©fûöíØ²e‹T#—Ë9©†1Æ´Œ¸¸8„‡‡ãÉ'ŸÄçŸCCC}‡ÅØ WQQ¡¶Ã»òyNNZ[[<¨Ç;88܃ƒƒ;%zøøøÀÀÀ@ÏkÅXï™››w›4RQQ¡ñ')) ÙÙÙ¨««Ê+o| n”™L///ëasç`l xxè;ÆclÀ«­­ÅµkלœŒ´´´N‰... ÄÔ©Sñ«_ýJ¸‰”½½½ž#gŒ1¦o...pqqÁÌ™3;½wïÞ=•›¦¥¥!..ÅÅÅþ›(ˆÀÀ@Lž<“&Mâ›:´ÓÐЀ„„œ>}çÎCZZŒ1uêT,_¾óæÍÃŒ3`nn®ïPcýÀÀÀ£F¨Q£°zõjúš\ºtIHÛ¿?ZZZˆyóæ!,, ¡¡¡055ÕsôŒ±Á†¯˜0ÆcŒ BFFFÂÝ]-Z¤ò^cc#222TE~þùg|ýõ×(((@[[ÀÎξ¾¾ÂC™(âëë ˜™™écÕ4Ú½{7rrr°aÃøøøôûò333¡P(…B††Èd2DDD`ûöí áFy/øúú £‡´¶¶âƈ‹‹C||<Þÿ}`Ú´iˆŒŒ„\.ǤI“øN9Œ1Ö…9sæ >>áááX¿~=Þ{ï=>n2Ö…®:«gff"//ÍÍÍBùöÕ•I¬í;«{{{s"cíH¥RH¥Rj,£)éJÙkŸt¥œ§¦QF8éjº}É$…0ÆcLÐÒÒ‚””\¹rEx¤¥¥¡µµNNN;v,'~0ÆÓ‰aÆaöìÙ˜={¶Êôމ"·n݉'PZZ ###`êÔ©Â#00pHÝÄ¡¶¶_ý5¾üòK|ýõר­­Å¤I“¿ýío˜5k¬¬¬ô&cl€077Çüùó1þ|@MM qîÜ9œ={{öìµµ5ÂÃñtéR,Z´ˆïc¢ ÚcŒ1ÆØajjŠ€€tz¯©© 999ÈÊÊBVV233‘••… .à£>½{÷‰®®®ðòò‚§§'<==áíí-¼öòò‚££c¿®×píÚ5ìÞ½=ö^~ùeLŸ>½Ï–WSSƒsçÎ!>>'OžD^^0oÞ<ìÞ½ááápwwï³åe††† BPP¶mÛ†ÒÒRœ? …o¿ý6¶lÙ'''Ì™3ˆˆˆà‹œŒ1¦ÆÜ¹sqìØ1DDDÀÑÑo¼ñ†¾CbL/êëëÕ&z(Ÿß¾}÷ïßÊK¥R•ÎåG÷ðõõ…………׈±‡“T*Úê477£´´Tíÿóµk×ììlá&¦¦¦pww×8ʈò9 ÒÓáÃõcŒ1¦w………¸pá‚üñÃ? ®®VVV BXXþüç?cêÔ©ðòòÒw¸Œ1ƆM‰"999* ‹‡BMM ,,,0iÒ$L™2Ó¦MÜ9sàâ⢧èûFmm-Ž=Š/¿ü§OŸFSSæÌ™ƒ7ÞxK–,áó Œ1Ѭ¬¬°hÑ"áF°8zô(Ž9‚+VÀÄÄ .Äã?Ž%K–ðµ Æ˜FœÂcŒ16„˜˜˜`øðᮡ“Euuµ,’œœäåå!11Ÿþ9ŠŠŠ„²æææðòòRIqss:Õ¸ººÂÉÉIgwFVGÜÖÖ†¸¸89rãÇǦM›°jÕª^ßi¦­­ ?üð  .\¸€¶¶6L˜0«V­BDDfΜÉw˜ÕGGGDEE!** ššŠøøx( üæ7¿Akk+&L˜¹\¹\Ž9sæÀ˜ï*Ëc€ÐÐP|øá‡X½z5ðâ‹/ê;$Ætª±±G÷ÈÈÈ@ee¥PÞÌÌL¥3x`` ¢££…:ìðáÃacc£Ç5bŒibll 777¸¹¹iLé꘠P(™™‰ŠŠ ¡|ÇcBÇç|LèGéé@p°¾£`Œ1Æú]EE¾ùæœ;wçÎí[·`llŒ1cÆ`Ú´ixúé§1uêTŒ=šG!dŒ16 x{{ÃÛÛ[¸~×ÚÚŠ´´4\½zW®\ÁùóçñÎ;ï ¥¥˜7oæÍ›‡ùóçÃÎÎNÏÑ÷Ìõë×±oß>Þyç,^¼ú1öpwwdzÏ>‹gŸ}¥¥¥8~ü8Ž9‚§Ÿ~Ï<ó V­Z…èèhL˜0Aß¡2ÆNaŒ1ÆcŒ?ãÇWû~CCòòò——‡ÜÜ\äää 77yyy¸té Tî®lhhggg•$‘öÏ•»½˜¥½xpgXHIIÁÚµkñ‡?üÏ>û,Ö¯_¯Õ Ä’’\¸p …'Nœ@QQœ±`Á|öÙgËåJ¥¢çÇúG`` ƒÚÚZ|÷Ýwˆ‹‹Ã¡C‡°cÇØÛÛcþüùËåX´h<==õ2cŒéÕªU«PTT„—^z nnnX¹r¥¾CbL”®FP¾.**è<€\.ÇêÕ«y4ƆSSSÈd2Èd2eº5(--­Ó¨Aí“FÔ%ŽøøøÀÒÒ²?Vïávç°v­¾£`Œ1ÆúEzz:Nœ8¯¾ú ‰‰‰ "Lš4 ‘‘‘عs'fÍš+++}‡9脆†"!!AoŸgŒ±¡ÎÐÐcÇŽÅØ±cñôÓOjjj””„³gÏâܹsø×¿þ„„„ <<>ú(üýýõy׈§N›o¾‰sçÎ! Û¶mnÂĆ®+°þæèèˆuëÖaݺu(--ÅðþûïãÝwßÅüùóñÒK/aáÂ…ú“16@pBcŒ1ÆÍÌ̬ËF ®®ùùù(..FAAŠŠŠ„¿éééHLLDAAª««…ÏÂÉÉIcÒȰaÃP__ßiY­­­€üü|üáÀ_þò¬[·/¾ø"¼¼¼:•oiiÁÍ›7‡øøx\¿~¦¦¦˜5kžþyÈårLš4 ‰¤×ÛŠˆpàÀüóŸÿDzz:êêê0bÄ„‡‡cÕªU3fL¯—1Xœ={ï¼ó.\¸€ÖÖV >Ï>û,žzê©^okKKKadÝ»w#33SååÅ_Äúõë!“ÉÈÈH„„„ÀÔÔTGkÆcƒÇ¦M›ŸŸ§Ÿ~2™ Ó¦MÓwHŒ¡¢¢Bm‡lå󜜡¾gll ¡vppp§DŸ~Í­}&==]åÂõ‹/¾ˆòòr|øá‡Â4…BÐÐPáµ2‰¥?âÓŲ”ó3/mÊj³ü¾Þfº¢m¬šÊ÷ÅvÔ´ü¾\ŽØù÷e›¡#ssón“F***4›’’’ºº:¡¼T*Õ8ʈL&ƒ——W¯Gµ|¨••åå@çcŒ±ÁîîÝ»8tè>ÿüs\»v öööX¸p!8€°°°Aqc"uõ2;;;Œ9›6m²eËôÕµµµéüó=©ßªõßþªÛkЧ?Úƒ¡½6Xâ(ô¹ßêÂ@þ¾û¶Õ–••.\(tX.//ÇéÓ§‡×_/¾ø"&OžŒ'žxË—/‡«««ž#VuùòelÞ¼‰‰‰Ɖ'¡ósb)—ëää„””8::ª}èÿ}LÛó‡ƒñ@u…ÔÔT<÷Üs¸zõ*jjjôíw¥ïzH.ûa?¾:::bÓ¦MØ´i’’’°cÇ<òÈ#˜2e þú׿bΜ9ú‘1¦g|‚1ÆcŒé”……FŒ#FtY®¡¡ååå(,,:×´ÿ{ýúõNw}îJkk+êêêðî»ïâwÞÁ¢E‹ð§?ý NNNB‚À™3gPUU™L¹\Ž˜˜,\¸ÖÖÖºZ}Á«¯¾Š«W¯bß¾}1bðÃ?`Ó¦Mxã7ÚêÌŸ?ãÆÃéÓ§1f̤§§ã™gžA^^þô§?étY2™ ÑÑÑˆŽŽF}}=¾ýö[áûçw`nnŽ™3g I$AAA:]>cŒ do½õîܹƒÇ{ ßÿ½ÚäIÆt¥«ÎÔ™™™ÈËËF}T;S+ëjí;S{{{w;¢\jjj‚““nß¾ÝéÂë™3gP^^޶¶6!AE.—£¢¢¾¾¾(..îóøˆH§V•óÓuY¦YmǾ^ŽØù÷g›A ©T ©TŠÀÀ@e4%µ)“ÔÛ'µ)ç©i”‘þNjpnÞ|ðwìXýÆÁcŒõÄÄDìÚµ LJµµ5–.]*tØHm1:¶3ZZZP\\Œo¾ùÏ=÷Š‹‹ñÜsÏé-¾o¾ùF¯ŸÔ×¹Ä#ÞoûÎPß¶öööX¹r%V®\‰––\¸pŸ}ö¶mÛ†—^z =öžþyÌš5K¯qVTT`Æ øôÓO!—Ë‘œœ< ®)*÷Ÿ§žz /½ô>þøcµïëã:ôPØ·uQWX»v-6oÞŒ“'O"11Qå&B}AŸßK/{(ìƒJ³fͬY³œœŒÍ›7cîܹX³f vïÞ ;;;}‡ÇÓ ¥žhŒ1¦Hp‡± ú½Ë {8H$>|¸Ïîš´oß>DGG÷ɼë/õõõP(xôÑG{<KKK̘1X¼x1|||t ÎÎÎøÿïÿÁÉÉIezZZ‡TBˆD"ÁÍ›71nÜ8aZFF‚ƒƒQTTÔoqáÌ™3ˆGBB*++…§r¹ ,€­­m¿ÅÃcúpÿþ}ÃÐÐIII°´´ÔwHZ»víôÙ8eÝ<66¶OæßÞ`­¯××׫MôP>¿}û6îß¿/”—J¥ïšïêê ___XXXèqz&** ‹/Æ“O>)L»yó&¶nÝŠêêjüïÿþ/‚ƒƒ…÷>ŒÃ‡ãÈ‘#ýŸ®/þj3?}.[ßt5BHOæÕS}½1ó(m]jnnFiii—ÇËììláî’¦¦¦pwwïòxéææ¦çµê#o½ìÜ Ü½«ïHcŒ bÛb½i^¼x›7oÆ÷ß3f`ãÆX¼x1ÌÌÌ´ž×@¢©n§P(ÌÌL=DÕwzRWV÷}µix„ÿ,q$ƒy› ôØz|úÐÐЀ£Gb÷îÝøþûï1}út¼ùæ›zI ¹zõ*–,Y‚¶¶6ìÝ»‘‘‘ýCW$ jkk1vìXìß¿sçÎíô¾¾ö/±Ë~˜þ´]ccc444ôkbð`Ø'ëòŠcÇŽá·¿ý-ŒŒŒpìØ±‘À¦N^c¬¿õõõëö4õ7¢·›bŒ1Æcƒ…¹¹9LLLD•mÇKKKÌ;_|ñÊËË‘€7öK2ÔÕÕ©M.è4\¼º;Uhš~óæM,X°–––°±±Á¢E‹púôi­Ë¤¤¤ <<ÖÖÖ°¶¶ÆÂ… ‘’’¢R¦ªª /¼ðd2ÌÌÌàêꊵk×âÊ•+Z•!"•Ž]0lØ0466ªÛt}ÆÅÅkÖ¬All,ÊÊÊœœŒèèh¤¦¦bÅŠpppÀäÉ“±mÛ6\»vmHž,bŒ=ü¬­­qüøq`ݺuú‡ @ÈÌÌDRR¾øâ ìØ±7nIJeË0yòdØÛÛÃÂÂ~~~ Åúõë±oß>dffB*•B.—ã­·Þ‰'œœŒªª*”——#55 Ø»w/¶mÛ†èèhÈårÊdÇÉ“'U¦=záááxôÑGqâÄ •÷Nž<‰ˆˆáõ‰' 333øøøà…^PI¤ÄÕµzKLJ¹¹¹X²d lmmaee…ððpܺuKÔrÄÔ?ÕQÖ‰•õc‰D¢rüJIIÁ#<"Ì÷‘G5_HMMÅ#<+++ØØØ ,, iiijëâbâï.Vm× òòò°xñbX[[ÃÙÙO>ù$îÝ»×i^=ݾšˆÝ®bÚíùûû ëÚ¾SÇ@i3è’±±1ÜÜÜ„¨¨(lܸÛ·oGll,’’’‘‘ºº:ddd 11@tt´pÁH¡PàÕW_Ž¿îîî077Ž¿kÖ¬Á–-[°oß>ÄÅÅáÚµk¨®®ÖóZ÷Ð?'ê; ÆcL'***°lÙ2Ì™3¶¶¶øî»ïpéÒ%,_¾|Ð'ƒteêÔ©¸Û!¹S›sßÊib뿺ž§¦Ï·¯c}½}{ÂÖÖK–,Ann®Æ{ÓžêHL[±»øÅ´•ŸËÈÈÀÒ¥K!•JU¶UOÚ@‰iiiX¸p!lllºÜbÚ%ºØbÛ†Ýme̺¸ö¢‰6ß›Øÿ'm÷厺kêj»i³þí—«î›7cýé“O>! ò044žѨQ£èù矧ÌÌL}‡L+W®¤çŸžÊËË»-«©ZÞqúÏ?ÿL#Gޤ„„ª­­¥ÔÔT Q)'¦Lzz:¹¸¸Ð»ï¾K%%%TVVF ™LFyyyB¹Å‹Ó¶mÛ¨¨¨ˆèúõë¬2/1eÔÙ´i…‡‡w»múKii)ÅÆÆRtt4¹»»rrr¢¨¨(Ú»w/ݽ{Wß!2ƘN={– iÏž=úEkÉÉÉ”œœÜgóŠŠ¢¨¨¨>›{ý]_ojj¢‚‚JNN¦ØØXÚµkÅÄÄPTT“L&#‰D"Ô¯LMMI&“Qpp0EEEQLL íÚµ‹bcc)99™ ú5þ¦¸¸˜œœœ¨µµU˜6~üxÊÏϧŒŒ 5j”0½­­\]]©°°P˜€öîÝKµµµTXXHkÖ¬¡§žzJe=­k)ç/†˜8”åf̘A ÒÒR*..¦÷Þ{¼¼¼(++«Ëe‹­j».éééäááAû÷ï§ââb!&wwwJOOïržwîÜ!OOOá³eeetðàA3fL¯â×ö”{WåЂ èäÉ“tÿþ}ÊÍÍ¥¨¨(Z»vmãƒØí*¦íÑqþÉÉÉäããC?þøc·q ´6ƒ¾ÔÕÕQFF%$$ÐÇLÛ·o§èèhŠˆˆ    ²¶¶Vi'K¥R  ¹\NÑÑÑ´uëVÚ»w/%$$PJJ ÕÔÔè{•:=šèÐwŒ1Ɔ±m1mÛŸ·nÝ"???òôô¤¯¾úª§á hšê¯ 4räHÑåÕMWWÿ]²dI§ú¯6±i3OMŸW§c{¢´´”8@3fÌÐ8±í)1Ķ»koˆm †††Ò·ß~Kuuuôõ×_«Ì·'m 1cÆÐÁƒ©¬¬Lã¶Û.éí¶Ð¦mØÝöÐåµ—®¶ŸØïM̾¯í¾Ü‘6íÂÞn7m×_]´Ÿ»Z±ç(zzÞ¡7ûÎ`O4|øpúùçŸû|y?ýôÙÚÚÒêÕ«©¹¹¹Ï—×Sí¿ïU«VÑ_þòï‰ß¿Ä–ëíqC]Œícèɹ¾ääd²±±¡––aÚG}Dîîîô÷¿ÿ]¥ì¨Q£:ÕïzslÑŹ@m]ý.‹Y¦®»ÚìÚ.»7ëªé<¤Ø~üñGš2eŠÊgÛÚÚÈßߟnܸÑeúÒÜÜL«V­";;»~9nj«?¯¿1Ößúúúu{šú›>œ5BÆëCœÂt‰Bç­·Þ"‰D¢’"‘HÈÅÅ…–/_N *ñ‚ÚÚZzþùçÉÞÞž-ZD{öì¡üü|µeÅ^[¹r%}òÉ'*Ó~úé'•rbÊ<ñÄ´cÇŽNËÛ¿?mذAxmeeE÷îÝS)“™™©2/1e”rrrè“O>¡)S¦ƒƒݹs§S™"%%…¶oßNr¹œLLLÈÀÀ€‚‚‚(&&†¨©©Iß!2ÆX¯mݺ•LMMéêÕ«úE+œ¢Yyy9%''Ó‰'hïÞ½C«W¯&¹\N2™L%™ÖØØ˜\]])((ˆ¢¢¢hÆ ´}ûvŠ¥ÄÄDÊÈÈpõ«hêÔ©ôý÷ßQFF?^xo̘1tûöm"z°ßNž<¹Ëy•——“½½½Ê4mêZõ´3€º8”óûðõàj IDATÃ;MÿÛßþÖé"`Çe‹­j¢i]žxâ Úµk—Ú˜ž|òÉ.çùä“OªýìÁƒ{¿®BŽ;¦2-33“ÜÜÜzŸ˜ÄnW1möóOMM%áFÁÔfHÊËË)%%…hïÞ½´uëVŠŽŽ&¹\NdaaÑ)i$((ˆ"""(::š¶oßNü1%$$PFFFÿv|©«#24$úâ‹þ[&cŒ±!¯/Bª««iĈ4}út*..îMxZûº^KK Ð'Ÿ|B...ôñÇwY¾»éêê¿?ýôS§ú¯˜Øz2Om:yjjO|øá‡ç#¶=%†Ø¶¢6í“®Ú‚çÎÓø¹ž´:ÔizÇm!¶]ÒÛm¡MÛP9MÛC—×^Äêê{³ïk»/w¤M»°·ÛM®Ö_×ËRη«õ{Ž¢§çt¹ï EEE4mÚ45jÝ¿¿O—IAAAÔØØØ§Ëé­ößwIII§¤žî_bËõö¸¡.Æö1ôä\_[[9;;Ówß}'L›?>%$$ÐÌ™3…iéééäììLmmm]ƧͱEçµ=~tõ»,f™º>îj³Oh{žµ§ëÚÕyHmb˜8q¢JòÇ×_M¿øÅ/´Š©¿544ÐĉiñâÅú¥Na3NaŒ±AˆB˜.qBcšSll,­^½š¬¬¬YXXмyóè£>¢ŠŠ }‡(Jee%>>€¬¬¬(""‚öîÝK999ú‘1Æz¤µµ•BCCÉ××wÐü® Ý„®:ûÊd2266Öª³oû»¥±ž{õÕWiÛ¶mDô ‰ù•W^Þ{å•WèÍ7ß$"¢¿üå/B¹®t¬ÿiS×ên^½ý,***ê4=33“\]]»ü¼Øú§6ñt5ßÌÌLrqqéržš>[VVÖ«øuÒqÄÁ––’H$=ŽOL b·«˜¶‡rþ™™™$“ÉèìٳݖŒm†Á@›¤Áö¿#}ž4xù2@ôŸ:Æc¬?ôEBÈ›o¾IjëÌ“öõåÃÕÕ•4–;]]ý·¡¡¡Sý·«Øz3Om:yjª3öº=%†Ø¶¢.Ú'¨¶¶V§Ë(++ë4½ã¶Û.éí¶Ð¦m¨œ¦í¡Ëk/Úè;¯í¾Ü‘6íÂÞn·®æ-fš®–¥íz¨;GÑÓóºÞw‹ÂÂBrppun­§ŠŠŠH"‘ÐñãÇûlºÒqÿùàƒ(44Tãûb÷¯Þ–{ÜPcwós®ï©§ž¢×^{ˆˆòóó…›óüâ¿ ÌÌL"Ò.Sì±E¿µÚ?ºú]³Ìþ:—¨nŸÐö>^«˜ôáèÑ£$‘H¨¤¤Dß¡¨à„ö0ã„Æ„8!„é'„0¦Þ¡C‡„‹I¦¦¦ôòË/Ó­[·ôV¯ÕÔÔÐÒ¥KiÍš5*ÓÅ^344ìö®4bÊ©½p€ÌÍÍ…r•••ô /L&#kkk ¡]»v©ŒŽ!¦Lwë9ØÜºu‹^~ùe255¶›º;š1ÆØ`PRRBnnn´jÕ*}‡"ÚPLY°`ÆßîqãÆQtt4}ðÁ”˜˜HYYY<’U?JNN¦éÓ§Qpp0%%% ï}÷ÝwBDDÓ§OW§¤¤„~ýë_“»»{§NØíiS×êHLÝKl]ͯ¾¾žŒŒŒº,+¶þ©íºRCCƒ¨˜Ä~¶·ñë:!D×ñ‰™ŸØí*¦í¡œÿˆ#ÈÉɉâââ´Š…õŸ¦¦&ÊÊÊ¢ÄÄDúàƒ(::šÆ§qßZ°`n¼k‘½=JÅc¬õEBÈÒ¥Kiùòå½ kPèX_«¨¨ ?ü|}})++«Ûò]Mצl_ÌS›ÏkÓžèj>bÚ.êˆm+jZ®.Ú‚bß[^]{CL»¤·ÛBWß%‘n¯½¨£‹ïMlû¯»umÿy±íBMÄn7]¬_Ÿ ÒîEOÛõ½9O4Ø­Y³†yä‘>›bb" »wïöÙ2tEÝ~8wî\úì³ÏÔ¾/vÿ[®·Ç®Êõæ\ß¡C‡hÞ¼yDDô׿þ•vîÜIDDûöí£ÿû¿ÿ#¢Û©cÿœÞ[tñ;Ø›ãGO–©ëãî@9ϪüLwç!µ‰¡¤¤„† Fµµµ”‘‘A£Gîv„™   €Pbb¢¾CQÁ !ìa6B ÀcŒ1ÆØ†;w",, xóÍ7±|ùrlÞ¼ß|ó õbXZZâïÿ;Ž;¦2]"‘tZ§òòòNŸ6lÊÊʺ\†˜2(//=HWyÔÕÕ ålmm±sçNddd 33Ï<ó :„•+WjUæaÐØØˆo¾ù›7oÆòåËñæ›oÂÀÀ@e_eŒ±ÁÈÑÑ}ô<ˆÃ‡ë;¦ÁŽ;ðü111xâ‰'˜˜˜àÇľ}ûðë_ÿË–-Ãòå˱bÅ <ÿüóعs'bccqéÒ% µµUß«òЙ4iòòò––†ŸþÓ§OÞ›6m233qûömäåå!((HxoõêÕ°´´DRR„ºXG}]ׇ’º:jaa!»\ŽØú§¶PXX¨6&‡n?[TTÔiººi}¿®è:>±ÛULÛCiÛ¶møê«¯°nÝ:|ÿý÷ZÇÄt§µµ¸tébcc±sçN<ÿüóX±b–/_ŽeË–á׿þ5öíÛ‡ü&&&ðññAHHžxâ ÄÄÄàÿøvìØ¡›€.^fÍ ørcŒ±ÁÍÏÏ©©©C®Ýegg‡µk×bíڵغuk§÷Åžûl4µ'îÝ»§ñ3=mO©ÓÛ¶¢¶mA]+..î4­ã¶Û.éí¶Ð¦m(f^ººö¢N_|o=Ù—ÛÓ¦]ØU b¶›.Ö¿/Û÷½=GÑ—ûÎ`×ÚÚŠ›7oÂÃãϖáìì (((è³eô¥½{÷â÷¿ÿ=*++;½'vÿÒ¦\oŽ]éÍÿÑ‚ påÊÔ××ãàÁƒÂÿÅã?Ž#GŽ ¢¢—/_Æ‚ T>§ïßD wëÝÓåéò¸«Í>ÑçY»;©M ŽŽŽÆ¡C‡ðî»ïbÆ H$:‰³/)e...zŽ„1ÖŸø ;cŒi¡NâÁHÏ‘0ÆØÃÍÎÎ/¼ðN:…{÷î!!!‹-ÂÙ³g {{{„††bÇŽ¸víš¾ÃUK"‘ ;;»Ót###XYY©LsqqAnn®Ê´‹/vúìܹs¡P(T¦Ý¼yZ• Ãùóç;Í?11“&MRY‡üü|NŒ,_¾_}õΜ9£U¥þ>yÖ[™™™Ø·o–-['''Èår|ùå—˜>}:bccQ\\ŒS§Ná…^€¾ÃeŒ± Åúõëñ?ÿó?Â1 ,&LÀ3Ï<ƒíÛ·ãÓO?ÅÅ‹‘••…ÆÆF”——#99ÇǶmÛ0oÞX" )  éý¼cŒ1={ê©§‘‘?üáúE/~÷»ßáøñã’Äžû¨4Õ×5µ'¾ùæóêi{JS\bÚŠšâ×¶-Ø],ÚR×îê¸-ĶKz»-´ivG—×^ÔÑå÷¦Ô“}¹=mÚ…šˆÝnÚ¬¿¦ï[×íçözsŽ¢¯÷Áî•W^Azz:6nÜØgËðóóÃÈ‘#±gÏž>[F_1bÖ­[‡W^y¥Ó{b÷/±åz{Üúæ\ŸT*Å„ °gÏ8;; áíííáââ‚;wbòäÉ®éööت‹s{½YïžÐõqW›}¢/ÃJ݇Ô6†§žz {öìÁñãDZfÍÄØ×þñ`ôèÑÉdú…1ÖŸº\„1ƘR-ÕGꇕcL[Ð0„—®ˆöœ±Á¤°°>þøcŠŠŠ"©TJH&“Qtt4ÅÆÆRee¥¾C$¢ÿß³fÍ¢K—.Quu5ÕÖÖRrr2ÍŸ?Ÿ^}õU•²kÖ¬¡_þò—”——G÷ïß§S§NQXXX§aPüñGòó󣄄ª©©¡›7oÒ¤I“èŸÿü§Ve²²²hìØ±ôÅ_PYYUWWS\\¹»»Ó±cÇTÖ!,,ŒRRR¨¡¡ŠŠŠè•W^¡%K–hU†ˆhæÌ™¬“mÛWjkk)!!bbb( €¥¥%ÉårÚµkeggë;DÆë3÷ïß'??? ×w(Ýêë!wûsÈêþª¯———Srr2ÅÆÆÒ®]»(&&†¢¢¢(88˜d2C¢›˜˜««+QTTÅÄÄЮ]»(66–’““©   _bLŽ9BèÓO?íôÞñãÇ 9rDezXXýæ7¿¡¬¬,jll¤ôôtZ³fM§úŸØº–:bNýŠC9¿ñãÇÓáÇ©¬¬ŒJJJèý÷ß'///ÊÊÊêrÙb럚xxxÐ¥K—¨©©‰NŸ>MDDtûömrww§÷Þ{Š‹‹…˜ÜÝÝ)==½Ëyfdd§§'íß¿ŸJJJ¨¬¬Œ>ÿüs îUüšbÕvÝÔmGMÓ{»};ÎOìvÓöP7ÿC‡‘¿¿?©L m}«««£ŒŒ JHH ?þ˜¶oßNÑÑÑ$—Ë) €¬­­…ã9’J¥@r¹œ¢££iëÖ­´wï^JHH ŒŒ jjjÒï ýô@tù²~ã`Œ16äˆm‹iÛþüä“OÈÈȈV®\IUUU= o@ëªñä“OÒ[o½¥2Mì¹ï®æ-¶[Koç©nš¦úzÇöĽ{÷èðáÃ4~üø^µ§š››ÉÚÚZÔºŠi+jŠ_Û¶`W´m ™3gÒgŸ}Öå¶Û.éí¶Ð¦mØÝöÐåµutñ½uœ®í¾ÜQOÛ…í‰ÝnÚ¬¿¦ï[×íçö´9GÑÓv}oÎ F•••´|ùr222R{ÞM×þýï“D"¡Ï>û¬Ï—ÕšöÃÆÆF3fL÷/±åz{Ü ê›s}DD¯¿þ:YZZvú?ûì3266¦×_½Ógz{líÉï`G½9~ˆÑÓ}Bì¶ÑfŸÐæ8¬‹uUwRÛß‚ÆÆF²··§ßÿþ÷ZÇ£Ÿ|ò I$’N×$‚þ¼þÆXëëë×íiêoÊ !Œ1¦… ª è4Öw(ì!Á !ŒõNKK %''ÓöíÛ)88˜ ÈÈȈ‚‚‚hëÖ­”œœLmmmz‰í»ï¾£ßüæ7äïïO&&&dnnN'N¤wÞy§SL¥¥¥´jÕ*rtt$KKKŠŒŒ¤ÜÜ\¡O{—.]¢àà`233#OOOzã7:-[L™Û·oÓã?N¶¶¶diiIS¦LétR@¡PÐc=FÆ #òõõ¥^xª««µ*CD4}útš9s¦ÖÛ±¯eddЮ]»H.—“©©) €€Š‰‰¡„„jllÔwˆŒ1ÖoÉÀÀ€<¨ïPºÄ !º×ÐÐ@”˜˜H±±±´}ûvÚ°aEEEQPP¹ººªt0633#™LFr¹œV¯^M111´wï^:qâ%''?´Ÿ4¹ÿ>™››SYYY§÷êêêÈÎÎŽîß¿¯2½¸¸˜V¯^MNNNdbbBcÆŒ¡Ã‡wªÿ‰­kuÔþûêŠØ8”¯SSSiÁ‚deeE–––´hÑ"JKKµl1õOMbccÉ××—LLLÈßߟŽ?.¼÷ã?ÒÂ… ÉÒÒ’,--iáÂ…ôã?ŠšoJJ -Z´ˆ,--ÉÚÚš"""èÎ;d``Щ¬Øø»ŠU›uÓ´u½}5ÍOìví®íakk«2ÿ‚‚•e8p@(;PÛ ý¥»c±VÇâîŽÂ{ïYXq»‹1ÆX?ë«„¢uxGGGrvv¦½{÷RsssOBºkg\ºtIx¿´´”ˆÄŸûÖ¶þ+&6mæ©©lWõûöí +++Z°`¥¦¦öª=•MAAA]®+‘ø¶¢¦øµm võhÛ@YYYAÖÖÖ·‘¸vIo·‘ø¶¡˜í¡«k/êôä{Ó{b÷eMºkêj»‰]¢®¿o]´Ÿ5­‡˜}¶7íúžî;ƒMss3ýë_ÿ"'''rrr¢³gÏöÛ²7oÞLFFFæ¼mGÝíƒIII$‘H:M»ß‹-×ÛãF_ë»~ýÿ³wïqQÕ‰ÿÇ_ 7D. ˆˆ šÔ2IÓÊR\¿•º]ÔÊ]Ü6Këûk±¶5+-µvM­mó²]vµÔon e*i™–›—´LS Q.‚‚ ÈE®¿?fgS—÷³Ç<Ïœ™ybÎÜ>ïóÙWåîî^UXXXcùùóç«\]]«¾þúëZ×ù¥ûÖ+y¼Ô~äJ÷—[–ø%ûœ+Ùï^ÉßÄåÜ÷Õlë•|y%ÏeeeU!!!U—•ÃH¯¿þz•ƒƒCÕÌ™3ŽR'B¤%k …»ÿ^(""—á4§éHG¶±›¹Ùè8ÒØÙÙ±zõjÆß ·¿téRâââä¶Eš¢ììl¶mÛFRR~ø!ééétèЛo¾³ÙLll,þþþFÇåää°uëV’’’øè£HKKÃÇLJáÇc6›=z4FÇ1L\\ >|¸ÖÔéMÅÞ½{ˆŠŠjÛ·¾6_³fMƒÜ~uÍéõzII $''“‘‘Á©S§jœ?vìùùù¶õ]\\ 44”ÐÐPüýýmÿö÷÷'$$777·H¤nß}÷£F"%%Åè(Ò‚”––’]kß™œœlûwff&Ö¯kœ ¬µï¬¾_5™LoÕ50y2¤§CR’ÑIDD¤•¹Ü÷bWûþ377—ùóçóÊ+¯Ð¾}{âââxôÑGiß¾ýUå•Öã•W^¡¸¸˜'Ÿ|Òè( ÆÎÎŽæ0LIï E_~~>o¾ù& .$##ƒx€¹sçâããÓ¨9-ZÄc=Æ]wÝÅë¯¿Ž··w£Þ¿ˆ4-kÖ¬!11‘wÞyÇè(õÊËË#>>žeË–1kÖ,fÏžmt¤:5æ÷o"­¡¿¿®®¾ñ¦ ~Ï""-H)¥8ádp©‹ãÆcܸq€åûÄÄD’’’xä‘Gx衇èׯf³³Ṵ̀aÃptt48µ4¤ŠŠ öïßORRIII|úé§TUUÑ·o_î½÷^FÍ7Þˆ½½½ÑQEDš„yóæ±~ýzfÍšÅâÅ‹Ž#Mˆ‹‹‹mr}rssë,‹$''³cÇRRR(**²­o2™ê,‹XÏáà /¥áØÙÙñÊ+¯0iÒ$\]]9|ø0Ó¦Mãá‡6:š43¹¹¹õæ’““9qâ8::âããcÛ× 2¤Ö>0$$¤å¿G©ª‚­[¡™#EDD®„ÉdbÞ¼yÄÅűxñb.\ÈK/½Äرc¹çž{¸å–[ô¹´Ôiÿþý¼öÚkFÇhuôÞPÄ8eeelÚ´‰U«VñÁàèèÈ”)Sxä‘GèÒ¥‹!™¦OŸNxx8¿ûÝˆà™gžáw¿ûž»EZ;;;þóŸÿ0þ|–/_ntœ:•••ñÆoðÜsÏáììÌG}ÄÈ‘#Ž%"Ñ7ª""W ËÑN=ð08‰ˆˆ\ŽÈÈH"##‰§°°/¿ü’„„V¯^Íüùóqwwçæ›o&66–‘#Gdtd¹²²²Ø¾}; $&&’››‹ŸŸ111¬ZµŠ˜˜˜&{Ô{£µoßž 0eÊ&OžÜ(G1‘–Ãd2a2™ˆŒŒ¬wúM'''“””ÄÉ“')//¯q›õÍ2ÒjMKƒILLdáÂ…<ýôÓØÛÛÓ­[7yä&Ožlt4iB.UvKNN&55•²²2ÛúÕËn¡¡¡˜Íæû¯àà`Ú´icà5ß~ iipÛmF'i0¡¡¡¼òÊ+<÷Üs¬\¹’•+W‹··7±±±Œ=š[n¹www££JñÖ[o¡AÙÙÙÙ~6¥YBôÞP¤q°yófILL$''‡èèh.\ÈÝw߇‡ñãqn»í6<ÈsÏ=Çã?ÎÂ… yâ‰'˜4i...FÇ‘FÍÃ?L¿~ýŒŽRCqq1o¿ý6/¿ü2iiiL›6?ÿùÏxzzMD dWÕ”Þe‰ˆ4q;ÙI4Ѥ‘F FÇ‘ ¾)¼®•Ëö\¤5²:LJJâã?¦  À6XgôèÑÄÄÄè½f¢¼¼œ]»vÙfƒÙ·o... 2Ä6Œ4‹ˆ\¾ªª*† ÀöíÛ NS[CO¹Û˜SVëõzݬ¥‘ú޶Ÿ’’Bee%NNNx{{ÛZ×W©Kqqq½EŒŒ RSS)((°­o2™êÕ(44”Î;눡—kÞ<øë_!3Tì‘Fv¹ïÅâýgJJ «W¯&!!]»váààÀСCùÕ¯~ň#èß¿¿Ê£""Ò¢TTT°wï^¶nÝÊ'Ÿ|ÂöíÛ©¨¨`ðàÁÄÆÆ2a‚ƒƒŽY¯ãÇó /ðÏþ///~÷»ß1eÊBBBŒŽ&"­ÌñãÇY¾|9Ë—/'??ŸI“&ñÔSO5é}huùý›Hckèﯫ«o¼©f¹¹äОö'‘_*44”¸¸8âââj –-[¦BAW½Ð㜘ÈÃÅÅôm׎þ½{ôÜsô›8çfòÁ‡ˆHScggÇ_þò®¿þz>øàÆŽkt$ieL&QQQõ¾þºpáéééu–E’’’lç­\\\ê¼m=ß­[·&qäA¹¶.µ¯HNNæÇ$//϶þÅûЍ¨¨ûŠððpÚµkgàµ07ZfQDDDZ™âãã‰';;›7òñÇóÊ+¯0sæL<==6l7Üpƒ bÀ€:Ò¯ˆˆ4+çÎc÷îÝ|õÕWìÚµ‹íÛ·sîÜ9üýý1b+V¬à¶ÛnÃÛÛÛ託¥K—.,[¶ŒçŸžW_}•+Vð /`6›ùÍo~Ø1cšÍ¶ˆHó“͆ xï½÷øä“OðóócêÔ©<üðÃtìØÑèx"Ò„¨""rrÉÅgÚÒÖè(""r 988Mtt4YYYlß¾„„æÏŸÏÌ™3éÒ¥ 111˜Ífbbbðòò28uëRTTÄ_|ARR6làðáø¹¹1|øpîž1ƒÈS§¸áäI8xþügËÉÛz÷†ÈHèÕë§Ÿ&“Ñ›#"Òä 8ñãÇ3sæLF¥£K“âììl;}JJJÈÈȨs–‘C‡qìØ1òóómëW^Wq$$$77·ÆØ<¹ ¥¥¥dgg_rvÌÌL¬¤;;;h{l›Íf&NœXãqoÒû„Æ“Ÿ_~ =dtCùøø0qâD&NœÀ¡C‡Øºu+Ÿ~ú)ûÛßxê©§°³³£{÷î 82hÐ úö틳³³ÁéEDD,ŸÁíß¿ßVÙ½{7G¥ªªŠÎ;3hÐ æÌ™Ãˆ#ˆˆˆ0:î/âççÇœ9s˜={6‰‰‰¼ùæ›üá ..ŽaÆqÇw0vìXÍV,"¿Xzz:|ðëÖ­ã³Ï>ÃÑÑ‘[o½•uëÖ1jÔ(4ì[DjÓžADä œå,&ôå°ˆHK×±cGÆǸq㨨¨`ÿþý¶Ù(î¹çªªªèÛ·/£G&66–~ýúa¯£š^sÉÉÉ$$$˜˜ÈçŸNYYýúõc̘1,^¼˜¡C‡âääTûй¹ðÝwpèÐO?׬3g,—›La)ˆDD@Tôí îî»""MÜ‹/¾HÏž=Y¾|9iÀ¦43...?[ÉÍÍ­wæ€;v’’BQQ‘m}“ÉTï,#¡¡¡é‹‘k$77·ÎB—õü‰'¨¨¨ÀÑÑÛcqÈ!µ£!!!zÏÖ”lÙ••ct‘&%""‚ˆˆ¦M›À©S§j °5kyyy8::Ò§OHïÞ½‰ˆˆ 22ƒ·@DDZ²ììl<ÈáÇùæ›oؽ{7ß|ó eee˜L&ȸqãl%F£#7ÆŽËØ±c)((à£>bݺuÄÇÇóÈdÏû› IDAT#pà 7ˈ#ˆŠŠ¢M›6FG‘&®¼¼œ={ö°mÛ6øÏþƒ››·ß~;«V­âöÛoÇ]cDägèJ‘+N:èdt iDmÚ´!**Ѝ¨(âããÉÉÉaëÖ­$%%±bÅ ž}öY:tèÀÍ7ߌÙlfôèÑ:òËUÊÎÎfÛ¶m$%%ñÑG‘––fûÝ.^¼øò·&DG[NÕY‹"{÷þTY¹ -—ûûÿT±þìß\]¯ýÆŠˆ4]ºtaêÔ©Ì™3‡x£#‰\S&“ “ÉDddd½ëÔ7(=99™¤¤¤ƒÒ­·Yß,#”.bq©2Vrr2©©©”••ÙÖ¯^Æ Ål6×x|kpEs³aÜpƒeVG©—¿¿?cÆŒa̘1TUUqìØ1[IdïÞ½¼ûî»äååàëëKdd¤­ ¢¢ˆˆˆ\êÅê?Ïü÷Àk^^^ôêÕ‹èèhüqHXXvvv'o|íÚµc„ L˜0’’6oÞ̺uëX´hO>ù$ :”#F0|øp®»î:}6("TVVràÀ¶mÛÆÖ­[Ù¾};øûû3räHž|òIbbbô½œˆ\BDD®@i*„ˆˆ´rÞÞÞ¶ÙC æ,>ú(S§N%""‚ØØXÌfsý³XHÙWøòË/±³³£oß¾L™2…ØØXú÷ïí>@®«(RY ÉÉpð ¥ òí·°m¼þ:”•ƒtë½zYJ"‘‘pÝuúÀVDZ'Ÿ|’eË–ñÖ[oñûßÿÞè8"Îd2ÙÊÁu)++ãÌ™3ulß»w/‰‰‰¤¤¤PYY €““ÞÞÞ¶ìõGDš«âââz‹¤¦¦RPP`[ßd2Õx \<»G—.]pUA»e¹pÖ¯‡Ù³N"""ÒìØÙÙNxx8÷Þ{¯myZZZA»ûöícåÊ•5Š"½zõ¢G„……ѵkWBCCéÚµ+mÛ¶5jsDDÄ@EEE$''óã?’œœÌ?üPgñÃZ.3fŒ­lhpú¦ÉÅÅ¥F‘óСClÛ¶mÛ¶ñ /ðøããííͰaÃ2d  ÿþ:ê¿H+pþüyöíÛÇîݻٹs'Ÿ}ögÏž¥C‡ 6Œyóæ1bÄzôèatTiÆT¹©¤Ò—¾FÇ‘&$44”éÓ§3}útŠŠŠøâ‹/HJJbÆ ÌŸ?777ÌèÑ£;v,ÁÁÁFG6TVV›6m"11‘¤¤$rsséÒ¥ 111LŸ>[n¹OOÏÆ doo)w„…Áر?-//‡“'-%ël"kÖÀ‘#PQNN–ëDEýtêÛô¡­ˆ´0~~~Lž<™_|‘|P%G‘‹8::@@@@½¥‘ .žž^çŒIII¶óV...µ "ÕÏwëÖ ÆÚD›Ký-[‘XBí¿å¨¨¨Ëááá´k×ÎÀ-C|ü1äçÃwDDD¤ÅèÔ©:u"&&¦ÆòêE‘C‡ñÍ7ßðïÿ»Æûÿ‘êç}}}{SDDäÊÊʪQúøñÇmç/~. £G*~\CDDDððÃSYYÉ·ß~k+ˆ,X°€¬¬,Ú´iCÏž=8p dÀ€ôéÓGŸÃ‹4c¥¥¥ìß¿Ÿ={ö°{÷nvïÞÍ÷ßOEE~~~\ýõÌš5‹áÇӻwïV9»’ˆ4 BDD®@iŒf´Ñ1DD¤‰ruuÅl6c6›™7oÉÉÉ$%%‘””ĬY³˜1c¡¡¡¶un»í¶Ô—’’vìØaû=ìÛ·† B||*¼õç®]»HII¡´´wwwºvíJpp0ÁÁÁXã¼³³³›&"ÒêYÚ––ÆÉ“'IKK#==“'O’’’Brr2çÏŸ,3ÖvéÒ…ÐÐPúõëÇwÞi+†††jfÎF`ooOŸ>}èÓ§3fÌàäÉ“¶Áâ»wïæý÷ß'??'''Ûº‘‘‘ôêÕ‹^½záççgðVˆÈÅN:ÅÁƒmìýû÷óÍ7ßPZZЧ§'QQQÄÆÆòÜsÏ1pà@:wîltdiÁT¹L唓AAèËJ¹<¡¡¡ÄÅÅGyy9»ví²ÍŒ±lÙ2[1ÂZi’ň«P½óñÇSPP`+Â<óÌ3ÄÄÄàââbt̫Ӷ- `9YUUÁñã?•DöKáÄ Ëå>>?Dúõƒþý¡{wKDD¤ âî»ïfþüùLš4IG+i...?[ÉÍÍ­wf†;v’’BQQ‘m}“ÉTï,#¡¡¡áà ‡[‹ÜÜÜ: GÖó'Nœ ¢¢°Ì|ãããcû[2dH­¿¡ìõzV®TI $$Àœ9F'iÕ\]]mƒK/VYYIjjj¢ÈÉ“'ùúë¯IHH ##ÃVËÌ¢têÔ‰   :uêD```ó:ʹˆÈ•)--­³ìqâÄ ÒÓÓIOO'33Ó¶¾““S}ñ¨Q£jÌøÔ©S'½‡o‚‚‚‚ âÎ;ï,ÏÁGe÷îÝìÙ³‡o¿ý–>ø€ììl¼½½éÕ«‘‘‘ôîÝ›ˆˆzõêEûöíÜ ‘VáìÙ³ÍgŸ}FBB ,`æÌ™øùùCll,111xyyœúòòå—_’À† HIIÁÝÝ›o¾™—_~™‘#GÔ’þjg÷Ól"wÜñÓòÜÜŸ "ÀÖ­°x1”•»»¥$e9õï=z@›6Æm‡ˆÈ%<ñÄôêÕ‹O>ù³Ùlt‘VÉd2a2™ˆŒŒ¬wúý[ »Õý[o³¾YF4è¿ù¸TY(99™ÔÔTÊÊÊlëW/ Y‹ÛÕÿÿÓF¯K¥!|ôœ?ÿì""""M½½½mFáÇ׹N}¯?>Ì'Ÿ|R«¬náÐßß“Éd;ñ²Î;ãèèØX›*"Òè¬ûÏÜÜ\N:eÛ^¼,++‹ÊÊJàòÚ ÷ñ-ƒ½½==zô GLœ8Ѷ<77—ï¾ûŽC‡Ù~®]»–3gÎ5?ß«ë$"—ÇúÙz}'ºuëFDD·ÝvDFFê±&"M‚ !""—éGèF7ƒ“ˆˆHKàëë˸qã7nìß¿ß6«Æ½÷ÞKee%}ûöÅl63zôhn¼ñÆ&5ï»ï¾³Ív²}ûvÊËËéׯ&LÀl63lØ0}yg2ÁÍ7[NVååpäìÝk9íÙc™M¤¸œœ ,ì§’HT dY."b°ÈÈH¢££ùÛßþ¦BˆHf2™ˆŠŠªwæ¹²²2Μ9Sgq`ïÞ½$&&’’’btàää„···m A}Åi8ÅÅÅõ=222HMM¥  À¶¾Édªñÿèâ"]ºtÁÕÕÕÀ-’Ví½÷ :´ßiÖ~®¬^YYIff&©©©¶ÁͧOŸ&++‹S§Nñí·ß²yóf²²²(..¶]ÏÎÎŽ:àë닟Ÿ~~~¶óÞÞÞ5N>>>x{{7©ÏÌE¤u©¨¨ ''§Ö);;›¬¬,NŸ>Mff&™™™œ>}š3gÎPUUe»~Û¶mkìë:uêDTT¾¾¾¶Â\çÎñóóÓ¾®•3™L5:huòäI:ıcÇøá‡øá‡ø÷¿ÿMJJŠm6/“ÉDXX˜íBçÎéܹ3ÁÁÁ´mÛÖˆM1DQQ'Nœ 55•ÔÔTRRRøñÇmŸÜÜ\Àò™xHHaaaDDD0fÌÂÃɈˆ sçÎo…ˆHýT¹LG9JxàatiaÚ´ic¸ONN[·n%))‰U«V1þ||||>|8f³™Q£Fب³³³Ù¶mIII|øá‡¤§§ãëë˰aÃX²d ±±±øûû7j¦fÉÁ"#-§I“,ËÊÊààAKAdß>Ëϵk¡¤\]¡OŸš‘=@þ‹ˆ~øaî»ï>Nœ8App°ÑqDä*8::@@@@½¥‘ .žž^ç“’’lç­¬Gû­¯,Ò­[7<<ôYJ].õ»NNNæ¨çQ À›À™Ú¿ë¨¨¨¿ëððpÚµkgôf‰Ô-'6l€×_7:‰ˆˆˆ40{{{ÛûŽŸSPP@FFgΜ©1púÔ©SdeeqìØ1233ÉÉÉáüùóµ®ß¾}{[9¤®ÒÈÅ—yyyið«ˆÔR\\L^^^­bGvvv½¥³gÏÖºwww¼½½m%Œ¯¯/þþþtìØ‘:ˆ»»»[*-IPPAAAŒ9²ÆòŠŠ Nœ8Á?üPc°ûûï¿Ï‰'(,,´­ëããS£ b=oýwÇŽu@iÊÊÊÈÊʪQø°–>¬çsrrlë»»»LXX7Ýt<ð€­8¤Y—D¤YR!DDä2å(á„CDDZoooÛì!ÉÉÉ$$$˜˜È£>ÊÔ©S‰ˆˆ 66³ÙÌСCqºÆ³HTŸµ$!!/¿ü{{{úôéÃoû[bccéß¿?vvv×ô~[%GGè×Ïr²*+ƒC‡~*‰ìÙ+VXfñð°”C®¿ÞR4¹ $"­ÓwÜA‡X¾|9Ï?ÿ¼ÑqD¤8;;Ûf©OII µ ¶#æççÛÖ¯^d¨«8‚››[cl^£¹Ôl,ÖgffÚŽêììL`` íwc6›é9¼'ÿñoÊæ•ñë²_3Ýi:Cbð–‰\¥þÓòÞç®»ŒN""""MH»víèÞ½;Ý»w¿¬õsssÉÈÈ 77·ÖÉ:‰õϹ¹¹œ>}šŠŠŠZ·ãââb›éäR§¶mÛÖZ·cÇŽ$(ÒÄר”””ÔZv©SIII­Û¼ø±ÀÀëÜWˆ³³³[/RS›6m.ùÙ^õÙh«fuøða>üðCNž}àÁ-ËÊËáÈKIdï^ز,€ÊJð÷ÿi‘¨(2Ú·7vD¤Åqrrâàí·ßæÙgŸÕ—;"­˜‹‹ËÏ–F¬µê*DìØ±ƒ””ŠŠŠlë[¿¤«k–‘ÐÐP‚‚‚pph:gçææÖYˆ±ž?qâ„íKHGGG|||lÛ2dÈZÛRç~µ„H`‘Ó"¢‰¦=¸Ÿû‰#¦ÆÞl‘«÷Ö[ð›ß€f±‘_À:øúr•——ÛŽèöìYòòòÈËËãܹs5~æåå‘™™É÷ß_ã² .ÔºM{{{<==1™L¸¹¹áææ†»»;^^^¸ººâææ†‡‡¸¹¹áêꊗ—îîî¶õ­×uuuÕ,Ò*PXXHQQ¹¹¹RXXÈùóçÉËË£¨¨ˆÂÂBòóóÉÏÏ·]~îÜ9 l—çææ’——g;ØBuÎÎÎxyyáééYãg@@={ö¬ó²ê3 ©ø%-UÛ¶m/ù¹^yy9§NâäÉ“dffrêÔ)NŸ>MFFYYY|ñŶeeee¶ë999áëëk{ uèСμªÏä¥ï[—sçÎÙf_:{öl309sÆvþôéÓ”––Ú®oý³Î¸Ô±cGúõ뇯¯/tìØ‘   ´‘V©é|ƒ&"Ò„UPÁ0…)FG‘VÎÕÕ³ÙŒÙlfÞ¼y$''“””DRR³fÍbÆŒ¶‡ÙlfäÈ‘õ~¡T\\ÌÎ;m×ß·omÛ¶åÆodæÌ™˜Íf¢¢¢y ¥^i9MšdYVP`™=ä?ÿ¯¾‚åËáÙgÁÞzôøi‘Aƒàºë,E‘_`âĉ¼øâ‹|þùç 6Ìè8"Ò„YjEFFÖ»N}¥ ëkÜê¥ ëmÖ7ËÈ¥JWêRe–äädRSSk|á]½Ìb}-^=_ppðU é‚ ãþûß^ö²”¥<ÏóÌe.ws7ñ}éû‹·Y¤A}õ8ÿ»ÑIDDD¤•qpp° ¼ÅÅŵÊ#ÖŸ¹¹¹¶Aéœ;w޳gÏ’ššZç öKñôôÄÍÍ <<}šœœÛ ÿï¾ûÎVÊÌÉÉ©U´tppÀÛÛ///<<Ⱥ¬]»v899áééYãù¤z¡Äú\h½=À¶ï‚ÚÏcðó…˺žë.vñsÂŬ¯ª«¾·¾†€šÏÖß™µÐ?='X_gØž÷¬¯+òóók,«¬¬¬7›Édª³ÀZ£ÜS}f¼½½m¯-DDäÚP!DDä2|Í×8áDFG©—³³3¿úÕ¯øÕ¯~Å‚ ÈÈȰ•CæÌ™Ãão>@ûôöÄÄÄðÚk¯që­·`pr¹¦üý!6Ör¨¬„ᅦ/¿„;aófX²ÄrY0x0 7ÞÝ»C=_¶‰ˆXÝ{ï½Ì™3‡%K–¨D(" ÊÑÑ‘àà`‚ƒƒë]§¸¸¸F™£ËÛop躸°wï^ÛÑyëâááa+w„……1|øp[ÑÃZ¹ÒCÁOâþûß^ö²ˆELcñÄ3žñÌ`=éitL‹óçaõj˜5Ëè$""""†j×®]ƒv·Ü´Î;GEE…­PWyÀ:€´zy zÂ:´  €Ó§O؆^<8õâBC]%¹2—c.ˆl„loo§§guìíí j–b¬%ꜭƒ¡«Zöòò¢M›6xzzÚÖ­^ ¹îî»_ò³¾K©^ÉÏÏçüùóPZZʹsçl%‰üü|ÛŒFÖçµôôôZËà§R\õÒDKaÝçW/ƒV/Í899áááAûöíqqq±ü<<zõUÛ:ùùù¶YF[é£%|iEïð XÀÛ¼Íë¼Îr–3‚ÄÇÿð?8èk1Ò[oAYLžlt‘Í:›‰õèæMÑÅG@¯~ts¨{VŒê~î(åu©ïˆò .à±Ç«uYõ³—«z)£.W3[ŠˆˆÔÔX%„ê¥Çêåk鲺KÍ\rñõës9³Y]üün™iÆŒìØ±ƒ­[·ü³3–ˆˆHË¥o‚DD.ÃNv2˜ÁFǹjmÚ´¡}ûö–ó¨ Òêyx€Ùl9e€Ö×_Ã_XN˗óς£#ôëg)†  7Ý>>ÆfÃpýõד Bˆˆ4-éé°?¼øbÅDFFiP°†ç‡ñÄóO°•­,e)ws7¾ø2‰I<ÌÃt¦³Ñ1¥µ©ª‚×^ƒ‰ÁÛÛè4""""b°¦TxX»v-ãÆ38‰ˆˆ45NNN¶RES.Z¬^½š›nº‰É“'óÅ_¨ ""ÒŠÙÿü*""­[1ÅìcCbt‘†áèh™dÆ X³ÒÒàÄ ËÑ| ‚Ï>ƒqãÀ×z÷†Gµk!+Ëèä"bQ£F‘˜˜xÅGeiPë׃›Ü|³ÑI c=f̬a G8Â$&ñþAºK,I$QE•Ñ1¥µøè#øþ{xøa£“ˆˆˆˆˆˆˆˆ´8mÛ¶åý÷ßçÔ©SLœ8‘ª*}î'"ÒZ©""ò3v³›RJU‘Ö%(î¹–,}û /6o†Q£,³‰ÜwøùA×®0i,]j)‘ˆH«0zôhNŸ>ÍÞ½{Ž""ò“„9œNÒ$t¥+ó˜G*©¼Ë»”PB 1t§;ó™O9FG”–nñbˬ„½{DDDDDDDD¤E fݺu$&&2oÞ<£ãˆˆˆATù;ÙI„bt㸻[sÍ›;vÀÙ³°e Lœ§NÁ£BHHÍ‚ÈñãF§‘rÝu×̇~ht‹óçáÓO!6Öè$MŽ3ÎŒc[ØÂ!q·1‡9ÈxÆ“D’Ñ¥%úúkËû…Ç7:‰ˆˆˆˆˆˆˆH‹ÍK/½ÄÓO?Mbb¢ÑqDDÄ*„ˆˆüŒÏùœh¢Ž!"""Ò´¸¹Y "³g[zåäXf¹çHI±DBC-§)SàÝwáôi£S‹È54räH>þøc£cˆˆXlÞ eepûíF'iÒzÒ“E,"ƒ ³˜£%†0€¥,¥B£#JKñâ‹pÝu–Y{DDDDDDDD¤AMŸ>x€{C‡GDD™ !""—PJ)ÛÙŽ³ÑQDDDDš677ˆ‰çŸ‡íÛ!7¶mƒÉ“áØ1ËO??è×þøGË Íâb£S‹È/0lØ0öíÛGa¡‹HƒƒÑIš…v´#Ž8ö³Ÿ=ì!Š(f0ƒ˜ÊTrÐèˆÒœýø#¬[O=vvF§i^}õUzöìÉwÜÁ¹s猎#""H…‘KØÉN )T!DDDDäJµm 7ß ÏÿÜ2èsýzˆ‰???ÞyòòŒN+ÒªôîÝ{{{8`ti­6l//2Äè$­F8áÌcé¤ó/þE:éÄCOz2Ÿùä’ktD1ÂêÕðÙg°h‘e@1”££#«W¯¦¼¼œûî»ÊÊJ£#‰ˆHQ!DD¤Åó Ÿ‹Ž.)"""Ò¤88@t4Ì›‡ÃÁƒ–YC²²`ÊKydÄxùeøá£ÓŠ´xnnntíÚU…1NBÜ~;8:¤ÕqÁ…qŒc';ÙÆ2”çyž`‚™ÊT ç†V£¨âãáþûáúëN#""""""""ÿÕ±cGÖ­[ǧŸ~ÊìÙ³Ž#"" D…‘:lb%”0ŠQFG‘K‰Œ´B>û NŸ†U« [7øË_,?##aöl8tÈè¤"-Öu×]Ç7ß|ct iÎûiæ01TQ¼Á¤“Î˼ÌNvÒ—¾ `KYJ1ÅFG”†4k–åñøâ‹F'‘‹ 8E‹1wî\>úè#£ãˆˆHP!DD¤ïñCŠ~FG‘Ëe2Á¸qðÆžn j6òe–bHd$Ìœ ;vT¤E‰ŒŒäðáÃFÇ‘ÖhãFËÏ[o56‡ØxâIqä Ÿó9¡„2iÀt¦“L²ÑåZûòKxåøë_Á××è4""""""""R‡©S§2iÒ$î»ï>Ž?nt¹ÆT¹HE$’È&EDDDD®–½=DGâEšúS9ä_ÿ‚›n‚ÐP˜>ÝR©ª2:­H³Ö¥KRRR¨ÒcID[B‚åyÝd2:‰Ô!šhÖ°†“œd&3YÏzºÑbXËZÊ)7:¢üREE0y2Œ÷ßot¹„×_î¸ãŠ‹5£¯ˆHK¢BˆˆÈEÖ³ž \àî0:Šˆˆˆˆ\ ÕË!iipð Lš}¤rˆÈ5JII ™™™FG‘Ö¤¼Ü2CHl¬ÑIägøáG<ñ$“Ì&6aÂÄÝÜM0ÁÌd&©¤Q®Ö£Bv6¼ù&ØÙFDDDDDDDD.ÁÅÅ…5kÖpüøq{ì1£ãˆˆÈ5¤BˆˆÈEV³šŒÀ_££ˆˆˆˆHCˆŒ„Ù³áèQص îºë§#Œwí O=‡R¤ÙèÒ¥ €¦‘Æõùç› £GD.“=ö˜1³†5|Ï÷Ld"+XAºK,I$Q…Ê¹ÍÆÊ•ðÀŠht¹ aaa¼óÎ;,]º”·ÞzËè8""r¨""RM6Ùld#÷pÑQDDDD¤¡ÙÙÁõ×ÃK/Ar2ìÙ&À¿þ–Ë^{ Ξ5:©H“ˆ³³³ !"Ò¸,%ϰ0£“ÈU#ŒyÌ#4Þå]J(!†zЃùÌ'‡£#Ê¥8qqðÄð?ÿct¹cÆŒáÿ÷y衇øú믎#""×€ !""Õ¬d%N8q'wEDDDD[T¼ø"¤¤XŽ:Þ·/<ù$øûCl,¬] ¥¥F§irìíí ääÉ“FG‘Ö$1Ñòü,Íš3ÎŒc[ØÂ!1’‘ÌaèÄxƳ“FG”‹¥¥Á¨Qpà 0w®ÑiDDDDDDDDä*¼øâ‹ 4ˆñãÇsîÜ9£ãˆˆÈ/¤BˆˆH5oñã;îFG£ØÛCt4¼ñdeYf ¸ûnðóƒ©SaÇc3Š41>>>ääèhî"ÒH†cÇTiazÒ“E,"ƒ ±ˆ#!šh0€¥,¥B£#J~>Œ žžðÿF'‘«àààÀš5k(**bÒ¤ITUUIDD~BDDþë[¾e?û¹ŸûŽ"""""MEÛ¶0n$$À‰Ÿ~ 7Ý0{¶e¹H+çããCvv¶Ñ1D¤µØ°:t€ë¯7:‰4€v´#Ž8p€=ì!Š(f0ƒ@™ÊTrÐ舭SAÜ~;œ97‚Édt"ù:vìÈÚµkÙ¸q#ýë_Ž#""¿€ !""ÿµ”¥„F4ÑFG‘¦(0ÐR9rvî„¡CañbèÚÕr¤äÄD¨¬4:¥ˆ!¼½½5Cˆˆ4ž„5 Ú´1:‰4°(¢xƒ7H!…'y’-l¡7½ÀÞáÊ(3:bëPXh™‘çØ1ؼ‚‚ŒN$""""""""×À7ÞÈœ9s˜9s&Û·o7:Žˆˆ\%BDD€ x‡wxˆ‡°ÃÎè8""""ÒÔÝx#üýï‘«VAI Œ¡¡ð •etB‘Fåãã£Bˆˆ4ŽœصË28]Z _|‰'žø-l!”P¦0… ‚˜ÉLN ÛLv6˜Í–Rô§ŸBd¤Ñ‰DDDDDDDDäzâ‰'øõ¯ÍøñãÉÈÈ0:Žˆˆ\BDD€7y“rʹŸûŽ"""""͉‹ ŒIIðý÷pçð—¿XŽšü›ß€Ž¤#­Dûöí9{ö¬Ñ1D¤5HLˆ‰1:‰À{̘YÃRHa3XÉJB %†Ö²– *ŒŽÙr;ƒ[ÊΟ~ ={HDDDDDDDD®1;;;Þ|óML&÷ÜsåååFG‘+¤Bˆˆ´zUTñ7þÆ$&ÑžöFÇ‘æ*<ÜRIKƒ¥K!%† ƒ¨(ø×¿ ´Ôè„" ÆÅÅ…’’£cˆHk#F@»vF'ƒH<ñüȼÇ{L`ÝéÎ|æs†3'læ¶m³ÌŠg2Á—_B÷îF'‘Ò®];Ö¬YÃîÝ»ùÓŸþdt¹B*„ˆH«÷1s„#ü?EDD¤Á}rÃ'|øá‡u^vàÀ:uêd;âÇgŸ}Æ Aƒpqq!$$„+VÔX¿  €?þñtëÖ WWW<==‰‰‰!11±Á·C¤IkÛ&O†]»`ï^ˆŒ„´Ì2{6dg7Ø]ÛÙÙQQQÁœ9s ÁÙÙ™ððp^}õÕZë&$$pà 7àææ†››7ÜpC½û‘ŸãììÌ… ŒŽ!"-Ý… °y3ÄÆÚé¹OœpbãØÂs˜»¸‹, 3Ïx’Hj°û¾ÔßP³}UU À-·Àðá–bHÇŽ›ADDDDDDDD]ïÞ½Yºt)/½ôï¿ÿ¾ÑqDDä ¨""­Þ–ð+~EozEDD¤Áu›ÞW^y¥ÎË–,YÂïÿ{8räwÝu=ö§OŸfÍš5Ì;—O?ýÔ¶þäÉ“)//'))‰sçÎqüøq¦OŸÎ’%KikDšþýáwàÇ-%‘Å‹!8~Ø2ƒHøÃþ@qq1IIIœ={–eË–±páBV¯^m[g×®]Üÿý<ú裤¤¤püøq¦M›ÆÄ‰Ù½{wƒä’–ÍÉÉI…ixŸ~ pûí5ë¹O¬ºÓyÌ#t–²”øbˆ ‚E,â<ç¯éýMŸ>½e½¿ÊΆ;î€?ý æÏ‡Õ«ÁÍ­ñî_DDDDDDDD uï½÷òÛßþ–)S¦ðã?GDD.“ !"ÒªýÀlbðˆÑQDDDE§»:qøða¾ûî»Ësrrxÿý÷‰‹‹`îܹ̜9“»ï¾ ÄÂ… y饗l×Ù²e ³gÏ&88GGGÚ·oÏèÑ£Ù´iS£n“H³Ð¹³eP]jªåˆË7B·n0i:tMïªcÇŽÌ;—°°0ÜÜÜ6lK–,aáÂ…¶uæÏŸÏóÏ?Ï=÷ÜC‡ðõõå¾ûîã¹çžcÞ¼y×4´š!DDEBôëg)WV£ç>¹˜ .LbûØÇöp7ñO@S™Ê\“û¹ë®»ZÎû«?†ë®ƒ}û`ëVxüq°³kœû‘&cñâÅ„††ò›ßü†ÒÒR£ãˆˆÈeP!DDZµWy•Îtf4£Ž"""Ò(ìíùÃþPë(¶Ë—/'66___¾øâ ÆŒSc¡C‡ràÀO§"""øÿïÿ‘žžÞðÁEZ 77Ëì GÂ?þ{÷BïÞ–#1ïÙsMîâþûﯵlðàÁ=zÔöï}ûö½1cưwïÞk’CZBD¤Q|ø!Ôñü¥ç>¹”(¢xƒ7È ƒ—y™ì¤/}À–²”bНú¶›ÿû«sçà÷¿·Ì¼3|887ÝÔ¸DDDDDDDD¤ÉpqqaÍš5=z”øøx£ãˆˆÈeP!DDZ­óœç-ÞbÓhC£ãˆˆˆ4š¸¸8Ö­[Gvv6üíocÚ´i¶uRSS ÇÎÎÎvjß¾=§N²­óÞ{ïqæÌÂÂÂèÙ³'<ðëׯ§ªªªÑ·I¤Ùqp€‰áàAøàÈÈ€!&¾þúÝtHHH­e&“‰ÜÜ\Û¿³²²l«ëر#™™™¿èþ¥ujÓ¦ FÇ‘–lÿ~HI©³¢ç>¹žxG9Èç|N(¡Lc0é$“|U·Û¬ß_­_‘‘°n¼û.¬\ ^^ w""""""""Ò,„……±lÙ2-ZÄ|`tù*„ˆH«µœå”Qƃ}ºÖòÓ§OãççwU÷+""Ò Àߢ¢j]¤ç>¹RÑD³†5œà3™É|@7ºC kYK9å—}[ÍòýÕñã–êÆŽ…#àða˜0áÚ߈ˆˆˆˆˆˆˆ4[ãÇçÁäÁ$%%Åè8""r *„ˆH«TB /ó2ñíiot‘F7}út^ýuÊÊÊX²d <òHˇÎúõë/ë¶œéÓ§qqqlܸ‘Õ«W7Dd‘–oÔ(Ø»×rtæ}û "ââ Ú‘£¯•þýû“Pkù† èß¿ÿ5¿?‘_,!ÆŒ±”)¯‚žû¤.þøO<Ç9Î&6á‚ ˜@0ÁÌd&i¤]Öí4›÷W……ðôÓ–×™‡ÃæÍðÎ;àí}íîCDDDDDDDDZŒW_}•   &L˜@ii©ÑqDD¤*„ˆH«´ŒeäÃã‘âÇÒúö…ß~ƒO>1„Ô¯o¾ƒ³éÞ#æ”»»;³gÏ&44”š5kR³fMBCC™3gÿú׿òàDDDòЪUP¦ ¼ðÂCoB÷INÕ¥.A‘H" YÈE.â7OðÁs Ù>¯Ðþûê—_ ukø¿ÿ3ÿz耭í£mWDDDDDDDDJ„Æ3eÊ&Mš”mf1ž•ÑDD Zœæ4£mtCyyyQºti˜íúfÍš±fÍš{>ßÓÓOOÏ|J'"ØØ€¿?tí ãÆAŸ>0g|ñ<ñD–á¦û‹Ü½®K—.téÒ%¯‹ˆˆä½¨(s';»,«ôwŸä[léö÷ׯüÊ—|É'|B øàÃp†ãŽ{ÆøB÷ï«cÇàý÷á믡eKs—§ŸÎ»í‹ˆˆˆˆˆˆˆH‰Ñ¿6oÞLÿþýÙ³gÎÎÎFG‘;¨Cˆˆ”(·¸Å$&1€8£7¦""Rr]ºt‰àà`zõê¥ÿ¬) œÍÝAv쀫W¡Y3øøcHI1:™ˆˆHþJJ‚ ÀÇÇè$R‚5¤!a„q‚„F<ñ´¤%ÍiN8᜼t²ðüûêüy5 40¿w\¼6oV1ˆˆˆˆˆˆˆˆˆ<’3fP±bEÞzë-nß¾mt¹ƒ BD¤D™ÃNp‚wy×è("""†±°°ÀÙÙ™}ûöbtÉ瞃Ÿ~‚)S`Ò$psƒ;N%""’¢£áæMèÐÁè$"”£¾øG»Ø…nøYøáâì¼}óèÒϸp7n@p0Ô­ óæÁGÁн;XX—KDDDDDDDDŠ–-[ÆÎ; 2:ŽˆˆÜA!"RbÜâAÑŸþÔ¤¦ÑqDDD c2™¸~ý:ß~û-åÊ•3:ŽˆäV©Ràë qqP­´l cƨ[ˆˆˆOQQðì³ðøãF'ÉÄ 7f2“Ó¦Ó|zýS,¾µàùrÏÓŠVDÉ-nLÛ·!276wóóƒ#G lm &ƒˆˆˆˆˆˆˆˆ”Mš4a„ |øá‡ìرÃè8""ò7„ˆH‰±…$’HFGytµkúuðÕWðÅàî‡JDD$ï˜LðÃàãct‘{zŒÇ €ßùh¢q‰7xƒÔ`,c9ƱüÙqz!H“&ðæ›Ð®¹$(Ê—ÏŸ}ŠˆˆˆˆˆˆˆH‰7bÄ^yåÞ|óM._¾ltA!"RB$‘D ô¥/®¸GDDDD$oXX@¿~°?ØØ@³fft*‘¼ñóÏpâtêdt‘*E)¼ðb)KI ?ü˜ÃjSo¼‰$’4Ò}Gii°h‘¹äÿþš6…ƒaÆ xì±Gß¾ˆˆˆˆˆˆˆˆÈ}XXX0{öln޼ɀŒŽ#""¨ DDJˆPB¹À 4:ŠˆˆˆˆHÞsu…M›àw`ÄèÛ’“N%""òh¢¢ fMó…ï"Eˆ3ÎÈŸüÉ7|@zðOL0ç8—û^¹b.úhÔzõ‚gž`ñb¨W/@DDDDDDDDäÞ™;w.Ë–-cþüùFÇ)ñT""ÅÞ9ÎL0£M5ªGDDDD$ØØ@HˆùâÙo¿…—^‚3gŒN%""òð¢¢ÔDŠ4lèF7¢‰æW~å5^c“pÁ…ît'†˜o$. =Ú´_…  aÃü?‘l´mÛ–#F0xð`âããŽ#"R¢© DDнø{ìÅ(££ˆˆˆˆˆä¿àçŸáÜ9psƒ_~1:‘ˆˆHî?ûöÑIDòDD'8A8áüÎïxãM#F׸öÏàƒ!0ÐÜ ¤Y3ذÞ{Ïüç"<\ADDDDDDDD¤P ¢qãÆ¼ù曤¤¤GD¤ÄRAˆˆk‡9ÌLf2žñØcot‘‚Q¿>lÛuë‚§'lÝjt"‘ÜY¹Ê–5wC)FJSš^ôâ~a»ð0µâ?¦q8ÝrÄoc}öµ}ž|fφ—_†ØXˆ‡€¨\Ùèø"""""""""¬­­ùúë¯9|ø0ï¿ÿ¾ÑqDDJ,+£ˆˆä§±Œ¥.uéMo££ˆˆˆˆˆ¬Ê•aÝ:xã h×Î|aí‹/JDD$g¢¢à•WÀÆÆè$"ïöm¸|.]‚+WÌßÇŽ™;}$$à¶?3`’eKÞ²&lôIÂ×_ÇíúøÚ §§E/ÊPÆè£¹§ºuëÊ€ðööÆËËËèH""%Ž BD¤ØÚÁV°‚5¬ÁJ?îDDDD¤$²±%K o_hß"#ÁÇÇèT"""÷wílÞ ³fDJ²””ŒÂ þú .^4vÜùëÕ«æóõÖ­Š?nÞ4\»–u»66P½:Ô¬ nnЯåŸz ߦMPÚ– l Ü>œ! å}> /}ñÃZÔ*ÐÃÉ©~ýú±víZúôéþ}û¨T©’Ñ‘DDJ]!-"Å’ ïò.žxò ¯GDDDDÄ8––0gŽù÷ݻÆ àînl&‘ûY»RSÍBD ¡C°gÄÅÁ¾}pàœ8a.ò°¶†Š¡Bówúïk׆2e tió¸ ÀÂÂ\ôQ®”/o^V¾ü?«V…R¥²axýýuŠSÌg>3˜A!¼È‹øâË«¼ª›ßˆˆˆˆˆˆˆˆH¡ÎSO=Å€X¾|¹ÑqDDJ}j "ÅÒ"±“ìf·ÑQDDDDDŒ—^rù2¼ú*ìÜ µt—i)¤¢¢ eK¨RÅè$R\ÅÅÁưe lÛgΘ‹>žxš6…!CÌÅ®®æNŽŽ±Õ €ÑŒæG~$Œ0zЃjT£'=Â\p)ð\"""""""""Ù©P¡´mÛ– гgO£#‰ˆ”*‘bç WÃüð£ÍŒŽ#""""R8XZÂâÅàé :Àöíæ;V‹ˆˆ&ii°f DŠ“ädØ´ V®„U«àøq¨\Ù\x4f ´jO?mîìQÈ”¢TF×ßøˆ¿¿&3™ÎtÆ_^â%,°0:ªˆˆˆˆˆˆˆˆ”p^^^øûû3tèP<<áÉáFÇ‘"(99™nݺñøãEDDD$oÙÙÁòåðÜsЧ|÷XèâA)Dvì€sçÀÇÇè$RÔ¥¤Àúõ°d‰¹äÊsÑGïÞЩ¸¹¹÷Aõ¨GA|ÄG¬d%á„ã7 h@_ú2€T¢’Ñ1EDDDDDDD¤ âǤgÏžlÚ´ KKK£#‰ˆ{TrâÄ ÜÝÝqssË«<""d7»Ì`æ0‡ž½ÕvNÎîÝ»9qâ„ BDDD¤xª^–.5w ùòK4ÈèD"""ÿˆŠ‚ºu¡A£“HQ”š ?þh.ùî;¸t Z´€?†W_5¿*l±¥Ûß_¿ð 3™É'|B t£#ÁÓ#@Ý EDò])£ˆˆä•ÛÜæÞÁwÞâ-£ãˆˆˆˆˆ^­ZÁ¸q0r$ìßot‘¬\ ]ºBŠ’Û·aóf<œœàå—aß>øÏ !¶mƒaÊM1ÈÝžáf2“œ Œ0ö²—gx†æ4'œpnpÃèˆ""""""""RÂ4nܘñãÇóþûïóóÏ?GD¤ØSAˆˆ3˜Áöð%_b…ÑqDDDDD ·?„fÍ wo¸uËè4"""pä:>>F'‘¢à×_!0êÕ3w>K/ ‰‡Ÿ†wß…5ŒNY`ÊQ_|ÙÇ>v± 7ÜðÇ'œðÃ4:¢ˆˆˆˆˆˆˆˆ” £FÂÃÃ޽{“””dt‘bM!"R,üÅ_¼ÏûŒbh,SL^ IDATdt‘ÂÏÊ ,0_xbtøþ{¨T ÜÝN"…ÕéÓ0y2<ó 4jsæ@pð ù;0ê×7:¥áÜpc&3I qŒc=ëy’'iE+"‰ä*‘üUªT)æÍ›ÇéÓ§;v¬ÑqDDŠ5„ˆH±0‚”£ïñžÑQDDDDDŠŽºuÍBÆ7ße[DDÄHQQо½¹hQ$ݰp!´k..ðÑGæ‚M›àèQ˜8Ñ\"Y<ÆcÀŽM4N8ñ:¯S“šŒe,Ç9ntD)Æ\\\˜:u*S§N%&&Æè8""Å– BD¤È[Ïz³˜iLÃ{£ãˆˆˆˆˆ-£F™/¢4L&£ÓˆˆHIuélÛ>>F'‘Âb÷nð÷‡êÕ¡OHM…Ù³áÄ ˜5 Ú´Rúˆ#'JQ /¼XÊRŽq _|™ÃjQ o¼‰" z(""""""""yïÍ7ߤ[·nôéÓ‡‹/GD¤XÒ§%’Á £cI…eî KŽ‚t…+ `Ý膺`@DDDD$׬¬à«¯ 6/6:ˆˆ”TkÖ˜mÛÖØb¬ `Êxê)hÞbbà½÷àÔ)ˆŽ†^½À^7„yÎ8H ò'ßð éL}êL0ç8gpB)n¦OŸNZZ£F2:ŠˆH±¤‚‘(‰…EÅ(FqƒLcšÑQDDDDDŠ.77èÛÆŒë×N#""%QT”¹ãC… F'‘‚vû¶¹0ÕÏÏÜ dìXhÐÀ\ràŒ ŽŽF§,vl°¡݈&š_ù•×x`‚qÁ…ît'†£#ŠˆˆˆˆˆˆˆH1Q¥JÂÃÙ3gß}÷ÑqDDŠ„ˆä“É„Éd2:F¡ÉQP6°"ø‚/¨JU£ãˆˆˆˆˆm&ÀµkðÙgF'‘’æÖ-X»|ÔýµD9y‚ƒ¡^=ðð€Ý»aòdøë/Xº¼¼@7é) h@Aœä$á„ó¿á7hDa\ãšÑEDDDDDDD¤ˆóññ¡W¯^ 83gÎGD¤XQAˆˆIW¸B?úÑnt¥«ÑqDDDDDоªUaÜ8 Ó§N#""%É–-pé’ BJ‚”s7˜îÝ¡fMsAˆ—ìÝ »v¯/”-ktÊ«4¥éE/ö°‡]ìÂþÃpÆ?üØÇ>£#ŠˆˆˆˆˆˆˆH6eÊJ—.¿¿¿ÑQDDŠ„ÈCIII!88˜fÍšagg‡Íš5#$$„[·nekaa……iii|òÉ'¸ººbkkKÆ ™3gN–m9r„N:áàà@•*U8p 7nÜÈØNNÄÆÆÒ³gO\\\°¶¶¦råÊ´mÛ–õë×g›ššJXXÍ›7ÇÁÁ;;;<==‰ŠŠÊÈ÷±Ü%»lù9G÷’]޼Þö7ðóó£J•*”-[–W_}•„„„lÇ&%%1tèP{ì1¬¬¬jnîu>”ŸUžãçŽ3•©9Þ/ÀÂ… ñôô¤B… ØÚÚR·n]ÆŒÃåË—3Ƥ¦¦2cÆ Z´hAÕªU±±±ÁÉɉ®]»“ã1"""""EŽ¿?T¬h¾8SDD¤ DEA“&P«–ÑI$¿ÄÇÃØ±P½:té/¢Eæ"Ô™3á©§ŒN(wqÙÌä'!„XbyЧhNs '‰$£#ŠˆˆˆˆˆˆˆHS¾|y"""X²d K—.5:ŽˆH±aõà!"™%''Ó¶m[¶lÙ’iy\\qqqüðì[·kkëLë}}}™={vÆãC‡ѯ_?èÖ­çÎÃÃÃS§NpýúufΜÉÙ³gs•ÑÃÃ#Óã .MLL ß}÷;wàÖ­[tìØ1K¡ÈæÍ›Ù¼y3&“)WûM—Ÿsô°òjÛ={öäÛo¿Íx¼bÅ víÚE\\•*UÊ2vùòå™–åfnîu>|ü |t‡ª‘U³Íx÷~M&o½õ‹-Ê´üÈ‘#„„„°fͶoßN¹råðóóË4W§Nbùòå,_¾“É”£1"""""ENéÒ£GèQàâbt") V¯6wŒâ%9–-ƒ3`ûvsÁÏ!з¯Þc!¨€/¾¼ÍÛüÈ„Î;¼Ã|@úà‡µP1—ˆˆˆˆˆˆˆˆäŒ——¾¾¾ <˜Ö­[óøãID¤ÈS‡ɵÐÐP¶lÙB… ˜={6gΜáÌ™3DDDP®\96nÜHhhh–çEGGÅåË—9~ü8:uÌmÀÒsêÔ)\]]Ù´iW¯^eÓ¦MìÞ½;WÛµkÇ?üÀ¥K—HII!11‘É“'c2™˜0aBƸ)S¦°~ýz ãÏ?ÿäæÍ›lß¾.]ºdº°ßd2eú6bŽV^m{÷îÝ¯ÍÆ©Q£‰‰‰gsá;w²zõj®^½š1_¹™›ì·Õ[W³Ów',–eŸ1»ýFDD°hÑ"œYºt)gÏžåÆlß¾gŸ}–ƒ2qâD¾ùæILL$%%…óçÏEÛ¶msàäÑÑæ×ùý÷U RD•¢^x±”¥ç8#ÁbS—ºxãM$‘¤’jtL)þûßÿR±bE|}}Ž""R,¨ Dr-½ÃÂäÉ“éÛ·/ŽŽŽ8::Ò¯_?>ÿüóLcîAÇŽ)W®Õ«Wϸè?...cLTTS§N¥M›6888ЦM¦Nš«Œ£FbÆŒ<ñÄØÙÙáââˆ#Ø¿Ƹ ðùçŸ3lØ0\\\°µµ¥E‹|÷Ýw¹ÚçòsŽV^mûÎׯÓÓ“°°0àŸ×îî±íÛ·ÇÁÁ!cYnæ&»óaY«eØW¶‡¡÷Ïx÷~gÍšÀâÅ‹éÖ­UªT¡L™2´hÑ"£¸#½óI5ؾ};Ÿ}öóçÏçÂ… tìØ‘uëÖåxŒˆˆˆˆH‘dk ãÆÁìÙpæŒÑiDD¤¸[¹ªV…ýËè$ò¨bcÍ^j׆ˆs'#G 2¼¼ ”>Ž(.ªQøƒ?XÁ zÐW\ $3è=¤ˆˆˆˆˆˆˆˆÜ›½½=sçÎeõêÕÌ›7Ïè8""Ež>‘\;|ø0;v̲.½ëD|||–u™»ººpõêÕŒe ´jÕ*ÓØ»ßÏ’%Kðöö&**ŠÓ§O“ššù®d7oÞÌø}ú±¤wÉ+ù9G+¯¶}÷vÚ´iÀÑ£G³Œõôô̲,7ss÷ù°ŒeÌe.ÓoM‡³÷ΘÝ~Ó ^xᬬ¬°´´ÄÒÒ’R¥JQ§NLû‹ˆˆÀÕÕ•õë×ÊÛo¿M½zõh×®gþ¾ .'cDDDDDЬ¾}¡|y˜>Ýè$""RÜEEAÇŽ*(ª®^…ðphÒ<<à?Ìï (ÈÜ%DŠ-K,ñÁ‡h¢‰'ž·x‹iL£:ÕéNwbˆÁĽ;m‹ˆˆˆˆˆˆˆHÉÕ²eK†ÊðáÃ9qâ„ÑqDDŠ4}Ê&¦téÒ™[XXäË~ÆÉdbàÀÄÇÇ“””„ÉdâÚµkù²¿¼”ŸsTPó§Š+æÙ¶þäOüðãÞ¡ý­ö¹Þ¯Édþà1--´´4nß¾ÍíÛ·3–ܺu www~ûí7¶mÛÆ¤I“èÚµ+¬]»ÿ)²J—??˜6 ®_7:ˆˆWgÏÂO?ÑI$·âãaìXsÁ‡¿?<ý4ìÙ»v¯¯ù½„”(õ¨GAœà YÈE.â7 iH0Á\à‚ÑEDDDDDDD¤™8q"ŽŽŽ 8Ðè(""Eš B$×êׯÀš5k²¬[µj 4x¨m§w­ˆÍ´|Û¶m9ÞÆ‘#G ¢~ýú…6lÈ26=ç÷ßßm¦OÜÝmä^òsŽŒ¶uëÖL7oÞ @­ZµrôüÜÌMúù°uÛVzÑ‹ÇyœILÊÕù®qãÆüôÓO˜L¦{~§³²²ÂÝÝÑ£GI\\ëׯÏÕ‘"ëwàÆ øúk£“ˆˆHqµjXY——ÑI$'ÒÒÌ]¼½¡aCX¾Ü\râÌŸÍšP [léF7¢‰f»hC>æcœq¦½ØÃ£#ŠˆˆˆˆˆˆˆH!aggÇܹsY³f _ë3I‘‡¦‚ɵ7Þx€áÇ3þ|Î;ǹsç˜;w.#GŽÌ4&·:vìÀ°aÃØºu+ׯ_gëÖ­ 6,ÇÛ¨^½:“&MâÂ… \ºt‰ÈÈH|}}³ŒíÙ³'#GŽdúôéœ8q‚äädvîÜÉ«¯¾š1ÎÑÑ€ÈÈHnÞ¼ùÀ ù9GF:t(›7oæÚµklÚ´)£†Oï䘛¹I?zýÞ‹í··‘Á®­»ru>¤ ÁÁP»6téb^öý÷pø0@¥JÆæ“BË 7f2““œ$Œ0ö²—gx†æ4'œpnpÃèˆ""""""""b0wwwˆ¿¿?ýõ—ÑqDDŠ$+£Há“Þ #;&“‰ádzzõj¶nÝJïÞ½³ŒñôôÌ(È­1cÆðõ×_sôèQZ·n±¼sçÎ=z+«Ÿ²¾¾¾Œ3†‰'2qâÄŒå½zõbþüù™Æ:”5kÖ°aÆ Â!C²Ýfûöí™;wn–"Ž;;JÜ)?çÈhÏ<ó žžž™–¹¸¸£ççfnÆŒÃœßæpaÈ-¦·rw>¤ëÛ·/{öìaêÔ© 0à¾c·nÝš¥Jº·Þz+ÇcDDDDDмÀÃvï77£ÓˆˆHq’œ 11bt¹—Ý»áóÏ!2Ê—‡þýaÐ ¨YÓèdRÄ”£¾íf7a„1„!Œa =è?þ4¢‘Ñ1EDDDDDDDÄ “&MbíÚµ 6Œ%K–GD¤ÈQ‡É5[[[¢££ ¢iÓ¦”.]š2eÊдiS‚ƒƒY·n666µíªU«²eË:t耕*UÂ××—O?ý€Š+>p#GŽ$$$„zõêakkKíÚµù裈ˆˆÈ2ÖÚÚš~øŒc±··ÇÓÓ“•+WfŒ eРAÔ¬YkkëfÈÏ92Ú‚ èß¿?+VÄÞÞžÎ;³eË*åðN€¹™›ª6”YVÇ=ŽØÍ}¸óáNS¦L!&&†®]»âì쌵µ5eÊ”¡qãÆŒ9’½{÷°cÇú÷ïO­Zµ°¶¶¦J•*¸»»3sæLfü}w䜌)òZµ‚Æá«¯ŒN"""ÅÍ?µkСƒÑIäN·oÃÊ•àé Í›ÃÁƒþ AA*‘Gæ†ó™ÏŸüÉ8Ʊžõ<É“xãM$‘Üâ–ÑEDDDDDDD¤€ÙÛÛNdd$ß~û­ÑqDDŠu‘ ÷êv‘[[[rÔâ~ÛÍn]½zõXµjU¦eÓ¦M qãÆÜŸ¥¥%ï¾û.ï¾ûnŽögmm}ÏñéÊ—/ß‹ü³Ûn~ÎQnÆæÕ¶ÓÙÛÛ3kÖ,fÍšõÐÛÍéÜøá‡ÉÚÄÁæq¼æ˜±ü^çCNŽç¥—^⥗^ºï˜çŸžçŸþ‘Ljˆˆˆˆ ýûC` „†BéÒF§‘â"* žy\\ŒN"`îØ²d‰¹èã×_¡eKsaHÇŽpŸŽÒ"ë1#€F3šù‘pÂyשJUzÑ‹Á ¦5ŒŽ)""""""""䥗^¢wïÞ¼óÎ;xzzæøÕ""¢!RuéÒ…­[·råÊN:ÅW_}Å{ï½Àk¯½fp:)(Ó™Î2–áú¡+‡¶Òù """"b”×_‡ë×aõj£“ˆˆHqa2™ B||ŒN"ýe.ütq__sW 6Öüú¨DòY)Já…KYÊ1Žá‹/³™M-já7QDa"÷76‘¢'44KKKFŽit‘"E!Rè|ÿý÷´nÝšòåËãä䄯¯/W®\ÁÍÍ ___£ãIø‰ŸÅ( dûøí:DDDDDŒôøãàé ‹DDDŠ‹={ 1Q!FÚ·üüÀÕ¾øÂÜì?`þ|ÈA—f‘üàŒ3’H"ßð éL}êL0ç8gpBÉOåË—çË/¿dÞ¼yüðÃFÇ)2T"…ÎêÕ«yùå—©V­666Ô©S‡Ñ£Góã?bccct<Ég¸@zàÿá?:DDDDD ƒ×_7w¹rÅè$""RDE“<ý´ÑIJžôÎÍšÁÆ æ_œŒN'€ 6t£ÑDó?þÇk¼F0Á¸àBwºCŒÑEDDDDDDD$ŸtìØ‘=z0`À.]ºdt‘"A!Rè´oßžµk×ròäI’““ùý÷ß™4iåÊ•3:Z‰f2™0™LùºÛÜæMÞ$4³K,u>ˆˆˆˆˆ¯¾ ©© ;ñˆˆH^ˆŠ‚ÎÁÂÂè$%Cr²¹óÇ“O‚‡\¼ßññàïeÊPäžžà ‚âǘÂ~ã7¼ñÆ 7 ç׌Ž(""""""""ylÚ´iܺu‹ÿüç?FG)T""…ÆxƳ |Ã7T¡ŠÑqDDDDD$]¥Jæ H£¢ŒN"""EÝÉ“ðË/æ.’¿.\€ñã¡zuðõ…瞃ýûÿ颂)BÊR_|ÙÃv±‹æ4g#pÆ?üØÏ~£#ŠˆˆˆˆˆˆˆH©R¥ “'OæË/¿$66Öè8""…ž B È¿þõ¯,Ëðóó£N:ØÚÚbggG“&Mð÷÷ççŸ6 iîÄÅÅaaa««kƲ¢tLÙå·°°ÈômeeÅã?NçÎÙ²e‹qa ’_s´ |Â'„†;îY¶ý(fºß잟Ç’SÏ>û,}ôQìKDDDDJX½nÝ2:‰ˆˆeQQ`g/¼`t’â+1FŽ„š5!, üüàØ1ˆˆ0w )âÜpc&39Á Ba+[iJSšÓœp¹ÉM£#ŠˆˆˆˆˆˆˆÈ#zã7èСo¿ý6ÉÉÉFÇ)ÔTb€'NÂàÁƒ3-ß»w/Íš5#<<œ?þøƒ””’’’8pàS¦Lɶ€¤ /0Ïɾ6oÞ @›6m€ÂLw»;vÒÒÒøë¯¿X¹r%/¼ðkÖ¬É×L…ý5ÎNnçèOþäÿø?ºÑA Ê}ð‡`äyV DHH§N2:Šˆˆˆˆ:Á¥K°m›ÑIDD¤(‹Š‚¶m¡ti£“?GŽ€¿?Ô«‹Á¨QðÇðñÇðØcF§És¨€/¾àÑDS›Ú¼Ã;¸âÊXÆr”£FG‘G0mÚ´ŒëmEDäÞTb€   nß¾M÷îÝ3-5j—/_¦iÓ¦¬[·ŽóçÏ“œœL||<Ó§OçÙgŸ5(qÎÝ],PÔŽé~Å&“ “ÉDrr2û÷ïçå—_æöíÛpJcåõ%‘Äk¼FUª2‹Y÷Üæ£x˜mäÅ~ b›÷Ò½{wRSS™4iRìODDDDJ€:u n]ˆ‰1:‰ˆˆU7nÀÆæ®S’wöî…^½ AXµ ‚‚ !¡|y£Ó‰ä»R” /–²”cc#XÄ"êRo¼‰$’4ÒŒŽ)""""""""¹T³fM>øà>ùä:dt‘BK!ìúõë,X°€_|;;»LëvìØÀ²eËhÛ¶-•*UÂÆÆ†úõë3xð`~úé'#"ç˜ÉdbË–-´nÝ(ZÇ”]þìØØØðä“O2{ölöïß_ ù ƒ¼ž#&Þæm~çwV°{ìó>t åàà@›6m˜7o7oÞ4:Žˆˆˆˆ/¾?þht )ªÖ¯‡›7¡}{£“±±æâš§Ÿ†ýûaöl8|ØÜ%DX¤„r‰8ÊQV°€ô &5 $3œ18¡ˆˆˆˆˆˆˆˆäƈ#hÔ¨,°›1‹ˆ5*)`ßÿ=—/_ÆËË+˺Š+”””£mYXXdúýßébccéÙ³'...X[[S¹reÚ¶mËúõë³Ýž……III :”Ç{ ++«ïëÿûçÎÃÉɉºuëæÛ1¥¤¤L³fͰ³³ÃÎÎŽfÍš­[·²=¦7nàççG•*U([¶,¯¾ú* ™Æf—?'²,[¸p!žžžT¨P[[[ê֭˘1c¸|ùrƘÔÔTf̘A‹-¨Zµ*666899ѵkWbþ¾ãnQxeŽê|U‡Ei‹¸Úé*íê¶Ë2GwfÉnYZZŸ|ò ®®®ØÚÚÒ°aCæÌ™sÏã¾óñƒŽ5»ýæf®³s¿c¹×÷Ýrrn¥óòòââÅ‹DEEå(ŸˆˆˆˆÈ½ø"üü3\¹bt)Š¢¢à¹çà±ÇŒNRtݾmžÇ矸xV®„={Ì]B,-N(R(Xb‰>DM<ñ¼Å[LcÕ©NwºC &tˆˆˆˆˆˆˆHageeÅÌ™3‰eþüùFÇ)”TRÀ6nÜ@“&M²¬ëܹ3-[¶¤_¿~Ì›7ýû÷“–öð­Ì=<û,dâĉ|óÍ7’˜˜HJJ çÏŸ'**жmÛæxÞ~jŽ~YD©ÈR´IlÃÙÿïÞsô ÑÑÑDEEqùòeŽ?N§N˜2eÊ}Ÿ—“cÍÎÃÌõƒÜ½ÿU«Vaaa••_ýuƸܜ[é4hÀŽ;*›ˆˆˆˆHU«Býúæ PEDDrã§Ÿàôiøûÿo$‡®]ƒÏ>ƒZµ`ÀhÙ~ýõŸn+"’c¥)M7ºK,»ØEÚð1ㄽèÅ^öQDDDDDDDDîa„ ”)S†Ñ£GED¤ÐQAH;uê*TȲÎÁÁ+VpàÀ‚‚‚ø÷¿ÿ««+W®\aîܹ<ýôÓìß¿?Wû5j3fÌà‰'žÀÎÎFŒpÏmM:•öíÛãàà«}mÙ²€Ö­[çÛ1-Z´€É“'Ó·o_qtt¤_¿~|þùç™ÆÜ}LmÚ´ÁÁÁOOOÂÂÂˆŠŠºoþû©R¥ Ÿ~ú)•+WÎX6kÖ,/^L·nݨR¥ eÊ”¡E‹ ß~û-5jÔ`ûöí|öÙgÌŸ?Ÿ .бcGÖ­[—ã91ú5¾ŸìæèË%_ÂJhhjȺšëî;GAÇŽ)W®Õ«WÏ(Š‹‹ËÍaåØÃÌun$$$гgOL&~ø!ÏÝñ¡~nέt+Vþù¹#""""’'žTt,""¹®®æò`W®À„ æ9?^þøfÍ2gŠÈ#qÙÌä$' #Œ=ìáiž¦9Í 'œÜ0:¢ˆˆˆˆˆˆˆˆÜ¡lÙ²|ùå—Ì›7˜˜£ãˆˆ**)`—.]ÀÞÞþžc7nL@@Ë—/çèÑ£ÄÇÇãååÅ¥K—3fLŽ÷µdɼ½½‰ŠŠâôéÓ¤¦¦fZóæÍlŸçéé™ã}¤‹çôéÓT­Z•† fYŸWÇtøða:vì˜e]zwˆøøø,ë<<¥¹OÎ IDAT<2=NïpqôèÑå‡:9œ¾yعΩäädºvíÊÅ‹iÕªãÆË´>7çVºôŸ3é?wDDDDDòÄóÏ›ïòž–ft)J¢¢ sg£S~W¯Bp°¹#Hp0¼ù&üö›¹Kˆ³³ÑéDŠr”Ã_ö³Ÿ]ì¢ÂœqÆ?þÇÿŒŽ(""""""""ëС]ºtaðàÁ|½žˆHq¢‚–Þäúõë9~NýúõY°`[·nÍñóÆÉdbàÀÄÇÇ“””„ÉdâÚµk÷}^zWÜØ¼y3óÎ{Lù%7ù«U«FXX:tàÂ… ¼÷Þ{ëL&iii¤¥¥qûömnß¾±àÖ­[¸»»óÛo¿±mÛ6&MšD×®]qpp`íÚµøûûç(wa}ï5GCJZ«4ð´?çÎãܹsÌ;—‘#Gfs§¡C‡²yóf®]»Æ¦M›2:pøü}WÂÜv8sqJ¿~ýHMMÍè€1xð`:uêĬY³HHH ))‰ääd>ÌW_}…»»{ƾ.\HbbbƼ,[¶ Óqö×ø~2æè•TB*‡Bï54ÏUNæ(?ÝïX³“×sîÀ 4€·Þz‹×_=Ûq¹9·Ò¥ÿœyþùç)£ˆˆˆˆH&¶¶P»6.^¼HïÞ½qttÄÑÑ‘¾}ûréÒ%<==3Š=îôÌ3ÏàééIÙ²eyá…8~ü8...Ü7ÿƒ|øá‡”)S†åË—³k×.úöíËСC9}ú4  V­ZØÙÙQºti4h€¯¯/;vìÌ]KzöìIõêÕ3æ% 0äd> Ãkü ]>é߀)ÂDë]­s5Gùé~󚼞ët¯½ö7nÜ`áÂ…9îÎó0ó–þsæÅ_|¤Œ"""""Y4j¤!""’3W®À–-ð÷YJ¼¿þ2‚¸ºÂœ9ðÁ`.)_Þàp"r/u¨CAüÉŸ,f17¹‰7Þ4 ÁsžóFG)1J•*ÅôéÓùñljŒŒ4:ŽˆˆáTRÀ:wîL¹r娰aC–u{÷îå½÷Þ£eË–TªT KKKìííiÒ¤ #FŒ`ß¾}<ýôÓ™žÊ Aƒ¨Y³&ÖÖÖ™Ö9’êÕ«‡­­-µk׿£>"""⡲ßk_é#<<<²\PŸ×ÇdkkKtt4AAA4mÚ”Ò¥KS¦Lš6mJpp0ëÖ­Ã&›; .X°€þýûS±bEìííéܹ3[¶l¡R¥J÷Íÿ NNN 6 “Éĸqã˜2e 111tíÚggg¬­­)S¦ 7fäÈ‘ìÝ»€;vпjÕª…µµ5UªTÁÝÝ™3g2cÆŒÍGaxï'ú:ö¥vbmL®ç(?Ýo^³“×sîðáÃ9›ÛyÛ°a*TÈè„#""""’gT"""9µv-¤¥™;„”dǃ¿¿¹dÑ"øôSs!H@ØÙNDrÈ[ºÑh¢ù•_ù7ÿ&˜`œq¦;݉!Æèˆ""""""""%³Ï>KŸ>}9r$×®]3:Žˆˆ¡,L&ÓC÷³Þ½{7nnny¨$6láááœ?{{{£ã<²Þ½{3þ|>ÿüsFŒatœLÒ‹îwšæü…ÅÃÌѮЊV˜0K,åÑ Òõëש\¹2$44Ôè8ENAþýfaaÁ’%KèÞ½{¾l?<<<£ÃˆHwÌ?k–²Ôà$"Rä}ý5ôëׯƒ••Ñi¤Xºt)=zô¸ï¿¿óJ~¿_Oo¾tiþÿ}©÷ëR"ôì ‰‰°q£ÑIŒqìL˜s炳3Œ}ú@67µ‘¢é*WYÌbf0ƒ8âpà _|y“7±§è$"RåôßbºB¤ø*Èÿÿ‘üsþüy4h@ÿþý 6:ŽÜ‡ÞIqV®7U‡Œ;KKËbóƒ-½{DëÖ­ NòpŠzþ‚Û9J&™.tá"YËZƒ`É’%XYY`t)Ž5‚”øýw£“ˆˆHa––fîR»—&&ÂàÁP¿>ÄÄÀ—_ÂáÃàë«b‘b¦,eñÅ—½ìe»pÃá Ç 'üðc?ûŽ("""""""R,U®\™ñãÇóù石¿þFDJ.„ÀÉɉѣG3cÆ ££ä‰„„L&S‘½3JQÏ_r3G·¹M/zñ ¿°ŠU8ã\ ån_|ñ£G¦ZµjFG‘â¨aC°´„ƒN"""…Ù¶mpîtìht’‚sö,Œk.Yµ &M‚_5wÖ²¶6:ˆä37ܘÉLNr’ñŒ'†šÒ”æ4g>ó¹Å-£#Šˆˆˆˆˆˆˆ+ä™gžaÈ!ÒÉ^D¤0RAˆAùù矎!’çF2’¬ ’Hžâ)£ã”X?ÿü3~ø¡Ñ1DDDD¤¸*]jÔ€ß~3:‰ˆˆfQQðÄæâˆâîÜ9s!ˆ«+Ì™~hîâï¶¶F§‘V øãÏoüF4ÑÔ¦6ýéOuª3–±$`tD‘b¡T©RLŸ>ØØX–,YbtC¨ DŠ5“ɤªÏô13•©,d!ÞxGDDDDDò““œ:et )ÌV®„NŒN‘¿ÎŸ‡À@¨Sfφ>€„0PŠH‰VŠRxáÅR–rŒcŒ`‹XDêà7‘D’FšÑ1EDDDDDDDŠ´æÍ›Ó¯_?FŒÁåË—Ž#"RàT""yâ+¾â> ”PºÑÍè8"""""’ßœœàäI£SˆˆHaõûïæ>>F'ÉW¯Bp°¹dút1Ž1‚”)ct:)„œp"€Žr”¬ =¨IM ä,g N("""""""Rt“ššÊÇlt‘§‚yd+YÉ`ó1”¡FÇ‘‚P­š:„ˆˆˆÙæÍàà`î2{6œ9+V@¥JðüóF§Ë[×®™ AjÔ€I“`øps!H` ”-kt:),±Ä¢‰æ‡x‹·˜Æ4\p¡;݉!ê|."""""""’•*Uâã?&,,Œ}ûöGD¤@© DDɶЃ `ðÑqDDDäo±Äb‰%w|Eþýuç2K,‰%Öè¸"RU«¦!""bvé\¿kÖÀ€ðøã0q"Ô¬ ‡.o\¿n>¦5 (ÈÜäèQs!H¹rF§‘"ª>õ "ˆDYÈBNro¼iD#‚ æ"Ž("""RäÅÆÆbii‰……EÆwdd$‘‘‘™–YZZ«ÏKäÿgïÎã¢*÷?€†YdÙ7G%/feämKqµ K «[Ѯխ´~vÕV»mšeiZ™÷^î-EÒJ¬LËJ4+°RAÙw}Ÿç÷Í43pfæ°|Þ¾|1s8sÎ÷™s˜ç™g%"¢,11'NIJeË䅈Ȫ8 „ˆzí(Ž"î°AîpˆˆˆHG €¢ÇýP aVˆˆˆÿŽBg/&"ò\\:~¶·juGÞpþ<‘L˜Ë–GÊgo´´6£Gw yàŽ ÿø‚‘Ù8Á ñˆÇaF:Òq5®Æ3x!Áݸ'pBˆˆ¬°°0(ÚK „…±½„ˆˆh ³±±Á† pðàAüïÿ“;""«á€"ê• d`fár\ŽíØ[ØÊéð†7¦`J·y´ l0Sá o+FFDƒ†¿?ÐÔÔÑá—ˆˆ†6Í€]B­­ ;UÜv›UÃêµHNÂÃGæÍNž~ðð;:"Ä"MØ„àe¼Œoñ-&b".ÃeØŒÍh@ƒÜ! (ÞÞÞ˜2e lm»i/±±ÁÔ©SáíÍö""¢îòË/ÇÍ7ߌÇ{ MMMr‡CDdBDFmÅV<†Ç  ?ãïœA b0ãð1>†#eŠˆˆˆº“€„.ù¸¡}ˆˆzÅϯãga¡¼q‘ü 1dýzËÆÑ“ï¿Þ|³çýÒÒ€K/nº©ã篿›6¾¾–‘ˆèÃ1‰HÄ/ø‡p*¨ð@p7îÆ¯øUˆˆŒ„„ˆV:NH`{ Ñ`±víZ”––âµ×^“;""«à€"2¨5xãe¼Œ»q·¶3i>ò1Ó„ ìÃ>¸@bƒ?YÝu¸v°3ú{[ØbæY1""TFŽìøY^.oDD$¿ž„ØÚO> DG['C22€éÓŽ7¼ÏáÃÀäÉ@L œ8$%*•uc%"ê$ QHBr‘‹XÏð.Ä…˜ŽéHF2ÚÐ&wˆDDDDýÚu×];»nÚKlm1oÛKˆˆˆ‹€€,_¾Ï?ÿ<ŠŠŠä‡ˆÈâ8 „ˆ zo¢:V ¹w¢ŘŽépƒöaÜà&s”DDDÔw¸#±…ØÁqˆÃp —!2"4ëë僈ˆä×Ý€{{àâ‹U«¬Og99À´i@ccÇà”¿ÿ]ÿ÷?üÐ1XeòdÀÉ 8zس˜0Ažx‰ˆŒð…/–c9²‘Ïð”Pb!ÁXÈCžÜ!õKîîîˆ58(ÄÎÎqqq>œí%DDDƒÉ£> ///üßÿýŸÜ¡Y„Q hÀKxI;«˜j¼÷q ®jìÇ~xÂSæ(‰ˆˆHŠ›q3ÚÑÞe{;Úq n‘!""4†   ¡AîHˆˆHnÆ„(€£#ð¿ÿu ‘CyyÇ`ª* ­­ãÿW_û÷¿þ ,\\qEÇÇ/¾èØ!O¬DDÙÀшF’ð;~Ç,Á»x£0 qˆCÒ´«~Q‡›o¾ííÚKÚÛqË-l/!""lœñ /`Û¶m8zô¨ÜáY„QoãmÔ Fo›jœÁD Þð–)2"""2Õl̆ ºvÐs†3fb¦ Ñ ¡PÎÎ\!„ˆˆ: "ðÎ;@h¨UÃÑjhfÏrsÖÖ?·ÛÚ·Ý\x!pútÇj GŽS§Ê'QŒÆh¬ÅZä!;°MhÂtLǸ/âET Bˆˆú…Ù³gÃÅÀ„ÎÎΘ9“í%DDDƒÑM7Ý„ÈÈH,[¶ Bpò "¼8 „ˆô4£/âEƒ3‰«¡ÆNìÄݸ›³‹ NpÂ,€´Ûìa…Xg8Ë ..BDD,ô·ÙÙwÝÜt“<1µ¶sç?þ¨?ÚÛ¢"à¾û€cÇ€9s䉑ˆÈŒáˆxÄc?öã$NbfáY<‹@b!â|#é8ó1÷ã~í âDDDDƒ…““,Xï¯öööX¸p!œÙ^BDD4X­_¿ßÿ=’’’ä…ˆÈb8 „ˆôlÅÖng SC­ØŠeXÆA!DDDÄb,F Z´Ï[ÑŠÅX,cDD4h Æ!DDÔÁÉéÏÇvvÀ¨QÀºuòÄ"ð·¿mÝthþøã®ƒEˆˆ¿à/Xõ(D!Öc=NᢅËp6c3êa¸ Ÿ ìÆn¼…·‡8£û T‹/FK‹N{Ik+/f{ Ñ`6qâD,Y²>ú(䇈È"8 „ˆ´ZÑŠçðÔP÷¸ïlÀ1³BTDDDÔWÓ0 #0Bû\ %¦bªŒÑ ÁBˆˆHcذ?+@r²þ6kzøaà?ÿé~0ˆ@q1ðöÛÖ‹‹ˆÈÊÜà†D$âN éˆ@ÂCð‡?îÆÝÈ@†Þþoá-ØÃpQˆB)JeŠžˆˆˆÈü¦M›†#tÚK”JLÊö""¢ÁníÚµ¨©©Á+¯¼"w(DDÁ!D¤õ>@1Š®üa [ÀŸ»° —á2k†GDDD½d [,Æb8üñïÜ;ØÉ ..gÒ!"" #O:ƒ¬_\|±oiγxhDDý…<° Ëp§ñ >üð7ü £0 ÍÐ/Û·¢E(Â_ñW|‹oeŠ˜ˆˆˆÈ¼-Z„––´´´`ñb¶— ?ü0¼½½ñÔSOÉ ‘Ùq˜;Q?ÔÐЀœœäçç#//¹¹¹ÈËË!88AAA DHH†iÜû`v 9z«ƒØÃ­hÅdLÆsxWâÊ>Ÿ‡ˆˆˆ,¯¡¡………(..FQQŠŠŠP\R —¿wÌÚœòJ ~ðù~~~ðó󃯯/üýýÍR¦ ¢!¦­ °µ•; "¢A«¶¶­­­8þòòòPQQ¡Ýذa A`` àÈ‘#ÈÉÉACCƒvŸ#F ((Èà`‘   øûûwÛˆ¡†ÏâY( €€€=ìц6LÇt<§˽!DDD$Yyy¹v‡î`’’ ´´ùùùÚŽ‚ P(àíí \à|à“ÔOPRR‚ÒÒRÙ“]]]oooÀÇÇGoЈ槗——ÕÓNDýT{;„¡c@nMM jkkQSSƒóçÏ£¶¶Vû¼¶¶çÏŸGMM šššPWW‡ÆÆF455ô¡ù©t]hMt~¬¡T*{<ž³³3œœœzL{EEÖ£ÁÖ¹]öBàüùózÛt¸èP‘:X¥³…þB ÜÖ§ãæ†\”zz¢ØÓ-puu…“½=ÜËÊàñÓOp?{nnnÚÿpww‡››ÜÝÝ9`šˆ†Œ …Fߎv¨¡Æ<ÌÛx÷à+F÷§ÖÖV”––v䡸¡©)))AKK‹öuŽŽŽðööF`` Fމüü||ùå—(((@mퟃ`4u(ºõ"šA#šú0 ""@‘––†Ý»wcÏž=(--…ŸŸ`òäÉðööF\\æÍ›‡èèh8;;Ë1YÊ¢E‹ðꫯbåÊ•HMM•;""³á€"3«ªªBaa!ŠŠŠììl½ç¹¹¹Ú™ŽÆw•J•J…«¯¾þþþÚçš…BÑå<Ïñûï¿cß¾}ÈËËÓΩ{??¿.çøiìOøÝçw( €ì€<'0c¬òž uºåC?«ªª››«7ÐÃÁÁ#FŒ€R©„¿¿?BBBpå•WjózÍÏ   ®CWJ;÷Ñ£GQUU…¼¼<½ÏÝùœÝž›ˆ!¢A¤¦¦•••¨¬¬DEE…ö±¡ÿyhVâèÌÙÙ¹Ë`ggg¸¸¸`Ĉptt„»»;ììì´«]¸ººÂÉÉ ÎÎÎz+aØÙÙiWÑШ1lØ08::Zí}²¦ææfí¤(MMMhllÐq­ÚÚÚôÑ.-E}{;*[[ÑÖÖ†ªª*´··£¶¦vÍÍhih@II » ÚÑ·3ÍÊ%ÚA"žžžFÿ1BûXs­ˆˆ‚ب]1ÜñÇ¿{q/Îá^À P kÛEo455¡²²²Ûz‘¢¢"äääèå¹NNNÚz¥R‰‹.º3fÌèRGáëëktÖïžÎ}æÌuiÛqrrê±^¤§s‘åTVVâÀسgvíÚ…ÚÚZ„‡‡ãÞ{ïE\\"":&ÅÌÎÎÆž={œœŒ÷Þ{ŽŽŽ˜6mâââ0wî\øúúÊœ"""2'…B—_~S§NÅ_|k¯½V̂BˆL ;ÃРÎ5•J¥ÞŒèèh½çÁÁÁ°³ëÝŸ¡³³³vP‡1UUUcÍÌÌDZZÎ;µZ ì0¶}T«PëZ‹-ª-z±j‘4=u(Ðü,--í¶3Ãøñã-Ú¡@©TjÏÓM9ÈXÇŒ“'OjgèÔ]uD7=Æ~ÀÃãÏi!"p@õcuuu(,,DYY™v6qÍ㢢"”––ê òÐíä 666;ý‡††jW‘ðððÀðáÃõV™Ð Ð ô Þstt´Ê`—ÖÖV½>º«»è©®®Fee%JJJð믿êÝ?ºe`°³³Ó(2räHøùùigžïüØÕÕÕâé$"2¤HBR·ƒA:{ /¡¥ØŒÍ°ë¦©±»º„΃=tiÚV4.T*•Åê4õþþþÚŽ¡Æt7©Fff&>ÌI5ˆˆˆdtîÜ9ìÞ½©©©8xð lmm…gžy7Üpº¼F¥RaÙ²eX¶lÊË˱wï^¤¦¦â‘GÁ½÷Þ‹‰'"667ÝtÆ'CªˆˆˆÈܦL™‚˜˜¬X±ßÿ½Áɺ‰ˆ¶Hý¡¥¥åååFWöÈÎÎFUU•vM#f°DDD„Þª!!!²7ä*•JDDDmÄФùÇŠQòY Ê*ËP8¶#ÍiiiÚôktNsç•FúCš‰ˆˆ,Í\"##»t ÄðáÃeJY÷¤ FzsìØ1¤¦¦özfMزó9QÿÑÖ°³3YYMM òòòŸŸ‚‚äåå¡  %%%(++CQQ‘v%]^^^zð'M𤷢CçÿJ¥R¦’µÙÛÛk¯{oUUUu»²Lii)Nž<‰ƒ¢¤¤z¯6l˜öÞ‘:â IDAT9r$|}}µ„„   ¸¹¹õ5¹DDz2‘‰v^éJ³ª¸ :&¤PC6´á=¼‡_*~Á{@qa±ä•N5ßï5í*i`'Õ ""ê233‘œœŒÔÔT;v J¥ÑÑÑØ²e æÏŸoÒê^^^X²d –,Y‚ÆÆF¤¥¥!55o¿ý6Ö¬Y•J…ØØXÄÇÇ#22’G‰ˆˆ°—^z 'NDrr2.\(w8DD}Æ^4d[-Có\»Zô& ­ì¡R©E§íÌW˜`^×}šššPXXhð½;vìX«¢t<Ò—UQˆˆˆ,©»Yufptt„§§ç€îÌ`næ˜YSSnãÌšDW!"3kllDvv6rssQPP€üü|½ÇË...Ö–ÅT*|}}áëë«íTïããooo– È¢4„¥jiiAYYJJJP\\ŒÒÒRk69s‡BNN´¯sssÓÑ  D@@‚ƒƒ1zôh899Y"‰D4H]«Q‡:T4W ï|²*³[•‹ÂšB×£´±åmå8ß~5ê4Ø6n¼€ôútÜóà=ða½•NNªADDd9ííí8rä’““ñÑG!??!!!˜1cV­Z…3fÀÁÁ¡ÏçqvvF\\âââ°qãF9r©©©øøãñúë¯cäÈ‘˜9s&âããc•Õ-‰ˆˆÈ|.ºè",^¼+V¬ÀüùóÍR~ "’{eÓ  ™mÉØÊyyyhmýs¹s¥R©¨0~üxÄÅÅé \ 2 =qrrê±áB·3§î5ÈÎÎFZZZ—Æ Íûol¥???ΦADDfÑSúægii)ÚÛÿœ SwFFvf°œþ0³¦¿¿ÿ èK$!¢^Ð þ4ô_wÂŽÎyùĉõêX‡@™ƒƒÐ㾺uŸºup………øé§Ÿº¬l¬[÷Öùÿ¨Q£ø7C4Ęc¥Ó ý/Ôÿ.­ô‡Ÿ÷+žïŸ+œTƒˆˆHšúúz|ñÅHNNFJJ ª««Ž›o¾±±±_­ÃÖÖQQQˆŠŠÂÚµk‘™™‰ÔÔTìÙ³óæÍƒ³³3®½öZÄÇÇcîܹ\Í‹ˆˆh€xöÙgqÁ`óæÍxà䇈¨O8 „ú½ææf]ÙãôéÓ¨©©Ñ@×4rFFFê=gŶùIéÌ©ÛÙC÷Z~óÍ7]:p:::" ÀèJ#cÆŒÁðáld""ÊÌÑ™Áßß_[NÐm d>ÓÏpfM¢~¬¥àŒ9Dd@MM ~ûí7œ|çÎCCCƒv¥RÙe°ˆîó6HY9;3DGG³3™gÖ$2!€ÚZ!Brrrpüøq;v ÇÇñãÇQRR £3º¦#ytt´¶#yhh(¿{ÉÌÆÆ!!! ÁÔ©Sõ~×ÞÞŽ³gÏâ×_Õ®èóÍ7ß`ëÖ­ÚÁѾ¾¾¸ôÒKq饗"""—^z)‚ƒƒåH Ñ€ÕוN5ß#U*W:%³à¤DDÔ´··ãÈ‘#HMMÅÇŒS§NaäÈ‘˜9s&–/_Ž˜˜8::ʦd...ˆ‹‹C\\œ6mÉÉÉø÷¿ÿ_|!!!˜1cbcc1cÆ æDDDý€R©Äã?Ž5kÖ 11r‡DDÔ+B½¦Û9ßРÎK‘ëVüªT*DGGë=gç|ꉔ™­4 ïÉÌÌL¤¥¥é BrppÀˆ#º]iÄßßßZÉ#"pØ™†©3k677£¢¢‚3kÒÐRW´·\¥‘hP:{ö¬và‡ægyy9lll†ˆˆ<þøã˜0aƇ   ¹C&¢^°µµÅ˜1c0fÌÄÅÅéý.77¿ýö~ùå?~ÉÉÉxþùç¡V«1räH½" •'D2âJ§4pR ""2§††8p©©©Ø½{7JJJ R©‹­[·šU4lmm…¨¨(¬_¿™™™HNNFjj*6oÞ ¥R‰èèhÄÆÆbþüùpg+‘l–.]Š7ÞxÏ>û,Þ~ûm¹Ã!"ê!ƒZZZŸŸ¯×©^÷qVVΟ?¯Ý_SY«éD¡×Á>$$®®®2¦ˆ† ¥R‰ˆˆ£3Zµ´´ ¼¼ÜàJ#iiiÚÇïíÎÃÂÂàææf­äY;3™ÎÑÑѬ3kæç磦¦FïuœY“úêꎟÀG4(dgg#-- ‡Æ×_œœЖëî¿ÿ~DDD 22žžž2GKDÖŒàà`ÄÄÄh·ÕÕÕáĉ8vìŽ;†]»váÅ_D{{;|}}1yòdDFF"** 'NäjŽ4 566j÷s¥S"é8©S^^޽{÷"55{÷îEcc#&Nœˆ{î¹7ÝtÆ'wˆ7~üxŒ?«W¯Æ¹sç°{÷n¤¦¦âÎ;ïÄÝwߨ¨(ÄÆÆâ†n@@@€Üá )ÎÎÎX³f î¹ç<öØc=z´Ü!™ŒB†(c«(hžŸ;wjµ@×U¢££‘ ·ªgå¡ÂÁÁ¡ÇΚMMM(,,4ø7rìØ±.KŸw^ý¦óà‘àà`ØÙñã–ˆäeŽÎ šÏNC ¯ìÌ@$]_fÖÔý;îËÌšš¿c¢>Ñ Z•G­VãÇÄ¡C‡pðàA>|åååpssCdd$î¾ûnLž<'N„‹‹‹ÜáQ?âêêªåV£®®Nû™rèÐ!<õÔS¨­­…——&OžŒk®¹“'OÆ%—\Âï$+)+¡ªªJïu\é”ȼ8©ÑÐ={ö 99GŽ££#¦M›†W_}sçÎ…¯¯¯Ü!Ê&44Ë–-òeËPYY‰`Ïž=xê©§ðÐC!<<ñññˆ‹‹ë1¯$"""ó¸õÖ[ñÏþÏ<ó Þÿ}¹Ã!"2{(Bº•£†:´çå塵µU»¿R©Ôv`?~<âââô:´‡††²¡Ž†''§g±êüw¦ù[Ǫ́Úyæ*Íß™±•FøwFD½ÅÎ DƒgÖ¤~+„ (MMM8|ø0öìÙƒÿþ÷¿(,,„››.¿ür<ú裈ŒŒÄ_ÿúWvŠ#"“¹ººbòäɘù$ÊÊʰoß>¤¦¦âÁÄwÞ‰k¯½ X°`\\\ä™,È’+±M„ˆºÕ—I5tëE8© e•••8pàöìÙƒ]»v¡¶¶áááHLLD\\\Ÿ¯$¶¯ÁêÕ«‘­rÓM7ÁÑÑÓ¦MC\\æÎ ___¹C&""ðlmm±zõj,Z´+V¬ÀE]$wHDD’q@ˆifÝÕíü­û8''GÛùÓÞÞ^^^FWö`§O¢ÏÑѱÇ«4 ¤†V9|ø0Î;‡††íþJ¥Òà`ÍãÎNEdìÌ@ƒÍôéÓ±ÿ~¹Ã qfM’¤ ðóø½”¨_8{ö,6lØ€wß}õõõ˜9s&þóŸÿ`öìÙì\MDýÊÈ‘#±dÉ,Y²õõõøä“O°}ûvÜqÇX¶lî¸ã<ðÀ •;T’¨¥¥ååå=N‚‘——‡ÖÖVíë:¯tªi éü]+’X7BœTƒˆèOçÎÃîÝ»‘ššŠƒÂÖÖQQQxæ™g8ù„•¨T*,[¶ Ë–-Cyy9öîÝ‹ÔÔT<òÈ#¸÷Þ{1qâDÄÆÆâ¦›n¸qãä—ˆˆhÀŠ×®òßÿþWîpˆˆ$c º™èvÚ64ÛNNêëëµûëvÚV©TˆŽŽ–¥Óvç%NNN1b&Nœˆ `ñâÅf¡F÷|º•žÆöënŸþH¡PŒÙZé‘ó}³ö¹-}>©Çÿâ‹/°aÃÃ…^ˆÓ§OãþûïG^^žzê)‹Å§K©Tj—‹5DÓ©ÝÐJ#iiiÚÇ?Ã:? ƒ›››UÒFd ƒ­3ƒTRó`S%„@]]Μ9ƒ””\qÅøðÃc†¨I.;2ƒþPNÑÅ™5¡ÂB!’QYYüqlß¾áááØ±c®»î:Πn·ÝvüqìÛ·‡ÂôéÓ%¿ÖPýÂ`«÷²k¾or\#kœóñÇÇÕW_­·mòäÉX±b^~ùe‹ž[ ___,_¾ÿûßñ¿ÿýÏ>û,.¼ðB,Y²ÿüç?áåå%wˆƒ†”•N õÚ=€¿Ò)ëFȬúSÝ'Õ ¢þ®­­ ß}÷’““ñÑG!??!!!˜1cV­Z…3f˜u2Ñ5kÖàèѣؼy3ÂÂÂÐÔÔ„ü<ò^xá‚•ÈÙÙqqqˆ‹‹ÃÆqäȤ¦¦b×®]xýõ×1räHÌœ9ñññˆ‰‰£££Ü!õ{óçÏǤI“ðÌ3Ï %%Eîpˆˆ$Qˆ>|‹:vìôXiÕßš]_÷ù¹sç´3988 00Ð`'i•J…Ñ£GÃÃÃC晦»Í›7ã…^À‰'0|øp‹Ÿ¯7ûõ'ÝÅl­ôÈù¾YûÜ–>Ÿ”ã+ üôÓO¸è¢‹´Û²²²‰ââb‹ÅfnMMMÚ†CƒßN:…ÚÚZíþº †V±öÊæfÍüM¡P`çÎX¸p¡EŽ¿yóf$&&ZäØý¹:3tî4Üß;3˜ÂœŸ›ÆŽ•ššŠ'Ÿ|?ÿü³YÎ#åœD•5Ê2ƒ¡œbLç™5 öãÌštõÕÀÅ6È õIII¸ñÆ­’W[º¼®)›'%%Yäøºz[^ÿïÿ‹ûî»NNNxñÅqã7ÂÆÆÆöÞ`Z!ÄÞÞMMM½Î ÅŲmïXó}L÷0œ={·Ýv<Øåw×\s ¶oߎàà`‹œ»·Ôj5vìØåË—£¥¥o½õ,X wXý–¹V:5T/"ÇJ§–º¢þ…u#}ÓÓ¤šŸ%%%z«ÞpR éßÅK"cêëëñÅ_ 99)))¨®®Fxx8âââkÑÕ%|||ðË/¿ÀÛÛ[oûÉ“'1~üx–yÌ 33©©©Ø³g¾ýö[8;;ãÚk¯E||<æÎ;àú6YÓÞ½{1gÎ|÷Ýw¸üòËågP°fû‘µõ‹þ¦¢ÒÓÓEzzz_aq•••"##Cìß¿_lÚ´I¬ZµJ$&&Šèèh¡R©„ ý¯T*EDD„ˆ‰‰‰bíÚµ"))I:tHdee‰ööv¹“dv=݉‰‰båÊ•zÛ~ùå1kÖ,áêê*\]]ŬY³Ä/¿üb–óéî—››+æÎ+\]]…···¸ùæ›Eyyy—}wïÞ-®ºê*áèè(BBBÄC=$jjjôŽ¥ùÿå—_ !„رc‡ÞvÝ´Íž=[›¶3fHJ›î±4ÿï¸ãŽ^¥§·1hÎÓÛãõô>jdddˆY³f áîî.æÏŸ/rrrz}nÍûuæÌqÝu× .×Å”´J¹7Oœ8!¦OŸ.† &ÜÜÜÄÌ™3ŧŸ~jôø£GÖÆÙm\UUUÂÃãÛ}¢ÎŸ§Ë—/ =~žÆÇÇ‹¥K—¨ÏSkæoÄÎ;-vüM›6YìØÖÐÜÜ, ´÷Þ¶mÛÄÚµkÅÒ¥KE||¼ˆŒŒ*•JØÛÛëÝŽŽŽÂÏÏO/O_µj•X·nÞ}ØÚÚ*w­ªóçæùóçÅC=$F%…¯¯¯¸õÖ[Å÷ßoò±4…ƒƒƒö¹”Ïe)qô”ÏêæMnnn"&&FdffvÉO¤ä9Rò®Þ¾w=ßXþgh»f›”òEoöííë…~=Œ‘R&éë58yò¤¸üòË…³³³ˆŠŠ§OŸâ¯ý«pqqS¦L999‹­§{ÚRe¨ÁZNé‰n9fÛ¶mbݺuze™ððp1|øð.×D©TŠððp-ÄòåËõò’ŒŒ Q[[+wòä3z´Ï='wÔOìܹSò÷ý¾²ty=>>^ÄÇÇ[ìøºL-¯«ÕjñÄO…B!î¹çƒßÙûƒîò¹îò¯³gÏê½æôéÓ¢¶¶VoÛÙ³gM>§½¯Ëê|ÌåË—›|Lcå»Îº;Þ—_~i°«¶¶V¸ººŠ‚‚í6[[Û.õ`†H-OIIg_ËXR뮈œœ1þ|áîî.\\\ÄìÙ³ÅÉ“'Mz?uI©çê˽ijút™rz:þ+¯¼"ÞyçƒñmÞ¼Y¬_¿Þhür«®®‰‰‰B¡Pˆ•+W µZ-wHVÕÐÐ DzzºHIIÑkçˆÂÏÏOØØØèÝ“NNNB¥R‰ÈÈHmýܪU«Ä¦M›DJJŠHOOCîýdÝëFX7º‘¡X7ÒÔÔ¤——«gïÜÎãääd°ž]“—hêÙÛÚÚäN¢É¤~ý!ˆL•““#6mÚ$bcc…ƒƒƒ°µµ‘‘‘bíÚµâÔ©SV‹ÃÕÕU455õ¸Ÿ)e!¤õ²¹ÊC})ošSnnn¿¸îDDDÉäɓŌ3äcаfû‘µõ‡þ¦z@Hcc£ÈÊÊÒë ªÛâîîn´3n…Õþýû‡d§Pž*ž=*&L˜ }~úôi(¶nÝ*JJJDII‰xçwD@@€8}útŸÏ§»_LLŒØ·oŸ¨­­¹¹¹">>^ÜvÛm÷Ý´i“¨¯¯EEEbÉ’%âÖ[oÕþ¾­­MŒ=ZTVVê½®°°PxzzŠºº:mÚ|}}Å[o½%JKKEyy¹Ø¾}»P©T"//¯Oi“šsÇ`Êñzz…âÌ™3"((H{ýËÊÊÄöíÛÅ•W^ÙçsOŸ>]|óÍ7¢¡¡AìÝ»·Ç{ÅÐù¤Ü›¿ÿþ»¸à‚ ÄþýûE}}½ÈÌÌ“'O6ØÀ£‘žž.BCCÅÏ?ÿÜmLBñÈ#ˆ9sæô¸ß`TYY)ÒÓÓERR’¶£¥nc‚nô£££^£´nËôôtQUU%[:úCm.ýu@;3È£óçܼyóÄêÕ«Eqq±hjjÇ‘‘‘’òjcû¤¦¦jË R?—¥Æaìœó¦òòr±cÇqá…m$0–çHÍ»úúÞu—ç;†±´t._\wÝuFËK¦ìÛÛ×›z=Œ¥µ§2I_¯ÁÂ… ÅÉ“'ÅùóçÅý÷ß/.¿ür±`Áí¶{ï½WÜpà ÍX¬–,C årŠ "++K:tH[žémþ´víZ±mÛ6±ÿ~‘‘‘¡×QwÐpqâ½÷䎂ú éSËë+W®âý÷ß·PDæÓS‰±üë矓&MÒÛ_­V‹1cƈ'Nô꜖¨Ë2å˜ÆÊV¦/==]¸»»ëu¸{ÿý÷E@@€xíµ×ôŽ7nܸÿN¤”YLMgoÊX¦–}®¼òJ±}ûvQVV¦'88Xo@†Ô¸M©çêí½ijúzs¤öìÙâÇ4ãñãÇEll¬Ñ4ôï¾û®°··ÿøÇ?äÅ,4ƒ–uËž-ëvl6ÔΡ;hY·ìY]]-wòú-ÖüyÖ°n„u#dÈP™TƒBh¨ÉÈÈk×®‘‘‘B¡PˆaÆ‰ØØX±mÛ6ÙÚd-Z$zè¡.}8 ‘Z6Ò@Ê>æ,õ%¿¶”ºº:‘’’"´ŸéááábùòåâСClg%""úÃþýûqðàA¹C8 „³þÐß´ßiiiÑvíܱXÓ G¡Pt連[ɤÛ9” ÆõôE³®®N¸¸¸hŸß|óÍbݺu]ö{å•WÄ-·ÜÒçóéî·k×.½mÙÙÙÂßß¿Ç×VVV OOO½mO<ñ„xã7ô¶½ð âž{îÑ>¿ùæ›Å‹/¾Øåx[·nK—.•sw¿“’sÇЗãzo¹åƒ×ÿ½÷ÞëÓ¹îg­4ÄÐù¤Ü›‹-|ðÞ>¿ýö›Ñ†ˆÌÌLÚí,999âƒ>“&M^^^âÌ™3&¥e¨hjjÒëd©;UDDD—FÝÏöÎù222´ƒ¹Ì­?dÐæbí!ìÌпuþœsuuzÛ²³³Mêô V«Emm­øñÇÅêÕ«…§§§øüóÏ…Ò?—¥Æa,.cy“f50C±Ës¤æ]}}ïºËóLíôй|ñÛo¿,/™ºoo_oêõÂP™¤¯×૯¾Ò>/((è²-//Ox{{[46c±š» ÅrŠùIY³ó Vƒjfͪ*!!>ûLîH¨Ÿà€Þ1¥¼þå—_ …B!Þ ±zª#é.ÿš8q¢^û½{÷Šk¯½¶×ç´D]–)Ç”2 DÊñÔjµðËö¥ IDATññGŽÑþ~Ú´ibÿþý⪫®Òn;}ú´ðññ鱇”2‹©éìMËÔ²¡¿W^yE¯#ªÔ¸M©ç¢w÷¦©éÓ%õI9þ˜1cDYY™ÁKKKEXXX·éè/¶lÙ" …Þ}ÕŸp¥Óþu#‡u#=ïÛÛ׳n„u#CÁ@ŸTƒBh°kkk‡Ë—/aaa€9r¤HHH)))’Væ°´úúzñÐC OOO1kÖ,ñæ›oŠüü|ƒûJ-Hé es–‡ú’_[ƒæ^Yºt© DHHˆHLL)))¢¹¹YˆduÍ5׈)S¦ÈÆ À!4˜õ‡þ¦Š?~Ù+ÇŽDDD˜üÚªª*dgg£°°EEE]çää ½½`oo///øûûC¥RÁÏÏOûXóÜÏÏ …¢·IÒ º» êêêàë닺º:€¯¯/¾ÿþ{„„„èíwöìY\uÕU(**êÓùt÷«¬¬„R©Ônkoo‡½½=Ôjµ¤×ëžçôéÓX´hÒÓÓµÛÂÂÂðá‡âÒK/í6m%%%˜:u*Nž<Ùë´IM¹c°ÖñŠ‹‹áçç×ës+ Ô××cذaÝÆÓ›Ø:ß›¾¾¾8~ü8üýý{<~vv6¢££±eËL:µÛ}CBB°`Á<öØcðõõ•œÒ×ØØ¨Í å999¨¯¯×î¯T*»ä ºCBB`kkkR }ÉßL¥P(°sçN,\¸Ð"Çß¼y3ûtŒ––”——£ªª EEEÚkÒùg^^Z[[µ¯stt„§§'üýýµ×ÄÏÏJ¥Ro[pp0ìììúšT’ óçfLL °råJDGG›ttË]ÎÎÎFTTüq„……þ¹,5cù¬±óTTTÀËË«ËkºËs¤æ]}}ïºËóŒ¥ÓÐvC勿æf8;;w)/™ºooÏeêõÊÜ÷oMM ÜÜÜjµ¶¶¶]¶ÙÙÙõªÜi©{º·e(–SäUUU¥—gÊO P]]­÷:¥R©—ÊOCBBàêê*OÂ~ù¸è" 3—'êW’’’pã7öúsÞ–.¯kÊæIII9¾.SÊë111°µµÅ¾}û,•yôTGÒ]þµ~ýzüþûïØ¸q#`Μ9¸ï¾û0gΜ^ÓuY¦ÓXÙª7u·ÝvÆŽ‹ÿû¿ÿCAAæÏŸ£GbÚ´iزe F…W_}?ÿü3ÞÿýnÓ%¥Ìbj:{SÆ2µìS\\ Ÿ.ñDFF¢°°Ð¤¸M©çzwošš>SË•Rïé鉒’ØÛÛw9FKK üýýQ^^n4ýÉŒ3 P(ðé§ŸZíœ=Ö‹¢¤¤Dïþvrrê¶<§yÌö ëaÝÈŸÇaÝHÏû²n„u#ÔwÍÍͨ¨¨è¶^ÄPû‚““S—¶CùhoÚ€éßŬÙ^DÔW 8pàRSS±{÷n”””@¥R!66ñññˆŒŒì—eÎêêjìÛ·{÷îž}ûƒ7bøðáÚ}¤– ¤ôº¹ÊC}ɯ噙‰ääd¤¦¦âرcP*•ˆŽŽFll,æÏŸwww¹C$""²ª¯¿þ×\s ¾þúkLžrrr´ÏóòòP\\¬½ D`` fÍš…+VôxŽþA›Ko„lذ)))(**BIII—!žžžðõõ…¯¯/üýýáãダ€x{{# >>>ð÷÷׫¨¥þ¡ógUuu5Ö¬YƒÝ»w£¬¬ —\r ,X€ûî»Ï`'¡îŽeˆÔÏe©q;§)ySO±KÍ»,ùÞ™ÚéÁZûJ}½©×£3©es_)ÛÌ›±÷ÃÜe(–S†êêjm'‚‚”––¢  %%%(,,Dqq1Š‹‹QYY©÷:///øøøÀÏÏsçÎŃ>h€?þX°¨¯œ­sNê×8 ¤w¤–×…P*•xõÕWñ·¿ýÍâq™CoêH4ÊÊÊð—¿ü¹¹¹(..Fll,233{¬4µ¼Ø—º,SŽ)¥œ!õx;wîĦM›ðÅ_ॗ^‚~øa¼óÎ;(//ÇO<©S§âÞ{ïíñ{¦”2‹¹Óih›9Ê>†ê~¤Ö™RvíͽٗôI¹FRooo––ƒ±ªÕj8::öø·Ð_lÙ²>ú(ªªª,ÒnÐÔÔ„{î¹YYY(..Faa¡Þu²µµ…^½ˆ±ú'''³ÇG}ú‘žcgÝHß_ϺÖéÔj5JKKõêEòóóQZZª­)**BQQµ¯³³³ÓÖ‹øûûcåÊ•˜4iRçã€LΟ?„„¤¥¥¡µµQQQ˜;w.æÍ›‡Ñ£GËžIêëë±dɸººbÛ¶mÚí¦äÁ=õ²9ËC}ɯ喕•…Ý»w#%%‡†½½=¢££±}ûvxxxÈ‘ÕLž<nnnØ»w¯Ü¡ hBƒYèojcñ3Ó€·iÓ&ÌŸ?_ûÜËËËà̉EEEðòò²fh€„„¸¸¸àðáÃhjj‚Âh¥îí·ßŽ­[·¶nÝŠ»îºKï÷^^^¨¬¬ÔC÷O1ÌÅÜ1H=žÔ÷ÑËË ÅÅÅ]¶WTTX<-=‘zoŽ1BòŒ‡«W¯Æ'Ÿ|‚;ï¼ßÿ½Ùb%"ꆎW_}YYYÈÎÎÆý÷ß?ü‹-2Ëñ¥~.÷5cy“¡mRŽ%%ï²ä{§P(ÐÜܬ·­sðþ¬¯×Cj™ÄÒ÷¯œ±õ‡2)QΜ9„ÈʆJ'¶‘#G"22~ø!Þzë-,]º´O¾-Q—eîcJ=^LL ~øá466bÇŽÚòÅ‚ ðÑG¡ªª ß}÷bbbz<§”2‹5êM-û*aäÈ‘&ÇmJ=л{³/e;©×HÊñÝÜÜŒvhjkkã¤4d±nÄð±X7Ò7¬aÝõŽ‹‹ ^{í5ìÚµKo»Ô²”~Rö1gyHŽüšˆˆˆÌëÉ'ŸÄ¾}û´¾‰ˆú#«­E¸bÅ ½YÙ«ªª­]Vó8##)))ÈÉÉÑÎ*coo///øûûC¥RigøP©TÚç\EÄ2Þzë-8p@»:DGGã£>Â#<¢·ïÇŒèèhk‡ˆo¿ý~ø¡Þ Æ*ƒããã±fͬ\¹Ÿ~ú)Þxã ½ßϘ1_}õ®»î:½í‡²eËpüøñnc1Ç=Ø×z{<©ïcLL víÚ…eË–ém?pà€ÅÓÒ©÷æ”)S––†%K–h·ýôÓOX´h‘Þ2ã´1ÿú׿pýõ×㫯¾Âرc»œ{¨tʱ†ÆÆFm¾`(ŸÈÉÉA}}½v¥R©Í .¹ä\ýõzùDo— Ê|ðA½ÙÅ[ZZP^^nt)÷ï¾ûÎà’Žðôô4º”»f[ppp¿_y°R(ÈËËC`` ¼¼¼pã7búôé 5Ëñ¥~.KÃX>k,oúì³ÏLŽYjÞeÉ÷Î××¹¹¹zùÍ×_ÝçãZK_¯‡Ô2‰¥ï_kÄfìž6wŠåyUUUéå†òÓ‚‚TWWë½N©TjóÊ€€Lš4©K~WWWy–• °‰2…BË/¿III¸ãŽ;äG’¾Ö‘Üzë­xî¹çP[[‹'Nô霖¨Ë2÷1¥O©Tâ’K.Á›o¾©]¡øs%ÇW_}—]v™¤™:¥”Y¬QhjÙ'--­Ë¬Gü±Þ ©q›RÏ¥aê½Ù—²”k$õø#FŒ@uuµÁ<ÕÕÕ1bDié/vî܉+¯¼ÒbíNNNxÿý÷õ¶566­),,ĉ'´«½©Õj½cu®1T?Âö ù°n¤+ÖôëFX7BjnnFEEE·õ"†ÚœœœôòÊñãÇ#::ºK>Ê6 "ÀÃÃ{öìACC8€ÔÔTüóŸÿÄßÿþw¨T*ÄÆÆ">>‘‘‘ýªÌ©P(pöìÙ.yƒ]—:O©e)ý¤ìcÎòùu_dff"99©©©8vì”J%¢££ñî»ïbþüùpww—;D"""«›5k"""ðâ‹/ru "ê¿D¤§§‹ôôô¾¨––QPP ÒÓÓERR’X·nX¾|¹ˆÂÏÏO( @NNNB¥R‰èèh‘ –/_.6mÚ$RRRDzzº¨®®¶HœƒîmÐØØ(Ξ=+vìØ!¦M›&Æ/N:¥·ÿ©S§D@@€xçwDII‰(--[¶lâôéÓ&¯7ûuÞ>cÆ q×]w‰³gÏŠææfqúôi±dÉ£¯¿ýöÛELLŒ¸ãŽ;ºüîìÙ³b„ "99Y”——‹šš±gÏ víÚÕcÌâÛo¿---â³Ï>&§§¯1ôöxRßǬ¬,$¶nÝ*JKKEEE…عs§¸øâ‹û”–Þ|u~Ô{ó矣Gû÷ïuuuâ§Ÿ~—^z©Ø¸qc·ÇÿðÃŘ1cDqq±Þö«®ºJDFFšÿPÔÔÔ$²²²Ä¡C‡DRR’X»v­Xºt©ö³}øðáÚÏõΟ퉉‰bÕªUbÓ¦Mbÿþý"##CÔÕÕY$NKæo;wî´Øñ7mÚd±cRYY)222´×X“'$$ˆèèh.<<<ô®3¡T*Exx¸^>¾nÝ:±mÛ6íõf^Þw?׈3fˆŒŒ ÑÔÔ$Š‹‹ÅO>ÞbÇ×eJyý«¯¾ …Blٲł™OoêHt577 OOOñä“Oöùœ–¨Ë2å˜Rʦï¹çž...âßÿþ·ÞöÿûßÂÞÞ^<÷Üs=¦ICOes§ÓÐ6SË>_|±Ø¹s§(//ׯ,Ξ=krܦÔsi˜zoö¥l'åI=þìÙ³Å?þh0ÆãÇ‹ØØXIé‘Û¦M›„B¡_ýµÜ¡ÔÜÜ, DFF†Ø¿¿är¢£££ÁrâºuëDRR’¶œØÚÚ*w<Öô;ëFúþzÖ°nd(hhhÐkïY·nXµj•HLL±±±Úö|ƒí>‘‘‘">>^,]ºT¬]»V¯-   ÀâñKý.fÍö""sjkk‡Ë—/aaa€9r¤HHH)))¢©©Iî%¾ýö[QSS#êëëEzzº˜6mšX³fÞ¾RËRúHÙǜ塾ä×Ö ¹W–.]*"EJJŠhnn–;D""¢~!))IØØØˆÌÌL¹C°¬ÙþFdmý¡¿i¿"Ecc£ÈÊÊÒkXÑ­drww7ÚÙ´sÇâ¡Ú˜Ò¹C®£££ð÷÷sæÌï¾û®ÑŠ€ŸþYÌœ9S¸¸¸1sæLñóÏ?›t¾Þìgh{II‰HHHÞÞÞÂÁÁA\xá…ÚŽ(†Îóõ×_ âûï¿7xîS§N‰ ˆáÇ 1iÒ$ñÑGõ˜6!:2þQ£F 1f̱{÷n“ÓÓ—úrMc·nÅ¿£££^¥¿f@RR’HOOUUU²¥£?dÐæbí!R544èu¤Ý´iS¯‹t;Ò¦§§‹‚‚¡V«åNb¿cès3--MÌŸ?_Œ1B888ˆQ£F‰‡~XÔÔÔ˜|,c¤|.KÃX>+„~Þäææ&bccÅ™3g„ÑØÅ/%ï2Ç{gìüeeebñâÅbäÈ‘ÂÅÅEÄÅʼnÜÜÜ.¯1¥|Ñ×}M-ËH½†H-“˜óþ•ºÍܱuwO›« ÅrŠizg« âùç厂úéSËë«V­öööbPˆ”:’îî™ÖÖV* û|N!ÌS—Õ›:SÊVRc<~ü¸puuõõõzÛëêêİaÃŒvúïLj™ÅœéìKÝ•æu™™™"&&F¸ºº 1kÖ,qòäÉ.é“ú~šRÏ%DïîMSËvR¯‘”ã¿òÊ+F?;¶lÙ"Ö¯_/9=rÙ¼y³°³³ëÒ9l â¤ÖǺ‘®±³n„u#¦ncÝÈà§ÉŸ4íï†ò§Î“{u—?i6fddˆÚÚZ¹“§Å!4Ôdddˆµk׊ÈÈH¡P(İaÃDll¬Ø¶m›lm²GŽwÝu—3fŒpppÎÎÎbâĉbÆ ]Úù¤– „è¹€Ô}ÌUêm~mIuuu"%%E$$$h?ÓÃÃÃÅòåËÅ¡C‡ØÎJDDd@{{»·ß~»Ü¡ XBƒYèoªøã—½rìØ1@DDDoaqUUUÚ¥f³³³õggg#77mmmÚý•J%T*•vév•J¥÷<446662¦ˆˆH?O;¦û<íü™:>O­™¿) ìܹ .´Èñ7oÞŒÄÄD‹ÛZZZP^^ntycËÉ;::ÂÓÓS»||çeä5Û‚ƒƒagg'c É’2331gΜ;wNîP¼$/ÝrLaa¡Á|¥  ÕÕÕz¯S*•zùˆ¡|%$$®®®2¥L&--À°aÀŽ@|¼ÜÑP?‘””„o¼}¨f’ÌÒåuMÙÜË~›Z^B`ÕªUxöÙgqçw⥗^ÂðáÃ-¡|’’’ššŠ>ø@îPˆô Ô{óìÙ³¸ýöÛñÕW_uùÝ”)SðÁ 88ØúIpþüy<úè£x÷Ýwñü«V­‚B¡;,«illì±^¤°°%%%P«ÕÚ×999õX/âçç??¿!õ~5ü.Þ¿ðz\š››QQQÑm½HQQQ—v'''ƒyGçŸ>>>°µµ•1…¦“ú]l ô‡ 2Unn.>ýôSìÙ³Ÿþ9ÚÛÛqÅW ..×_=ÆŽ+wˆdyyyØ·o¯;Q¼÷Þ{¸ûî»qêÔ)„††Ê΀cÍö7"këýM}H¥R ¥R‰ñãÇݧªªÊà`‘ÌÌL¤¥¥áܹsÚ†ìܬR©0zôhxxxX+yDDfÑÔÔ„ÂÂÂ.Ÿ…šÇ§NBmm­vÝΑ*• ÑÑÑzÏÙÉžÌÅÁÁþþþð÷÷ï6/::Hë‘Ç£°°çÏŸ×{±Î¾º ]AAApww·dRÿŸ½;«²Nÿ8þFÙ•EÙ„$4S´eZŒ\ ÌJÊQÇÊ S3—ÜrK+s©ÜÊܪýi3#5¥MŠ-fÚ2Z®9š îâ"ˆ²ýþ8ÃPÜ/Ëçu]\œs8Ëçœó<ßû¾åÙØØ0sæLzõê…³³3¿ýö/¼ð 0­ZÒÏCÊCi-f ¡mÛ¶Ub1C¹Ù·òòà¶ÛL'©vlllxõÕWiÖ¬ýû÷gåÊ•Lž<™=zTØ‚ûeccÃ?þÈÔ©Syÿý÷MDZªìÏÍàà`Z´hÁ’%KèÙ³§õò%K–вeË Y ’——Ç’%K=z4yyy|üñÇ<öØc¦c•;'''œœœ¨_¿þU&]«©ÆÆÕT£ŠÓ{ñŠE?)%í/éÿÀµŠ###¯ø@DªžÀÀ@âãã‰'33“¯¾úŠ„„&OžÌ¨Q£'66–˜˜Ú¶m«âáJlûöí$%%‘˜˜Èúõëqrr¢]»v,\¸Î;km“ˆˆÈ êÙ³'¯¾ú*o¿ý6ï¼óŽé8""Åhï=–… ‘‘‘W<˜ráÂ>\â"éäädöìÙSlqiáN´+MiР...åµy"RÍ ¾ÒdÂÓ….ýA||¼õïÙí·ßŽ›››Á-)™“““õÿíÕ\«³æŽ;8rä©©©äååYo§Îš[RR3fÌ`ìØ±Ô¨QƒÐÐPÈSO=e:Zµ¤Ÿ‡ÜŠë]ÌpìØ±b“´˜¡íØ66 ®q"Æ<þøãÜwß}Œ5ŠÞ½{3eÊÆŽK×®]±³³3ï–EEE1`Àš7on:ŠH1•ý¹9mÚ4úõëW¬ díÚµ¼÷Þ{S].''‡eË–1iÒ$vïÞMïÞ½™œþýûóÌ3ÏTÚE‹ýD*’ªðÜ´µµeáÂ…Å.[°`¡4—;räùË_xï½÷HMMåÉ'Ÿdùò優µÔ©©FÕ¤÷â‹~r©K'^iÿˆ&Šˆ)5kÖ$**Ѝ¨(fÍšÅöíÛIHH ))‰ àééItt4111téÒEEÀDVVkÖ¬!))‰åË—süøqBBBˆ‰‰áƒ>Д‘RÖ§OÞxã fÏžÍäÉ“MDZÒ*ÄRr=P®´h{ïÞ½$''[´mggGݺu¯:iDKDª¾¢Åf%ýýØ¿?YYYÖë-6+©àCÅf"×O5ED®M‹ª©­[¡iSÓ)Dä¿5jÄG}Ä”)Sx÷Ýwyûí·?~<;v¤W¯^<ôÐC*š‘ ëܹs|þùç,^¼˜/¿üúôéÀ0¯Ú»Þ¦pyxIM5:ÄÅ‹­·QS © 4éTDªªˆˆ"""˜0aû÷ïgùòå$%%ѧOúöíKTT111tíÚ???Óq«•“'O²råJ’’’X¹r%çÏŸ§yóæ<ÿüótëÖÆ›Ž(""Re9::òâ‹/2yòdFŒ§§§éH""€ BÊ•§§'‘‘‘WºIùùùüòË/Ö";vP§Nzè! D§N4VDDÄZµj1`ÀfÏžÍK/½„“““éH""*©l¯y°¤è"¯K¢'''sðàArrr¬×÷ôô,uÁŸ< IDAT±X¤ð|ƒ t`Dä—þž]ú»véÁ‚Âß³k§lýž‰ÈÕ”FgÍÂ⑚—ÞÿµGøûû«M¤Ðb)W¿ý¹¹Ð´©é$"rêÖ­ËÓO?ÍÓO?]l‘Ƽyó7n®®®´nÝš¶mÛÅ=÷Ü£â>¹eyyyüú믬[·Žï¿ÿž5kÖpúôi¼¼¼èر##FŒà¡‡ÂÅÅÅtT©ÀÔTCDn„&Šˆ˜áèèHtt4ÑÑÑ̘1£XÃìÙ³­ ±±±*`¸E÷Ý$$$pôèQkͬY³T`#""R 8·Þz‹¥K—Ò§OÓqDDTR]ÏÒÂÉ—.hß¾};ÉÉÉìß¿Ÿüü|ìíí©S§N‰Å"…ç===ËkóDÊ\á$ž’~GŽ9ÂÁƒÉÈȰ^ÿÒI<ÑÑÑÅÎë ¡ˆ”%uÖ©ºÊr1Cýúõõ^nÎÖ­àà¡¡¦“ˆÈ ºt‘Æ–-[X»v-k×®eîܹLœ8WWWÚ´iÃÿøGî¹çš7oŽ›››éè"RÁedd°iÓ&¾ûî;Ö®]ËúõëÉÌÌÄÇLJ?þñL˜0{ï½—¦M›bccc:®TAjª!RuiÒ©ˆHåQ£F "##‰ŒŒd„ ìÝ»×ZØÐ­[7xàˆ¥sçÎøúúšŽ\!œ>}š5kÖ˜˜ÈgŸ}FFFáááÄÇÇ{Íâh1ÃÛÛ›?ýéOÌœ9“gŸ}Vû=EÄ8­P®¦<==­oÆKrñâE:T⤑äädöìÙCZZšõú…‹Î®4i$((HݤB¸xñ"'Ož¼âdÂÓ….}nGFF{žß~ûíZ#"•BE鬀]Yo®H…¦Å R©mÙƒþ–‹Tj5jÔàÎ;ïäÎ;ïäÅ_`ïÞ½Önþ|ðãÆ°¾.ühÓ¦ uêÔ1_D ÊÈÈ`óæÍlܸÑú±sçNòóó©W¯QQQLš4‰¨¨(Z´h¡¡R¡\oSììlNŸ>}KM5®´_DM5D4éTD¤º aРA 4ˆ“'O²råJ’’’:t(ýúõ£yóæÄÄÄЭ[77nl:n¹Ú¿?Ë—/'))‰o¿ý–š5kÅk¯½F×®]ñóó3QDDD®ÃСC¹ãŽ;X½z5íÛ·7GDª9„H‰ìíí¯y`¤°›VIS6nÜÈ8wîœõú—NQ¸´x$((H;hå–]iúMáù””ëAºK§ß\:Ù£ð´ˆHu£Îš"7N‹¤ÚøùghÙÒt )…ûjzõê@JJ 7ndÓ¦MlÚ´‰¹sç’ššŠ 6$22’-ZдiS ÔÂo‘*¤  €””vîÜÉ–-[Ø´i7ndÏž=àããCdd$=ö-Z´ 22’ÀÀ@Ó±EJEaaÇ­6Õ(œÈ®¦R]\m_aÑý#št*"RýÔ­[—^½zÑ«W/²²²X³f IIIÌ›7‰'BLL qqq´mÛ¶Jî_ؾ}; $%%±qãF<==‰ŽŽæý÷ß§K—.ÔªUËtD¹AMš4¡]»v̘1C!"bœ Bä¦]O7­¢CŠ.Ìß»w/ÉÉÉÅçÛÙÙQ·nÝ«N©W¯^•|ó/×§hRI“=öïßOVV–õúžžžÖçNDD±±±Åž[*B¹5ê¬)Õ3ˆ‘Ÿ¿üݺ™N""å ((ˆ   {ì1ëe‡*V$2sæLŽ9€««+7&<<œ°°0ˆˆˆ 88Xï½E*°¼¼<öîÝËŽ;øí·ßرc;vì`çÎdffàççG‹-èÑ£‡µøCkE,ÔTCªƒë™tzðàA222¬·)lÈUô9[Ò¤S>‰ˆT?ÎÎÎÄÆÆË{ï½Ç† HJJâÓO?eöìÙxyyѱcGâââhß¾=¦#ß”¼¼<6lØÀÇ ä,Y‚ӧñ ¢C‡¼òÊ+tèÐAÓ¾EDDª€ÁƒÓ¹sgvìØAxx¸é8"R© DÊÔµ†äääpâĉ'|ÿý÷—-®sppÀÏÏFBCCÕ9¡’ºpᇾâdßÿôôtëõ Y> ;i>‚ƒƒqvv6¸E""R¨4;k®[·î²)dê¬)7C‹DnÂþgÏB«V¦“ˆˆ!þþþøûûóÈ#X/;sæŒuùo¿ýÆöíÛùöÛoIII,¯Õ7nL£F á¶Ûn³¾—P±ˆH9ÈËËãàÁƒìÝ»—={öX÷»íܹ“ÿüç?\¸p ã¾ûîãùçŸ'<<œððp<<}šX÷·-þHII!''777ëï[§Nx饗§qãÆ¸ººÞ QS ¹Ue9éÔÏϯÂ/Ä‘Ê-""‚ˆˆFŽÉø×¿þEbb"Ï=÷½{÷¦uëÖÄÆÆòØcj:.@±œ«V­"//Ö­[óòË/ÿ/ç8 :At4LŸM›šŽ."""¥ÀÆÆ†2lØ0&MšDݺuMG‘jJ!Rá9::^³sVÑ—$''sðàAëAO°,V(©X¤ð|ƒ Ô%ë\úý¿ôgpé¢ËÂïI“=êׯOpp0666·HDD*ºÒê¬Y8‘L5+¶ÒZ̢Š"·â矡Y3KQˆˆÈ5¸ººÒ²eKZ¶lyÙ׊6(üؽ{7_~ù%)))Ö×e%ý/¿ôt½zõ´Aª¼Â××îs»ÒdÝ¢ûÞüñbEWÚï&Ru¨©Fõ¢I§""RÝO|||±É“'OfÔ¨Q„‡‡KLL mÛ¶-×÷9E'™¬_¿Þ:ÉdáÂ…%O2 „Å‹¡2Z´€Þ½áµ×ÀÛ»Ür‹ˆˆHÙxúé§?~<óçÏg̘1¦ãˆH5¥‚©®çÀGábƒK ¶oßNrr2û÷ï'??øßNò«M©.]* '´\i²Ç¥Æ ~¯¢££‹ ÄÖVzDD¤|¨³fŦŠ"•ÌÏ?C«V¦SˆHàééIddd‰¯Ï.\¸À¾}û8pà‡¶~>|ø0Ÿþ9‡"--Íz}'''ñõõ¥~ýúxyyáíí]ìt½zõðòòÂÑѱ<7S䪲³³9qâGŽáĉ¤¦¦rôèQRSS­—=z”ƒrþüyëí<<<ð÷÷'00???Zµje=@pp0*Þ‘"J«©ÆÆIJJ*V¼ Å 8Cœ°/r¼Ô_‹ˆˆ\­Ê–jãj‹ .^¼ÈÉ“'Kœr‘œœÌÞ½{9sæŒõú…9®4i$((WW×òÚ¼›rµm.zºÐ¥ÛYé¶YDDäJÔY³th1ƒH•“[¶@ß¾¦“ˆHçàà@ãÆiܸñ¯“™™É8tèµhäèÑ£;vŒÿûßÖ…õ™™™Ånçîîn-ñññÁ××—Úµk[?êÔ©Sì|íÚµõºC®K~~>§OŸ¶~œ:uªØùÂ×Ç©©©ÖççÙ³g‹Ý‡‹‹ õêÕÃÇÇ///ÂÃÃi×®AAAøùùáççG`` ...†¶RDªº[iª‘½u+Í?ÿœ;wÒïÄ rr.kªQ´WUoªq³“N/Ýw¤I§"""—«Y³&QQQDEE1kÖ,¶oßNBB‰«Y°jžéžDGGC—.]¨U«ÖM=NVVkÖ¬!))‰åË—süøqBBBˆ‰‰áƒ>¸ù©$66=ï¾ ¯¿|“&Y.‘JiàÀLŸ>e˖ѳgOÓqD¤RAˆÈÙÛÛ_ó`GáNü’¦elܸñšÓ2.-)ëiWšŠRxþjSQ.ìQxZDD¤º+ÏΚW+)ïÂ-f©æ~ùΟ‡»ï6DD »êõ²²²8~ü8ÇŽãĉ;vŒãÇ“ššÊ±cÇØ¼ys±û.\¸ì><==/+©S§µjÕÂÍÍ OOOÜÜÜŠ}xxxX¿®I •Ã… ÈÈÈàìÙ³¤¥¥‘‘‘QìãÌ™3dddžž~Y¡Ç©S§Š5‘)T8M°ðùãããÃwÞ‰——¾¾¾ÖÂÂÓÎÎζ\DäæX›j¸¸@B|ø!À¼y,èÝþ{Ü£,›j˜Ø—pµé²…ûG4éTDD¤|EDDãGöåïãåy/³êÓUôéÓ‡¾}ûELL ]»vÅÏÏïª÷uòäIV®\IRR+W®äüùó4oޜ矞nݺ]µyÅ sq‘#¡G=ž|æÎ…éÓáÎ;KïqDDD¤\øùùѵkWf̘¡‚1B!"7àz€= P´cïÞ½Ôÿ䎜9ÃSÿ-ÂË’ŠE Ï׫W¯ÄÎE‹SJ*ú8xð 9·çÀyÿ{œÂƒ ±±±Å³Aƒêz)""RŠn¥³fÑÏÛ·ogݺuÅ O›‚ý {êØÔ¹êâˆëYPp=‹\RRØ™™Iár-f©†¾ûêÖ…k,¾©Hœ &88øº®Ÿ™™yÙ„‡K'=œ:uŠƒröìYk¡À¹sçÈÉÉ)ñ>íííqssÃÝÝwwwk‘ˆ‡‡vvv¸ººâää„££#nnnØÚÚâé鉭­-nnn8::âääTìk€õë—ž®j222¬SåΞ=k-¤>sæ 999œ;wŽóçÏ“m½î™3gÈÍÍ%##ƒììlΟ?oý¥¥¥Y‹?ÒÓÓIOO'##ƒ‹/–øøvvv—ù6TiÒ¤‰µà£¤é2šä!"UÚþý0yòÿ AæÌ"… …*CSÒštZÒ~M:)Ç8Æd&3¬Æ0†÷ÎðþÃ9}ú4kÖ¬!11‘qãÆ1xð`ÂÃÉ‹‹#66ÖzÜfïÞ½$&&’À† pppà`úôétîÜ__ß² ïï‹ÃÀ0t(DFZŠD¦Mƒ²~l)UC† á®»îbíÚµÜsÏ=¦ãˆH5£‚‘RfíŒQü @DùcÆð§gŸåàÁƒ¤¤¤pèÐ!<ÈøöÛo9tè§N²ÞÌÙÙ™   üýý8tè)))deeY¯S§Nüýý ¤Q£FDGGãïïÏÖ»¶òVè[üðδ´mY.Û/"""7.qòäI§¦sPgŽðÈ'päÈŽ?NJJ ?ÿü3‡.Ö‰ÒÆÆooo|||¬Ý¯>líŽ]tš‡››~~~x{{ãççG«V­ðñ¡ß´iœzàÒßxêÖ­[6ß©¸Ö­ƒ¨((¡X]D¤ªpqqÁÅÅ…€€€¾maABÑiE?ÒÒÒ¬E$.\àÌ™3œ?žÔÔÔb …E …Å7ª°È,Å(…E —M ¨Q£®®®äååaoo_âý]O±IÑ¢+IOO·N©-”™™i-Ä(zúj6WãêêŠÝU j‚ƒƒqppÀÍÍZµjáááqÙ„—¢S_o8‡ˆH•v… 7êz›jäæærüøqŽ=ʱcÇ8zô(Gåøñã>|˜­[·’à›@Æî rûþƒÞÞÞøûûãååEZZ©©©×܇âííÍwܵ—¯¯/õë××d'‘ d#pÇ—xÉzYíÚµ‰‹‹#..ŽóçϳzõjV¬XÁܹs™8q"¡¡¡ìÞ½ooobcc1b>ø NNNå¿­ZÁÚµðñÇ–É! °a–Ó&òˆˆˆÈ kÕªwß}7óæÍSAˆˆ”;„ˆ”—¡~}jŒM£#AAADEE•xÕ¬¬¬ËŠE8@›6m $ ‚‚‚®xà¡€Ö²–A¶ƒXÇ:lÐâ-‘ʬnݺ,©»„c#9$™Ðá¡%^/++‹#GŽpìØ1Ž;f-9räÍ›7ÇÇLJúõëãëë{íÅ ø÷ì‰ÿÀp¢©‚ `ýz1Ât‘ ËÑÑGGG¼¼¼Jõ~ ‹EΞ=Knn.iii\¼x‘ÌÌL.\¸`mR8ãÒÓ…÷STvv6ßÿ=;vì {÷î%v/ÏÊÊâÂ… WÍX·nÝk.Š-œ‚RTa±Æ¥_wvvÆÁÁá²Ó...ÖÂlmm©U«V±û‘2RF… 7ÊÖÖ???kËKPÀmÜF<ñ 5¼XÑHáþ‘ÔÔTüýý/+ò¨W¯>>>Ø–ó6‰ˆˆÈ­ùX–± gJ~oêääDçÎéܹ3ùùùlذ+Vйsgþð‡?TŒ‰^66110{6¼ñ|ðLšþ³šõˆˆˆTñññôë×'N”úñ‘«Ñ^M‘ò°l$&Br2\GgAggg »¥‡µÁ†ù̧-XÄ"žæé[º?1ë4§y×y‰—¥äb°¼–hذ! 6,îÞÝÒ•*>¶lÿv‘jbçNHM…?þÑt‘jÇÉÉ '''<==Ký¾ÓÓÓ fäÈ‘Œ?¾Ôï_DDª€¢… þþÆ A®×Ö°}<ÅSÔ©S‡:uê\s«ˆˆˆT^0˜Á´¥-óøuݦF´mÛ–¶mÛ–qº[àäd™ Ò«L˜`yýõî»0cTäÜ"""Ÿþô'† ÆâÅ‹y饗®}‘RRJÜEª¸³gaèPxöYh׮ܾ)MéG?†1ŒSœ*÷Ç‘Ò3†1ØbË(F•ÿƒÏ™éé0n\ù?¶ˆ˜µn8;ÃwšN"""¥húôéðâ‹/šŽ"""Í¡C0h4n «VYö ìÞmiQA‹A±ˆÖ´&Œ[k¶%"""•Ã"ñoþÍ»¼‹ UpzF½z0>üü3¸¸Xö<ñ„¥hWDDD*$'''ºwïÎܹs)((0GDª„ˆ”µ# 7¦M3áu^Ç{Æ£n"""•Õv¶ó>ï3•©Ô¢Vù¨WÞ~Û2¦ü»ïÊÿñEÄœuë uk°·7DDDJIZZ³gÏfذaxxx˜Ž#""ʼn0d„†Âòå0wn¥(8ËYþÉ?yЧLG‘rAcC<ñ4£™é8e«ysøúkËë³_~ðp5ÊÒœTDDD*œ¾}û²gϾùæÓQD¤QAˆHYúáX¸fÎOOc1jQ‹©Leóø‰ŸŒå‘›7„!4£æÏæB<ý4tè}úÀùóærˆHùúö[¸çÓ)DD¤½õÖ[Ô¬YSÓADDÄâÜ9˜:6„¥KaÂعžy¦Â‚ZÆ2òÉçIž4EDDDÊÁ$&qžó¼Ê«¦£”ŸØXر&O¶L ƒ /Ït2)¢iÓ¦üá`þüù¦£ˆH5¢‚‘²rñ"<û,´oݺ™NCOzr/÷2€ä“o:ŽˆˆˆÜ€ù˜d’™ÉLj˜~ ÿþû𠝼b6‡ˆ”߇”xàÓIDD¤”œ:uŠÙ³g3|øpÜÜÜLÇ“²²`Ö,¸í6Ë”ó!C`Ï9M§»!å¯t¡ ž˜kÎ%"""åc{˜ÉL&0ºÔ5§|ÙÙÁ A–×l]»Â€p×]–¦>"""RaôíÛ—O?ý”ÔÔTÓQD¤šPAˆHY™6Ͳpjî\ÓI°Á†wx‡Ílæ}Þ7GDDD®S6ÙŒ`=èAQ¦ã@ýú–®¡o¿ ë×›N#"e-9\\,ED¤JxóÍ7qpp ÿþ¦£ˆˆˆ)99–nÒ ˜1–I {öX&ƒTÂbÁÝìf=ëyš§MG‘r0”¡„B?ú™ŽbNíÚ–ÂÞmÛ,Çmî»Ï2AdÏÓÉDDDxâ‰'puuå¯ý«é("RM¨ D¤,ìßo¼ãÇCƒ¦ÓXEÁ@ò2/s‚¦ãˆˆˆÈu˜ÍlRIe SLGùŸçžƒèhË4´ìlÓiD¤,­Y÷Þ öö¦“ˆˆH)8yò$ï½÷£FÒt‘ê(? , ´,üýw˜2<£GÆÉÉÉt)KGŽ@ß¾pç°?$&¿þÍš™NVêŽr”/ø‚çxÎt)c£-¶¼ŒŽE\·† -Á_}§NAóæ–׉©©¦“‰ˆˆTqqqÔªU‹%K–˜Ž""Uœ BDJËÁƒ0y2Œ AA¦Ó\S(¡ a£ÍQT*""R‘lf3ãoLa öØ›ŽsuõëÃ;ïÀœ9–&"R9„•“cù|ö,,_n™äçg)‘Jå7ÞÀÓÓ“gŸ}Öt)+™™0u*4n +WZÞ§oÞlé]E½ÏûxàÁ#ø€±cÇj:ˆˆHU”“ X:@O cÆÀ®]5kšNWfòÉçC>äižÆÓqDDD¤ f0wr'=éi:JåU£ôêe™ ýâ‹0q"4m ¦“‰ˆˆTy=zô`óæÍlݺÕt©ÂT"R’“áŸÿ„Y³À¡òxpÆ™·y›Å,æ¾1GDDD€/ù’Õ¬f2“±ÁÆtœë7gäåÁ!¦“ˆÈͺÚ{™š5ÁÓþú×r‹#""¥ãõ×_ÇËˋ޽{›Ž"""¥-1""à… sgعFŽ„jP¸ŠUìg?½Ñÿ7‘ªlËø–o™Ãjh‰Ó­sq ,Ä­[ÓOB»vð믦“‰ˆˆTYQQQ³dÉÓQD¤ Ó»%‘[uñ" > =d:Í {ŒÇèD'^àrÈ1GDD¤ZË'ŸÑŒ&–XÚÑÎtœS·®¥#é¢EðÉ'¦ÓˆÈÍpv¾ò×òó\'q IDATáo³ü®‹ˆH¥qàÀ>üðCÆŽ‹C%jb"""×ðãpÏ=ðÈ#pçðÛo0>x{›NVn²{¹—0ÂLG‘2ržóŒd$½èÅÝÜm:NÕâï‹[^W^¼‘‘– "ÇŽ™N&""R娨ØÐ½{w–,YB^^žé8"RE© DäV½ó¤¤Àô馓ܴYÌb{x—wMG©Ö>â#6³™ÉL6åæÄÆÂ3Ï@¿~pü¸é4"r£\\J¾¼fM7ÎÒ)NDD*•×____žzê)ÓQDD¤48O7lh™ ’m:™ˆˆH•Ò³gOŽ9·ß~k:ŠˆTQ*¹§OÃoÀK/Aƒ¦ÓÜ´†4d8Ãy…W8ÌaÓqDDDª¥\r™ÈDþÌŸ‰ Âtœ›7s¦eÊ@ß¾¦“ˆÈ*© ÄΚ5ƒ±cË?ˆˆÜ’””-ZÄøñã±··7GDDnÅùó0q"„…Á¯¿ÂŠðÕWв¥édFü…¿à‚ ò¨é("""RFqˆ7y“1Œ¡>õMÇ©Úll .vì°4š1BC-D L§©7nLË–-Y²d‰é("RE© DäVŒoéÂ5|¸é$·ìe^Æ /†Sù·EDD¤2ZÄ"RHaãLG¹5µjÁ‡Z§hg†HåâæVü¼ 88À'ŸX CDD¤RyõÕW  W¯^¦£ˆˆÈ­HL„ðpxóM˱ˆ­[-:«©|òYÈBžâ)œq6GDDDÊÈp†ãƒƒl:Jõáä#GÂÎðÐCл·e2Ý÷ß›N&""R%ôèуO>ù„¬¬,ÓQD¤ RAˆÈÍúÏ`Á˜4ɲð±’s‰9Ìáoü¯øÊt‘j%‡&1‰gx†BLǹuíÚÁ Að °¿é4"r½.}_SP Vêiˆ""ÕÕž={X¼x1ãÇÇÖÖÖt¹;wBÇŽðÈ#ЪüöL˜`)Ú®ÆV²’}ì£ýLG‘2²žõüƒ0é8âh:NõS¯ÌŸ?ýd)ùãá‰'t¼GDDäuïÞ¬¬,MG‘*H!"7kØ0Ëxögž1¤Ôt¤#±ÄÒ~\à‚é8"""ÕÆû¼Ïaó2/›ŽRz¦L±,"ïÙòòL§‘ëQ«–e*X&!ÆÇC·nf3‰ˆÈMyõÕW ¦{÷ˆˆÈ:sÆÒd¡iS8yÖ­ƒeË Àt² asxiD#ÓQDDD¤ ä“Ï ÑŽv<Â#¦ãTo-ZÀ7ßÀòå°iDDÀ¨Q‘a:™ˆˆH¥äííMtt4K—.5EDª „ˆÜŒ¯¿†¤$ˈöš5M§)Uïò.‡9Ì,f™Ž"""R-\àoðñÄÓ€¦ã”XºÔrà7L§‘ëáâ5jXŠA‚ƒaÆ Ó‰DDä&ìÞ½›>úˆ &h:ˆˆHe’Ÿ‹C£FsæXº2·ic:Y…±‡=¬b`:Šˆˆˆ”‘ø€_ù•hßd…k™V÷Æ–É!Âj&""rzôèÁ_|ÁñãÇMG‘*F!"7*?ß2$&Ú·7¦ÔÈHF2‘‰¤b:ŽˆˆH•7Ÿùœä$£e:J鋈€É“áÕWaÃÓiDäZ\]-ñllà“OÀÙÙt"¹ 'N$$$„'Ÿ|Òt¹^ß|Í›CŸ>ð§?ÁΖ‰}5t¯¨9ÌÁæaÓQDDD¤ œå,ãOúÓ”¦¦ãHQvv–)v{ö@×®0`Üu¬]k:™ˆˆH¥òè£âèèHBB‚é("RŨEœÈúðCزÅÒ©«ŠÉH–²”a #½ø)+Ùd3iô§?~ø™ŽS6^|V¯†ž=á—_ V-Ó‰DŒÊÌÌ$++‹ŒŒ .\¸@VVçÏŸ';;›sçΑ““Czz:yyy¤¥¥‘››KFFyyyœ={ö²û+¼’çâÅ‹Öó®®®ØÙÙ]v=lllxô§Ÿèü½uk~]º”5jàî®®8::âä䄳³3¸¹¹akk‹‡‡5kÖÄÝÝýŠ#""åc×®]üýïgéÒ¥Ô¬bSmEDª¤C‡`ÈøøcKª?†ÐPÓ©*¤,²XÄ"F0‚šèœˆˆHU4 \ä"ão:Š\IíÚ0kôëÇý÷Z^ÇΜ ·Ýf:ˆˆH…çââ£>ÊÒ¥Kyá…LÇ‘*D!"7âÜ9?žÞÒñºŠ²Çžwx‡ö´g%+yˆ‡LG©’Þå]ÒHc#LG);66–‚Ú;î€Áƒ-§E*¹3gÎpüøqNœ8Á©S§HKK#==ôôôb§ÓÓÓ9sæL±Ërrr®yÿîîîÔ¨QOOOjÖ¬I­ÿR-Þ(TôëEÕ®]ç">ÒÒÒ((((v¼¼<öíÛg=ÿ Y¼d%'[‹Prrr8wîÙÙÙœ?þšù]\\pwwÇÝÝëiwww<==‹]æááA:u¨W¯^^^899]óþEDäÊÆOãÆ‰‹‹3EDD®&7Þy^y|}aåJèÔÉtª m)KÉ$“gxÆt)¿ó;s˜ÃLfR‡:¦ãȵ4n ‰‰œ C‡BX˜¥HäÕWÁÝÝt:‘ íÉ'Ÿ$66–C‡áïïo:ŽˆT*¹S§Âùó–ƒ4U܃<Èc<Æ ÑŽv8âh:’ˆˆH•’I&oò&ˆ>¦ã”-ooøË_àᇡ}{èÖÍt"‘ˤ¦¦røðaŽ=ʉ'8qâÇŽ³ž.zyÑÉÎÎÎÖ"‡¢Å·Ýv—}ÍÅÅWWWpvv¶Nܨ(Ó5»Æ× §ddd››KZZùùù¤¥¥qîÜ¹Ë c OïÝ»·XLᔢ\]]ñññÁÇÇ///¼½½­§½¼¼¨W¯>>>âââRvß‘Jhûöí$$$°lÙ2jÔ¨a:Žˆˆ\ɦM–¦S¿þjY<7a8jÿûµÌcÝè†7Þ¦£ˆˆˆHx‘ %”çxÎt¹ÑÑ–×·~ãÆÁÒ¥–Ï€­–¤‰ˆˆ”$::777>ûì3M ‘R£Wß"×ëða˜>&N„ºuM§)3™I8á¼ÍÛŒaŒé8"""UÊlf“M6Ãf:JùèÔɲà¥_?hÓM'’j$==ƒràÀ:d=}ðàAëùììlëõ­Å^^^øøøÐ¬Y3¼½½­E ¾¾¾xyyQ·nÝ QÄQž\\\pqqÁÓÓó–ï+33ó²â›cÇŽ‘ššÊ‰'Ø»w/?üðƒõkyyyÖÛzzz@`` øûû@PPþþþøùùáààpËED*‹W^y…ððp}ôQÓQDD¤$ii–fSsæ@TlÞlé¤,×´Žulbó˜g:Šˆˆˆ”$’ø‚/øš¯±Õ2¦ÊÇÖâãá‰'`Ê9æÍƒ·Þ²4 ‘bèØ±£ BD¤Té”Èõzí5K!ÈÀ¦“”›ËX&2‘ît'˜`Ó‘DDDª„L2™Ît^äÅê5ú|útX·zö„¯¿†š5M'’*äСCìÞ½›ßÿ½Øç””222¬×sww·„††rÿý÷+ ¨_¿>nnn·¤z),.iРÁ5¯[PP`-¹´ gûöí|ùå—>|˜ .XoS¯^=‚‚‚ µ~4lØÐÐPÜÝÝËpËDDÊ×¶mÛøôÓOùä“O4DD¤"JL„þý!'ÇÒ=ùÏÓ©*9Ì¡-hE+ÓQDDD¤”]ä"ÃFWºr÷™Ž#·ÂÃÃRòì³0f ÄÄX&ˆÌ˜Mš˜N'""R¡téÒ…^½zqòäIêV“æä"R¶T"r=öüæÎ…jÖavCXÄ"3˜å,7GDD¤JXÈB²ÈâE^4¥|9:¢Eкµ¥3ÔÈ‘¦I%súôi¶mÛÆ®]».+ü8þ<nnnÖEÿ<ò 4°NŽ T±G%fccƒ··7ÞÞÞÜqÇW¼Þ±cǬÅ"`ïÞ½ìÞ½›~øýû÷“““€——×e…"5"<<{{ûòÚ,‘R1nÜ8î¼óNyäÓQDD¤¨={`ÀXµÊÒaÆ ¨SC”‚cãŸü“ùÌ7EDDDÊÀ,f‘B ÿâ_¦£Hi …eË,Á†…æÍ¡woxýuðò2NDD¤Bˆ‰‰¡fÍš¬\¹’^½z™Ž#"U€ BD®Ç¸qÕðŸ¯=öÌc÷qI$CŒéH"""•Z9Ìd&ñÄãE5Üñݼ9Lšdé ‘‘¦I”““î]»Ø±cÛ·ogãÆìرƒ}ûöQPP€ƒƒ~~~„‡‡Ó©S' @HH!!!c£N»Õš¯¯/¾¾¾´juy÷àÜÜ\k‘HÑ>úˆ]»v‘››‹­­-„‡‡IDDááá„……©ë¾ˆTH›6mbùòå¬X±BÿED*ŠœË”Ì àöÛáûïá0ªRšÇ<Üq§ÝLG‘R–J*“˜Äp†Ó€¦ãHi»ÿ~ظ–,±4KH°|<¸Ú5b¹”››÷ß?Ÿ}ö™ BD¤T¨ DäZ¶o‡>²|ØVÏ_™{¸‡'y’xv´ÃgÓ‘DDD*­%,áGÂÓQÌ:þõ/èÖÍr0 V-ӉĠ3gÎðã?òË/¿°yóf¶mÛÆþórss±··',,Œ&MšÐ·o_š6mJ“&M0[*)[[[kñÐ¥.^¼È®]»Ø¶m[¶laëÖ­,Z´ˆýû÷àîîN“&MhÚ´)Íš5£eË–4kÖ ;;»rÞ ‘â^yåZ´hÁÃ?l:Šˆˆ|û-ôë))–o£Gƒ&ÐÝ” \`óèG?q4GDDDJÙ˼Œ+®ŒDÓÄ«¬5,WÞ|&N„>°4‹‹3NDDĨ.]º0tèP233qqq1GD*¹ê¹º]äFŒÕþÍè fИÆLe*™h:ŽˆˆH¥”O>oñæÏh:Ž95jÀÒ¥–i!Ï=ÿø‡éDRNrrrزe ?üð?ýô?þø#»ví¢  €   š6mJll,cÇŽ¥I“&4jÔH‹í¥ÜØÛÛÓ¤Iš4iB·nÿë>œžžÎ¶mÛØºu+[¶laÛ¶müío#=='''Z´hÁ]wÝÅÝwßMëÖ­ 2¸"RÝlܸ‘Ï?ÿœÏ?ÿ\ÓADDL;~†·t@~øaøâ Ëäq¹i‹YLiô£Ÿé("""RÊ6±‰¿òW–°´²Êsq±LÏëÓÇR0ýä“0ožeª^³f¦Ó‰ˆˆÑ¥Kú÷ïÏêÕ«éÒ¥‹é8"RÉ© Däjþýoøì3X±Â²p±óÅ—ñŒg4£éAnçvÓ‘DDD*Où”ì$ÓQÌóñ±…<ø ,Xññ¦IHMMeíÚµlذü‘M›6qþüyÜÝÝiÕª]»våî»ïæî»ïÆÛÛÛt\‘¹»»Ó¶m[Ú¶mk½¬  €;wZ ›¾ùæÞyçrssñõõµˆ´iÓ†?üá888Ü©ÊÆGëÖ­éÔ©“é(""ÕWAüßÿY¦a::²eе«éT•^¼Ë»t§;¾øšŽ#"""¥¨€3˜»¹›nt»ö ¤êð÷‡Å‹aà@2Z´€=`Ú4ðÕk>©^|||hݺ5Ÿ}ö™ BDä–© DäjF†V­,ݼ„y‘Å,f ù’/MÇ©t¦0…ÇxŒpÂMG©î¿F‚Aƒà®»àÎ;M'’[”™™É† HNN&99™_~ù5jDdd$qqqDEEѼysjTó‚k©Ülll #,,Œ§žz øßœuëÖ±qãF–.]ʘ1c¬SD¢¢¢ˆŽŽæÞ{ïÕä)6là‹/¾`ÕªU¦£ˆˆT_[¶ÀóÏÃÏ?Cÿþ0i¸ºšNU%|É—la ‹Yl:Šˆˆˆ”²øˆïùžø4í²ZjÕ ¾û>þFŒ€† aØ0Ë1#GGÓéDDDÊM—.]˜2e ¹¹¹ØÚj9·ˆÜ<ý¹’テի᫯ÀF;!l±å]ÞåîáS>åQ5IDD¤ÒXÍjþÍ¿™Ë\ÓQ*–W_… à‰'`ãFps3Hn@nn.›7o¶€¬]»–‹/Btt4#GŽäÁÄÃÃÃtT‘2gggGdd$‘‘‘ÖËŽ=ʺuëHNNæ£>bêÔ©¸ººÒºuk¢££‰ŽŽ¦E‹Øè=§ˆÜ„W^y…¶mÛòàƒšŽ""Rýœ?¯¼3fÀÝwæMд©éTUÊ fð ÒŒf¦£ˆˆˆH)Ê"‹ÑŒ¦7½iE+ÓqÄ$ˆ‹ƒ˜˜=ÛR\ýá‡ðúëðç?kŽˆˆT >ú(#FŒ`íÚµ´k×Ît©ÄÔ’UäJÆŽ…´t®«(¢èIO1ˆL2MÇ©4&3™y–´4¥b©QÃ2ü̈7F®CZZ ôêÕ‹ÚµkÓ²eK,X@HH .äСCìÙ³‡ùóç§b©ÖêÕ«G\\óçÏçÀìÙ³‡·ß~OOO¦M›FË–- ¢oß¾$&&’““c:²ˆTëׯgõêÕ¼öÚk¦£ˆˆT?_ wÜ œ9–æR*)UÛÙÎjV3„!¦£ˆˆˆH){ƒ78Ã^åUÓQ¤¢pr‚‘#açNèÔ z÷†Ö­aýzÓÉDDDÊ\Æ iÒ¤ Ÿ}ö™é("RÉ© D¤$Ÿk×Z:VËeÞäM2È`“LG©~â'¾æk^æeÓQ*&??KQȲe𗿘N#%8räsçÎ¥}ûöx{{Ó³gORSSyë­·HII±€ôêÕ ???ÓqE*¬âããY¶l©©©üôÓO<õÔS¬_¿žÎ;S¿~}ž}öYÉÎÎ6WD*°qãÆÅýjd""R~ÒÓaÐ ˆŽ†F`ËKcu..u3˜ÁíÜN:˜Ž""""¥è˜Á ^áêQÏt©hêׇùóá§Ÿ,E"QQ–éòû÷›N&""R¦ºtéŠ+LÇ‘JN!"—*(€qã KK×¹Œ>¼Ê«¼ÍÛüÆo¦ãˆˆˆTx“™Ì]ÜÅýhÁÞuêÆAÿþ–E5bÜÉ“'™1cmÚ´! €áÇãîîÎ_ÿúWRSSù׿þE||<¦£ŠTJ5kÖ¤U«V¼öÚklݺ•]»v1|øpvìØA—.]ðòòâ‰'ž`ùòåäææšŽ+"Ⱥuëøê«¯xýõ×MG©>’’,S@þñK#ƒ¤$0ªJJ%•¥,e(C©¡Ã˜"""UʆPŸú¼À ¦£HEÖ¢|ó ,_›6ADŒ¦“‰ˆˆ”‰N:‘’’Âo¿i¦ˆÜ<íI¹Tb"üú+Lœh:I…ÖŸþDÁ@šŽ"""R¡íb+X¡é ×cÒ$ˆŒ„îÝ!+Ëtšj)??ŸU«Vñä“OâççÇ„ hÔ¨Ÿ~ú)'Nœ !!îÝ»ãîîn:ªH•ʈ#ذadÚ´iœ:uŠÇ{ŒÀÀ@^~ùevïÞm:¦ˆTcÇŽ%::š{ï½×t‘ªïøqèÕ bc- ¤¶m³œ—2óïáŒ3=éi:Šˆˆˆ”¢¯ùšòOf1 ‡ÿgï¾ãkº?Ž¿nÄŠ±G#”P5c«Uj–RU«4MKŒ¦5*Ô¡F•Ú´U³ÚÚA‹ZEk·DäkEe *Väþþ¸m~Ò(!‘Onò~öqí=÷Üs^÷T¿ßœÜó92šÎ{Тœ< cÆÀgŸÁsÏÁœ9pÿ¾é2‘$åååEŽ9Ø´i“é±c"òoãÇCË–ðüó¦KR´t¤c6³ÙÎv–³ÜtŽˆˆHŠ5é¦0-ha:%åst„¯¾‚Ë—¡·&§K—.1~üx<<â#p`ÃL§HjÒ°!üò LŸË–Añâ¶D¢£M—‰ˆˆ<777<==Ù´i“é±S"òQ£ÀË ^|Ñt‰Ýy›·©D%|ñÅŠÕtŽˆˆHŠð=ßsšÓøâk:Å>eÈkÖÀµkй3Xõ3Fb„††Ò¥Kj×®Mtt4?üðßÿ=µk×6–"øøøðÒK/1iÒ¤8Ë;F³fÍÈš5+Y³f¥I“&;v,Î:‹‹ÅÂÉ“'©V­NNNÔ®]›   Ž?Ž——ÎÎÎÔ«WsçÎÅÛ÷±cÇhÚ´iì>š6múÐ}<¸/‹Å‚··÷u>Ìš5kèܹóC_ûöÛo©Y³&™2eÂÝÝ???nükpÖõë×ñóó£X±bdÊ”‰üùóóæ›o²ÿ~BBBâ4ñ×_ÅYöà:çÏŸ§U«VdÍš•¼yóÒ©S'"""âµ>|˜—^z‰,Y²-[6^~ùå4ûËÑL™2áããÃÉ“'™>}:kÖ¬¡xñâøûûs÷î]Óy"’„Ö­[ÇÞ½{ùè£L§ˆˆ¤.÷îÙnHP©ܹ{÷¸q1£é²4g<ãiCÊPÆtŠˆˆˆ$‘œ`6³ÃÝ8K’ž£#øøØfš÷ö†m³ý­_oºLDDä©4lØmÛ¶™Î;¥!"‡úu0Lw¥x80“™ìa_ð…é‘aÓhLcžã9Ó)ö+~øâ ÛÏiÿºP_ÆjµòÉ'ŸP²dI~úé'Ö®];KˆÄõöÛo³víÚØçAAA4jÔˆ-ZLHH:u¢U«V\¸p!v=ë߃•üýýY°`¡¡¡”/_žN:1|øp.\ÈÅ‹)]º4ýúõ‹³Ï   ^~ùeÚ¶mË™3g8sæ mÚ´¡I“&ÅÛ‡Õj}Ì;÷‰:fëÖ­T®\ù¡¯µjÕŠ®]»ÉÞ½{‰ŒŒ¤wïÞqÖéÚµ+Ù³ggÏž=\¿~õëׄ——îîî9r„*Uª`µZ)Q¢ÎÎÎÄÄÄP¢D ~ýõWÜÝÝc?Ÿ··7=zô 44”Eÿþqï|êÔ)Ú·oÏ€ cï޽ܼy“&Mš<ò³¦véÓ§ÇÇLJӧOóÁ0~üx*V¬È¾}ûL§‰H9r$-[¶¤jÕª¦SDDR½{¡B1Âöøùg¨XÑtUš´™Íàh:EDDD’~”¥,Ýèf:ER3WWÛ î£GmBš5ƒFC„( IDAT 7MIIêÕ«Ç‘#G7""vHBDƌ矇¦MM—Ø­JTâmÞ¦?ý¹Æ5Ó9"""Fà[ÙJú˜N±õêÁرðᇰy³é»ráÂ5jÄ|@¿~ý8~ü8-Z´0•b•.]šàààØçþþþøùùñî»ï’;wnræÌI§N2d'NŒ÷þž={Rºti\\\κ={ö¤I“&8;;S¨P!ÆŽËæýwïïïÏ!Chذ!NNN”)S†Ï?ÿü±Ç)­Èœ93þþþ;vŒ‚ R«V-FŒAtt´é4I„o¿ý–Ÿþ™¡C‡šNI¢¢à½÷ fMÛŽ·ÝM8]:ÓeiÖXÆò/Q™‡Xû³†5la S™Šƒ.M’äàá˗ömnìýÎ;fºLDD$AêÖ­‹ƒƒC¼ï”EDBg]"gÎÀªU0x0X,¦kìÚÆC Ãn:EDDĨiL£%hLcÓ)©C¿~ðê«ðÆbºÆ.ìÞ½›Ê•+söìY~øáüýýÉ”)“é¬íŸY*þHûöíã­×¬Y3¶lÙoyÅî$œ/_¾xË (@Ø¿¾x ä•W^‰·­Ö­[˜ î'í|PDD... Ú««+‘‘‘q–U¯^–-[²qãÆØE‹w,»víÊìÙ³cŸÏ˜1ƒ÷ß?Þ>êÔ©ç¹»»;¡¡¡q–mÛ¶ ÄYVªT©xûLëŠ/ÎÆ™9s&'N¤~ýúñþü‰ˆ}°Z­Œ5ŠÖ­[S¥JÓ9""öo×.(_-‚¹sm7(ZÔtUš¶}üÀ|ȇ¦SDDD$‰Üå.H:P‡:ƒHRªW´ý¼ÿí·PªŒwî˜.y$^xá¶oßn:ED섈ŒmûÂçÕWM—Ø=W\Ç8f2“_øÅtŽˆˆˆ׸ÆR–Ò›ÞºëUR±X`Þ<È—Ú´[·L¥h_~ù%õêÕ£zõê>|˜5j˜N² 'Nœ xñâ±Ï#""pwwÇb±ÄyäË—‡ LÊš5kì?;88èÆ8::Æ[†··7nnn8::ÆnóßV¬XAÕªUñõõ%GŽÔ©S‡O>ù„{÷îÅYï7Þ`ùòåDEEÌÙ³giúÙ]]]ã<Ϙ1ãCY®\¹ù¹ÄÆb±àããî]» ¡fÍšý3!")Ïš5k8pà}ô‘éûvë /¾%JÀѣЭ›n•Œf4^xñ"/šN‘$2‰I\àcc:EÒ*èÒ‚‚ Oð÷‡çŸ‡+L—‰ˆˆ}ú0iÒ¤ØY*äñfÍšÅÖ­[yÿý÷c—5nܘ~ø!Þº;wî¤bÅŠI²ß† ²zõêxË׬YCÆ ã,û¯‰éÌ™3'ׯ_·|÷îÝŒ1ww÷ØD6¸Äb±páÂÀ60¥}ûö¬[·ŽÍ›7Ç[·k׮̜9“µk×Ò¥K—Gv=Ê‹/¾H```œe‡¦L™2O½Í´ _¾|lÙ²gggš4iÂÍ›7M'‰H¬\¹’£G2tèPÓ)""öéömÛ¬ µkƒ»»mVÍ ’‚Œf4¥(E3š™N‘$2€¸àB?ú™Nùnn¶ï—öîµ'T¬h›ADÛŠˆH S»vmصk—é±3ºBHÒ®ÈHX°úõƒôéMפ*å(GOzòŽîÞ+""iÇF6B=éi:%õjÙÒvAO¯^ðóϦkRŒððp^ýuš6mÊäÉ“Mç¤x·oß&$$„e˖ѰaCfΜɖ-[pqq‰]ÇßߟádzråJ"""¸qãßÿ=:t`øðáIÒ1|øp˜;w.W®\!,,Œyóæo dÏž=Ü»wÍ›7S¨P¡Dw–,Y2v@ǃjÔ¨Á€ áîݻѣG‡nÃÛÛ›ãÇsçÎþøã&Mšo0 @óæÍ ¡]»v899%ôÅ3tèPFŽI`` 7oÞäÈ‘#tïÞÞ½{Ç®M¶l”÷o9sædݺu\¹r…^½z™Î‘Lj‰‰aÔ¨Q´oßžråʙα?{÷Úf™5Ë6+Ȇ ð÷ÏÐ’2œå,_ó5C‚ƒ¾®Iö²—¥,%€œxúߊ<3U«Â®]°lìÜ %JØf¹}Ût™ˆˆY³f¥\¹rìÞ½ÛtŠˆØý†UÒ®Ï>³ éÞÝtIªô1“‘Œ|ÄGIºÝjÕª±nݺ‡¾vøðaÜÜÜˆŽŽàǤjÕªdÊ” wwwæÍ›gý7n0`À<<£FÂÝÝŒ3R²dIf̘oÝï¾ûŽjÕª‘%K²dÉòÈŸmžÄˆ#X°`ÁÎ$‘–ýsL, ‹…ìÙ³S³fM–.]JÇŽ9xð qÿ»uwwgÕªU,[¶ŒâÅ‹“?~FŽÉôéÓiÕªÕC·ý¤Ë<<<ذa«V­¢X±b-Z”•+W²aÃJ”(§' €Ž;âì쌯¯/3gÎ|¢Î‡iРŒ·|ñâÅܾ}///²fÍJëÖ­iÖ¬Y¼þÀÀ@2gÎLݺuÉ–-Õ«WçöíÛ,Z¦@²eËöÐ;6ÿµ¼\¹r,Y²råÊEóæÍi×®]œ+/^¤dÉ’üìiU¡B…˜7o .ä§Ÿ~2#"ðõ×_süøq† f:EDľü3+H­ZP¸0;f7³‚¤„sËä4‘‰¢íio:EDDD’€+}éKMjò*¯>Õ6t-€$ ‹ÚµƒãÇaØ0€’%m3ˆX­¦ëž¹´vÞ!"bjÔ¨¡!"òÄMˆqïžíÎ`>>àìlº&UÊF6Æ3ž®ÿå…W’l·oß¾L:5ö‚´MŸ>wß}GGG~ÿýwÚ¶mË´iÓhÖ¬¿ýö¯¿þ:Å‹çÅ_ k×®¸»»H¸qã»wïfúôé4oÞ7’ú98À_@•*ðúë°y3¤Kg4©gÏžäÊ•‹ÀÀ@òçÏÏèÞ½;¹sç¦}{ÛÅ-{÷îåÍ7ßdúôé4jÔ«ÕÊæÍ›éܹ3›6m¢J•*Oµï‹/2gÎf̘««kR~¬TÃú”_âxxx°råÊ'ÞvB—m€Ã† ÛÒ®];Úµk÷ÔÓºukºuëÆ[o½gyžwî\Z´hAžû &L€%KŒ¦¼ùæ›ñ–U¯^S§NÅ>?tè-Z´ˆ·^Ë–-õËœíÛ·óÒK/‘!C†§Þ†¤]&LàÇ|¦û¨U«uêÔ¡B… Ït?ÿX¸p!Y²dI–}Ù«F‘>}z¶oßn:EDþåþýûŒ=šÎ;SªT)Ó9"")ß¾}P±¢mFðO?…íú÷þ&Ï-“Ëe.3‹Y d ™Èd:GDDD’À(Fq›ÛŒdd¢¶£kĨŠáÇaíZ8tÊ”± <¿qÃtY’K ç""ö¬hÑ¢,XÝ»w›N;¢!’öüðüü3ôíkº$MðÄ“>ôa0ƒ #,I¶éããÃêÕ« lK|úé§ôêÕ+vóçÏS²dI,Kì#GŽ„††Æ®³lÙ2ÂÂÂ(Q¢¥K—¦[·n¬]»«Õš$""’vìcG8B7º™NI{ºv…þýÁÛÛö‹zCÜÝÝã-suuåêÕ«±ÏÿøãØ/¬”7o^._¾üÔû>wîÅ‹ê÷KÚæèèÈçŸþ̶oµZ¹{÷nœ;‹‰y™2e¢`Á‚œ?ÞtŠˆüË’%K âÃ?4""’²Ý»þþ¶YAÜÜìzV™<·L.ã .øàc:EDDD’ÀÎð Ÿ0‚ä"W¢·§kĸ-àäI3ÆvS²çžƒ9s &ÆtY’I ç""öÎËË‹½{÷šÎ;¢!’ö̘5j€——é’4c8ÃÉLf>$i.fÈ•+mÚ´aöìÙ¬]»–|ùòQµjÕØu²eËÆ¥K—°Z­q÷ïß]§hÑ¢¬Y³†k×®±lÙ2ªW¯ÎèÑ£ñööN’NI;æ3O<ñB?_1n¼ü2´m gÎIppxü©UÞ¼y¹råJ¼åW®\!_¾|O½o«Õš ý‹ýhÔ¨‘éIÒ¥KçüHDÌ»ÿ>cÇŽ¥[·n”,YÒtŽˆHÊuø0T­ 'ÂèÑv?+ȃLž[&‡PB™Ã3˜Ìd6#"""IÀ?ŠQŒwy7I¶§k$EHŸÞv“Ù3glß=ùúÚÎAvì0]–$Rûy‡ˆHjP¹re8`:CD숮’´åÒ%øö[xàîòìe%+“™Ì|泇=I²Í¾}û2kÖ,îÝ»ÇôéÓéÝ»wœ×ëÕ«ÇÚµk´­Œ3R¾|y|||ذa_ýu’4ŠˆHÚp‹[,g9Ýén:%írp€¥K¡`AhÙ®_7]ôP+Vä»ï¾‹·üÛo¿¥bÅŠO½Ý‚ ’ˆ²´Í’ w~Ò}Ĥ¢;=蟻õÙÓ¾}||ˆŽŽ~EfÝ»w .àææf:ED°`ÁΞ=ËàÁƒM§ˆˆ¤L÷îÁ°aP¹2äÌ 'NÀÀ¶sÂ4äY[&‡±ŒÅÞâ-Ó)"""’¶²•ïøŽ)L!=é“l»º@RŒœ9á“Ol3æÍ uëÚf 6]öÌÙóy‡ˆHjP©R%.\¸ Y™D$ÁÒÖoÉE>û ²g‡6mL—¤9íiOcóïMâ/¨*[¶,Ï=÷ÇçäÉ“¼öÚkq^>|8#GŽdÑ¢EDDDpóæM¶nÝJ³fÍbשS§K—.åÂ… DGGsùòe¨S§N¢ûDD$íXÁ þâ/Þà Ó)i›³3¬] С¤À»ÞðÁ :”/¿ü’°°0ÂÂÂøòË/ùè£4hÐSo·N:lÚ´)U^´žVmݺÕtÂ3aµZínßµk×NÔŸ)ÕöíÛ¹uë–Î}DR{÷î1f̺wïNÑ¢EM爈¤<'N@µj`» kË(RÄt•ÏêÜòY %”¹Ìe(C5;ˆˆˆH*M4ïñ¯ð iœ¤ÛÖµ’â<÷¬[g; Ò¥m3ˆ¤Ð”%{=ïI-*W®ŒÅbá—_~1""vBB$í¸wæÍȘÑtMš4iœâŸñY’l¯oß¾Œ;–·ß~› 2ÄyÍÓÓ“õë×óõ×_S´hQrçÎÍèÑ£yÿý÷c×9r$ß|ó /¼ðY³f¥víÚÜ¿Ÿ¯¾ú*IúDD$m˜Ï|ZÒ’|hzdãŠᅦ~€øËè5j0þ|¦NJ‘"E(R¤S§NeÁ‚q¦»RmÛ¶åêÕ«¬\¹2 kE sçÎüüóÏœ;wÎtJ’š5kU«VÕEç")Èüùó¹xñ¢¾Pù7«æÌ*UÀÑ‚ž=ÁÐÌs)Á³:·|ÖF1Š\äÒì """©Ä fpŠSL`Â3Ù¾®©aCøå˜>¾ú Š· XO7)K,{=ïI-räÈA‘"E8pà€é±"iÇêÕðÇðöÛ¦KÒ,<ðÃ! !”ÐDo¯aÆdÊ”‰wß}÷¡¯¿ð ¬_¿ž?ÿü“¨¨(¶mÛFƒ b_ñÅY¹r%áááܺu‹Ó§O3nÜ8\\\Ý&""iÃYβƒt§»éùGåʰp!Lžl»h(nÿõù¶ìüùó´jÕŠ¬Y³’7o^:uêDDDD²Ó„€ãÇÓ´iSœqqq¡uëÖO4¨âرc4mÚ4¶§iÓ¦ñzs\BBBâü; ⯿þг,$$€N:ñÍ7ß$¸=¥ûñÇùæ›oð÷÷7""»{÷.ãÆãí·ßÆÝÝÝtŽˆHÊqî4h¾¾Ð»7ìÚ¥J™®zfLŸ[>Kç9Ï<æ1„!dD7ì±w‘D2ŠQô£x<“}èZI±m7¢ýýwðö†¡\9X¿ÞtY‚¤æó‘Ô¦råÊþëó?l™··7=zô 44”EÿþýsD“î˜&ô˜œ9s†—_~™¶mÛÌ™3gxõÕWyýõ×ÛúOÏ?ï?sæLì@–&Mš”$ÇÅÝÝ#GŽP¥J¬V+%J”ÀÙÙ™˜˜J”(Á¯¿þ{QvåʕٲeK‚ÚSºˆˆ:wîLË–-yùå—MçˆÈß>ÿüs._¾¬ÙADD´b¼ð‚í&O{÷¸q>½é*yJcC^òÒn¦SDDD$ f0Ž8&ú;ÿÿ¢kÄ.¸ºÚÎSŽ…²e¡Y3hÔŽ7]&""©D¥J•8tèé ±ަD’ʼn°clÞlº$Ís‰ÉL¦-méF7^äÅ'Þ†ÅbÁÉɉƳpáÂ$oIˆbXÌbÞäMõcuÊóñÇíÚÙ.*QÂtÑ3åææÆÂ… iÕª¥J•bÀ€¦“mðàÁÔ¨Q€—_~9öâüüüâÜ®S§Nܽ{—‰'òÉ'Ÿ°uëVæÏŸOŽ9¨P¡K–,¡X±b nxpp†““S§N¥ÄüYú¯Þ¤Ô³gOš4i€³³3cÇŽ¥~ýú ~b)$ì˜øûûÓ¯_?ºwïg{ÑÑÑìÙ³ç±þþþôïß?Îû½½½ùóÏ?1bK–,‰³þÓ—råÊÍáÇ)_¾<7n¤pá±ÏÁößשS§»½”îæÍ›´n݋ł LçˆÈßn߾͸qãxçwpss3#"b^X¼ó|óm¶ï)SÀÉÉt•$Â9Î1ŸùÌd&È`:GDDDéW~e.s™Ï|²‘-É·¯kÄîxxÀòå°m¼ÿ>T¨ÝºÁ¨Q;·é:±cåÊ•ãüùóDFFÆ~ç-"ò_4Cˆ¤ sçBÑ¢¶éåŸ6´¡)MéE/îqï‰ßoµZ¹yó&«W¯&[¶¤ÿ%“ˆˆHBì`¸@':™N‘‡±X`Á(V Z´€k×L=s-Z´`Ú´i 4ˆI“&™ÎI´ªU«>ty`` íÛ··¼Y³fqfq¨^½:-[¶dãÆDGGP´hÑD Ôpuu%22ò‰z“R:uâøàÓ)""æmØåËáC°u+Ìž­Á ©À(F‘Ÿüt¡‹éIïñ/ðÂ3ûnD׈ݪ_ßv.3w.|û-”*ãÇÃ;¦ËDDÄN•-[€'N.{ !’úݽ K—Â[oƒþȧŸð g8à f˜Ny*ËXFyÊ㉧éù/™3Ûî*û×_о=ü=( 5ëÕ«S¦LaàÀ¼ûî»Ü»÷äƒoS §ÿ¸ð+""www,KœG¾|ù ‰]oÅŠT­Z___räÈA:uøä“O|LÂÂÂðööÆÍÍ GGÇØý}â r‰ŽŽÆÅÅ%I÷›\V®\I•*UÈ!û÷ï§\¹r¦“DäŸ~ú)×®]cÀ€¦SDDÌÙ½*U²ýõjX¾þ5àWì×Ç|L! љΦSDDD$‘nq‹ ¤ ]ðÂËtŽHÊ–%‹m0È©Sàåe»iYÆpø°é2±#‹…2eÊpüøqÓ)"b4 DR¿yóàå—¡P!Ó%ò/ƒDnró˜Ny"›ÙLa´§½éIˆ*U`ñbøôS˜4ÉtMRï% IDAT²xå•WØ»w/QQQT¨PÏ>ûŒ˜˜ÓYI¢qãÆüðÃñ–ïܹ“Š+Æ>·X,\¸p° 0hß¾=ëÖ­cóæÍqÞ÷_³~ìÞ½›#Fàî£#@œÁOâQ3‹¤ =¦ =&/½ôß|óM¼å[·nMPOÆ Y½zu¼åkÖ¬¡aÆ ÚÆ“èÚµ+3gÎdíÚµtyÈ®_¿NΜ9“|¿ÏRxx8]ºt¡]»v´mÛ–;wâææf:KDpóæM&L˜€¯¯/yóæ5#"’ün߆Aƒ Nðð€_…Ö­MWIúßXÄ">â#q4#"""‰4 „ÎƘN±… Ù¾ŸÚ»nÝ‚Šm7³ýãÓe""b'Ê–-ËÑ£GMgˆˆЀIÝBB`ëVxë-Ó%ò™ÉÌLfò_±m¦sDDDl˨F5ŠRÔtŠ$TÛ¶0c K–˜®IžžžìÛ·ž={Ò»woªW¯ÎLg%š¿¿?ÇgåÊ•DDDpãÆ ¾ÿþ{:tèÀðáÃã¬ëííÍñãǹsçüñ“&MŠ7  `Á‚ìÙ³‡{÷î±yóf ý=¼F 0€îÞ½KPP=zôxªæÿÚGJ‘ÐcšÐcâïïÏäÉ“™?>aaaDFF²|ùrÆŒIØ—ÅÇ' €¹sçråʘ7oñþ'…æÍ›B»víprrŠ÷ú… (Y²d’ï÷Yˆ‰‰aΜ9”*UŠ­[·òÝwß1{öl²dÉb:MDþeæÌ™üõ×_ôë×ÏtŠˆHò;zªW‡Y³l÷ׯ·Íî(©Ê‡|HiJӑަSDDD$‘.p‰Ld(C)€~nybU«Â®]°lìØÅ‹Ûf¹}Ût™ˆˆ¤peË–åØ±c¦3DÄh@ˆ¤nóçCž<Ь™éùMhBKZÒƒÜáŽé‘ǺÍmÖ²–×yÝtŠ<©=à½÷ÀÛM×$‹Œ32nÜ8Ž;†‹‹ U«Våµ×^ãÔ©S¦ÓþÓ?³iX,–‡Î¬áîîΪU«X¶lÅ‹'þüŒ9’éÓ§ÓªU«ØõÉœ93uëÖ%[¶lT¯^Û·o³hÑ¢8Û  cÇŽ8;;ãëëËÌ™3X¼x1·o߯ËË‹¬Y³Òºukšý}^ñ`×ãzµÇ}þG-{ÔòGm31Ç4¡Ç¤X±blذ•+WR´hQŠ)¼yóøòË/ÔëááÁ† XµjÅŠ£hÑ¢¬\¹’ 6P¢D‰§:.:VdË–^½z=´çСC4jÔè‘Í)A`` •+WÆ××—N:qòäIš7on:KDâæÍ›Lž<™>}ú'OÓ9""Éçþ}=*U8|||LWÉ3°}¬e-ãO:ҙΑDêOò’—÷xÏtŠˆý²X ];8q† ƒ€(YÒ6ƒˆÕjºNDDR¨Ò¥KAxx¸éIá4G³¤^V+,] ;Cúô¦kä¦32”a*SÈ@Ó9"""´™ÍüÅ_´¥­éy“'Õ+ðê«°s'<ÿ¼é¢dQªT)6mÚĪU«øè£ðôô¤K—.|ðÁ<÷Üs¦óâ°&à‹V®\ùÈu4h@ƒ »­víÚÑ®]»xËóäÉÃâÅ‹ã-íµ×âüüü(Q¢;wîä÷ßgàÀäÉ“Çt¦ˆ<¡)S¦CŸ>}L§ˆˆ<;÷î¿?4jU«Âñãд©é*I·¸ÅÇ|Ì;¼ƒ¦sDDD$‘üñç.wÆ0Ó)"iG† з/œ9¯¾ ={ÚΫvî4]&""yxxh†y, ‘Ôé‹/ ^=ps3]"Oh*S¹Ìe&1ÉtŠˆˆHV¬¬e-­im:E’’‡‡ínµ{÷B·niþNKNNNtéÒ…;vðÛo¿Ñ¾}{¾úê+ªT©B‘"EèÓ§Û·oçþýû¦SER«W¯²dÉÚ´iCîܹiÛ¶-!!!péÒ%.\H­ZµLgŠÈSºvíÓ¦M£_¿~¸ººšÎy6Nž//˜8`Õ*È™Ót•$“ÉLæ×ÊPÓ)"""’H¿ñ3˜ÁhF“ý<'’ìræ„O>£G!o^¨SÇ6ƒHp°é21 xñâëÿDä14 DRŸ?ÿ´ÍÒ±£éy n¸1”¡Œa ÁèI9róœç^1"I­JøæÛÅJ½{›®I1J•*Å„ 8{ö,‡âÍ7ßdûöíÔ¯_Ÿ¼yóÒ½{w¾ýö[nÞ¼i:UÄn?žY³fѨQ#òæÍ‹··7·nÝbÊ”)\ºt‰;wòî»ïâââb:UDiòäÉ888hvI¬V˜3*W†ôéá×_mwµ•4#œp&1‰ /yM爈ˆH"½Ïû”¤$Þx›NIÛJ—¶]ÿ´e œ=k{Þ·/\¿nºLDD’Q¡B…ˆˆˆ **ÊtŠˆ¤`"©Ï·ßÂýûЪ•éyJýèG Jà‡Ÿé‘XëYO~òSžò¦SäY¨_–,Ù³áãMפ8*T`äÈ‘=z”³gÏ2lØ0N:EëÖ­Éž=;•+WfРArçÎÓ¹")Ö7 dРAT®\™"EŠÐ¯_?2eÊÄܹs¹rå 6lÀÇLJ¼yu!HjÁ´iÓèß¿?Ù²e3#"’´®\–-Á××6À~×.ÛLŒ’¦|ÌÇd$#ïñžéI¤ïùž l`3pÄÑtŽˆ4l¿üÓ§ÃW_Añâ¶D4›»ˆHšàææÀ…  —ˆHJ¦!’ú¬\i;Ê‘Ãt‰<%G™Á ¾ûû/‘”`hNs,XL§È³Ò®|ú) S§š®I±ÜÝÝéÛ·/»víââÅ‹,\¸çŸž¯¾úŠF‘+W.š6mÊäÉ“ùå—_ˆ‰‰1,bÌÍ›7Ù´i  R¥JdÏž&Mš°}ûv5jÄæÍ›‰ŒŒä»ï¾£K—.š D$•š4i2dÀ×××tŠˆHÒڸʗ‡cÇ`ûv7Î6Cˆ¤)g9Ëlf3‚d%«éI„»Ü¥ýhG;^äEÓ9"ò ôéÁÇ~ÿ¼½aà@(W6l0]&""ÏX¡B… ‘GÓp~I]þú 6o†3L—H"Õ¡¯ó:½èE}ê“…,¦“DD$ 'œŸù™ 0"Ïšܺ~~1#ôèaº(EË—/;v¤cÇŽ±uëV¶mÛÆøñãéß¿?9sæ¤ZµjT­Z///ªV­Š«««ár‘gãÌ™3ìÛ·ýû÷³oß>:ÄÝ»w)S¦ 4`ذa¼øâ‹dÏžÝtªˆ$“ððpfΜɰaÃÈšUÉŠH*që d»;m§N0s&èãÒ¬Á ¦Ex‹·L§ˆˆˆH"}Â'œç<›Ød:EDþ‹««m0~÷î0t(4mj»iîÔ©àéiºNDDžœ9s’9sfΟ?o:EDR0 ‘Ôå»ïàÞ=Ûõb÷à9žcÁÓ9""’†md#8PŸú¦S$9ôí 7n€¯¯íŽKÞÞ¦‹ìF‰%(Q¢ï¼óV«•#Gް}ûvöíÛÇÂ… >|8‹…’%KÆñòò¢|ùò¤×„ÅÎ\½z5vàÇ?'}úô”/_///zõêEýúõÉŸ?¿é\1d„ dÊ”‰d*"©Åб#\¹_|:˜.ƒö±¯ùš¬ =:§±gW¸ÂhFóàŽ»éyœ’%aùrض Þ*T€nÝ`Ô(ÈÛtˆˆ$!‹ÅBÁ‚5 DDIB$uY¹êׇ\¹L—HÈG>†3œù7xƒR”2$""iÔ6P›Ú¸àb:E’ËСpû6¼û.dÉ¢‹œž‚Åb¡|ùò”/_>vÙ•+WØ·o_ìcÈ!\¿~L™2Q¾|yžþyÊ•+GÙ²e)_¾<9rä0ø Dl¬V+ÁÁÁ=z4öqøðaNŸ>ÕjÅÝÝjÕª1xð`¼¼¼¨X±"™2e2-")@XX³fÍbäÈ‘8;;›ÎIœû÷aüxð÷‡_„íÛ¡@ÓUbP 1ô¥/5©IژΑDÄ ²’U3¥‹Ø›úõáÐ!Xº´]75`øùA† ¦ëDD$‰,XK—.™Î‘LB$õ¸y6n„)SL—HêMo±ˆ>ôÑÔ´""bÄ}M béIn£FÙfŸëÜ¡];ÓEv/Ož<´hÑ‚-ZÃo¿ýƾ}û8tèÇŽcÕªUDFFP @Ê•+g H™2eȘ1£É!©Xdd$GމøqäÈŽ?Î_ý…Åb¡hÑ¢”+WŽöíÛS¹re¼¼¼È›7¯élI¡ÆŽ‹³³3ï¼óŽé‘ĹpÁv^´gmPÈ{ïÅbºJ [Ä"p€ƒÄ‚þ<ˆˆˆØ³Cb‹XÊR²ÅtŽˆ<)èÒ^}&N´ äŸ?ßö=—¾ÛIòäÉCXX˜é IÁ4 DRÀ@Û]œ[µ2]"IÈGf0ƒ:Ôa5«u§1Iv¿ð DИƦSÄ„qãàÆ ÛÅONNЬ™é¢TÅÁÁ2eÊP¦Lºuë»üêÕ«?~œƒrâÄ vïÞÍŒ3¸uëùóçÇÓÓ“bÅŠÅyxzzjfy¬;wîpñâEŽ?Ή'Ž}œ={«ÕŠ‹‹KìL5:tÀÓÓ“ *3gNÓù"b'._¾ÌìÙ³;v,NNN¦sDDžÞ7߀·7äÎmR¡‚é"Inpƒ! á]Þ¥<åÿI±¬Xy÷¨F5^çuÓ9"’Y²Øƒ¼õ íÛÃìÙÏ?oºNDD!W®\œ}šÓ§OsôèQV¯^Mxx8éÓ§ÇÝÝØG‘"E(\¸0… ÂÕÕÕÔG’dChh(çÎãüùóûç&44€téÒQ¸pa<<<(Y²$Íš5£téÒxzzâææføSˆˆ½;v,...¼ýöÛ¦SDDžÎíÛ0p L›f ?k–íâ"`#¸Ímüñ7""""‰ô_ð?±—½šõK$µ(T/__xÿ}ÛÀþŽm³‡èš*»”;wnvîÜi:CDR0 ‘ÔÁj… àÝwM—È32‘‰”¢£ÍXƚΑ4d;Û©G=}’–98ÀçŸC¦L¶A!_ ­[›®JsÒ§O§§'žžžñ^»ví§OŸŽsÑÿÞ½{Yºt)±ëeÉ’…"EŠP¨P!ÜÜÜ(T¨Pì`777Š)BæÌ™“ócÉS ãÂ… œ?>vÐÇ… 8wîçÎ#44”{÷î¶A… ¢D‰xzzÒªU«ØÁBÅŠ#C† †?ˆ¤F¡¡¡|þùçLš4Iÿ¿""öéøqxã øßÿàË/¡CÓE’‚Ä f@¹Èe:GDDD!Š(†0„ît§ ULçˆHRóò‚]»`åJøà(QúõƒAƒlßy‰ˆˆÝÈ•+aaa¦3D$Ó€I‚‹¡ysÓ%òŒä!ó1ýèGºPšÒ¦“DD$ ˆ&šŸø‰‰L4"¦Y,0}ºmpH»v°d‰.ŠJA²gÏN•*U¨R%þ—–·oßæÒ¥K\ºt‰ÐÐP‚ƒƒ æÒ¥Küú믜>}š?ÿü3výL™2áêêJÈŸ??®®®ñžÿóÏyòäÁÑQ§Õ‰uçÎ"""¸zõ*W¯^%44”K—.Åþó¯¿þJúôé åÂ… ܽ{7ö½®®®äÏŸŸ P¦L4h@(V¬ÅŠ£P¡B¤OŸÞà§‘´hÔ¨QäΛ·ÞzËtŠˆÈ“±ZmƒáýüàùÿcïÎãª.óþ¿»ì¨ì‹Š‰î¸+jšæ¸t—VN˘u—mSVwe3eiÚ6m·3ó«ii›Ê¹srÌrÉ,Å}ALTÜ”U@ÖßgÎw@pKå{¼Ÿ=Îã|9ïC¢ßs]×çúô´Ž»GG›JìÌÃ<ÌU\Å f˜EDDD.Ñ‹¼HÌcžÙQDäJ±X¬óZ&Xç¹^x>üæÏ·vƒ´hC<Gо}{Nœ8A]]ýÝ-"MÐÊi¾ù W/³“Èôð Ÿp?÷³šÕÚ©]DD®¸Ml¢„®æj³£ˆ=°X`ÁkQÈo~µµÖÛb×<<<Œâ€³9qâddd——Gvv6yyyäåå‘••Err2ùùùäååQSSc<ÏÙÙ™€€üýýñóókpìççgŸyíëëK›6mðððÀËËË¡»T”––RUUEqq1•••QXXHAAq\TTÔà¸þuAAAƒ‚€6mÚо}{BCCqvvfóæÍÄÆÆr÷ÝwIhh(DEEiç}±;|ðÁüñÄÝÝÝì8""./îº –-ƒ§Ÿ†Ù³ÁÙÙìTbg–°„ïøŽÕ¬ÆESŒ"""í0‡y×y!Äì8"r¥yz¬YpûíðüópçðÖ[ðæ›0x°ÙéDDä<Ú·oOuu5EEEøûû›GDìFk¥eXº&MRåz ç„æÏ b YÈT¦šIDDZ¸Õ¬&Œ0bˆ1;ŠØ ‹Å:80}ºõë[o55’\ºvíÚÑ®];z÷î}ÎÇÕÕÕ…"¶Â‘ÂÂB£À¡  €ÂÂB233IMMmPøP^^~Î×öõõÅÙÙœñóóÃÅÅÜÝÝñôô4ÛT‰³³3¾¾¾ô~kjjaÀŠ;l ©©©¡¨¨ˆêêjJJJ8}ú4eee”——SQQqÎïS¿¦þqTT”ñµ¿¿?DHHÞÞÞ ^gÕªUL›6¿üå/|öÙg 6ì‚Þ§ˆˆ^xáBBB˜>}ºÙQDD.ܪU0m¸¸À?@b¢Ù‰ÄURÉã<ÎT¦2’‘fÇ‘Kô8Aò ÙQD¤9…‡Ã;ïÀŒÖîC‡Â7«¯B‡f§‘³ðññ ¤¤D!"Ò$„ˆãË˃mÛà™gÌN"Í ?ý¹“;y„GÇ8üð3;’ˆˆ´`?ò#£ev ±7 üïÿ‚««µSÈÉ“ðÛßšJšÅb!((ˆ   ‹~nýîÅÅÅTTTP^^ΩS§¨¬¬¤¤¤„êêêEUUU”––µÉÊÊ¢¶¶¶ÁëÛ 5Îtúôé&w¨÷÷÷oÔNØÃãAǨ¨(£ Š­àÄÍÍ ///£Ã‰··7®®®øùù×þþþ\ôÏèlFÍŽ;¸ë®»¸úê«yúé§™={6ÎÚ±ZDìÌÑ£Gùë_ÿÊ[o½åÐÝŸD¤©®†ùóaÞ<¸áxï=¸ŒçqÒ²¼ÁdÁ V˜EDDD.ÑüÀ"±”¥¸£î–"­RB¬YK–À#@\<ôuíÕ›6‰ˆˆù¼¼¼ë""MQAˆ8¾U«ÀÉ FŽ4;‰4“?ð³˜yÌã5^3;Žˆˆ´P5Ô°‰M¼ÎëfG{õê«? ™™ðòËf';æææft‹È¿þõ/þøÇ?2kÖ,~úé'>ùä"""ÌŽ&"b˜7o‘‘‘L›6Íì(""çwø0ÜvìØo¼3gšHìX9¼ÄK<ÅSt@»‹ˆˆ8²jx”G™ÀÆ3Þì8"b¶I“àÚkáí·á¹çàÓOáÙgáî»­k±DDÄ.xÿ»XO!"r6:sÇ·j ¾¾f'‘fÒ–¶¼À ,`)¤˜GDDZ¨ì¤”R3Øì(bÏf΄¿ý ^î¼ÓºÃ®ˆ\‹…™3g²mÛ6òóóéÑ£_~ù¥Ù±DD8räŸ|ò ³gÏÆÕÕÕì8""çöñÇг'TVZ BT "çñOà‡ó¸ÙQDDDäý…¿°—½Ú KDþÃÍÍú¹ðàA˜2x„µkÍN&""ÿf+9uê”ÉIDÄ^© DߪU0z´Ù)¤™ÝÍÝô£¿å·ÔQgvi6°_|‰#Îì(bï¦MƒE‹àË/áÆ¡¢ÂìD"-Z||<7ndòäÉÜrË-<ôÐCTVVšKDZ¹9sæÅm·Ýfv‘³+*‚_ÿ¦O‡ûîƒõë!&ÆìTbç6°ÏøŒ7yO<ÍŽ#"""— €æ0‡‡y˜®t5;ŽˆØ›ví`Áøùg ‚áíD6;™ˆH«çåå¨Cˆˆœ Bı=jýà¡‚VÇ 'þÌŸÙÀ>åS³ãˆˆH ´ bÎ8›EÁ¤IðÔ¿ú•u¡•ˆ\1^^^|ðÁ|þùç|ôÑGŒ9’cÇŽ™KDZ©ðÙgŸñÜsÏáââbv‘¦mÙ}ûÂO?Á²eðê«Ö]`EΡ†æaF2’)L1;Žˆˆˆ\¢ÙÌÆ 'žá³£ˆˆ=‹…¥KaåJ뚬nݬDŠ‹ÍN&"ÒjµiÓ'''„ˆÈY© DÛÊ•àé ƒ™DL@3˜Áÿ:w¶~¾¬©1;™ˆH«c±Xpqq¡ººÚì("b§T"ŽmÕ*ëb;ww³“ˆI^àê¨ãYž5;Šˆˆ´ ¹ärˆC aˆÙQÄÑÄÇÚ5P^nm¥½w¯Ù‰DZ¼˜˜6nÜÈÕW_͸qãxå•W¨««3;–ˆ´ûöíã‹/¾`îܹê""öçÄ ¸î:xüqøÝï¬,…†šJD9Ìf6ó8ñÄ›GDDD.Ñ£ =zÀwß™LD¤ÕqiBæ| IDATvv¦FEy"r*Ƕf Œiv 1Q¼Ì˼Å[$“lvi!¶² úÓßì(∢£aÝ: ‚ÁƒaÅ ³‰´xÞÞÞ|ùå—¼öÚkÌž=›n¸bµ¯‘fðÜsÏÃÍ7ßlv‘†6m‚þýaÇX½æÌ'M É…{˜‡ñçyÚì("""r‰±ˆ•¬d pÒ2!ù%àå—á矡{w?ÆŒÔT³“‰ˆ´*‘sÑ'=q\GŽ@V jv1Ùt¦3ŒaÜ˽ÔRkvi¶³Žt$€³£ˆ£ ².ººî:ë ø+¯˜H¤Å³X,Ìœ9“•+W²qãF Àž={ÌŽ%"-ØîÝ»Y¸p!sæÌÁI‹¬EÄ^ÔÕÁ‚ÖÎÚÝ»[ B† 3;•8˜å,çüƒ?óg¼ð2;Žˆˆˆ\‚Óœæ)žân!‘D³ãˆˆ£‹‰… áûï!7úô{ï…ü|³“‰ˆ´x*‘sÑL¥8® ¬­ ÌN"&³`áÏü™d’ù3;Žˆˆ´)¤Ð›ÞfÇGçî}¯¿¿ÿ½u@¼ªÊìT"-Þˆ#ؼy3¾¾¾ <˜ýë_fG‘jΜ9ÄÅÅ1eʳ£ˆˆXååY ÒÜúä믡];³S‰ƒ)§œx€›¹™‰L4;Žˆˆˆ\¢×xL2y‰—ÌŽ""-ÉèÑœ ï¿‹C×®ÖÍÑ*+ÍN&"Òb999Q[«Í²E¤i*ǵq#ôêžžf';Ðî<À<É“ä£DDäÒ¤B/z™CZŠ™3áÿ€Ï>³.Î*(0;‘H‹ÅÚµk¹é¦›¸á†xíµ×ÌŽ$"-Ì®]»øê«¯xþùçÕDDìÃêÕÖñò={`íZ˜3ô÷“üÏó<ùäóo˜EDDD.Q9ü?ð{~OQfÇ‘–ÆÉ ¦Mƒࡇ¬ŸC{ö´Î‰‰ˆÈeçââBuuµÙ1DÄNi6@׆ 0x°Ù)ÄŽÌcî¸3›ÙfGVJ)9¨!ryMž ëÖÁ¾}0`ìÝkv"‘ÏÝÝ÷Þ{7ÞxƒY³fq÷ÝwS¥.="r™<ûì³ôêՋ믿Þì("ÒÚÕÔXÝŒc/ON†AƒÌN%êg~æu^çE^$œp³ãˆˆˆÈ%z‚'ðÃÇxÌì("Ò’y{[?—ÚæÀ¦N…k®;ÍN&"Ò¢TUUáêêjv ±S*ÇT^))*‘|ñå^á]Þe›ÌŽ#""*…j©U‡¹üzõ²5ÀСðý÷f'ifΜÉW_}ÅçŸ΄ (**2;’ˆ8¸ääd¾þúkæÎ‹Åb1;Žˆ´f™™põÕðÊ+ðúëðÕWÖÏ"¿@-µÜ˽$À}Ügv¹DÙÈg|Æ›¼‰'žfÇ‘Ö 2>þØ:VV}úX;ˆ?nv2‘áôéÓ¸»»›CDì” BÄ1mß••ÚéL¹ÛÁäAj¨1;Žˆˆ8 ìÄ:ÐÁì(Ò……ÁO?Áر0n¼ôÔÕ™J¤Å»þúëY¿~={öì!11‘ôôt³#‰ˆ›3g}ûöeâĉfG‘ÖlÉèÝrsaãF˜9ÓìDâàÞà ¶³øgœÍŽ#"""— –Zf2“‘Œd SÌŽ#"­ÍÀ°n|ñ¬YW]eí RQqîçíÛ‡5KDGTYY©‚9+„ˆcÚ¶ ÚµƒNÌN"vèÏü™ìä}Þ7;Šˆˆ8 =ì!Ž8,h·g¹BÚ´Ï?‡·Þ²€_w˜J¤ÅëÕ«7nÄÙÙ™Aƒ±mÛ6³#‰ˆÚ¶mK–,áùçŸWw1GuµõsÄõ×Ãøñ°u«µ¡È%8Ìaæ0‡gx†8âÌŽ#"""—è#>bÛx“7ÍŽ""­•Å7Ý»wÃ3ÏX»Zvíjí ÒÔFi0j”uc`mè$"ÒHuu5555xxx˜EDì” BÄ1¥¤h’KÎ*Ž8f2“ßñ;òÈ3;Žˆˆ8˜}ì#†³cHk0cüðƒµûÝ€°s§Ù‰DZ¼ððpÖ¬YCïÞ½9r$K–,1;’ˆ8˜gŸ}–„„~õ«_™EDZ££GaøpøÃà/±.¤ñö6;•8¸:ê˜Á :Ó™YÌ2;Žˆˆˆ\¢Jxš§¹—{é…ÖTˆˆÉ<=aÖ,Ø»ƃ;ï„!C¬.ë{óMÈɱn 6v,™“WDÄN>}@BDä¬T"ŽI!rÏò,žxò;~gvq0i¤© DšÏС°ctè`m¡ýá‡f'iñ|||X¼x17Ýt7Üpï¼óŽÙ‘DÄAlݺ•ï¾ûŽ^xAÝAD¤ùýóŸÐ§uQÌæÍpÏ=f'’â}Þg5«ù€pÅÕì8"""r‰æ3Ÿ *˜Ë\³£ˆˆüGx8¼ólÚ®®Ö¢›o¶vÉÍ…ù󡦯ÚóÐ!˜0þ½øYDDT""ç§‚q<55Ö–‚*‘sðÁ‡7xƒù lhpߎð%_š”LDDìÙiN“A† B¤y²e0s&Ü}7Ü{/TVšJ¤EsuuåÃ?ä¹çžãþûïçÅ_4;’ˆ8€§Ÿ~šÁƒ3vìX³£ˆHkRQaý¬0y2Lœ[¶@÷îf§’"‹,žäIåQúÑÏì8"""r–±Œu¬kpÛA²€Ìe.íioR2‘sè×~ú >ÿÜúù6>n¼ªªþó˜ª*káÈ´iPWg^V;R\\ €¯¯¯ÉIDÄ^© DOZ”—« DÎëfnæZ®å^jNsšxntãn¡€³#ŠˆˆÙÏ~j¨¡+]ÍŽ"­‹ ¼ü2,\h122ÌN%ÒâÍž=›·Þz‹Ù³g3sæLê4¹$"g±~ýzV¬XÁóÏ?oviMÒÒ`ð`øÛ߬Ÿ>þ<=ÍN%-È fОöÚA\DDÄÝÍÝ$’ÈT¦’u,ùQ¥3¹ûLN'"r L {öÀwBRR°v ù¿ÿƒçž3'£ˆˆ)(°®sô÷÷79‰ˆØ+„ˆãII±.˜‹5;‰8€ÿåI#ÇyœXbyŽç8Íiê¨c+[ÍŽ'""v&4œpâ*®2;Š´V7Þ›7Cq1ôî _mv"‘ï¾ûîãÓO?åí·ßfúôéTWW›IDìгÏ>ËСC=z´ÙQD¤µøøcëΩ..°};üú×f'’æ>à;¾ã>À‰ˆˆ8’ È"‹:êø'ÿ¤ ]˜Ît–°„7xW\ÍŽ("r~°k—õsoSjkaÞø0”¡,`é¤SC n¸±™Í&§{sˆCDfG‘Ö¬[7ض n¿n¸ÁÚ»¬ììß½jjš/ŸH tË-·ðÕW_±páB¦OŸN~§D¤ž¤¤$V­ZÅüùóÍŽ""-ŇÂÁƒMßW^3gÂôép×]°ntîܬñ¤å;Æ1çqþ‡ÿa8ÃÍŽ#""")™dê°vº­¢ŠÓœæS>ÅoŽsܸODÄ®-^ «W7îr¦ßþ–,ižL""vª  ‹Å‚ŸŸŸÙQDÄN© DÏþýcv ±sÕT³€t¡ I$Å ¶û7²Ñ¬x""b§2È Š(³cˆXwEZ°¾ú ¾ùú÷‡Ÿnü¸¤$èÑ~ÿûæÏ(ÒÂLš4‰%K–°hÑ"î¸ã…ˆˆaöìÙŒ5Š‘#GšEDZ‚¥Ká¿ÿ&Nl\ø½{7 ÿ»u±Ë‚àæfNNi±ê¨ã¿ùoBa.sÍŽ#"""¿@2ɺ€ÔPCeLg:ÃÆv˜”NDäTV£‚Ó.]œ:vèï5i½ ñññÁål]•D¤ÕSAˆ8žƒµ#šœÓQŽO<ÿÃÿPFU4ÞM –Z„ˆˆH#™dN¸Ù1Dþcòdëw@ h]fSZ ·Þj=~õUX¾ÜœŒ"-È5×\òeËX¼x1·Þz+ÕÕÕfG“ýðÃüøã<÷ÜsfG‘– °ÐÚõÃÉ €ûïÿÏ}l-÷ò‚-[`ÂórJ‹ö6o³ŠU|ÄG´¡ÙqDDDä¨ß!¤¾Zj©£ŽÍl&Þç}Ò‰ˆ\€äd8|jk­!œ«0¤¶ÖÚEdìXÈÈh¾Œ""v¤  ³cˆˆS¹˜8žƒáž{ÌN!v¬Š*òÉÇ §]AΔO>™dAD3¦{vŒc$’hv ‘†¢¢àÇaþ|xì1X»Þ{ž|rr¬áNNpË-š ¡¡f'qhÇç믿æºë®ãŽ;îàã?ÆÙÙÙìX"b’¹sç2vìX†nvi f΄‚ë9|m­µdà@kç¿/¾€‡‚×^W×ó¿–È/p˜Ã<õïÿ0Àì8"""ò mf3Õœ{#“6´¡+]›)‘ˆÈE8ÐZ²cìÜi½lÝ ééPW..àì §O[_] 'O˜1°iøù]з9uê•••ŽËÊÊ8ýï×¾Øã3ÕÕÕQXXxÞ,çz³qwwÇÓÓó¼ó÷÷Çb±œ÷5.åØÍÍ //¯‹Ê/"—ONNÁÁÁfÇ;¦‚q,yyPTW]ev±cWq)¤0 ìf÷YÃ,XØÌf„ˆˆˆABÄn¹¸Àœ90t(L›f(?pÀ:(ÖÅd¥¥póÍÖâ-^¹$£GfÉ’%Lœ8‘ûî»wß}÷¬“)"Òr­X±‚5kÖ°víZ³£ˆHK°r%|òÉÎámzÈÚpåJ=ÚœlÒ*ÔRËÜA4ÑÌf¶ÙqDDDä*§œC:ëý®¸Ò–¶,c½éÝŒÉDD.LYY%%%”TWSIep0§† ¡¼¼œê¢"\öîÅïèQ¼"83“ÀœÜ*+¡¦ÒÒHîÖßõêEuu5………TWWSRRBEEååå ?.…‡‡mÚ´itÜ///ÜÜÜÎùz...øøø\T†’’’óv2¯¬¬äÔ©Sg½¿~!Jyy9•¡)¶â6mÚàáá...àì쌯¯¯QLâé鉻»;¾¾¾¸¸¸àïïoü,l?WÛÏÏßßooo|||.¨F¤5ÉÌÌ$"BkEäìT"ŽåàAëuçÎææ»AëYÏíÜÎb7Ù2× 7¶°…ÉL6!¡ˆˆØ›j8Îq„ˆ}3~ú  ‹¥áb²ª*X¿^}žzʼŒ"-ĨQ£øú믙4ižžž,X°ÀìH"ÒÌæÍ›Çøñã6l˜ÙQDÄÑ[ »œ¬ XêsrOOèßßœlÒj¼Â+lf3›Ø„ç^¨$"""ök';©¡¦Éû\q¥;Ýù–o !¤™“‰HKVSSCAAÅÅÅRZZj-ê()¡¸¸˜¢¢"ãëÒÒRŠŠŠŒÛlµÝV[[{Îïåë닳³³µ¸ÀÝß=èÄÕÔÐõôijÝÜoT€`+R8[!ÇÅ·tõ‹C~é±­ø¦¸¸¸QNqq1999 cûsd{̹Øþ¿úùùáããƒÞÞÞøùù·ÙŠGüüüðõõmð8|}}?#"Žîرcôî­b_9;„ˆc9p\]!*Êì$â¼ðb‹øàwü AaÈiN³ fÅ;“C5ÔF˜ÙQDÎí™g¬Ý@š0¯­…§Ÿ†áÃaÈæÏ&ÒÂŒ;–¿þõ¯üæ7¿!88˜ßÿþ÷fG‘fòÝwß‘””ĦM›ÌŽ""-Á#@~~ãb€êjÈɻ ›?›´ ÛÙÎæð"/Ò‹^fÇ‘KL2.¸PMÃãqæWüŠÏùO´«ºˆ4­¬¬Œ“'ORPPÀÉ“'Ïy\ÿR\\ÜäëÙí×ïìàãワ¯/QQQÆ×¶ÛšZÜf'‰ 1írþPZ©6mÚÅ/¦d8}ú4eeeF“³Õ/@***"##£ARaa¡QpÒ???hÛ¶m£KýÛÏqâìÛ·¯ÁíMu,ñððhð;о}{BCC $00ÐÐP‚‚‚ $$$??¿Ëò>DÎTWWGvv6áááfG;¦‚q,ÙÙjv q@7s31Ä0žñä“OU”SNiÄkrB1ÛIN€9; ˆœWn.Ü{¯µàã\‹Éjj¬»ß}7,ZÔ|ùDZ°û￟ŒŒ î¹çÂÂÂ3fŒÙ‘Dä Z²d ›7oVw¹tÅÅpçç?‡ëcî¹G"Éeõ?ü9ä°œåZ *""Òlb“1×mù÷âO<À&'‘Ë%''Ç(èÈÍÍ%//œœŽ?N^^Ç7ŽOŸ>Ý๶…ê¶Åê½{÷>oWWW“Þ©Èåeë4tÑÏ­ªª:oÇœüü|RSS¢«üüü¯áîîn‡Hpppƒã‚‚‚Ö&•ráNœ8AEE… BDäœT"Žåøq 1;…8¨ÞôfÛ˜Ä$RH¡šjœqf [T"""R€Ú¹CìTi)„…ÁÞ½Öb TW7ýØêjøúkí0,r½ð 9r„›nº‰¤¤$ºwïnv$¹êêê˜7o×_=ýû÷7;Žˆ8ºÇ³k×Ö6}¿««µ;HHÜr Üz«ŠAä²ZÆ2Þá>çs¢ˆ2;Žˆˆˆ\¢jØË^\pÁ 7þÁ?Ïx““‰È…*((àСCdee‘Ýà8++‹ôôtJKK<' €ÐÐP cèСÆqýÛ###UÜ!ò ¹ººÅ£  €¬¬, ŒßãúÇû÷ï';;›ÌÌL*++ç¹¹¹Ñ®];ÂÂÂˆŽŽ&44Ôø¶Ý…‹‹–÷ 8p€èèh““ˆˆ=Ó¿âXrr G³Sˆ %”$’¸‹»ø’/©¥–ÍlfÓÌŽ&""&+ o¼qE¥b§¢£aÏk‘ôš5°r%,^líâêjí R¡Y]<ü0  ½z™—[¤…°X,üõ¯e̘1\wÝulÙ²…víÚ™KD.³¯¿þš­[·òÞ{ï™EDÝ?À‡6î ââb-àŽˆ€É“ᦛ`èPkÁ·Èe”Gwr'Ó˜ÆT¦šGDDD.ƒ½ìå4§±`!˜`–³œxâÍŽ%"X7ÉÊÊâðáÃ=z”cÇŽqìØ1222ÈÊÊ"##ƒãÇSSSXÇ›CBBˆˆˆ ,,ŒØØXFMxx8‘‘‘F‡€€“ß™ˆœK@@Àÿžž>>ÀÆCë­Ö;õóóÃÉÉé’2‰´fdeeqèСF—´´4ãw¾þÎÿ¡¡¡ôêÕ‹n¸¡ÁÎÿêæ!Òú´mÛ–¶mÛ{ÖÇTUU‘——gt©ßIh÷îÝ|ÿý÷=zÔ8'qss#""‚èèèF—«®º ??¿æz{ríß¿Ÿ.]ºh~[DÎI«ıääXÀI«RUUE~~>ùùùäåå‘››K~~>ÅÅÅSXXh׿RTTDmý²Ï4õ>ÄÍóon²ÃÉÉé¢OŽë/¨´±¢\Û÷ô÷÷Ç××·ÑÅßß???|}}i×®ÁÁÁ´oßÞ¸hÐ@Dä—)¤ξø\Ä %%%”””4:Ç)..¦´´”òòrJJJ¨ªª¢ðª« <œ¨£G‰Í̤ßñãD:û÷óvx8óÚ´¡¢¢â-’«ÏÙÙ__ß³ÞïââbL@^Šó?]ÌùÕÙøùùáêꊯ¯¯11êãル«+þþþ¸ººâíím,¬ÿx|||ðõõ5®mçpÚ‰§ek×®‹-bèС̚5‹×_ÝìH"r™|õÕWüüóÏ|üñÇfGqh'Nœ0Æòl—'Nç³õÏo |]VVvÁß§©Ýë/v»g[ÀWZZJUUÕ½†««+>>> ÆìÉÈàú¬,ê€c!!ìïÛ—ÌpëÖ   Ú×ÖÒ>;[cyrÅü?°ŠUüÄOøröÏo"""ެ¬¬Ì˜.,,4.EEEQVVFEE………FGAA1®XXXHEEeeeçŸS>‹ómjs)c…MAW÷­Æ=ÍâyÅÜŸuÿ/z]Ûø®——øùùúûûsæÆùµí\×6þYÿr1çß"Ž ##ƒýû÷sèÐ!>l|>|˜¼¼<Àú»j,ºž8q"?ü°ñµvì‘_ÊÕÕ•°°0ÂÂÂHHHhò1¶ŽDgþ=µ{÷n¾ùæ²²²ŒÇM§NŒ¿£:uêD—.]Ôm¤¥¥cv ±s*Çqú4© ¤9~ü8ÙÙÙF <[Ë̼¼j;­ßØv_ýpÎÚ)®¹Ü÷ŸCÛy)ü§+týŽÏ¶sX[‰í¾ÒÒR***ŒÂl[ñÌÉ“'ãú3EEEMn4äææÖ @Ä6ZÿüÖv©¿Ñá™5Š4§êêjÒÓÓ9tè©©©ìÞ½›ÔÔTvîÜiü»Ntt4½{÷fòäÉÆbê®]»j,_DLc±XŒ1£ÄÄÄF÷WVV’™™Ù¨“ÑòåËÙ¿¿±9Š»»;;w&>>ž¸¸8âã㉎Ž&>>^ÿNÛ™}ûöñ«_ýÊì"bçT"ŽÃ¶S›Z™9„ªª*Ž=jœT44”ˆˆÂÃÃ‰ŠŠjÐ:°C‡Ú¡PDZ•*ªpÇÝìbçÊË˃’œ” IDATÉÍÍ%++Ëh×{üøqãß_[±‡íÚÖ¼>ooï„……5( ¨_ôÚÔíÒ§§'žžž@ó.,--mPÈc›=s÷kÛí999ìÙ³§AQЙE?îîîÆŸú…#¶‚Þàà`BCC $44Ô® uZº[o½•¤¤$îºë.zöì©yDÜÂ… IMMå‹/¾0;ŠH³«©©áرc &ˆ=Jff&ÙÙÙddd4èÜáááÑ H!>>¾Á¸m<Ïöµ© å.‘——^^^¿øsÆ_×ÕÕ5Zà˜——×`,ïØ±clÞ¼™¬¬,£¬;DÅ"gŽåÙ7JëVH!S™ÊhFó™GDDZ±ÊÊJrrrŒsJ[aqvvv£9ÍS§N5x®››[ƒóËvíÚѱcÇ&»SÔ/@ð÷÷o• ´ÝÝÝqw·Îm\é±ÑÒÒÒÝXšêÐb»mß¾}¬_¿Þ7?³€ÇÛÛ»Q‘Hhh¨1Bdd$ÁÁÁý™BÌUZZÊÞ½{Ù»w/{öì1®8@UU‹…:Э[7ÈwÜA·nÝèÚµ«6Ø‡åææfŒ5%77—´´´/~ôÑG=z”ºº:\]]¹êª«ˆ‹‹£k×®ÄÅÅ7¶Æs-³ÕÖÖràÀºtébv±s*Ça›tü÷B/1_uu5 55Õh™yðàA:DFF†±`@@;w&**Š„„®»î:ˆˆˆ 44”ÈÈHcŸ\<[ÁÌ…(++#==œœ222ÈÊÊ2vuüá‡xÿý÷‰f"##L,ÇÄÄÇUW]eì"."ÒRTS‹N[­ÒÒRÒÓÓÉÈÈ0&'sss]sssÉÎÎ6vL±ñ÷÷7çÒ¹sg Ðh1ýcM^µÞÞÞx{{ú‹ž_WWwÖî1õ;ÆÖ­[Iòú»n{xxHXXXƒ‚‘   "##‰ˆˆ ""‚`uZ¼"Þ|óM6mÚÄm·ÝFRR’1ù."Ž¥¦¦†çŸž_ÿú×ÄÆÆšG䊨©©áðáÃìÞ½›}ûö5*þ°-Îòòò"::šŽ;˨Q£Œ1<[Ú¶mÛšün—Åb¹¨±¼òòr£KFFFƒnË7näïÿ;¹¹¹Æk‡††6ËëÒ¥ ݺu£[·nmê¨ã.*>æc,¨£Œˆˆ\§OŸ&==ÇsôèÑÚÎ]Ž?Þà9!!!„„„N`` ±±±Ô¨¸ØODÚ-ÛxhDDÄE?·¨¨ˆãÇ7*޶u…ÉËËcÇŽMþù±yÚ Ómk :tè@ÇŽéСƒÆÝ…¬¬,’““Ù¾};ÉÉÉ$''säÈÀº8:&&†ØØX¦L™Ò`q³>+‰HkDPPP£î"eeeìÝ»—´´4vïÞÍÞ½{Y¼x1¯½ö•••X,:vìHŸ>}ŒKß¾}ñ<©\˜}ûöqêÔ)zõêev±sZñ&ŽÃ¶3È¿[³J󩬬dß¾}ìÞ½»ÁeÿþýTVVâäädìH×¹sgÆŒcGGGkk;âééiLŸÍÉ“'tv±]¯X±‚ŒŒ jkk“¸¸8ãKLLŒÛDÄa© ¤å*//7:•eddžžNff&™™™Æqaa¡ñxOOO"""ŒÅõ½zõ"((Ș´ 4&.µ¯\ ‹ÅB»víh×®Ý?§¶¶–ÜÜ\£HÉÖ¡ÆÖµæðáÃlܸ‘ãÇ“——g<ÏÝÝÈÈH£3œ­X$22’¨¨(ÂÃÃ/*‡X¹»»óùçŸÓ¯_?ž{î9^~ùe³#‰È/ðÅ_°oß>-Zdv‘KVUUÅþýûIÛÔÔTc7ÔŠŠ """Œ‚¡C‡6( P©}iÓ¦ ]ºt9瀥¥¥ |l—M›6qøðacüÖVäcÇ‹§[·nøúú6ã;’+éOü‰Å,f%+iãvÙóUUU‘žžÎ‘#G\>Ì‘#GÈÊÊ2ºÞz{{ÌÂÃÃéÑ£‡:>ÞXÚ¡Cœ›ã-:œ˜˜^}õU|ðA&OžÌ€ÌŽ$"¡¦¦†ùóçsûí·Ÿsó{ÔÔ8ÞöíÛ)// 44”øøx†Îý÷ßO\\½zõÂÇÇÇäär9y{{Ó³gÏ&Ï «««IOO7Æñ:Äúõëyë­·8õï ˜BCCŒã 4è‚;˜ˆýØÆ6žäIæ2—QŒ2;Žˆˆ8Û˜jýsÛ×¶bbÛxjtt4ñññLš4ÉG £S§NK•ËÎÍͨ¨(¢¢¢Îù¸‚‚c¼³þœÀ·ß~ËþýûŽßõ×9œ9î©«ö/++‹µkײyóf£¤°°gggºvíJŸ>}xâ‰'Œ…ÈþþþfGiQ\\\Œ Kþë¿þ˸½   AaÞ¢E‹xùå—©©©Áßßß(0`‰‰‰ê$ò %''»»»ÙQDÄÎiÅ›8[‡„\Vûöí#))‰M›6±uëV~þùgªªªðññ¡OŸ> 8x€îݻӵkW\´rnnnÄÇÇÏM7ÝdÜ~úôiöîÝË®]»Ø¶m[·nå믿¦¤¤777zôèABB$11ñœ»Šˆ˜¥šjœÑbg{VZZÊž={HMM%--ƒ—¢¢"\]]騱#;w¦k×®Œ?žÎ;EDDíÛk—Ti=ÜÝÝ #,,Œ„„„&SVVftÊ9|ø°ñ;µfÍþú׿RRRXÏ;uêDçÎéܹ3]ºt1:Åif̘Á¢E‹¸ãŽ;HNNV÷ òé§Ÿ²ÿ~/^lv‘óÚ»w/IIIlÞ¼™mÛ¶5ÇëÝ»7ýû÷ç¾ûî£{÷îtëÖMÿ ...Æ"·I“&·×ÖÖräÈRSSÙ¾};Û·oçý÷ßgîܹ€uGݾ}ûÒ¯_?†Jÿþý5.lÇNr’¹‘á ç÷üÞì8""b‡rrrHII!%%…´´4£ƒÜÉ“'ðòò¢k×®tíÚ•ë®»Ž'žx‚èèh:vì¨BQ±kßäý¹¹¹9r„C‡ö“’’øàƒŒévíÚѵkWºuëF·nÝèÕ«½zõR÷DÙÖ°¬Y³†¤¤$<ˆ‹‹ =zô oß¾L™2…¾}ûÒ³gOm\*"b¢€€FŨQÿÙ˜âÔ©S¤¤¤E"«V­bÁ‚TWWsÕUW1lØ0†ΰaôvìmß¾½Qב¦¨ D‡­Cˆ>ÐýbµµµìÚµ‹5kÖ°víZÖ¬YCNNžžž$$$0|øp}ôQúõëGLL NNNfGáîîn ŽÝvÛm€õÏ[ý]*·nÝÊ'Ÿ|Byy9¡¡¡$&&’˜˜Èˆ#ˆ×Ÿ71] 5êb'Š‹‹Â={ö°k×.öîÝËÑ£G©««ÃÃØ˜:wį̂Q£˜1c†±H=**J] D.‚§§§1ÙÙ”ÜÜÜÅW`ëÖ­üýïçĉ€uÀ×¶³ž­H$..Žðððæ|+¦²X,¼ûî»ôèуùóç3þ|³#‰È¨©©áÅ_dúôéÄÄĘG¤ššRRRŒ1¼¤¤$rssñòò"!!#FðØc‘ q<¹hNNNMŠäää4è6³`Ážzê)<<<0`Ç'11‘!C†àíímâ;›Zj¹Û©¡†Ïø 'ôwˆHkfë g+þرc)))?~€ˆˆbccéÛ·/·Þz«Qirr‘+#((ˆ   &;ú¦§§³oß>ÒÒÒØ³gûöícÅŠdffB¯^½èÝ»·1ƒ‹‹æ‘.§ššöîÝ˺uëHJJâ§Ÿ~"==OOOúôéÃ7ÞÈСCILLTçVj̘1¬\¹²Ùž©ßïJ¿žˆ½óòòbÈ! 2ĸíÔ©S$''³nÝ:¾ÿþ{|ðAÊËË ¦ÿþ 6Œ¡C‡2pà@\]]MLoêêêØ±cS¦L1;Šˆ8}RÇQYi½vs37‡ƒÉÎÎféÒ¥|ûí·üøãàççǰaÃxä‘GHLL¤_¿~¸éç*—™““±±±ÄÆÆrûí·PYYÉ–-[X»v-k×®åé§Ÿ¦¸¸˜¶mÛ2räHÆÏ„  19½ˆ´FN8QMµÙ1ZcÇŽ…ƒÛ¶mc×®]¤§§ЦMbcc‰‹‹ãÞ{ï%66–øøx:uꤢ‘fb›4|˜ï¾ûÎì("9r„o¿ý–o¿ý–µk×c&Æ ã‰'ž`ذa$$$hbT®˜&L˜À„ ŒÛ8`ìÎûå—_2þ|\\\èÓ§×^{-'N¤ÿþ*J2É\æò=ßó#?ˆvpimŽ9ÂúõëÙ°a7nd×®]TTTàêêJ\\½zõâÚk¯5µ·k×ÎìÈ"v#**Ѝ¨(®¹æš·Ÿ8qÂ(¦JIIaÙ²e¼ñÆTUUѦMºwïΠAƒù„·Þz‹ýû÷SVVFLL &LàÖ[o¥{÷îWü}šÉËË‹aÆ1lØ0fÍšEee%[·neíÚµ$%%ñòË/SXXˆ¯¯/#FŒ`ܸqŒ7Žèèh³£›îÀ¨Cˆˆ\KÝ%ük»mÛ6.[ ‘³Z²®»ÎÚ)¤M³ÓØ­ÚÚZ¶mÛÆ7ß|ÃÒ¥KÙ¾};Œ5бcÇ’˜˜HÏž=[ì",q,555ìܹ“5kÖ°bÅ V¯^MEE L˜0‰'’Ðä ȕԜÿ¾Y,¾üòË+6óî»ï2cÆŒ+òÚ"-ÍÜI.¹,e©ÙQZ¬œœ£ðÃvÅb¡K—.$$$ЫW/£Ó@ÇŽµ¨HÄåçç"?ÿü3Û¶mcçÎTTTàååEïÞ½‘~ýúѵk×ó»^WWÇØ±cÉÊÊbûöí¸»»›©Ù,\¸©S§6ˤΕ>_·›/\¸ðм~}:_7OUUݺuãšk®áwÞ1;Ž´RÕÕÕ$%%ñí·ß²téRvïÞ¯¯/cÆŒaôèÑ$&&ßìã""ç’ÍÚµkùñÇùî»ï8räŒ7Ž &píµ×jçÞf²’•üŠ_ñÿøÜ˽fÇq(úYÌžÖCTVV²mÛ66lØÀúõëY¿~=ÙÙÙ¸ººÒ·o_ DŸ>}ŒñUm(rùTVV’ššJJJ ÉÉÉlܸ‘äädªªª 3vBùä¾üòK²³³‰ŠŠ2vs5jmTD# ¬¬Œ~ø¥K—²téR222 cêÔ©üæ7¿¡OŸ>Í’Ãþ¾\´ÀLäÂ=Àìa«Ymv”cÿþýüôÓOüôÓOüøãF«õÎ;7X Þ·o_üüüLN+"WRUU»víjP¶sçN*++ñññaÈ!Œ1‚#Fп‡Ô=xð ={öä¹çžãÉ'Ÿ4;N³QAÈ/£óuó¼ûî»üö·¿%--N:™GZ‘ÚÚZÖ¬YçŸ~ÊW_}Eaa!]»ve„ Œ?žÄÄD-‡’ššjt¨^·n£Fâ¶ÛncòäÉxk,ÿŠ8ÊQúÑk¹–OùÔì8""Ç BêêêØ¹s'Ë—/gùòå¬_¿žŠŠ ÎC† ¡_¿~š1AYY[·n5 ´6lØ@~~> 6Œk¯½–k¯½–=z˜Õååå¬X±‚E‹±dÉ Œ.ƒãÆcÈ!=|¹mÿ÷ßÏŒ38tè ©Z.„\y®®®TTT8ÌfÅçú¹½ûôÒKìØ±Ã˜ÏÎÌÌäúë¯gõêÕøøø4zÎ÷ßß &88˜Ÿþ™   Û½{7ñññvñÿÌžTUU±nÝ:£›TJJ mÛ¶eÒ¤IÜpà Œ;¶ÕœÿÞyç9r„Õ«[Æ„HKfëM[oyµ8ëuuµ¹9ìHzz:/½ôñññ$$$°téRî»ï>RRR8zô(o½õ&Lh5'Aâø<==™8q"o¿ý6éééìØ±ƒ3f°dÉúöíK÷îÝyå•WÈÈÈ0;ªˆ´@žxRN¹Ù1ZZZï¾û.·ÝváááÄÄÄðÈ#““ÃŒ3X¹r%'OžäÀ|ñÅ<ñÄ\}õÕ*i\]]éÓ§wß}7ùË_زe %%%lÙ²…W^y…¶mÛò§?ý‰¡C‡Àرcyá…X·n•••fÇ¿(;wæ‰'ž`þüùäää˜GDšPUUÅË/¿Ì=÷Ü£bi6?ÿü3³fÍ¢cÇŽ\}õÕ$''3{öl8ÀÞ½{yýõ×=z´ŠAÄáÄÇÇóä“Oòã?’——ǧŸ~Ч§'÷ÜsÁÁÁÜvÛm|ûí·Tk\ÿ²© ‚ÉL&œpÞã=³ãˆˆÈe”ŸŸÏçŸÎôéÓ §wïÞ¼öÚk„„„ðöÛo“––Fnn.‹/fÖ¬Y$&&jXÄ$žžž >œ§žzŠýë_äåå±wï^Þzë-yå•WèÙ³'áááÜu×]|ùå—œ8qÂìØWTmm-«V­búôé1yòd:ÄìÙ³9räÛ·o祗^bĈ*¹@ ++«Ñí»víb„ øøøàããøqãØµkWƒÇX,, {öìaРAxzz’˜˜ÈHMMeàÀx{{sõÕW“žžÞä÷?~¼ñ=Æßà{Ø^ßb±ðãÿŸ½û‹âêß~¯ÔeAQP–HlÑ V,Q¤X¢HŒ P A#Ö'SÔØcl±l1ò¨  J°ƒÑ',Á¨ˆé@zø¾øc_Ö]`–Ýe<ŸëâJö0sÎ=EæìÌœ™Ë€   ©rEòÊóz=5Ëž>>pttÄ?ü )Û²e |}}å©A%%%r¯‡wïÞ ‘CGG#FŒÀ† pçÎ$%%᫯¾BBB&Mš„¶mÛböìÙøã?PUUÅw\µúóÏ?1lØ0¾c0 ÓD°!LÓQ= ¤²’ßàòåË;v,lll°e˼ÿþû¸víâââðí·ßâÝwßå;"èD¯^½°bÅ ÄÇÇ#22ÇǦM› ‹1~üxüïÿã;"Ã0Í¢¸òòr\¸psæÌ••ºvíŠÅ‹#''sçÎÅÕ«W‘——‡ððp|óÍ7ppp€‰‰ ß±†Ñºººèׯ|}}qäȤ¥¥ááÇغu+ÌÍÍáïï{{{˜˜˜ÀÕÕl2M—.] ¬\¹’ï( Ãȱwï^¤¥¥áË/¿ä; ÓÌUTT ((vvvx÷ÝwqìØ1xzzâÁƒ¸uë-Z„N:ñ“aT¦U«V˜2e Nœ8ôôtlÙ²?Ƹqã`ee…Õ«W7™þœ&ó’„ßð„`73 Ã4u999øé§Ÿ0lØ0´mÛžžžHNNÆÜ¹sñ÷ßãÙ³g8|ø0fÍš…Î;ó—a˜:téÒ³gÏÆ‘#G‘‘›7oÂ××ñññpww‡¹¹9FŒ€€äææòWe=z„åË—ÃÚÚø÷ß±~ýz¤¦¦âÊ•+X¸p!¬­­ùŽÙ$ݼyb±Xª,!!£GÆøñã‘””„””¸»»c„ xúô©dºê›ÌW®\‰ŸþéééèÕ«ÜÝݱbÅ üðC$&&JnàwvvFBB€Wç<:uê„ÜÜ\Œ10uêT¤¥¥ÁÔÔEEE å•GÞòÕe^^^ðõõEzz:¢¢¢PRR‚%K–pžŸˆ$?ûöí«uz˜0afΜ‰ÜÜ\\¿~¹¹¹˜7o^Ù몯fÛeee°³³Ã¶mÛ(·¾^ ±XŒèèh\½zURÎ¥êùììì@D°µµ…¡¡!ªªª`kk‹;wîÈ웯/wõ2nذAªÜ××K–,AZZΜ9#•«¾ý®f= Ù·ÊÛÛ¡¡¡’ÏçÎÃÈ‘#9Ï?~üx,[¶ yyy*É󦱱±Á¢E‹põêU¤¦¦bíÚµ¸ÿ>F±XŒ¯¾úJ%ƒ4Mjj*’’’0tèP¾£0 ÓT¢¢¢(**J™*†»?ÿ$ˆž=ã; /***èØ±cdggGhøðátâÄ *//ç;Ã4ª²²2 ¡¡C‡²³³£cÇŽQEE…ÊÚhÌã V[ýj«›aš›u´ŽlÉ–ïïùóçtìØ1š>}:µjÕŠPïÞ½iÕªUtíÚ5zùò%߆iFhÏž=4vìXÒ××'---zÿý÷iÛ¶m”’’Âw¼:ýòË/¤¥¥EÑÑÑ|GiÁÁÁ¤äi&ÎÔÝ_Ÿýúë¯4eÊ …¤««KãÆ£#GŽPQQßñäÒ¥K4iÒ$ÒÒÒ¢öíÛÓòåËéßÿå;V“Tó¼jEE¥¦¦Ò¡C‡È‚~ùå©ig̘A7n”©cÿþýäçç'SïåË—%ŸSSSeÊž«j}Ý¿ŸÄb1ݸqCfEÚèÝ»7ݹsGòùÌ™34räHÎ9ä•_ºtIîï¸îwÕõ4dßV4oµ¢¢"‰D’ÏB¡ÊÊÊ8ÕMDT\\L , SSSrqq¡]»vÑÓ§O9ÏÏÈ÷àÁZ¶lµk׎´µµÉÍÍMjhê>L:::M¶Ÿ Oc^c˜Æ¦ ÷›²7„0MGõB^¾ä7BCCѵkWL›6 ÖÖÖ¸qã._¾Œ‰'²Wi2o]]]¸ººâÏ?ÿĵk×СCL:]»v•‘Ï0 £(D(F1ß14REE~ÿýw¸ºº¢M›6˜6mÒÒÒ°råJÉk¾¿ýö[ 8ÚÕ}6†aè@Ÿ IDATÔ©¼½½qêÔ)dffâèÑ£h×®V¬X±XŒ~ýúaÇŽùD=ôéÓ ,à; Ã05ìÙ³ÙÙÙXºt)ßQ˜fèåË—øá‡СC|ýõ×?~<âãã {{{¾#2 /Äb1Ö®]‹ÇcÆ 8wîºtéwww¤¥¥ñ¯É8óXŽåØ„Mƒ1|Ça†aàöíÛøä“Oжm[Ìš5 D„Ÿþ8rä\]]ѲeK¾c2 £ÆÆÆpssCPP222°oß>TVVÂÓÓmÛ¶…——îܹÃwLN"""0`À¼ÿþûxôè8€””¬]»]»vå;^“%  ­­ KKK,]ºðôô”š.""S¦L‘™ìر—)ïÓ§äÿ-,,dÊÚ·o¬¬,™6&Nœ(S—««+"""$ŸgÏžŸþYjšÀÛÛ»Áy¹6l˜Ôg±XŒôôô×§•\“8zô(.]º„={öHÊT±¾’““1~üx8pýû÷—ù½"mÌœ9’Ï;wîÄ¢E‹8娼LÕ¹¸ìwÕ²o7Õñ¶. °uëV$%%ÁÓÓׯ_Ç{g3f   @%ßDݺuÃúõëñèÑ#9r1bˆ°°0¾ã)í?þ€D"ßQ†i"Ø€¦é06~õß7¨#”€±cÇÂÕÕÄÇqüøñZ;Ç ó¦8p ~ûí7ÄÆÆ¢ÿþpuuŸq㘘Èw4†aš s˜# Y¨BßQ4Fzz:¾þúktèЮ®®(,,ÄîÝ»ñìÙ3\ºt óçÏg¯ùf¦ÑaòäÉ8|ø0233qþüy¼ûî»X¾|9Ú·oéÓ§ãæÍ›|Ç”ؼy3þøãœ>}šï8 Ã(--ÅÆáëë KKK¾ã0ÍLDDzõê…o¿ýóæÍÃãÇñã?ÂÆÆ†ïh £1D"æÍ›‡¸¸89r×®]C×®]±iÓ&¼|¥ˆ‡xˆ©˜Šé˜Ž…XÈw†aFAçϟǰaÃЧOܸq7nDZZN:…3fÀÐÐïˆ £”Ñ£Gó¡I122‚‡‡Μ9ƒ´´4lذ×®]CïÞ½1bÄ¥n’W§ÈÈH 2£GF›6mpóæMDEEÁÓÓ“=ÈTˆHò“——‡uëÖÁÇÇ)))RÓåää@,KTÿXXXÈL ¼ÚߪµhÑBnÙë7¶gggKn°¯©]»vÈÎΖ|~ûí·a``€èèhÀŸþ‰–-[JÝ”¯h^®LLL¤>ëéé)tƒ>WYYYðòò‚••´µµ%ù•‹ àøñãRýU¬/ggg¡¸Xþƒicúôé8vìJJJ””„äädŒ£Ü ä–sÝïª5dßn¨ S§N’Ï;vDjjªÂõcêÔ©8tèRRRPZZ ???•d|“éèè`òäÉÇõë×Ѻuk|ðÁ°··Çõë×ùŽ× D„³gÏ*ýïa˜7 Â4ff¯þ+§“×ÜTUUaÍš5èÙ³'ž>}Šÿýï ”ê\ªÚë}@ 8ÇŽS[»|SÕ—µ¦Ð¶ºÛãs]¾ýöÛ8|ø0.]º„G¡GX·nªªØMÝ Ãpg T Ùhþ}ú$''ãã?†X,ÆÞ½{áííÄÄD\¼x³gφYu¿¬‘¼ÞG100@·nÝàíí¸¸¸FÍ¢jê8vòµ¾šË¯›ËrÔ¦¹,Ÿ®®.qàÀ¤§§c÷îÝxøð! {{{\¸p¡C‡bÒ¤Iøúë¯Õr1ŠaÅøûû#77K–,á; ÓŒäææâ£>ÂèѣѥKÜ¿ß}÷LMMÕÖ&ëkN½š¢©-Ÿ––¦L™‚˜˜,^¼+V¬À»ï¾‹7nðM#å!ã1ÝÐ {±—ï8 Ã0Œ"##1xð`8;;ÃÐÐ/^DLL æÎÛhçXå]~ý§!õi*ví—ª¾&ûú>* aee…ñãÇãàÁƒ(//¯uÞÄÄDøøø@,COO­ZµÂ AƒðÕW_InZ^ÝhyèÐ! 8­[·†P(D¯^½°|ùrÄÄĨtyêÒ¦M|þù爉‰ADD„B!5ê&Ö‚‚øúúbèС …¸~ý:N:;;;¾£5[­ZµÂ¬Y³0kÖ,¬X±BêwfffÈÍÍ•@RýSRR¢’öÍÌÌä¾m#==]æø5{ölìß¿°ÿ~©·ƒ4V^uòðð€H$ÂÕ«WQZZ*É®Œââb¸¹¹aãÆèÑ£‡ÔïT±¾V®\‰Ó§OÃËËKî÷lEÚhÓ¦ † ‚   øûûÃÏÏOmÇEö»Æ õöggg¥ï‰D"lݺ'OžT6SÀpúôiDFFBWWC† Áܹs›Ü›Xþù礥¥aìØ±|Ga¦ aB˜¦ÃÔ€œ¾“¨U~~>\\\°víZ¬[··nÝÂСCÕÞnõ–êNþË—/ƒ9sæ`Þ¼yرc‡Ú3ðÏ›²»mu·§ 7¸ >·o߯š5kðÝwßaìØ±M®SÏ0 ,ðê‰#ÏðŒç$ü)((ÀüùóѵkW\½z»wïÆ£GðÝwßA,ó–«f?¥ªª 8r䬬¬0pà@¹ñ[S°õżi ññÇãÖ­[¸|ù2Œáää„‘#GâîÝ»|ÇÃwß}‡èèhœ:uŠï( óF{ñâ~øáÌ;íÛ·ç;ÓLܾ}}úôÁõë×qöìYœ8q¢QÞÂú{Ls! ±bÅ }ºÔï#¯¢Ð‰U«VA,C[[”ÈòÙgŸaÀ€˜5k–L&U¬¯iÓ¦¡_¿~øïÿ‹I“&Éü½T´™3gb×®] …§§'§ ¡È~טüýýqñâE,Z´HR¶hÑ"ìÚµ«Ö¿Ï!!!Rý>@ ÷ /ÚÚÚìMqj2hÐ üñÇøå—_püøqôìÙÿý7ß±8;}ú4Úµk'Õwa†©)!**Š¢¢¢”©‚aÓªQ@ß)Ô&;;›úôéCVVVô÷ß7zûµýI'›FNÓx”üSؤÚVw{|®Ë×ݼy“,--©oß¾”­Ð¼y|@ÁÁÁj«? ÿÍdUË£<ÎÓy¾£ðâôéÓdiiImÛ¶¥€€*//ç;’”ÚŽ1aaaÔ³gÏFN£:ê:vò±¾4© Œæ²µiîËWíÊ•+4hÐ ÒÑÑ¡o¾ù†^¾|Ékž &PŸ>}¨ªªŠ×êÜhû—ºûë“'O¦É“'«­þšX½ñlÚ´‰D"eddð…i&þúë/266&ÊÊÊjôöYÿX3êÕÍaù*++iÕªU$è»ï¾ã;ŽÆ˜GóHHBú›ÿzÃ0LsÆõ»XC¾FFFRÇŽÉÒÒ’~ýõ׆ÄS©×û õ}nHš†]ûmêZ¶€€‹Å”ŸŸ/){òä õíÛ— åÎ.U§¹¹¹Üs÷ïßטõzìØ1jß¾=Y[[Óµk×½ýÀÀ@Ò××§1cÆPNNN£·ÿ&©kŸsww§~øAò999™zöìIǧììl*,,¤°°0²´´¤“'OÖ[/—²¸¸8²´´¤½{÷RFFeffÒ¾}ûÈÒÒ’âããeæŸ={69::Ò'Ÿ|"ó;EòÊÃu™ÖÊÊŠ"##©¼¼œÎŸ?OVVVµNïääDÞÞÞ”œœLeeeOžžž ^·?ýôõìÙ“JJJäN§êõD¶¶¶ôìÙ3I™¢m”••‘©©)-_¾¼ÞökËQ_9‘bû]C×?—¼/^¼ ääd:zô(5ŠÞy犋‹“™',,ŒúöíK'Ož¤¼¼<*--¥øøxZ³f YZZRtt´TýöööI………T\\LQQQ4jÔ(Zµj§ŒLÃegg“³³3éëëÓáÇùŽÃIÿþýÉËË‹ï*ט×ߦ±iÂý¦l@Ó´têD´nÊ«@´zõj²¶¶&]]]zûí·iÇŽ2Óþþûï4`À200 0`:uJé eee4tèP‹Å”””¤t} Q[G¸  €ôôôdÊïÝ»GcÆŒ!CCC244$'''ºwïžLèñãÇôÁ¡¡!™››ÓŒ3äÞ¤_W—.]’ÔW3ëóçÏÉÐÐRSS%eZZZ’é.]º¤ðrsY6"¢ÐÐP^ÓêãíÙ³‡;FÑ6ÚF-¨“êÏjú¹ †auS×€°°0Ò××§±cÇò2h˜‹ºŽï\¯{r½¨ÌùM®Y¸.ã›píW™m‘œœ,ÕŸŒ§çÏŸK•%''ךáõœŠÞ#Pß²ÉãããC_ýµäóÂ… eú)u144¤ÒÒRÎÓó%33“\\\H(ÒéÓ§­ÝãÇS‹-è‹/¾ ÊÊÊFk÷M$"##%¿¯>¶ÄÅÅ‘››“H$";;; ©·^®eDDÑÑÑäììL"‘ˆD"9;;KÝà^ÓŸþIj½Ã%/×uS[^E¦=vìÙØØ®®.ÙÚÚRhhh­Ógdd‡‡™››“®®.õèÑCr.¼!ëVOOOî÷øšÓ4t}KÕ•šš*U```ƒÚxùò%‰ÅbJKK«7ÃëË]Ûú¨mç²ß)»o×—WOOÚ·oOcÇŽ¥Ôy¬¸ÿ>MŸ>ÌÍÍIOOÄb1y{{SBB‚Ôt×®]#ooo²µµ%]]] …Ô»woÚ±cG³~˜&©¨¨ Å‹S‹-8ý{âSFFF“ÈÙl@ÓœiÂý¦l@Ó´ôïO´x±Ê«@>>>´|ùrЧ¢¢"º|ù2½õÖ[$™îÚµkdjjJ‡¦ÌÌLÊÈÈ ÀÀ@211¡›7o*•á›o¾!###Љ‰Qvq¬¶Npxx8uéÒEª,>>ž,,,Èßߟ233);;›é­·Þ¢'OžÈÔëèèHgÏž¥çÏŸÓãÇÉÕÕ•fÍš¥pQQQÔ²eKª¨¨ÌwðàA²´´¤­[·JÕ×µkWN£^_nE—- €Š‹‹)==<==iæÌ™RÓ$$$P‡hÿþý”‘‘AYYYHƒ RºíÑ£GÓ_ýE%%%tæÌ…N V·gee%É–‘‘A{÷î•aÿðáCêÒ¥ …‡‡Sqq1Ý¿Ÿ†*÷Kvµ¨¨(‹Åµžh,ÑÑÑdhhH+W®ä<& U…]üðC©ùmCEú ]_\sN˜0V®\IÏž=£ÒÒRúçŸhÈ!uöjz½/”MG¥=zȧ¾íÇe½pͬȺSd»ÖVoCö§êyëës*»ÔÝåÓÝ»wÉÚÚšúöíKÅÅżåpqq¡AƒñÖ¾º±! ÃúëcýúõdhhH™™™|GašÊÊJêÛ·/õïߟ^¼xÁ[Ö?VMÿ˜õñ¤ëД>ÞÊ•+IWW—bccyi_œ¢S¤EZ´6¨¥~M?WÁ0 £nêróæMÒÕÕ%///©ë˜š¦®ã;—þÉëuÔv=Pý7.Y¸,ã›ríWÙmMvvvRóVUU‘­­-ݹs‡sŽše\îP¤Îšþþûo©ï>ݺuSèÁ›Ó¦M£ Pnn.çyøRQQAü1éééÑß«ÿÍq·oß&}}}š?¾ÚÛbF󓇇ß1F%>ÿüs …t÷î]¾£Ôj÷îÝ$‰¨¨¨ˆï(*Ç„0Í™&ÜoÊ„0MË„ DS§ª¼ZôÍ7ßÈ”Ÿ>}š  ùcÆ Ú¸Qö†Ùýû÷“ŸŸŸL½¯¿V066–Ú·o¯pUUUÔ¶m[©W¢Ž5ŠÂÃÃiðàÁ’²øøxjÛ¶-§‘Ô¯ŸLRdÙ^—››K¦¦¦Reîîî´mÛ6™iþùg¥ÚêûÉëäµ'/ÛæÍ›ÉÝÝ]òyÚ´itèÐ!©ibcck½ø|ÿþ}‹Åõ>…»±|ÿý÷d``ÀùiLšp€VvƒÃ(f OéS•ÖY^^N–––2ƒ>³³³©U«V’W’{xxH½ò™ˆèäÉ“4fÌÉgCCC*((PY¶ .@ Pè©Y|©>ÆTUUÑóçÏéöíÛ´råJ255¥ .ÈL[Û1’뱯ºžË—/K>W?I§fÙ“'OÈÜÜ\©6jËÙÐþHÍzëZ_\sʼ9&))‰óMhµõ…Ž=Zë…º¶—õÂ5³¡Oꟸ4ù\Ã0LcPõ€—/_’­­-9;;kü“ôùî.¯R³Žº®*ÛãšEž7ñÚ¯ª¶EïÞ½¥œ9s†FŽÉ9Çëe\îP¤ÎšŠŠŠH$I> …B…ÞàX\\L , SSSrqq¡]»vÑÓ§O9Ïߨ*++ÉÑÑ‘:wî,y“›º899Ñ Aƒ4þïÃ0ê¼zëKŸ>}èŸþá;èDEE 0€\\\øŽR«aÆÑT5Ü« Ø€¦9Ó„ûMÙ€¦iY¼˜¨_?•W €eÊsssÉÄÄDò¹cÇŽôøñc™é=zDÖÖÖ nßßߟŒŒŒx}Z-‘ìkðP»ví(<<\fÚ¶mÛRJJŠLù³gϨ[·n2õ¾þdÒÒR ªsæÌ™´fÍ""zúô)õû¿}bäÈ‘’§~lÞ¼¹ÁO‰QdÙ”©/==]©¶(¼Ïpm/))‰,,,¤¦KMMåTRR½õÖ[u£[õ Au½\l@Ãh–Ïè3NÃU^ïÚµkÉËËKªlÆ ROSéÔ©ÅÅÅIM“››K–––’Ïýû÷'ooo•]”ùäzøð¡Üik;Fr=öU×SXX(ù\YY)·ŒkŸ¦¶6É©H¤¾õÅ5çèÑ£iÈ!töìÙ:/4)º,ÙÙÙµ^(Ttû½¾^¸f–GÑå·]k«·!ûלêÚNªê‡j‚_~ù…tttä~§k,#FŒ qãÆñÖ¾:±! Ãúëê·fÍjÙ²¥Ì Õ ÓPcÆŒ¡‰'òƒõÿOCÏ×5$[mõ²>žz’®®.åååñ¥Q=¥§dEVäDNô’Ô{£Ÿ¦ž«`†i ª¾^tìØ1ÒÖÖ–{ÜÕ4Š~w¯íü]}וí¿qÍÂeºæ~íW•ÛbÛ¶mRT3f :uŠSye\îP¤Îšž?.3 ¤´´”SÝ5åççÓÑ£GÉÃÃÌÌÌhúô锟Ÿ¯p=!))‰´µµé×_U[Õ…¼xñ¢ÚÚ`¦i@:::´`Á¾£0ŒJ…‡‡¹çQù–ššJ-Z´TÛ\°!Ls¦ ÷›¶Ã4%: j©Z,Ë”™˜˜ //Oò9##æææ2ÓµmÛÏž=kpÛÑÑÑèׯ \‡ªÐ«b "äååaݺuðññAJJŠÔt999‹ÅR?2Ó¯ÖeMzzzxõ·Iñ:]\\pñâEÀ‘#G0}útÀÔ©S Ø1c´¸æÈÊÊ‚——¬¬¬ ­­-™îuÙÙÙ°°°)—W¦èzUvŸ©-[»ví-5™™§:QTT„ââb¥²©’H$‚îÞ½Ëw†a4\tA,bU^¯BBB$[+++±{÷n|þùç’ižJII bcc±oß>tîÜYî´µ#¹ûªIþ¿E‹rË^_ÿж!¢}yê[_\s?~ýû÷ÇܹsajjŠaÆaûöíxùò%§µµÓºuëZç©mûq]/ÊfVd9Ù® ÙŸ¸ö9•]æÆî‡òaúôéÐÒÒÂÕ«WyËàçç‡Ó§O#..Ž· ó&),,Ä–-[°páB˜ššò‡i&îÞ½‹áÇóë+Ú?–‡õñ¤iRoĈ(//Ç¿ÿþËw”FSˆBŒÁÃA‚6´ÕÚž&ž«`†iª"##Ñ¿X[[ó¥Á¸öOªÕw=P™þ›¢YêÓܯýªr[LŸ>ÇŽCII ’’’œœÜàkî·{êÁƒèÔ©“äsÇŽ‘ššªp=ÆÆÆ˜:u*:„”””––ÂÏÏO%UÍÆÆvvvøë¯¿ÔÖÆíÛ·¡««‹¡C‡ª­ †aš"Byy9¶nÝÊw†Q©áÇCWWwîÜá;ŠŒ£G¢eË–pvvæ; Ã0MÂ4-¶¶@~>Àñbœ"ª/ÖÕ¥mÛ¶ÈÌÌ”)ÏÌÌ”{‚‡«/^@__¿Áó«K«V­0kÖ,Ìš5 +V¬ú™™rss¥Ô¼Ý\ëtttÄÍ›7ñâÅ =zÓ¦M¸¹¹!$$yyy¸~ý:ÕšÃÃÃ"‘W¯^Eii©dyõÉ0”““Óà¶UÅÌÌLêâ]µôôt© ­[·æ||åÊ•8}ú4¼¼¼pãÆ •eU–P(TË:d¦yé‚.È@òWÿÄ 033äI“ ………ú÷ï/™¦eË–HKK“ùû_YY)™ÆÆÆ'Nœ@~~>‚‚‚0hÐ ¬]»^^^ g*))Aee¥ÔJo®Ç>¾ÛhŒ>×œÆÆÆØ²e ‘””„¹sç"((HÒãÒŽ¼¾PCTs]/Êf®­muï;òpísªb;5f?”ÚÚÚ …(,,ä-Ã|±XŒÝ»wó–aÞ$[·nEee%æÏŸÏw¦yñâ„B!ß1TæMï³>žæªþw¦é9U¥åpƒ²‘³8‹Vh¥ö65ñ\Ã0LSUTT‘HÄw ¥píŸT«ïz 2ýE³Ô§¹_ûUå¶hÓ¦ † ‚   øûûÃÏÏO©Á8ꀉ'J>;;;#<<\©:E"¶nÝŠ“'O*Om QTT¤¶ú‹ŠŠ  ¡£££¶6FY£Gæ;Ã0M˜ŽŽï× kŒI“&AOOï( Ã4Al@Ó´ØÚ¾ú¯šÞRŸ>}ú ,,L¦ü÷ßGŸ>}\ï[o½…û÷ïkì³æÌ™ƒÐÐPäææJÊœœœpùòe™i¯\¹ÒàVÍie IDATuÁµN¼÷Þ{صkÚ¶m+Œcjj lÙ²ýúõC«V »hÆ5Gdd$V­Z±X míWOk“wâÎÑÑQîI£ê·œ4¤mUqpp@HHˆLù‰'ààà ù>^å¹EDˆ‰‰mõß ¢©'QæMÕ]ñPåuÏŸ?þþþxùò%vìØyóæIýþý÷ßGhh(§ºôôôЫW/øøøàìÙ³V8OëÖ­Ñ¡C¥/45\}ÕFmÇÆèpÍ)ðôéS¯.N™2§OŸÆ… ¤æ«mYjë ?^áÌ\× ×ÌòÔ¶±ïÈõϩìvjì~(nÞ¼‰¼¼<ôîÝ›· ZZZ˜3g8€‚‚Þr¨CõCªªªxN¢<@ ±ßÏî °}ûv,^¼X扤 £ŒN:áÞ½{|ÇP™7¥ÌúxÒšB/::4ò|žªU¡ ðÀMÜÄ)œBth´¶5í\Ã0LSõÎ;ïàÖ­[Mz #×þIµú®*ÓQ4K}šûµ_Uo‹™3gb×®] …§§§J2ªš¿¿?.^¼ˆE‹IÊ-Z„]»v!??_î­É÷xÜ¿#GŽ„‘‘‘ÒÛº1¤¦¦ÂÝÝíÛ·‡––V­™›Â²Tk*9ßdñññ(((Pëñ´!>|ˆ¨¨(L™2…ï(jCDìßÃ(©ú¶ÜK¤„¨¨(ŠŠŠR¦ †QLE‘®.Q` J«­ëŸBÍßýõ×_dbbB‡¦ÌÌLÊÌ̤Ç“©©)ݸq£Áíß½{—Ðùóç\‡*ÔµÜÝÝé‡~|NNN¦ž={ÒñãÇ);;› ),,Œ,--éäÉ“œê}½\‘:×®]K"‘ˆ>,U~øðaÒÑÑ¡µk×rZfer899‘··7%''SYYÅÇÇ“§§§L}‰‰‰Ô¡CÚ¿?effRNNS¯^½”Z ùþú´{÷î:ë "[[[zöì™ÂYUéÌ™3€îÝ»ÇiúÆ<¾ikkËìǪ ¶º¦9ª¤J’~¦ŸÕRÿ¨Q£èË/¿¤¶mÛRYY™Ôïbbb¨]»vtðàAÊÎΦ¢¢"Šˆˆ 1cÆH¦:t(Ò“'OèåË—”žžNË—/'—åÙ¼y3‰D"Îù¤Èq¯®i¹ûj«‡K™"mXYYQdd$•——ÓùóçÉÊÊŠˆë(ºÍ €œœœ(&&†JKKéÙ³gôå—_’««+§ey½/”MGŽ¡!C†p^ÇÕ¸®®™å©m9Ù®ò4tâÚçTv;©»Ê·ÒÒR4h 2„ï(”——G"‘ˆ¶oßÎw•:}ú4 çÏŸ«½-u÷×=<§*¶“ºú¡|+))!WWWjÕªýûï¿|Ç!"¢Ï>ûŒºtéBUUU|GQ™?ÿü“PzzºÚÛRwÝÇLJÔVM¬¿®yyyÔªU+Z·nßQ˜f(''‡Z·nMŸþ9oXÿX±þq}õ²>žæõñ"""H Ðo¿ýÆwµû†¾!-Ò¢ãtœ· šv®‚a¦1¨z@Ñ?þHZZZ*¹n¬.uï¹öO¹ØÐþ›"×`¹.cs¼ö«®mñòåK‹Å”––Æ)‡"ß ¸.ãëëCOOÚ·oOcÇŽ¥Piii­óÞ¿Ÿ¦OŸNæææ¤§§Gb±˜¼½½)!!Ajºk×®‘··7ÙÚÚ’®®. …BêÝ»7íØ±C#ÏÙ…„„––íܹSím’––:uJím1 £ì÷6eæoìðõuš˜©š¶¶v“º™\OO¯Î¿é5iòz¯©©ä|S…††’–––ZÜÛ¥¥¥Ô¦MZ³f ßQÔÊÁÁ :eš­Æºß´¸¸˜Èí«³!LÓãéIÔH7f4¦””233#777zùò%ßq¦Yxùò%¹ººR›6mèÑ£GœçkÌ㛡¡!íß¿_mõ³ÌFqsi.õ§þj©»¤¤„ôõõééÓ§j©¿!rrrhðàÁdbb"w9èKLL Y[[óƒiæhÀ€djjJW®\á;ŽDõ["ÿ÷¿ÿñEeþùçÀéiêÊRwÝÏÏìííÕVM¬¿®Ë—/§Ö­[sÈ0 qôèQìß0èAll,™™™Ñ”)SøŽ¢v»h H@{i/¯94ñ\Ã0Œº©c@Ñ«@èèèÐÞ½üþmge“‡‡ß1˜H[[›æÎÛhmz{{“H$Ò¨sªš.44”LzzzdmmM ,97U=ÈéñãÇôÁ¡¡!™››ÓŒ3ä¾ù¥æà³–-[ÒĉéÑ£Gœo8Wd~Eò×üùä“OªCÙ¬u 6KHH<êõéîÝ»GcÆŒ!CCC244$'''ºwïžLŽ;wîÐèÑ£ÉÀÀ€ŒŒŒÈÙÙ™Î;ÇyÜ»w\\\$í¸¸¸È´SWÞääd©ºãããéùóçReÉÉÉr×áë¹–.]ªÐúášûÁƒ4`À …dooOñññCýû÷'‘HD#FŒàt޼uY×ÀAye\¶k~~>-X°€lllHOO,,,hæÌ™tãÆ ""Îë¼¾zj[¦×÷úòªz=3ÿßåË—ÉÀÀ€>ýôS¾£È $mmmJMMå;ŠZÙÛÛÓüùêy8+Ãð­±î7ÍÏÏ'tþüy™ßµÃ45ÿ TUñD¥¬­­ñÛo¿áܹs˜2e ^¼xÁw$†iÒJJJðá‡âÂ…  AÇŽùŽ$—ŽŽ^¾|Éw †aj„A¸ÛxÕ‹óóó±qãFxzzÂÒÒR¥u+ÃÔÔ/^„››\]]1uêTûì3lݺ•ï8 ÓlDEEaذaèܹ38Àwµ Bæa6`¼àÅ[M=WÁ0 ÓTíÞ½_|ñ|||0}útäääð‰a8¸yó&6n܈… ò‡••…©S§Â××_~ù%vìØÑhmïÞ½ÎÎÎ5jöîÝÛhí6e&LÀÌ™3‘››‹ëׯ#77óæÍ“š†ˆ^^^ðõõEzz:¢¢¢PRR‚%K–HM›˜˜|øá‡HJJBbb"ÜÜÜ0uêTNy_‘üôêÔ "ìÛ·O¡:”ÍZA^™¯¯/–,Y‚´´4œ9sFòû„„Œ=ãÇGRRRRRàîîŽ &àéÓ§’éâââ0eÊ|ñÅÈÊÊÂõë×Q\\ gggNë !!A²‰‰‰HLLĤI“àì쌄„NyÅb1¢££agg"‚­­- QUU[[[ܹsb±Xîz|=Û† 8¯Er¯\¹?ÿü3ÒÓÓÑ«W/¸»»cÅŠ8xð RSSÑ­[7,^¼XnƺòÖüá‚ëv9s&Zµj…k×®¡  gΜABB  Ð:¯¯žÚ–©æþÁ%¯ª×3óJ@@1vìXìܹ“ï820a´oßžï(jUZZ }}}¾c0L“V}Ÿ©ŽŽŽì/•iÂÞÂð"*Š ú÷_¾“¨Å•+WÈÔÔ”zõêE>ä;Ã4Iÿþû/õìÙ“Z·nMýõ—Âó7æñ­M›6j}µ/{Z)Ã(.‰’ºBª{ê200 WWW*((PY½ªvîÜ9zë­·H__Ÿ-ZDééé|Gbš‰S§NѨQ£ÈÐÐZ¶lI}ûö¥ƒò‹i†ÊÊÊhïÞ½Ô¡C200 Õ«WSyy9ß±äò÷÷'¡PH¹¹¹|GQ‰ôôt@þù§ÚÛRw}õêÕÔ¹sgµÕ_믫Þ_|Afffôüùs¾£0o€M›6Q‹-hÆŒÝÏgMWUUEþþþ¤¯¯OÎÎÎTTTÄw$µ:M§I—ti1-æ5GS9WÁ0 £êzCHµsçÎQ»víÈÔÔ”víÚ¥±ç&¦&¤££C ,à;ʯ¬¬ŒvîÜI&&&diiI.\à%GUU}ûí·$hÊ”)ìš‘‚rssÉÔÔT¦øàœ;w™íZß:çZ²y«iÊznªÊË˱cÇtëÖ QQQ8{ö,V®\ @Àw4[¶lA—.]àààÀwµKKKköoAau«ë !l@Óôèè½{7oòDmÄb1"##±qãFìØ±Ý»wGhh(ß±F£8qÝ»wÇ®]»ðý÷ß#22ÖÖÖ|Ǫ—®®.;iÆ0h ªt@HS¢««‹yóæ!%%;wîÄÍ›7Ñ»woØÙÙÁßßyyy|Gd†TTTàÌ™3øðÃÑ¡ClÞ¼žžžHNN†¿¿¿ÆŸPlÙ²%&Ož,u¡) …h×®ùŽ¢4KKK¡°°ï(Œ‚Ö¯_CCC|öÙg|GaÞ NNNˆÅĉ1kÖ, >wïÞå;Ãh¼üü|øùù¡_¿~¨¬¬ÄÍ›7±zõjhiiñMmþÂ_p…+Æc<ö`웆aÕ122¦M›GGGÌ;ÖÖÖX¹r¥ÌM¿ üÙÒÒÒðí·ß¢cÇŽ˜7oÆŒƒøøxlܸQ#xáììŒû÷ïÃÇÇ‹/F—.]pàÀÉ Ñ ••///XYYA[[ Î›ŽMLL¤>ëééÉÜTž-¹ ¼&yeò(2¿¢ùåQ¦e—µšÜòœœˆÅbI¦ê ¤¤¤Hå033S¨Íšj[ŽvíÚ!;;›s^àÕ¥Ž;†’’$%%!99cÆŒip¶ºÚS4·‘‘‘äÿ[´h!·¬1˜Âu»?~ýû÷ÇܹsajjŠaÆaûöí2n­os­GÙ¼Õ4e=75Ø¿?:wîŒÿüç?˜3gîÝ»'''¾£É•––†ÿþ÷¿X²d‰d;7W………(**Òøë· £éªï3eB˜æcÀ௿øN¡VÚÚÚ˜?>bcc1tèP¸ºº¢wïÞ8tèûbÍ0ÿ§ªª aaa8p &Mš„ž={"&&~~~Mæâ±‰‰ òóóùŽÁ0Ìkìa+¸‚JTò…7úúúðòòÂýû÷…>}úà‹/¾@›6m`ooíÛ·ãÉ“'|Çdæ óâÅ „……áÓO?…••ÆŽ‹””ìØ±?Ɔ `iiÉwLÎ>ùäDGGãÖ­[|GQ‰Î;#>>žïJ«>ÍÞÒ´<{ö ?ýô–-[VçÅT†QSSSøûû#** •••èÝ»7F°°0¾£1ŒÆyöìV®\‰·Þz ‡Æ?ü€«W¯J=å²9º†kp áˆ#8-4s— Ã0Œò¬­­ñÓO?áÑ£GX¸p!öíÛ+++É9Vy7Z2 ÓüàСC?~<¬­­€Ù³g#!!»wïFÇŽùŽ(ÅÀÀ7nD||û ]ºtÁæÍ›Ùµn‰D¸zõ*JKKADJߨmff†gϞɔçää¨|~UäW¦e—•Ký¹¹¹’L5JJJ$ÓµnÝZ©ã²™™™ÜAŸééé 4iÓ¦ † ‚   øûûÃÏÏOmo6PeîÆÄu»cË–-HLLDRRæÎ‹   L›6Mª¾úÖ9×z”ÍË4L^^~øá¼ýöÛðõõ…³³3°nÝ:¾^°mÛ6˜˜˜ÀÝÝï(jW}Í­]»v<'a˜¦­ú†òÞøÌ„0M“£# <}Êwµ³°°À¡C‡póæM¼ýöÛøøãѹsgìØ±ÅÅÅ|Çc^aûöíèÔ©\]]amm¿ÿþaaa°²²â;žB”=©À0ŒzŒÁd#7pƒï(¡oß¾@jj*aii‰o¾ùÖÖÖ°³³Ãºuëpÿþ}¾c2 ÓLeggãçŸÆ„ кuk¸ººâÁƒX²d xZž¢ìííѽ{wìß¿Ÿï(*ѹsgÄÅÅñCiÕBRSSyNÂ(bÆ 066†ßQ˜7XïÞ½qõêU„„„àÅ‹øàƒп;v ••oî`s†€þù3fÌ@ÇŽ±ÿ~,[¶ III˜?~³âmÜÆXŒÅ(ŒB‚  m¾#1 Ã0<°°°ÀÒ¥K‘˜˜ˆàà`˜››cÙ²eèСÜÜÜðÛo¿¡´´”ï˜ Ã¨Ñ‹/püøq¸ºº¢mÛ¶øôÓO¡££ƒ£GJvÓ¡C¾cÖ©cǎسgbcc1f̬Zµ VVVðõõm6½iˆÈÈH¬Zµ b±ÚÚ¯úûÊÞàíè舓'OÊ”_¼xQåó+’¿¶A ʬe—µ>NNN¸|ù²Lù•+W¤N0bÄDDDHMs÷î]tïÞ]ª¬¶uààà€™ò'NÀÁÁAáÜ3gÎÄ®]» OOO…ççJÕ¹ ×í*ðôÿî/433Ô)Spúôi\¸pAfÞºÖ9×zjÛ?¸æe%yˆÜêÕ«1~üx<|ø?ýô“Æß?VXXˆ={ö`Á‚Ð××ç;ŽÚUaoaåx5PQ)!**Š¢¢¢”©‚a¦´”ÈÐ( €ï$.))‰üüüH$QË–-ÉÃÃ~ÿýwª¨¨à;èUee%]¹r…|||¨eË–¤¯¯O«ò¶óøöÑGѤI“ÔVÀøw’aTÅ–léKú’ïëåË—tåÊòóó£öíÛ²°° É“'Ó¶mÛ(**ŠªªªøŽÉ0LTXXHááá´téRêÛ·/µhÑ‚ôõõÉÁÁ¶mÛFiii|GT©M›6‘±±1óEi›6m"+++µ·£îþzUUÒÞ½{ÕÖF5Ö_W´´4 …´sçN¾£0Œ”[·n‘‡‡ikkS»víÈÏÏ®\¹Âw,†i4999@C† !Ô«W/  /^ð­ÑÜ¡;dJ¦äHŽTJ¥|Ça†aþ×ïbêþþ™ŸŸO¿üò 7Ž´µµI(’ƒƒmذ݇Á0ÍDbb"ÐäÉ“ÉÈȈZ´hAC† ¡mÛ¶QVVßñ”VPP@Û·o§.]ºzçwhÆ ôôéS¾£5*'''òöö¦ääd*++£øøxòôô$y·åÕv«Þë剉‰Ô¡CÚ¿?effRNNS¯^½j­£¡ó+’ßÊÊŠ"##©¼¼œÎŸ?/9¬HÊd•·®j+«–œœL={ö¤ãÇSvv6RXXYZZÒÉ“'%ÓEGGS§N(<<œŠŠŠèîݻԧOÚ½{7§uG–––´wï^ÊÈÈ ÌÌLÚ·oYZZR||<ç¼ÕÊÊÊÈÔÔ”–/_^ï´õÕ[W{ÊæVt{44ïëe\·+rrr¢˜˜*--¥gϞї_~I®®®2mԵιÖSÛþÁ5/×寭ìMðäÉZ¿~=uïÞP×®]éǤÂÂB¾£)dÆ dddDyyy|GiÛ·o'333¾c0ŒÚ4Öý¦'Nœ TVV&ó;6 „iº>øàÕÏ*;;›¶mÛFýúõ#Ô¡CZ¶lÅÄÄðaTêÞ½{´téR²²²"dggGÛ·o§œœµµÙ˜Ç7___>|¸Úêg7˜1LÃͧùÔƒzð£I¨¬¬¤k×®ÑÚµkÉÑÑ‘D" víÚÑ´iÓÈßߟ>žÞ½{ãîîN@@Ó§OgÚ´i˜››«·ÃÜÿýdggsìØ1µ£Ü‘+W®0xð`"""ðóók·¯Óãõ¥K—’ÍÁƒÛík€Œ×ÛBzz:C† aÆ ¬X±Bí8Bü¦ØØX¾øâ þóŸÿ““ƒ——AAALŸ>I“&¡§§§vD!nÙÕ«W9|ø0aaa8p€ªª*¦L™Â’%KX¸p!ýúõS;b‡;ÉIæ0‡»¸‹oø†¾ôU;’Bˆfnö½˜Zë!´ó«aaa„……qôèQêëëqqqÁßߟ€€üýýenUˆN@£Ñ¡œ9{ö,£Fbúôé=ò½^MM ß}÷_ý5ûöí£¸¸î½÷^æÌ™ÃèÑ£éÝ»·Ú1…èôvíÚž}ûضm›ÚQz ùžwœ>}šƒ²wï^Ξ=‹™™,X°€9sætéõï¾û.ï¼ó—/_ÆÊÊJí8bܸqŒ;–7ªEˆvÑQó7ndýúõh4šëîÓmׯ,D{ „+àèQ˜3Gí4ªòððà7Þàõ×_çĉ„††²ÿ~¶nÝŠ±±13fÌàž{îaæÌ™ØÙÙ©WˆëdddpèÐ!öïßÏáÇ)++cèС±uëVüüüºí„ö€(((P;†¢“™Œ &ìg?Ïò¬Úqº¥àã…^ ¾¾žèèhNŸ>Mtt4?þø#[¶l¡¡¡sss|}}=z4¾¾¾Œ1gggtttÔ~Bˆ;”››Ë… ”Ÿþ™äädìííñõõåÁdôèÑøùùabb¢rbõ,_¾œY³f§§§Úqn›««+¶¶¶;v¬] B:‚··7?üðƒÚ1ÄMxçw°²²bÙ²ejGâ¦x{{³nÝ:Þzë-~üñGöíÛGHHëׯgÀ€Ìš5‹{iÓ¦õ˜“¢ë©ªªâĉ8p€ýû÷“€‰‰ 3gÎäÃ?$((ˆ¨S5Ç9N Ànvc€Ú‘„Bt1ÍçWW¯^M~~>ÇŽ#""‚“'O²k×.jjj°´´düøñøùù1~üxFމ‘‘‘Úñ…è¶JKK‰‰‰!22’'NI^^}úôaôèÑLž<™?ýéOLœ8±G‡ûôéÃüùó™?>µµµ9r„={öðÉ'ŸðÆo0pà@fΜɜ9s˜>}:ÖÖÖjG¢SéÕ«QQQ¬_¿žO>ùDí8=‚|Ï;‡¬¬,e³‘Ç“ŸŸ££#÷ÜsëׯgòäÉÝ¢À²¸¸˜¿ýío¼ôÒK=fþ·¡¡ØØXüqµ£Ñå•””Üp]…t]›Üu|ü±ÚI:¥””:¤ –ÊË˱±±QvÝ•c„Z´;¥„……N\\ÊÏæ¼yópwwW-_G¾¾íÞ½›û￟òòr Û¡Û‘ì8,Äy€È!‡£U;J·SWWÇ¥K—ˆŽŽVvÏJLL¤¡¡}}}Œ··7^^^ʵ‡‡‡ŠÑ K\\\‹ë¬¬,lllZt=z4666*§î\quueáÂ…üíoS;Îy衇(..æÀíö5:b¼þÝwßqÏ=÷P\\Ü®ÅJ2^¿3iii 2„-[¶°|ùrµãqG’““ #44”C‡Q[[{ÝÐÞÞÞjÇ=Tyy9'OžTv=§ºº bâĉèëë«UuG9JALa !„Ї®»[¥Btg½CÈo©¯¯çܹs„‡‡ÍñãÇIIIZÎÃhçUå|°·N£Ñ(óœÚ ohllÄÚÚZÙä* €€€ ¤øf$''ʾ}û8~ü8555²†EˆkôêÕ ===žyæ>øàµãôò=WǵkÇâããÑÑÑaìØ±JGeŸn÷šðꫯòÏþ“+W®`ff¦vœqêÔ)ÆŽKll,^^^jÇ¢]tÔüÁK/½Ddd$‘‘‘×Ý'!¢kûÛßàí·A£¾Òrý×TVVɱcÇøé§Ÿ8uêUUUØÚÚ2iÒ$Ƈ¯¯/#Gޤ_¿~jÇÝHEE111DGG+?ƒCCCÆŽˤI“˜8q"ãÆ£o'ù=îÈ×·sçÎ1räÈvôÊ3!îÌ>öq/÷r‰K f°Úqº½òòrâãã¹xñ"ñññÊÂòÔÔTššš000ÀÃÃ///<==quuU.=y·-!:Buu5W®\Q.—.]RNˆj»™››·(âòòòbèС²ÃÛMzûí·Ù°a]úò?ÿùOV­ZEQQººíÓ˜¶#Æë;;;˜6mZ»}¯ß™'žx‚°°0e²èVÊÊÊ'<<œcÇŽqúôijjj°··Wæñ|||9rd§™KÝGCCƒR¼úôiŽ?ÎùóçihhÀÓÓ“ &0aÂ&Nœˆ£££Úq;•ïøŽ…,dóø‚/Ð¥}ÆBB!î\W/iMZZ111ÄÄÄpîÜ9bbb¸zõ*MMM˜››3räHFŒ§§'nnnxzzbii©vl!T—““CBB‰‰‰ÄÇÇ+¿?EEEôêÕ å÷gĈŒ9RÆÁm¤¼¼\)8×¾÷­ªªÂÆÆ† &À„ 6l˜l&„]\CCçÏŸçøñã?~œððp²³³144d̘1Lœ8QÙ§;¯[ÌËËÃÕÕ•×_—_~Yí8fݺulذ¬¬¬nWà#„VGÍ<ðÀÔ×׳{÷îëî“‚Ñµåæ‚½=|ú),^¬vš.¥¦¦†Ó§OsìØ1Ž?NTTEEEèèèàáá¡ìháëëˈ#ºõ`K´æÅÍwJihhÀÜÜœ±cÇÀ¤I“¸ë®»:킎|}«¨¨ÀÈȈ={ö0wîÜ6?¾,0âÎÔSÏ ±Œe¼Å[jÇé±´…"±±±JÁÈ¥K—HMM¥®®SSS\]]qqqiQ(âêꊽ½=½{÷VùYÑù¶(úh~ÉÌÌTgmm­,hÞÉG ?îLvv6ŽŽŽ|þùç<ôÐCjǹmñññxyyŘ1cÚåktÔxÝÅÅ…¥K—òú믷Û×ñúíKMMÅÍÍ?þ˜¥K—ªGˆvU]]Í©S§8vìáááœ:uªÅ<ž¯¯/>>>Êf/ýû÷W;²è"ˆçÌ™3DGGsæÌbbb(//G__ŸáÇãïﯜ”—E£7¶›Ý,bKXÂV¶Òy*„Yw,iMii)çÎkqIHH ¬¬ øeNÕÝÝwww<<>>lÚ´éºû¤ Dt}óçCI üø£ÚIº>¾ÝºÑÑ™Ýê<Þˆ#022R9µPK}}=iiiÊ<^rr2±±±ÄÄÄPQQžžC† i1—çëëÛ£NL߉­låižf+øéEטBˆž¬§„ÜHQQ‘2h>6Оç033kunÈÅÅGGGy&:íÏuk—ÔÔTåg[û~ÉÅÅEyÏäâ₳³s—9—ÝS466rñâEes„¨¨(RRR€_þGŨQ£”"gggu !D•œœ¬œ={–³gÏ’ €³³³²yðĉñööî±çäÏ;‡¯¯/Ÿ|òIÚ䪢¢KKK>üðC–-[¦v!ÚMGÍ8;;³bÅŠVÏÃKAˆèúBCaî\HJWWµÓt;W¯^%::Z™Œ'11‘ÚÚZz÷î““^^^¸»»+»r»¸¸0hРUÁÛÕÖÖ’’’Òb²,11‘¸¸8RRRhllD__wwwewhooo|||ºüdKG¿¾-Z´ˆÒÒRöíÛ×æÇ–fBܹK\Âr™ÌT;ޏ999\¹r…äädÒÓÓÉÈÈ ==´´4222(((Pkhhˆ££#vvv888`ccƒµµ5ØÚÚbii‰•• Pñ q½ÊÊJ²³³ÉÎÎ&77—¬¬,rrrÈÉÉ!##ƒ´´4233¯ûy4hvvvØÛÛ3hÐ ìííqrrÂÕÕUNê«è»ï¾#00„„ÜÜÜÔŽsÛ–.]J||•‚.ÆÊÊ +++üüüZ½¿ªªŠÔÔT233ÉÈÈ 55•ŒŒ 233‰‰‰!77—¼¼<åDüÒmÄÒÒkkk¬¬¬°´´T Fˆ¹¹9 P.&&&õtE7QUUEAA………Êu^^¹¹¹äææ¢ÑhZÜ®¨¨hñùæææÊϦ££#Æ ÃÞÞ±··—¦NlÖ¬Y899±yóæVÛ¼v=ô³gÏæòåË b+ÔŽ$„BÜ1†ÎðáÃ[½?;;»E‘HJJ ©©©œ>}F£œ'†_Î[YYakk«Œ+­­­±³³ÃÂÂ333eŽUºÑöL%%%ää䟟O~~>yyydff*EYYYdff’››«t÷€_æ?mllpppÀÝÝ]™OÓ^¬­­U|V¢½™™™1mÚ4¦M›¦|¬¶¶–¤¤$¥ƒfdd$›7o¦ºº===ZtÐôööÆÃÃ~ýú©øL„¢óÒþ]mÞIN;¯XYY‰®®.nnnøúú2wî\|}}5j”ü]ýÛ·oçøñãDFFö¸uaÛ·ogöìÙR "D((( ºº[[ÛVï—U¼¢ëÓÕ…G…O>W_ÙÍ®Ýéêêâî»ûu÷]ÛŠõêÕ«$''sôèQÒÓÓ©­­U;`ÀlllpttTŠDlmm±²²bàÀÊE¬Ýš‚‚òòòÈÏϧ  €œœ4 h4ÒÓÓÑh4*Ÿ£¯¯ƒƒƒ²;ÊìÙ³[,055UñuÓ¦MãÅ_äÌ™3Œ=Zí8BˆV<Á<É“¤“ŽjÇmÄÐÐPÙ!ùFššš”Âk»0h¯£££ÉÍÍ%??¿Eñü2nÒ‰ÜèÚÌÌ ###Œ•kLLLzÜ„PwQVVFii©r]ZZJqq1eeeJ·Žªª*òóó[~h?~-sss,--±´´ÄÆÆ___,,,”èVVVØØØ`aa!;Üuq:::<û쳬Y³†·Þz«Ë.¾6m–––ìÚµ‹W_}Uí8·møðáXZZrèÐ!)éD’’’رcÛ¶m“ „¸ ::: <ccc²³³Ñh4œ;w===‚‚‚˜m!ꀔ¹<é4qk ”EpÍÂeff*syYYYäåå)Ÿ£««‹­­­2w7a„ţáö²—ì ˜`µ# !„ÂÚÚkkkÆ×êýUUUh4²²²®ëæpñâEöïßOvvv‹óÅðËyJíXR;ví¹bSSSLMM111QnËÌ_|¡v!º…ÌÌL)ÝÜóÏÆ ðé§ðôÓj§éÑÌÌÌðõõmµõQSS“Rœ ÝÉN{R3--ÈÈH233)))iñy:::-&ý,,,°°°ÀØØ333Œ¯»˜˜˜(÷uµ õõõÊ¢Á’’eaóKQQ¥¥¥äåå)ÅÚKóRLLL”ÝmmmñõõÅÖÖ{{{åc²SŠº†Š­­-”‚!:©E,âu^çïüø@í8¢õêÕKé42tèÐß||ii)………Ja测ý ÉÉÉ!>>^ùwQQMMM­¯ÿþ)…"¦¦¦Êx§ÿþôë×¾}ûÒ§OLLLÐÓÓÃØØ éß¿?zzz˜™™¡§§Gÿþý144”“f@cc#%%%ÔÔÔPYYIyy9uuuSWWGYYUUUTWWSVV¦ÜW[[Kyy9ÅÅÅÊØ¬yHó¯edd„ŽŽÅÅŘ˜˜0hÐ †ŠŸŸß¯ IaPϲ|ùrÞ|óM>ýôS^|ñEµãÜ]]].\È—_~Ù¥ Bz÷îÍŒ38pà«V­R;Žø?kÖ¬ÁÍÍx@í(Btzìß¿Ÿ¾ÿþ{tuu™6mÿþ÷¿™7oÞoîÄ\TT¤Ìáeee)›ŒdffráÂ¥HúÚ±¬‰‰Éu‹øÌÍͯ›¿Ó.èkþ±®¸“`mmm‹9»æEÁÍ‹ƒKJJ®›Çkm.ÏÈȨEÑ͈#”›ï®-cDuSÌ\æržó♨v$!„¢Ó044ÄÕÕWWWššš8þ<‡æÂ… üüóÏTUUáééÉ”)S?~<ÞÞÞ-:Däçç“““C^^©©©ÊÇ Zýz­Šhoзo_LLLèÓ§2×j``€‘‘ýû÷ÇÀÀ@‡ö”…µµµTTTPZZJuu5åå唕•)ó åååTWWSZZJEE555×{4¿]]]}Ý×èÕ«æææÊ¹ýâììÌØ±c[œó·´´Tî—9kÑ–ôõõñññÁÇǧÅÇëêê¸|ù2qqq$&&Ç‘#Gøè£”nÜ–––xxxàìì¬Ýk ðmllÔx:BqÛ4² Ló aÉÍÍ~ÙF»‰âòåËñððÀÓÓ“ÁƒËÆ/m`íÚµTUUñöÛo«¥Ã}ôÑG˜˜˜0wî\µ£Ñ-\¹r…Þ½{3hРVï—‚Ñ=X[òe°n,_=d²¦«éÕ«—²s̵o¼›«©©Q&÷´;r_»K^BBB‹ÂˆÒÒÒëvÑêÓ§²Pòf®¯ejjJ¯^½nê9655Q\\|ÝÇ+**”ɵÚÚZ*++•Ň­]·F___99®-v8p ÞÞÞ-N°_¼x‘ÿûß³lÙ2^}õUìíío*¿PG¯^½ b×®]üùÏV;Ž¢zèñ"/ò'þÄ«¼ŠjG”öµÚÉÉé–>¯¼¼\)((++SN¨5/4Ðh‹B¯^½Jyy¹2~())¡®®ŽÒÒÒ›úšÚ¢ E‘Hó“ŸFFFÊîëÍ;–üÖøÈÌÌì–žk´ã¦ÑpÀ/…8Ú…tÅÅÅÊ¢Dmq†¶ø~)¾-++»© Ú±1zzzÊÉã~ýúajjŠƒƒC‹î.FFFÊ8­y!vÁc¯^½¨ªª",,Œ¾ýö[Ο?——ÁÁÁÌž=OOÏÛþž‰îÁØØ˜Gy„M›6ñüóÏw¹w­‡zˆ>úˆ‹/ÞTA]g5gÎ{ì1JJJºlÇ–î$..ޝ¾úŠ;vÈBh!n ??Ÿï¾ûŽ<ˆžžÓ¦Mã“O>aþüùÝô±ÌÌÌ033ûÕ¿ã­v¸hÞ½6;;›¸¸¸¿6ÓŽ›´ãÏk¯µÀ´¶Œ¾¾þ-•hçë®URRBcc£2ÎÔ^—••)ãÉæ×­éÓ§ÏuÅ/dèСÊN×Í7¿ÑÎíÉN×W )DEç8Céºc!„¢=äääpìØ1ÂÂÂøî»ïÈÈÈ`àÀL™2… 60kÖ¬.\ù-ÚâƒÖ ®ýXrr2%%%TWW+…UUU­Žû®¥ÕŽ7å<2üÿyÏæç•mƒÂæó«·ª®®ŽòòòVïÓŽQç—µó¡ÚóÎðÿçNov^ôÚB¥ÈÆÁÁ¡C‡Þ°§ùµ‘žžžžž×ÍÃ755‘––FBBñññ\ºt‰«W¯Ijjªò;f``p]‘Hó‘þýû«ñ´„=Xyyy‹Bk‹?´…›}úôÁÉÉ ggg† Æ‚ ðôôÄÃÃGGÇ›^'nÍéÓ§ùàƒزe T;N‡ª¬¬dË–-<ûì³Êš!ÄILLÄÑÑñ†¿S½šn´íMˆŽŽhµ€.=†>ú¥8Dô8ÚJJKK•‰¿ÒÒR*++[d\[€Q]]­ìþ\UUÕâ˜Í'ÓšÓžèmm¯µI?íbBíõµ…(Ú…—Ú]»[Û%ñVvE©­­eçά]»–ôôtxàÖ¬Yƒ««ëM£'Sãõ-<<œ &pîÜ9†ÞfÇݺu+O>ùd›Oˆž¬’Jœpâ)žâMÞT;Ž¿J;q[ ‡ IDAT®¹¶³EEEÅuªpãâ íB8h½¸¢57ZT×\EEÅo.ÔÓv4¹‘æ'bo¦ˆE{²¶W¯^Ê"CSSSeÑ ö¦¦¦èééÝÒbÉÛÕÐÐ@dd$!!!ìÚµ‹ììl\\\ $88™€í¡’’’ððð`÷îÝÌ›7Oí8·¥±±æÏŸÏ´mw­Ž¯çççcmmÍ®]»X°`A›_Æë·æ 66–óçÏKAˆÍddd°{÷nBBBˆŒŒÄÀÀ€©S§Ì‚ :å‚í¦*Úî¸ÚBhm¡tCCƒ2½ÑuóB`­m¶R]]ÝêÜš¶ÐäZÚ1å®›¥hçò®ífÜSv˜î)"ˆ` °Á†oùGÕŽ$„âÜì{1Yñë*++9qâaaa„……qæÌttt;v,AAALŸ>Q£Fuª÷o¿Ö£®®Nk6/ÆÐÞ×|^T{nhu\ 7ÞHPK;ûk;oßhcíÜ&´ÜøG;Ú|nUÛEZ;öÕÞwm·CCÙ¢«ill$33óºÅÖÚ묬,å±–––8;;3hÐ lmmqttÄÖÖ{{{ìíí±±±‘÷ŠBˆ›V[[«tíÍÈÈ@£ÑžžNff&iii$''“——§<ÞÖÖöºîFÚk[[ÛN5&ë jjj=z4–––„……õ¸s¾[¶láå—_&%%+++µãÑî:bþ`éÒ¥dggsðàÁVï—‚ѽ,_GBBÜæNBt'uuuìØ±ƒ·Þz‹ÔÔT|ðA^ýu¬v´NM×·¦¦&† Âìٳټys›W˜ Ѷֲ–l$…Œ#Bˆ¶ÓØØÈ‰'Ø·oÿûßÿ¸rå ƒ bîܹãçç'µ=LPPùùùDFFªå¶ýõ¯å­·Þ"==½Mw†ìèñºŸŸîîî|öÙgm~l¯ß¼ØØX†NHHH»çÑÕ¤¥¥±gÏBBB8qâ&&&̘1ƒÀÀ@.\xK]2„¿n';YÆ2&3™ìÄcµ# !„¸CRr{9{ö¬RrüøqjjjpqqaúôéLŸ>Ù³gKQ¢G©®®¾®P$-- FCZZÙÙÙÊ&\½zõÂÊÊ ;;;ìììpppP F´·”DBˆî«²²Rù[‘‘‘q]ÁGff&999JÑ©®®.VVVJ¡™££ãu…·²Ñ°h¯¼ò ›7oæüù󸸸¨§CUTTàááÁܹsÛt œYGÌŒ?ž1cưqãÆVï—‚ѽ\¾ °m,Z¤v!:ÚÚZ¶mÛÆ;ï¼CFF<ò¯¾újpÞ,µ^ß>üðC^yåRSSÛ¬U ,0¢mRˆN¼Æk¬b•Úq„ÝXll,!!!|õÕW$$$`aaÁìÙ³ föìÙ¿ºs è¢££¹ë®»8xð 3gÎT;Îm)--ÅÁÁ5kÖðÒK/µÙq;z¼þ÷¿ÿ7ß|“œœœ6?¡"ãõ›·páB’’’ˆ‰‰‘9Ñc¥¦¦òÍ7ß(E ¦¦¦Jg±™3g*ÝÓ„m£‰&ÞäMÖ²–çxŽø€ÞÈkBtRró®^½JXX‡æ‡~ °°f̘ÁŒ3˜>}:ÖÖÖjÇBˆN«¡¡ììlÒÓÓ[,ü¾|ù2—/_F£ÑP\\¬À/Ïmll°°°ÀÆÆKKK,,,°¶¶nqÛÊÊJŠG„èD***ÈÉÉ!''‡¼¼F BD÷““îîðüóÐF ª…ènêëëÙ¹s'ëׯ'66–ÀÀ@^yåüüüÔŽÖ)¨ùú¶~ýzÖ®]Kbb"öööw|Ö­[‡¿¿?þþþ¬^½šÀÀ@Yd£’•+W²uëVV¯^ÍöíÛÕŽ#„hE_úò6o󱂌`„Ú‘„=ˆ““/¼ð/¼ðyyy8p€–/_Îï~÷; äÁÄÊÊJí¸â7ŽY³f±fÍš.[âììÌܹsyï½÷¸ï¾ûºìûŒ|U«VQQQ!»èu°3gÎðí·ßÚe~„¸Í;‚EDD0pà@æÌ™ÃêÕ«¥Dˆôß±„%8à@Q8ã¬v$!„¢MµÖÄÐÐzè!¦OŸN@@jGBˆN©¶¶–¤¤$¥è#66–¸¸8®^½JSSÆÆÆ 2///¦OŸ®¸¸¸´É×ïÓ§¶¶¶ØÚÚâííý«mll¤   E‡ƒ¢¢¢ëþ]PP@RRR‹ûZ«wïÞ˜™™abb‚±±1FFFôïß###LMM122jñ1333åvÿþý166ÆÔÔ”þýûK·SqSjkk)//§¸¸˜ÒÒRÊËË)++£¼¼œ¢¢"åvYYeee·øXii)%%%ÑØØØâØ:::˜››·èžcnnŽ‹‹ £Gnñ±æ0`½{÷Vé;"º‚ÿýïüãÿ`ûöí=®àý÷ßç§Ÿ~"""Bæ³…hc§NbРA¿ZL,BD÷T_>>`k ªFˆ.!<<œõë׳ÿ~† Æïÿ{-ZÔ#wyPûõ-44”{ï½—={ö0oÞ¼;:–ì8,Dûh¢‰ L@}~äGµã!ùùùìÝ»—={öFCC“'OfÞ¼yÌ;·M: uh»„ìß¿Ÿ»ï¾[í8·åÂ… Œ9’;vpÿý÷ßññÔ¯çååaooÏÇÌÒ¥KÛì¸2^ÿmäääpêÔ))ÝNll,!!!„„„‡……³gÏ&88˜9sæôÈ9!ÔÒDïñ¯ò*‹XÄÇ|L_úªK!D;éIBš€:tˆ””¥$ @ @„âÊÊÊHLL$>>žØØXå’’’BSSýúõÃÓÓ“¡C‡âííͰaÃðòòÂÁÁAíèm¢¤¤¤EáHaa!ÅÅÅ·´8ÿFKûô郑‘ÆÆÆ˜˜˜ ££ƒ©©)zzzôïß éß¿?zzz˜˜˜ ««‹‰‰‰òCCC ”Ç˜šš¢££ƒ‰‰ ½zõÂÔÔ´ƒ¿c=‹öÿ·¤¤„úúzJJJ¨««£¼¼œªª*ª««)//§®®Žââb())¡¶¶–ŠŠ å1eeeÔ××S\\L}}½RÄQ^^Þjw@ùÿ½Ù"$SSS¥˜C[ØabbÒÁß1Ѥ¥¥1jÔ(|ðA¶lÙ¢vœwæÌÆÏ›o¾É+¯¼¢v!:\{ÏÌ;—>}ú°k×®>F BD÷uü8Lšß~ j§¢Ë8sæ ëÖ­c÷îݸºº²zõj/^Ü£ZAw†×·eË–±ÿ~Ο?G»{Ë3!ÚO4ÑŒa !„°€jÇBEii)û÷ïgÏž=8p€ŠŠ FÍüùó™;w.^^^jG·(88˜˜˜.^¼ØeÇå>ú(Ç'!!áŽw€Sk¼þÀ‘‘ADDD›SÆë¿.::š»îº‹0kÖ,µãÑ&´E _}õ 8880þ|‚ƒƒñóó“]…PA,f1G8ÂzÖó/¨I!D;ëÎ!ÉÉÉ„‡‡¡€ôíÛ—Q£FIˆB´";;›øøx”ë„„ÒÓÓ_Š<<<ðòòbذax{{ãíí³³³¼‡ÿ ååå×ukh^8¢ýw}}=EEEÔ××SVVFuu5UUUTTTP[[Û¢è@[Pp³š‡´Õí122úÍÍ=næ1ZÚïÇ>¦¨¨H¹Ý¼PçNnßŒÖ zôõõéׯ}ûöU ƒtuu133CWW###LLLZz\Û•¦ÿþ7AˆŽRWWǤI“(//'** CCCµ#u¨ÌÌLƇ‡‡ßÿ½¼>Š©½çìììX¹r%«V­ºác¤ Dto>QQRÝ+Ä-INNfãÆlݺcccžzê)ž}öY¨v´v×^ßJKKñññÁÖÖ–°°°Û^4' Ì„h_ðG9ÊyÎcŠì4#„è|ª«« '44”²²²pvv&((Hžv!éééxzzòÆoðòË/«ç¶dddàææÆ{ï½Ç³Ï>{GÇRk¼ÆŒ3¸páC‡m“cÊxý×Í™3‡’’Nœ8¡v!dÇŽ\ºt GGGæÍ›Gpp0þþþÒýFá ÷qõÔ³‹]ŒcœÚ‘„Bt€îRÒÔÔD||<üôÓO=z”ÌÌLúö틟Ÿ“'OfòäÉÜu×]w¼9ƒBtu†¸¸8bcc‰‹‹#99™ .““€‰‰ ƒÆÅÅ///¼½½ñòòÂÃÕӋk]Ûa¢yAIó"‰›¹ÝÐÐ@iiéMßnMcc#%%%¿šùfs-“ß<‡ó[166V~†oõvó–ÖnkÛ¼ CÛáEˆžä¥—^â_ÿúÑÑѸ¹¹©§C•••1qâDjjjˆˆˆÀÌÌLíHB¨¢=ç233±··çÈ‘#Lž<ù†“žó¢{ûðC1–/‡µÓÑ¥¸¸¸°qãF^}õU>úè#6oÞ̺uë¸ÿþûyå•WdgévfllÌ7ß|ƒŸŸÏ>û,[·nU;’¢ÙÈp†óÏñ_¨G!®c``ÀôéÓ™>}:|ðgÏž%44”¯¾úŠM›61pà@æÌ™Cpp03gÎì²Ý'º;^~ùeÖ®]Ë¢E‹°µµU;Ò-³··çé§ŸfíÚµ<òÈ#«é–M›6ÁƒóÉ'Ÿ°aõãt{‘‘‘˜BgǦ»7±ŽšãõuëÖñÞ{žN¿~ýîøx2^¿± 6P[[Ëþðµ£qSšššHKKãüùóDEE‘——Ç€1b¾¾¾¸ººv»ù!ºªRÃR>Ÿô9qöqÜsæî9{½šä÷S!z’ŠŠ ^|ñÅß|œÚB²³³9}ú4„‡‡óóÏ?SSSƒ ¾¾¾àïïϘ1c¤ˆ¢GÉÏÏ'>>ž„„ˆ‹‹#11‘ÔÔTÑÓÓS:}xxxàé鉇‡2'„¢ÍDGG3qâDž}öYÖ¯_¯vœUZZÊ=÷ÜÃ¥K—8tè#FŒP;’ªjÏùƒçž{ŽÈÈH~þùç_}Ü„Ñe¼ü2üóŸ îîj§¢KklldÿþýlÚ´‰°°0FŽÉSO=Å#<‚Úñº¥ÿüç?<þøã¼óÎ;¬^½Zí8BˆV¬`»ØÅyÎc½Úq„â–¥¥¥qðàABCCùþûïéÝ»7&L 00ààà.Ù‘¢; áàøñãøûû«ç¶lÙ²…•+Wõ›»˜tF………8::²~ýzžyæµãt[üfëc!ÔÖØØÈ‰' a÷îÝdffââ⢼~¨QqCb)KÑGŸ/ø‚ LP;’BˆN¬# Bjjj8{ö,QQQœ:uŠˆˆRSSÑÑÑaøðáøûûãç燿¿¿lØ)„芊ŠHNN¾îrñâE²³³èÓ§®®®x{{+]?´… ²á‘Bˆö¤Ñh3f C‡eÿþýèè訩ÃäääDFF?üðžžžjGBuí9àååEPPÐožIAˆèêê àÄ R„h§OŸæý÷ßg÷îÝX[[óÜsÏñøã3`Àµ£u;›6mbåÊ•¼õÖ[üéOR;ŽâTàƒƒÄ÷|O/dgQ!D×UXXÈ?ü@hh({ö졲²’Q£Fȃ>ˆ‡‡‡Ú{´9sæpùòeΞ=KÿþýÕŽsË™4iåååœ>}]]]µ#ݲ§žzŠï¿ÿž¤¤¤5Áß‘¦L™B}}=ÇW;Š×i^BVV^^^¤ÚîÑBˆ_WM5«Y͇|È}ÜÇV¶bŠ©Ú±„Btrí¹ #))‰S§NETT111ÔÖÖbnnΘ1c7nþþþŒ;Vv³Bt[EEE$%%qùòe’’’”ËåË—),,@__ggg† ¢\Œ»»»È !„PEUU“&M¢¬¬Œ“'Obbb¢v¤ÍüùóÑ××çàÁƒ È?þHTT®®®jGê0Û¶mcÅŠ°sçNÌÍÍÕŽ$D§Ñ^!Û¶mã‰'ž °°~ýúýêc¥ Dô,;w¢E°y3<ý´Úi„èvÊËËùïÿË–-[8þ<¾¾¾<ùä“,Y²DZ²¶‘–,YB`` Ÿþùo¾Ð !:Ö»¼Ëë¼Î0éjÇBˆ6¥] »oß>vïÞÍåË—qttdöìÙ2{ölôôôÔŽÙ#ìÙ³‡… ²wï^‚‚‚ÔŽs[Þ|óMÞ{ï=Ο?ß%'ËçÍ›G~~>ááájGév&OžŒ¾¾>‡R;Šèáš|õÕWäää(E Ò1Kˆ®¡žzÞç}Ö°†QŒâK¾Ä•®7îB¡žÛYÐQ\\ÌÅ‹‰ŽŽV.ñññ455accƒ¯¯/øûûãëë+çÏ„ÝBaa!W¯^mµÓG^^ðKч““ƒ¾®ÛÇ Aƒºd'a!„=ÏÊ•+ùè£8xð S¦LQ;N‡(((àé§Ÿ&$$„—^z‰õë×K±¦×h¯‚G}”´´4Ž9ò›•‚Ñóüå/°f ìÞ sçªFˆn+::š7²sçNÌÌÌxì±Çxê©§4hÚѺ¼cÇŽqß}÷akkË7ß|ƒ“““Ú‘„ÿ§‰&³˜ Š(†0DíHBÑ.ššš8sæ {öìaïÞ½\¼x‘HPP³fÍ¢ÿþjÇìÖ}ôQ8Àùóç±¶¶V;Î-«­­ÅÇLJòÃ?t¹‰ãˆˆ8rä“'OV;N·qøðafΜɱcǘ0a‚ÚqDÔ¼dçÎäææ*E ‹-ÂÍÍMíˆBˆ›M4ËXÆe.óoñ/ C×o!„Pßo-èÐh4DGGsæÌå’‘‘€““>>>øøøàë닯¯/–]!ÚRUUYYY­vø¸rå ÅÅÅèêêâèèx]§¼½½100Pù™!„·ï/ù ¯¿þ:;vìàþûïW;N‡Ø»w/O=õ:::|úé§Ì˜1CíHBtJíQR__ «V­âøÃo>^ BDÏôÔS°m9cƨFˆn-;;›Ï?ÿœÍ›7£Ñh˜:u*Ï?ÿ<Òòú¤¦¦2oÞ<222øüóϹûî»ÕŽ$„ø?ÕT3‰I”PÂINbŠ©Ú‘„¢Ý]¾|™={öðí·ß‰žžS¦LáÞ{ï%((;;;µ#v;¥¥¥Œ1‚áÇóÍ7ßtɱõ… ;v,øÃxã7ÔŽs˦N À?þ¨r’îcâĉôëרEô 555:tˆ¾ýö[JJJ”"‡~˜!C¤È[ˆ®¤šjÖ±Žwx‡±Œå>Áwµc !„袴 :† F\\.\àÂ… œ;wŽóçÏ“ €«««Rø¡-0`€šÑ…â–“’’Bjj*)))\½z•””åRRR¢<ÖÆÆ''§ë.ÎÎÎ899Ii!„ÝÒ¶mÛXºt)|ð/¼ð‚ÚqÚ]||<+W®äðáÃ,Y²„7bj*k_„¸‘ö(Ñn¤wùòe\]»óµ„ˆž©®áÂ8yÕN$D·W[[Ëîݻٲe xyyñ»ßýŽÅ‹cnn®v¼.©¢¢‚§Ÿ~š/¾ø‚_|‘wß}}}}µc !€,²Ã¼ñf?ûeR!DRPPÀ?þHhh({÷´///‚‚‚ Äßß¿K/tFÇŽcêÔ©¬[·Ž—_~Yí8·eË–-<ÿüó:tˆiÓ¦©ç–h»„üôÓOLœ8Qí8]ÞÁƒ™3g'OždìØ±jÇÝ\uu5‡&$$„½{÷R^^ΨQ£ dÉ’%75±.„è|"ˆ`9ËÑ a-kyŽçèMoµc !„èbÒÒÒ”Â#GŽpåÊÒÒÒ¨««C__///† ÆðáÕâY%„è쪫«Ñh4­vøÐh4dee)533SºzØØØ`kk«üÛÝÝ]:C !„èq¾ýö[.\ÈŸÿügÖ¬Y£vœv•™™É»ï¾ËÖ­[:t(›6m" @íXBtzíQò»ßýŽŸþY9ö‰nr{ IDATo‘‚Ñs•”€öÅêÀ°·W7=HLL ÿøÇ?عs'µµµÌŸ?ŸÇœ©S§Ò»·œ¤½UÛ¶mã™gžÁÝÝO?ý”áÇ«Iœâ“˜Ä<Á&6©G!TQ]]Mxx8¡¡¡|ýõ×dddàèèÈìÙ³ dæÌ™ôéÓGí˜]Ú_ÿúWþøÇ?²oß>fÏž­vœÛ²páB"""ˆ‰‰ÁÚÚZí8·dòäÉèêê¦v”.oܸqXXXªvÑMUUUFHHß|ó Œ?žàà`î»ï>éf%D–GæÏ|Â'ÌaÿäŸØ#óýB!~]zz:ñññÄÇÇG||<.\ ¸¸€Aƒáè舮®.ÇÇÑÑ[[[ttdó!DçRWWGaa!………äççSPPÐâvnn.ÊãMLL°´´Ä ,--8p –––XZZJ‡!„¢™˜˜þú׿2uêTüqµã´›‚‚öîÝË?ü€±±1÷ÝwS§N•Mþ„¸IÉÉÉ̘1Ÿ69^CC¶¶¶¬\¹’?þñ7õ9R"z¶ôt¸ûn((€ÿýüüÔN$DR]]Mhh([·nå‡~ÀÎÎŽ‡~˜+Vàää¤v¼.åÒ¥K,]º”Ÿþ™—_~™×^{ CCCµc Ñã…Â"ñ,ÏòwþN/äͲ¢g‹eß¾}„††râÄ ™:u*AAAÌ;+++µ#v9MMM,Z´ˆ°°0N:…³³³Ú‘nYQQ>>>xzz²ÿþ.5¹|ôèQ¦L™ÂÑ£G™4i’Úqº¬}ûöÄ©S§¸ë®»ÔŽ#º‘æE {ö졲²R)¹ÿþû±±±Q;¢âÔQÇf6³–µô¥/å¯,b‘Ú±„Bt" ¤¤¤(Úâ„„JKK°´´ÄËË †ΰaÃ6l&&&444(B5ÔÔÔ••EVVééé×ÝÎÌÌ$//Oy¼¾¾>ÖÖÖØÚÚboo­­-8::âèèÈ Aƒ000Pñ !„]ÇçŸÎþðî¿ÿ~6nÜØ-79މ‰áã?æë¯¿ÆÒÒ’gžy†Ç{L6ôâ6˜˜˜´Ù߉#GŽ0uêT.]ºÄ!Cnês¤ Dˆòrx䨻~Ö¯9,D‡KLLä³Ï>ã³Ï>#??Ÿ©S§òä“O2wî\ôõõÕŽ×%455ñ¯ý‹U«VallÌk¯½Æ²eËÐÕÕU;š=Ú.v±„%Èç|N¤•¶B¤¥¥qðàABCC9|ø0õõõŒ7Ž   î½÷^<==ÕŽØeTTT0qâD*++‰ˆˆÀÜÜ\íH·ìäÉ“Lš4‰U«VñöÛo«ç–Ìš5‹¢¢"¢¢¢ºT1Kg2fÌìììØ³gÚQD7PYYÉÿcïÎ㢺ïý¿dßY‡}APÄ}oPq#à’H¶fiÓĤɽI·é/mÚ^MÚ>Úæö6MÚ&51·MkŒ!q%®Ä%*’(Š (Ⱦð3Ì0ðûcœ#(&jÄòyÎãû8gæÌ Ÿ1Çœ™ïûûùüóÏIMMeóæÍèt:fΜIJJ >øà°ëD$„ØNvò_ü%”ðc~ÌOù©œo !Äf0(++#''‡ÜÜ\e{áÂeU|www¢££‰‰‰Q¶111B¨F¯×S__OUUEEETVV^³_RR‚ÑhÀÚÚOOOüüü C£Ñ\³,Œ„Bˆo©¦¦†çž{Ž­[·òË_þ’µk×ÞUßÿhµZ6mÚÄúõë9qâS¦Lá…^àÁ” ˆCÄOøà}ôQµË¹agΜaÒ¤I|øá‡<øàƒj—3ìlÛ¶U«VqòäIâââÔ.G SMMMlß¾´´4vîÜÙ/òÐCI*!î"G9ÊZÖò9Ÿ“DæÏ„¦vYB!îöövòòò¸pá999\¸pÜÜ\ 1 XZZBtt4ãÆcܸqÄÄÄ…³³³Úå !F½^Oee%ååå”––RQQ¡ì———S^^NMM æ)[ÖÖÖh4 Äßß_Ù __ß»rer!„b¨èêêâÍ7ßä׿þ5îîîüãÿ >>^í²n‹ÎÎNöî݈ رcÜwß}üð‡?döìÙj—'„裩© þô§?±fÍš~œB„諳ÞxþügS0dÕ*øÿ€yóÔ®Lˆ©²²’ýë_¬[·Ž¢¢"¢££IIIá‰'ž 44Tíò†¼‚‚Ö®]˦M› äG?ú?øÁpr’Õ…PCe<Áá¯ð ?å§Ø!m¹…âjF£‘cÇŽ‘––ÆöíÛ9þ<$&&’œœÌÒ¥KeÇuœ;w޹sç²xñb6nÜ8,W|饗øë_ÿÊþýû™5k–ÚåܰÇœ#GŽpþüùaÆQKoo/“'O&<<œO>ùDírÄ0£ÕjÙ±c©©©ìÝ»£Ñ¨„@~øa¼½½Õ.QqeÁZÖ’N: XÀ«¼Êæ¨]–BˆA¢Õj•.EEEÊ~qq1===XYY¤tú #::šI“&áèè¨vùBˆ»\gg'UUUvô¨³˜º]¯«GXXAAA² ŽB¡½^Ï?þñ^{í5xá…øùÏ>ì¿lhh`÷îÝlÙ²…Ý»wÓÙÙÉܹsyâ‰'X½z5...j—(„ÀŸÿüg~ñ‹_PQQqS¿§b z=lÛëÖAz:ÁÊ•’sçª]#NOO‡bÆ |úé§´´´pÏ=÷ðÈ#pÿý÷ãîî®v‰CZQQo¼ñï¿ÿ>ÖÖÖ<öØc¬Y³†ñãÇ«]š#N=¼É›ü’_âƒÿËÿ²œåj—%„CZQQ;vì --ƒbeeÅܹsIJJbõêÕøKgÇ~:IJeËx衇X¿~ý°kaÝÓÓêU«øòË/ùꫯ R»¤RRRBTT¿ûÝïxñÅÕ.gØøôÓOIIIáÔ©SLœ8QírÄ0ÐØØHZZš±°° !!””V¬X«««Ú% !n³Ãæ·ü–Ýìfóx•W‰'^í²„BÜäçç“——G^^çÏŸW®·µµ¦ Ô‘‘‘Œ7ŽÈÈHÆŽ˸qãÇÚÚZåW „¸ÛtwwS]]MYYUUU”——SQQAee¥r[YYÊcPº{øùùáïïO@@~~~Ig!„bˆºpáï½÷|ðmmm¬Y³†Ÿüä'Ãö»Ç®®.Ž?ÎÞ½{Ù³gYYYXXXÏ}÷ÝÇŠ+Ðh4j—)„ø&L`öìÙüýï¿©ÇI Dˆo’ 7¦MPRÑѦÎ!‰‰0c ÃW…κººØ»w/©©©lÞ¼™îîn-ZDJJ ÷ß¿¬üô5y÷Ýwyï½÷(((`öìÙ<õÔSÜÿý’úâ« ‚óc6±‰éLg-kI$Qí²„bÈ«­­U:‡ìÛ·NÇÔ©SINN&11‘I“& »Ä`صk+W®ä™gžáÍ7ßT»œ›ÖÚÚÊœ9s5j‡ÂÍÍMí’nÈÏ~ö3ÞyçòóóñòòR»œ!¯··—I“&ÅG}¤v9bkhhà³Ï>#55•={ö`ii©„@V®\)ç³BÜ…ŒÙÌfþÈù’/™ÃÖ²–E,R»4!„·@«ÕöëòaÞÏËËSVÏ×h4ý:}˜÷ÃÂÂT®^q·¸º«Ç@Ý=JKKéîîVãîî®tñ¨³‡F£A£ÑÈç‘B!Ä0ÒÙÙÉ'Ÿ|»ï¾ËáÇ åÉ'Ÿäé§ŸÆÇÇGíònJee%ÇŽ###ƒÌÌL²²²èêê"44”Å‹³xñb.\( ) 1Œ|ñÅÜsÏ=?~œ©S§ÞÔc%"Äêí…cÇ࣠- .]‚Ñ£añbX¶Ì´õõU»J!F”ŽŽ>ûì3>øàöìÙƒ••III<öØc,]ºTV‡ºŽÞÞ^öïßϺuëØºu+–––$%%ñÈ#°lÙ2lmmÕ.Qˆã gø5¿æ>!–X~ÎÏYÍjF!_!Ä7éììäóÏ?gûöíìܹ“ŠŠ üüüHLL$11‘E‹áää¤v™ªùôÓOy衇xî¹çxã7†ÝÓ%%%Ì™3‡öîÝ‹ƒƒƒÚ%}£ŽŽÆÇ’%KX·nÚå y›6mâ‘G!;;› &¨]ŽbêëëÙ¹s'©©©ìÞ½kkk.\HJJ «V­ÂÙÙYí…ƒ 6>äCþ—ÿå"I$‘y‘Ô.M!Ä7hiiáâÅ‹\¼x‘üü|eäååÑÒÒ€««+‘‘‘DEEÅØ±c‰ŠŠ"""•_b¸êêꢦ¦†òòrª««©¨¨ ªªŠŠŠŠ~Ý=̇ìììúuò¨»‡F£‘¿MB!Ä]¢««‹ôôt¶nÝʧŸ~J[[+V¬à©§žbÑ¢Eâ“Wcc#gÏžåôéÓdff’‘‘AII –––Œ?žÙ³g3kÖ,fÏžMxx¸Úå !nQbb"­­­>|ø¦+!nUQ¤§ÃްotuAXÌ™sçÂ’%¬v•BŒÕÕÕlÚ´‰7òå—_âååÅòåËY¹r% ØÙÙ©]â¤Õjùä“OøðÃùâ‹/puueùòå¬ZµŠÅ‹coo¯v‰BŒ'8Á«¼JiLa /ð)¤`‡üíBˆ•““CZZéééÐîëâŋ̛7 &––6,‚Ë~ø!=öÇŽcúôéj—3dõôôÇ„ ذaƒÚåˆ!¢®®Ž]»v ¹ï¾ûFtÈOˆ»]>ù¼Ïû¼Ç{´ÑÆ<À+¼BQj—&„¢®®.%ôa~˜·ÕÕÕXYYJDD‘‘‘ÊˆŠŠÂWÖBܽ^Omm-eeeJàãêàGuu5uuuýçííAAAh4š~ómžžž*½*!„BÜ)ÍÍÍìÚµ‹-[¶°k×.ÚÚÚ˜:uªò½ÙPíÒÞÞNnn.gÏž%''‡³gÏrîÜ9ªªªððð`ƌ̚5‹Y³f1}útY@Iˆ»Ä©S§˜2e ;wîdéÒ¥7ýx „q;´·ÃáÃðÅpè?DD˜"3fÀÌ™0~M||<óçÏçã?òõ÷ööÁ`àÈ‘#Ã.„s§|øá‡<þøãœ;wލ(™ì;’•••±yófRSS9vìvvv,X°€””î¿ÿ~Õ.Q1HôèÙÂÞáqˆPBYÞâ)<ðP»>ž•+W²bÅ Ô.NGAA*û”––ÒÓÓƒ½½=ÑÑÑL˜0˜˜bcc‰‰‰Áßß_íò…ƒä¾û¸˜¬¬,FuÓ—@ˆƒ¡½22L‘Œ S@¤µaÊS8dÆ ˜<BBÔ®Vˆ»ZßF÷íÛGww73gÎ$99™û™d=êêj¶mÛÆ–-[8pàÝÝÝÄÅűxñb/^Ìœ9s¤M²ƒ¨Š*Þç}Þå]J)e! y†gXÎrlß=!„¸F£‘ììl%ròäIìíí™={6IIIÜÿýCæàÁ²gÏî¿ÿ~âããÙ´iÓ°ûbüàÁƒ,[¶ŒG}”uëÖÝÒ`wÒ™3g˜:u*o¾ù&Ï>û¬Úå 9F£‘ñãÇ3}útþùϪ]ŽPAii)[¶l!55•ŒŒ \]]•nN÷Þ{/j—(„DyäñOþÉû¼O=õÜ˽ü²˜ÅX AJ!„¸ŒF#eeeý&åç瓟ŸOQQƒ0­²Áرc;v,ÊЇâjÝÝÝÔÖÖ*;***ú<Ì·×ÔÔÐwšÒèÑ£•N¾¾¾øûû_sÝ××wÄ,î"„BˆWTTÄþýûÙ¿? ºº777.\¨ê¢Á”––RZZJYY™²_\\LAAåååÊû!ÆŒCxx8cÆŒ!22’ØØXÂÂÂdÑ1!FsçÎ˧Ÿ~ʪU«né9$"ÄRTGŽ@V= ÙÙ`4‚‹ L˜` ŠL™11¦ë2ÑZˆÛÎÜpëÖ­ìÚµ‹––¦L™ÂŠ+X¾|9±±±C~r™:::ÈÈÈ ==ôôt²²²ppp`öìÙ$$$ÀäÉ“åßNˆAÐCûÙÏ:Ö±…-8áD2ɤÂR–bµÚ% !İSRRž={HOOg÷îÝ´¶¶Mrr2 ÄÇÇù.·âøñã$%%LZZÞÞÞj—tSvìØÁêÕ«yüñÇùûßÿ>ä?ÿéOÊÛo¿Mnn®¬Öt•>ø€'Ÿ|’œœ"##Õ.GÜ!%%%lݺU ¸¹¹‘””DJJ K–,‘„¸Ë5ÒÈ&6ñI&ð}¾ÏÓýôS***ðòò">>ž„„’’’ðóóS»Ì!éÒ¥KìÝ»—½{÷²ÿ~ššš bñâÅ,\¸¹sçÞõ«l ¡†2Êø˜ùˆ8Á ¼ðb5«y™Ç>ž™3gJ@díííœ:uŠ£GräÈ222hllÄÉɉ‰'2wî\æÌ™Ã¼yóäK(!n“\rÙÄ&¶³l²qÄ‘…,$‰$IĵKBˆaI«Õ²gÏ>ûì3vïÞM}}=¡¡¡$&&rï½÷?lW:ìêêâÙgŸå_ÿú¿ùÍoxùå—Õ.馘C! |ôÑGßêƒÿÁöýïŸÝ»wsæÌ¼¼¼Ô.GUƒ¨¨(.\ȺuëÔ.GÜF999¤¦¦’––FVVžžž,[¶Œ””–-[&“…„¸Ë1rƒ|ÌÇlf3õÔ3i#4ö³ŸN:™Ä$Id˘Ît¬ yBq³ŒF#_}õŸ}ö;wî$;;;;;î¹ç–,YÂÒ¥K‰ŠŠR»Ì›öúë¯ó³ŸýŒx€uëÖáää¤vI7,##ƒÄÄD¦L™ÂæÍ›¯ùP~¨hnnfâĉÄÅűuëVµËQÕ»ï¾ËóÏ?O^^žœ;ÝÌ!?þ˜óçÏ@bb"IIIbС#t¶±íl§–Z&1‰yx€Päï¼B˜õööö w˜‡ùzEE…ÒåÃÍÍÐÐPe„……õ»>”ÃðBˆ›s£!ÊÊJZZZú=ÖÙÙ???¼¼¼”‡——___|}}•°‡¬X-„B5•””››Kvv6§N";;›ÂÂBzzzpwwgÒ¤IÄÅÅǤI“ˆŠŠº¡Ï–kkkû…=.]ºDAA°7^ž7éää„F£ÁÓÓOOO\\\pqqÁÉÉ º»»imm¥££ƒ®®.šššÐëõ´µµõ èõzÚÛÛoË¿‰»»;8::âìì¬FÌïãÌ]oooå6 ’qwª¨¨`üøñ<õÔS¼þúëßúù$"Äݨ³Ó ),4‚‚+ûeepùƒe¼½!<Ü4ÆŒé¿?ÂW.â딕•qäÈ¥Mavv6ƒ___%2sæL¦N*oÊP^^®D2339sæ ]]]8::§D$$"Ä·£CÇްƒlc%”àˆ#³˜EÂåË$&a…Ú¥ !İSQQÁîݻٳgéééhµZ‚ƒƒY²d K–,aáÂ…C6 pµ½{÷òè£âááAjj*ãÇW»¤vîÜ9qqqa×®]ª]Ò€Ž9B||<ï½÷ßûÞ÷Ô.GƒÈÈH–.]Êßþö7µË·Èùè£ÈËË#00U«V‘’’ÂìÙ³±°÷•BÜÍi$4¶±=ì¡ÖÒíÔ IDAT“N¦1•¬ä~î'‚o·z™B g:ŽÊÊÊ~«ÎšG^^mmmØØØ töè;4 ~~~*¿!Ä·ÑÕÕECCZ­–ªª**++ûí÷½­ººš¾SuìììpwwÇÏÏFÓo¿ïmÃæ3'!„BŒ•••äææ’““£lÏœ9£t/Óh4ÄÆÆ2fÌBCC•Ž­­­tvv¢ÓéhnnF¯×ÓÚÚJKK 555Ê{§††ZZZhoo§££C ÕXXXô»~=£FÂÍÍ [[[pvvÆÆÆWWWììì°··ÇÅÅ\\\°··ÇÎÎnÀÛ\]]±°°P®ØÚÚÒÕÕÕïgš'}kojjRöÍÇZ[[©¬¬¤¦¦†ššêëëû=“““ø ¾f!€€™[%Ä0”˜˜Haa!§Nº-sL%"ÄH£×Ã¥Kýƒ"æí¥K¦ã..W"áá AAbÚJÛX!dee‘™™©„ªªª°²²RºˆLŸ>É“'KÀaÝÝÝäåå)­ û¶ƒ´±±a̘1ýB"Ó§OÇÆÆFí²…vŠ("ýò娧gœ™Á % 2™ÉŒb”Ú¥ !İb4ÉÎÎ&==ôôt:DOOqqq$$$””4ä'JWTTðÐCqâÄ Þ|óMž~úiµKºa•••$&&RWWÇgŸ}F\\œÚ% 襗^âïÿ;§Nb̘1j—sǽóÎ;¼øâ‹äçç¬v9â&˜C 7n$??Ÿ   V®\IJJ sæÌaÔ(yï(ÄÝ,—\v^¾æ0–X²€¬`ËYŽÚ% !Ä¡×ë)//0ðažämæîî>`à#,,Œàà`ù~@ˆa¦©©‰ššêêꨫ«Sºx˜;yÔÖÖ*·777÷{ìõ:y˜WzöòòR&öÙÛÛ«ô …B!èƸ^· NGQQÕÕÕ”——S]]M}}=Z­–îîn¬­­±³³Ã‚Q£Fa4éèèP:v|¾Í C¿ÛœœpqqÁÝÝooo<== ÀÏÏ{{{ÜÜܰ±±ÁÉÉ GGGlllpwwÇÆÆGGGœœœ†Uç4½^Omm-UUUJH¤²²’ÚÚZŠ‹‹•Î(€éß(((¨_—ɰ°0"""ˆŠŠ’÷šB Aëׯç™gžá‹/¾`öìÙ·å9%"„¸¢§ÇÔAäê®"……PZ Wîëæf †›F` éº94âë Cx“ƒ­¤¤„cÇŽ‘™™Iff&§NB¯×ãàà@ll,“&MbòäÉLš4‰ &HÀá*ƒsçÎõ ‰ôí$2~üxbcc?~¼²ïéé©vÙB +}"ûØGMøàÃ4¦1—¹ÌaÓ˜†-¶j—*„ÃJCCû÷ï'==]»vQVV†§§'óçÏ'!!{オ˼Fww7?ÿùÏyýõ×yâ‰'xóÍ7q& hµZV­ZÅéÓ§Ù²e ñññj—t®®.¦OŸŽ³³3‡Q“Àôz=‘‘‘$%%ñÖ[o©]ޏæȆ ((( $$„åË—KDˆ vö³Ÿìd»(¡OLWWÑÑÑ$''“Àw¾ó!NKKãÉ'ŸÄÙÙ™>ø€9sæ¨]Ò éêêâñÇgëÖ­üå/’]Nrrr˜:u*¯¾ú*?ùÉOÔ.çŽùË_þÂO~ò.^¼H@@€Úåˆôôô‘‘AZZŸ|ò ………„††’œœ,!!îr=ôpšÓà»ÙÍ|“™Ì2–‘H"Ó˜†%#'È(„¸{UWWSZZJiié5eJJJèêê®LFh„……áêêªò+B\­³³­VKUU•••_»_SSCOOòX[[[F»»;~~~h4ÜÝݯ¹îççG@@Àú G!„êjmm¥»»­V‹Á` ­­M Xô=ÖÝÝMkkëM3?ç71wÐpppÀÖÖö»e8::¢Óé”ðHcc£­¨¨PºN8;;θq㈈ˆ 22’ˆˆÂÂÂÐét”””P^^NEE…²_^^NYYÕÕÕÊ{.kkküüüúu°è»õóóÔÿN#•Á`   €óçÏsáÂe{áÂåÿ+///ÆGTT111ÄÆÆ2qâDÜÝeNˆƒE§Ó1oÞ<ºººøê«¯°³³»mÏ-!ÄíÓÝm Š”–Bq±©ÛHi©i””˜Fß7ªžž¦H` ©£H@€)0âïo øø€L:w)£ÑH~~>§Nâäɓʶ©© KKK"""”pCLL ãÇ'<<+++µKRªªª8wîgÏžU¶¹¹¹ttt`aaAXX±±±ÄÄÄ0a¢££‰ˆˆí…ø½ô’Gdp„#ã¸À(F1ŽqÌd&Ó˜ÆT¦2‘‰X3|Ú« !„š:::ÈÈÈ ==;v››‹££#³fÍ"))‰+V¢v™ÔÖÖòôÓO“––ÆüÇðú믋÷N½½½üáà•W^á©§žâ/ùËkþûßÿžµk×òå—_2qâDµËt:ŽˆˆV¯^ÍŸþô'µË}˜C ©©©|òÉ'TVVFRR)))Ì;Wí…ƒ$‡\¾ä 4â YH"‰,e)>ø¨]¦Bܽ^Oyy9•••TUU)=Ì×óòòúM¤rww¿¦»‡y„„„`aa¡â«bdëé顾¾¾ß0w먯¯§¡¡ººº~Ý= C¿çðòòR†¯¯ï€=ÌdžKwT!„BܘÞÞ^ššš® h´´´ÐÝÝ}ÓÇZ[[1 »®®®XYYáêꊭ­-JH£ï±¾a ó1kkk\\\°··ÇÎÎN¹Ÿ³³s¿ðÇ@‹ù´¶¶*xs÷ˆ¾û}ñcÆŒ!""‚ˆˆÆŽ‹¯¯/666èt:åüªï¹Vii)Ýæ£¹r~Õ·“bßý   ™ç4Ä”––’——Çùóç• ÈÙ³g•yƒ‚‚ˆU"±±±DDD`i)‹Æñm=ýôÓ|òÉ'|õÕWDDDÜÖç–@ˆâÎjl¼))1u)/7m+*L£¹ùÊý­­Ma‘À@S@Äßßñó3HÌiÁ-î"—.]âäÉ“dgg“““ÃÙ³g)**¢§§[[[¢££‰ŽŽfüøñJX$$$DVl½Jee%YYYäææ’““Cnn.çÎSNl5 111„……­ì‡……©\¹CS=õd\¾|É—œä$-´`‹-™È´Ë—©Leã°@¾8BˆoRPPÀž={ؽ{7 ½½ÈÈH/^Ì¢E‹ˆWmbBoo/ëÖ­ãÇ?þ1cÇŽåƒ>`üøñªÔr³ÒÒÒøîw¿Kll,Ÿ~ú)ÞÞÞj—¤0ÌŸ?Ÿææf¾üòËÛºêËPôÆoð³ŸýŒÂÂBYåk0;vŒÔÔTRSS©ªª"::š””xࢣ£Õ.Q1Š(âG8ÊQv±‹2ÊpÄ‘YÌ"áòe“äN1¤iµÚkÂ}'%+«ÏÚÚÚâïïÍ$$óõÐÐPé*-ÄÔÙÙI}}=uuuÔÖÖ*!†††~A¾ãê),£GÆËË OOOeô vô zxzzÊ„C!„bÒëõ´··ÓÑÑAWW--- š››Ö7¼qõ±ÁÒÞÞNII %%%ý:!šÏ•êêê”ûšÏ‰BCCñööÆÅÅ¥;Iß•••èõzF…¯¯/H`` ²„F£‘÷^w‘ŠŠ Μ9ÃéÓ§9}ú4gΜ!??Ÿîînìí퉉‰aâĉLœ8‘)S¦'ç×BÜ„÷Þ{5kÖ°mÛ6’““oûóK D1ôtt˜B#ÕÕ¦mß°HßÉå7 8;_ÛaÄ××44ðò2sqQïu ñ-tvv*ÁsH$77—’’ÀÔªqܸqL˜0˜˜¢££‰ŒŒ$((HV3룫«‹¼¼ BˆëéêêâÈ‘#ìÙ³‡}ûöqúôi¬¬¬˜9s&‹-bÑ¢EL›6펯¾sñâEžxâ ²²²xùå—ùùÏŽ­­í­áVœ9s†•+W°eË–!ÕãÒ¥KLž<™Õ«Wóî»ïª]ΠÑét„‡‡óðÃó?ÿó?j—3bõ |üñÇTWW+!|qãÆ©]¢â6«¦šÃ&tö²—bŠqÀÙÌfs˜Ë\¾Ãw°aèwÿBŒ ßÔÝ#??ŸÖÖVåþ­>Û÷zhh¨,ž$Ä êììD«ÕRUUEee%Z­V×»íjîîîh4ÜÝÝ•áçç×ï6óu///ùnF!„Díííèõzš››éîîîÐøºcæÆ»vvvØÛÛãä䄵µ5nnnX[[ãìì<à1s@ã뎙} ‡ó…êêjÊÊÊ”°Gqqq¿ð‡¹“€››Fy/eoo••ÝÝÝtttPSSCUUeeeý:«™ß“ ÔÕC£Ñ‚£,Ð<âét:rrr”€È™3gÈÎÎF«ÕbeeEtt4S§NUÆÄ‰5 %ÄpuàÀ–-[ÆO~ò^{íµAùB _55¦ÐHy9TVö”•™Ž×ÕÁåU¡°³S8ÄËËññooÓmÞÞW‚$ò¦V ---\¼xQé€aÞ`ccC@@@¿ÑÑÑÄÆÆâ"©~ÊÊÊ® ŠäççSZZ €µµ5aaaDEE®Œ1cÆ,«0pŽsJ@ä$'9Ç9ôè±ÃŽb”€ÈÄËäo‘B ¤¾¾žžžÎž={())ÁÉɉ™3g’@BBS¦L¹#µôôôðÞ{ïñÒK/áããúuë˜?þùÙßF}}=<ð™™™¼õÖ[üà?P»$EZZË—/ç½÷ÞãÉ'ŸT»œAñÇ?þ‘µk×RXXˆÚåŒ(}C ›6m¢¦¦F <üðÃDFFª]¢â6ºÄ%2Èà‡8À (À;f2“ùÌg ˜Ît €!T3Pw¾×êîquØC&$ 1xÌ¿£_ì0_ohhPVŒ6³³³»n¨c Û|}}e!3!„âk˜C:ŽÎÎN%”ÑÚÚªtÆèéé¡©©Iéðp+éêꢣ£ã†jrvvÆÊÊ www¬¬¬ú4nõ˜9 Ñ÷ØHQWWGee%eeeJ8¾´´”ŠŠ %ð¡Óé°°°`ôèÑŒ='''lllèííUþû655Q__ßïùÍA___üüü”­øúúˆ½½½/_Ü%ŠŠŠ8qâÇçĉœ³˜…=òy¥bðéõzêëëû…=ú>.^¼HKK‹rÿoêî"Å…øÌÝ;n´ƒGMMÈ2³³³»¡`‡»»;²ø˜Bˆ»žÑh¤¥¥ƒÁ@[[›º0wÁ0‡.š››1455)¹:¨q#¹QnnnXXX(Ý0œ±±±ÁÑÑ{{{ìììpttÄÆÆ,--qssÃÒÒWWW¬­­qrrRî;PxÃ|Lܘžžjjj® y˜ƒ•••”——+a\\\pppÀ‚žžt:]]]ýº€@ÿŽæ÷eæ}óVÞŸ µôôôŸŸÏ‰'”qêÔ):::°··'..®_'‘¨¨(9ÿ#B~~>óæÍcÒ¤Ilß¾}P;èH D!úÒjMÁ‘ª*ÓV«½²ß÷¶êjèûçÓÎÜÝMÃÏϹÞupsSï5ŠÍh4réÒ%òóó9þ¼t¸pá555€i¶ÐÐÐkB ááá„„„`kk«ò«:),,T"}÷ÍmÐG…¿¿ÿ€ÿ–!!!x{{«ü*„PG)¥ýB"Ùds‰KôÒ‹=öDMÌåË&M4Á«]¶B F£‘ììl% rèÐ!ŒF#“&MRº‡Ì›7oÐÞ·ýûßÿæ¿þë¿°°°àw¿ûO<ñÄo±þùçŸóÝï~777RSS™0a‚Ú%ÑÓÓÃÒ¥KÉÏÏ'++ ~Çu:666Cþñçž{Žõë×óüóÏóòË/ãããÃïÿ{~ýë_SXX(ïw‘N§cß¾}¤¦¦²}ûvš››•È£>ʘ1cÔ.Qñ-URIYå(G8BYèÐáƒÓ˜Æ¦0—¹ÌaŽ@„·]GGeeeʤ¥ŠŠ eRSUUeeeTWWcþªÙÎÎŽÀÀ@‚‚‚”LPPr»L&âÆ™'666ÒÐÐ@}}=µµµÔ××+£®®®ßm]]]ýžÃÁÁOOO¼¼¼ðööÆÃÃOOO<==ñööÆËËK¹nB!Äp Õj¯ j˜;^˜Cmmm š››éééð1_Ô0?æF™_Ô0wÇ05nä1× wˆ;G§ÓÑØØØ/\kÞ¯ªªêw¾ÔÝÝ­<ÎÎÎ;;;¥3‚^¯§³³ƒÁ ÜÇÆÆFéÜáíí¿¿?>>>JgFƒF£ÁÇÇGþ»‹aÇh4’››Û/$rúôiºººprrbòäÉýB"cÆŒòß7 q3 HHH@£Ñžž>è]o%"„·B§3u©ª‚º:Ó0w"©¯7šš+Çú¤»S÷OOS§OOÓðò2u"ñòºr›ù¸´@w@SS“én(,,¤®®0µ£ ìףèêꢢ¢¢ßÊxæŽ-ùùùʶ¶¶øûû+«á]½B^hh¨œðˆ£vrÉåçÈ!GÙ–S€ .ÄÃxÆC ãGQ¤råB¡®¶¶6233•€HVVÌž=[ ˆLž<ù¶¾§hjjbíÚµüõ¯eÒ¤I¼õÖ[Ìœ9ó¶=ÿ`(//硇âÔ©SüùÏæ©§žR»$jkk™û,6l`Íš5üæ7¿Q»¼»NßȶmÛhkkcÖ¬Y$''“’’BXX˜Ú% !nQ7Ýœæ´ü8Â.q K,‰$R ~La ÑD3 ù¬@qëêë멬¬¤¬¬L™¸d˜·}'¿ÙØØàç燿¿?øùù)AsØÃ××WÅW$ÄÐöM;}Wfvvv×íÖq½Û„Bˆ;A«ÕÒÛÛKSS===477_ÔèììD§Ó)A––ŒF#Z­öº¹^¸ãF888`kk«„.\]]ûuÉpqq¹&¨q+15ÄðÒÛÛKmm-µµµTVVRSSCuu5UUUTWWSRRBuu5555tttô{¬ò0 ýæã^^^øúú*!ܾ×Í!___ äŠÇ`0pöìYŽ?®„Drrr0 ¸¹¹1uêT¦M›ÆŒ3˜1c†|Ö †­³gϲxñbÙ½{7£GôŸ)!„¸ÚÚ® Œ˜¯×Õ™®×Õ™B$õõpÕɦ`ˆiëáa£G_Ù7‡HÌ×Ôy­â®ÔÒÒÒ/ RXXHQQ………”••a4ðòò"<<œ°°0¥Fpp°²•î"&ƒºº:ªªª® ŒQZZúµ‘¾¡ Œˆ‘ ‰¦~!‘\r9ËYê0…Õq$Š("‰$šh"‰$Š(Æ2äX!ÄÈSTTľ}ûØ·oû÷ïG«ÕâççÇ¢E‹X¸p! .¼m“B²³³ùÏÿüO222xúé§yíµ×ðòòº-Ï=º»»ùÅ/~Á믿ÎòåËy÷ÝwUÿÂ%33“{_ýêWüô§?eÓ¦M<öØctwwãããCyyù^ùËÏÏOéŽ`mm À÷¿ÿ}^}õU|||Ô*í®ÑÙÙIzz:©©©lݺ•öövfÍšEJJ )))2ÉKˆa¨‡òÉç$'É"‹L2É"‹.ºð‹™Ìd³˜Íl¦2Gd±!ÄÓjµ)«Õš·æÛÊÊÊhmmUîogg§L$7/RcÞ7oƒƒƒ‡ô{R!î”––©¯¯ï×½ãzÛ†††W···gôèÑxxx(Û¾ãêc£GÆÓÓS~…B|#½^O{{»¨0wÀ0oÍûæ E{{»ò½^¯/Ì2Ì[sxÃÈhjjR 7:õÐÖÖ%táä䄵µ5®®®XXXàî.¬­­qrrúÚ Æ7=FŒL×ëæQZZJii)eeeÔÔÔPWW§ÌsÓâ¨æ÷ZÝÝÝ×ümccƒ··7xzz^Æí{]£ÑÈ!n‚N§#;;[ ˆ?~œ .ÐÓÓCpp03fÌ`æÌ™LŸ>É“'Ëßx1äeffrï½÷ËöíÛqvv¾#?W!B1µ·›‚!ÕÕW$æÀHCÃÀãêUìíûG¼¼®\hxz‚››:¯W kz½žâââk#%%%ÓÒÒÀ¨Q£Ðh4×EÌûÁÁÁØÙÙ©üj††o Œ”””(N ô…mß/mÃÃÃq“ßmq—j¤‘ó—/yäqžó\àÅcĈ%–„ªt‰$’pÂÃüñ—u…#‚ÑhäĉìÛ·ôôtŽ;†^¯'**Š °`Áâããñðð¸åŸÑÛÛˆ xùå—ikkãå—_æG?úC8¤~ìØ1}ôQÚÚÚX¿~=IIIªÖóÆoðÒK/ñì³Ïò·¿ý @ùÂi×®],]ºTÍò®Ë`0`gg7àª|ÖÖÖXZZòâ‹/òÿþßÿ»#+ßÜM:::øüóÏIMMeË–-tvv2sæLRRRxàÐh4j—(„¸AÝts Jøã$'É&›6Ú°ÆšñŒgæåË,fA„Ú% !†(NGeeåuƒæINæ…fe2Òõaaa¸»»«øª„P‡y¢à7uéè;‘°¡¡½^Ís}]׎ë u !ÄÈp³¡Š¾]6®î¶a4iiié÷|­­­JG s¨ãF™Cæ­yß¼0w½0oÍá s üuqqÁÒÒR h¸¹¹1jÔ¨ƒBÜ £ÑH}}½2êêê”`‡¹³GMM Z­–¦¦&ºººú=Þ‚ÞÞÞkxxxàåå…F£!00ooo<==ñööÆÇÇGéìáåå%!î°ææf¾úê+233ùꫯøòË/©««ÃÚÚš‰'*Df̘ÁرcÕ.WÅÖ­[yì±Ç˜?>üñ )!„¸[46š‚#Fêê®ì›ï£ÓõKËëFF¾2ÜÝûo]\ÔyÍbXÐjµS\\LII —.]Rö‹‹‹innVîÛ70B``  “È.ÓÏ3| IDATëõ”••Q\\Üo%‹òòrÊÊÊ())é÷Ÿ»»;@@@AAAÊ¿k@@€tow•.ºÈ# —/æÀH>ù´cúݰÎ1—/æˆy?ˆ ,‘Õö„w§ŽŽ2228räGåСC ÂÂÂHHH !!Å‹ãêêzKÏýÖ[oñÛßþgggþû¿ÿ›üàCvÓ¦¦&žþy6nÜÈóÏ?Ïþð‡Wú÷¿ÿÍ;ï¼ÃÎ;q¤sƒÁÀ¬Y³8yòd¿/¦¬¬¬¸÷Þ{Ùºuë üÜo«  €ˆˆ¯Ÿ¸lii‰››ÕÕÕXYYݡʆ§¾!Í›7£Óé”ȃ>(­Ñ…ºé&<²ú\NqŠ:°Æš"˜rÕÅYÑNaêvu¸ãê.ÕÕÕÊ{E<<<¾¶³GHHŽŽÒaHÜý:;;¯ oÜHÐãjæ`Ç@áë=|||†ì9¯Bˆ+®U\®¸:Tq»ºlܨë…*ú†+ú^wssÃÂÂWWW%tÑ7¤áì쬄0ú†:•.B¨¥³³S vÔÔÔP\\¬„;ªªª¨¯¯G«ÕÒÒÒB{{;º«ç5 ÀÒÒ\\\pww¿&äauøúú*û666wàÕ !n§ÂÂB¾üòKeœ:u ½^ÏèÑ£ûDf̘!‹_ˆ;®§§‡_ýêW¼öÚk¬Y³†·Þz kkë;ZƒB„b$koÿúÀHßÑØh´¶ÆÒrà Èleúˆ§Õj•pÈÕ‘²²2”û:88\j $ @ ŽÈê"&Z­–òòrJJJ” HYY¥¥¥”——S^^Þou ___üýýñ÷÷' ___‚‚‚Ðh4øûûãçç''Lâ® EKÑU—œË—&š°Æš@ àM´LØBÜUÚÚÚÈÌÌ$==ôôtNž<‰……qqqJ@dîܹ7µzIMM k×®eýúõDGGóë_ÿšäääA|ßÎÆyî¹çðòòbýúõÌ›7O9ÖØØHxx8MMM,]º”´´´Û>Ù§µµ•Õ«W³ÿþ~«9›YZZRVV6$;BìÝ»—%K–|ãýþû¿ÿ›_ýêWw ¢á§©©‰íÛ·“ššÊ¾}ûèîîVB ?ü0ÞÞÞj—(„¸òÉïþ8ÉI:é0ü1•©Ø!Q…Izzz¨­­¥ººZY¹¶¢¢¢ßÖôè;ÉÉÅÅ…€€å3¹   üüü”ÏíüüüðññQñ• qûÆ~††•mßý††eô]pÊÌÑёѣGãááÁèÑ£ñôôTöÍÛ¾ûæ­;„bð™æ@†9¨aï8¥K†¹›Fß Gß.WwÛ¨Ëƺ:\ñM¡ŠÛÕeCˆáÌÎÕjµ\¼x‘óçÏS]]M]]UUUÔÕÕ)ásÀêëXXX`gg‡££#NNN¸ººâîîNPPaaaâééÙ/¤ëëë‹……ÅzÅBˆ¡¢««‹S§Nõ ‰1jÔ(ÆŽÛ/ {Ç'ç‹‘£¥¥…Ç{ŒÝ»wóÖ[o±fÍUê@ˆBˆ›ÓÛk …˜Ã!7³íì¼öùo,8âæví0ɈÐÑÑ¡„ÊË˯é†QZZJ[[›r77·»aøùù)_œÞÊŠ×w£êêjåßÒüïZUU¥l+**èìó{kooO@@€²š†F£Q¾„öóóSŽÉjb¸(,b¾\⽘N4hˆ!æš°H$‘8!¡4!ÄðV[[Ë¡C‡HOOçÈ‘#äææbooÏäÉ“™;w. ÜsÏ=7ô¡éùóçyå•WضmÓ¦MãÕW_½¡ð€ª««ùáȶmÛxúé§ùãÿˆ““Ï>û,ï¿ÿ>ƒKKK^|ñEþøÇ?Þ¶Ÿ[QQÁ’%KÈÏÏ¿îªÖÖÖ¼öÚk¼üòË·íçÞ.o¿ý6/¼ð€A³W_}•_þò—w°ª¡O«Õ²cÇRSSÙ»w/£FbÞ¼y$%%ñÈ#àåå¥v‰Bˆ>zéåÿ³÷æArÕç½÷§÷}›½G+I€d@`°lðŒ°]ØŽ‚!vR8ñƵ)‡r®q½I¹^'\Ç×÷½àÛ 1Ž+eâ$6‹`‹AHlB ¡uÖžé½OïýþÑýûÍéžžži¤‘FÏçT×Y{útk4çô9Ïçùîg?/7†—x‰—y™×x%¸€ ¸°1\ÄEœÍÙ’:(K˜B¡ ¥Ž±±1>ÌØØØ4Éctt”J¥¢ŸçõzY¶lýýý 200 =”ü±bÅ ix#œÒ˜ çókúÿ¢p»Ý:Z“<çÕÐ@átäXd •|¡’0TB†JÌP *YC¥f¨Žù R3”4¡dŠ™¤ŠÖ”Ö´¹¤lÂéL¥Rarr’¡¡!Ýlrxx˜ÑÑQÆÇÇurG"‘ “É`Åb‘N¥§V«U T@@§tôõõ100ÀŠ+tcÊH$ÂòåË¥–C„cbll¬IÙ¾};©T ÇÃE]Ä%—\ÂæÍ›¹ä’KXµjÕbï®°xüñǹþúëÉçóÜ{ï½\z饋¶/"„‚ 'èË!3 #3É$ñ8T«ÓžÇÓ^i}Ì$”ˆù»dH$Zi”Hbî4áõzY±b…¾È000Ð$6,[¶Œh4ŠK¤#}ÑçðáÃZ æðáà éî†UÓÿÑþþ~ýÙªÜýýý,[¶Œ¾¾>}Ã[nÌ §I’ìe/oò&{ƒšb –³œ³8‹38ƒU¬b5«YÅ*V²’å,Ç{A8µæÉ'Ÿä‘GáÁäСCøý~6oÞ¬D.¼ðB,ËŒ?c×®]ÜrË-Ü{ï½lÞ¼™[n¹…÷¿ÿý'ð]Ì{/~ñ‹øý~n¾ùfn¼ñƦó€þð‡|á _X×Û°a»wïžu»U«VñÖ[ouüœƒ›o¾™Ûo¿½)yNa±X¸í¶Û¸é¦›aÏŽ>ø ^xá¼»rONNrß}÷i ÄjµrÅWpÍ5×ð‡ø‡r£SN&™ÔÂÇ®Æð2/“& ÀjV³±1œÏùlbkY‹éB)KUÈ><<ÌÐÐÐŒãÖkaæ‚õ™ÆªX]NvŠÅ¢–4‰Äœ…ŽD"A:žöóìv;‘H„p8<«Ð¡¶S @`>A„Ã|$Œt:M¹\>fC¥sÌ%_¨„ •|¡’0T2†*øViJÜP†.”€¡„ €H$ E %r‚pt¤ÓiöïßßÔRI$ Ø¡þf”Ëåi×ÁÍØív¿ßO0$ÓÓÓC__ŸN3\½z5Ñh”îînº»»%M„E§Z­òꫯ²}ûv¶mÛÆ3Ï<ÃË/¿L¥R¡¯¯‹/¾˜K.¹D»»»{—…S„B¡À׿þun½õV>üásçwÒ××·¨û$Bˆ ‚pjJA"qtÆE´iø|sJÌ`B¡úCŠÙO)b±˜ÌbÑ#GfÌ)¢Ñ¨îÔFµèÐ××wÚ'b”J%FGGõg«d‘#GŽ0<<¬/2Åb±¦ç…ÃaýYšes—ÄùÂ%œôäÈM“Dö³Ÿ!O]H³acÁi¢È*ÓàÁ³ÈïF¡3ûöíã‘Gá‘GáÑGerr’ÞÞ^Þ÷¾÷qÅWp饗²aƶÏݶmßüæ7¹ï¾û¸ôÒKùÚ׾Ƈ?üáY%‡_þò—tuuñ¾÷½ï8¼£é¨´|jµ:-¹Ãf³ñðÃóðÇüZ>ú(7Üpo½õVÛn¸f~ûÛß.Èk.$W_}5ÿùŸÿ9­ œÅbáÎ;ïä3ŸùÌ"íÙÂc_üâù×ýWn¼ñF¾÷½ïÍúœ‰‰ î¿ÿ~î¹çzè!l6›–@>ö±IçIAXDJ”x×ÙÍn^ážã9v³['† ³¡1œË¹l`p=ô,ö® ‚p†1«ä144¤‹2‘H¤£ÜFY¹r¥­ '%G›Ô122Ҷ˳Ûíž“ÐÑú¢^ANZÌRÆñ–1ZS9æÃbÈ*uC„ÅÁ0 FGG9xð ¾ç>22ÂØØLNN’L&I§Óär¹&±c&¬V+v»§Ó‰ÇãÁçó  …Btuui¹CÕE¬X±‚³Ï>[¾ï‚°äÈf³<ÿüóìØ±ƒíÛ·³}ûvÞzë-Î<óÌ&Iä /Äëõ.ò '?þ8_úÒ—8pàßùÎwNšû¡"„‚ §æt’v|~æmÆÆ`¦â,·»ž@Òúðxf^g^ÂIÖå÷t¦\.k¡A¥`(iD]h9|ø0©Tªéy½½½ô÷÷Fh’E–-[¦×©‹«§+Åb‘±±1Ž9Âè訖EFFF´4räÈÆÆÆ(‹úy.—K–ê"T¿Ž”5OŸîŸ±pr'Î>ö1Äà ³Ï4¼É›$˜*8‰a kˆeAÖ´ "‹øNAšQ]u~ÿûßóÈ#°uëV’É$Ñh”Ë.»Œ+®¸‚~ðƒ¬\¹²éyO<ñßüæ7y衇¸è¢‹øë¿þk>ö±µ-Êårô÷÷“ËåøÁ~Àç?ÿùòÞîºë.>ûÙ϶-„²Ùlø|>ž{î9Î:ë¬c~­r¹Ì]wÝÅ×¾ö5](ЊÃáàꫯæç?ÿù1¿ÞBÒšpb±X°X,Üu×]\ýõ‹¸g ËÞ½{ùøÇ?Ξ={(—Ëôöö222Òöw6‹ñÀpÏ=÷ð›ßü‡ÃÁûßÿ~®¹æ>þñË TA8ÁdÉòZcxµ1ìb{ÙK… nܜ˹¼·±‘œÇyld#ƒHA8ÙÉd2 366Æøø¸žÑ_Ú]gr»Ý LK´]¾|¹¾þ400@__Ÿ± ‹Š*0>±C¤AN”<¡¤ ³,¡ %f(CýmTÂ…-`*iCIJâPSÕëMRÆÑÊó‘0‚Á 6›MË*C„S%¥:tHר”ŽT*E:ÖI™L†\.G>Ÿ§T*Q©TÚžËAýú«ÍfÃápàv»ñz½‚Á ]]]twwëû郃ƒ¬X±‚Õ«W³lÙ2IëAè@,cûöíìØ±C‹"ãããØív6lØÀÅ_Ì;Þñ.¾øb6lØ âìiÊáǹù曹ûî»ùÀ>À?þã?²fÍšÅÞ-!‚ ‚0µZ=i$™œz¤RÍÓ*¤Ý:5݇c*u$©Í)$æi•P€ß_‡Ãõ±œhžPÔu£YÝ\V7›ÍëÌ]8Ünw[YD%‘ôööê 4~¿ßáâ‹Åš>ËÑÑQ†††ôç¬nö7]s¹\ZÐQŸi¿þœÍÓ½½½r#S8)ˆç8ÈAö75€Œ3®· Ö©"*eD%¬`ýôcE~¯AXÊå2;wîÔ "O<ñ…B5kÖpÅWpÅWpùå—ëô¯çž{Ž¿ÿû¿ç¿þë¿X¿~=_ýêWùä'?‰ËåÒ?óÇ?þ1ŸýìguŠÛg>óî¼óÎYSEŽ…T*Å™gžÉÄÄÄŒ7Þìv;«V­âÙgŸ]0!urr’o|ã|ÿûßÇjµNCCCCôôœ<Ýé™L˜ºy÷ÝwsõÕW/òž-¿þõ¯ùÔ§>¥oÈ*žxâ .»ì2 ~øà׿þ5¿ùÍop:\~ùå\sÍ5üÑýÑin/'‚a†y•WyרÓ^ã5r588‹³8‡sØÈFÞÖÎâ,lHA„ œ Ôj5ÆÇÇÓb‡šÖ×Ô´aMÏWEë­r‡9‰V¥{‰ —Ë‘L&õ#‘H豚6¯k•:òùü´Ÿ9“Їg•:¤£© íPé­²EkªLÉJÐh•-” ¡d sú†4Ôë©tù D %^(áÂ,Z´¦[(‰CIJÖP’LI3É*C„Ó%ä8p€#GŽèDuzrrRŸÏ)™Ã0 Êå2•J¥cJÔ“:l6N§³Iê0'uD"ººº´Ô±jÕ*Î<óL)@A8ìß¿_'ˆìرƒçŸžL&ƒ×ëå /lJ9óÌ3{w…ãH*•â»ßý.ßþö·éïïç;ßùýèG{·¦!Bˆ ‚ œ(Ì $IfÚ&ƒ6Ý‚5­‰$­)%íRKZ—uuÕç…¡V«é›ØfYddd¤©Káè訾®ðx<ôööFéííÕÓJdP7¸ÕºÓUl(—ËMêBœúŒÕ´úw0ñY­Výù©Ï³»»›žžzzzôºžž½\:1 ‹AG8¢SEZ“Fp€ SIV­)#æ´‘(QV²’Ò\„ãO.—ã©§žâÉ'Ÿä÷¿ÿ==ö•J…³Ï>['ˆ\yå• ñío›Ÿÿüçtuuñ¥/}‰n¸žž.¾øbžþy-„œ}öÙlÛ¶M߸_hn¼ñFþùŸÿ¹mR‡‡ÃÁ»ßýnz衽·gÏþò/ÿ’­[·bµZõ{·ÛíÜzë­|ùË_^°×:&&&´œ¢nbÞ{ï½'åУ¡\.sË-·pË-·`±Xš~ögÆyçÇ=÷ÜÃSO=…ÇãÑÈÕW_Ïç[Ľ„¥I™29È>öñ ¯°›Ýìc/ñcŒ"ÄYœÅÖp.ç² ¬a Ø€¹Þ!'šB¡ÀÄÄñxœááa†††t±»y^uÎm=ÿŠD"ZâœqzùòåR¨),(årYËñx¼­ØÑúPÛ©õí¾O¨¢åp8L(ÒvBG»eg> AŽd %hÌG¶P‚†’-ZÓ0Ú¥oÌ%[(Q¢U¶hMÄ€)ÙBIJ®PéJаÙlƒA`*=C ’’!Â\PUúF:&‹5%sLNNÇõùZ6›Õ3‹Å"¥RiV™ê×B‡N òù|ZèˆD"ú>³:‡[¶l+V¬Ðé"u‚ œšT*vïÞ­SDžyæ^~ùeJ¥ÝÝÝZ¹øâ‹Ù´iË–-[ì]Ž‘t:Íí·ßÎm·ÝF¥Ráæ›oæ+_ù î“´¶R„AA8•0Œö2É\–™çç#—ÌE$¹ä˜)‹Œ3::ÚÔq¦nˆæ‹QJlèëëÓ)f‰A]t2/;]o’OLLhi¤U '‹éG»®à¡Pˆ¾¾¾iŸ¯Zf^®.ô Âñ¦H‘Cbˆ!ra†9܆â‡a„2S7zé%J”¬`Ëd¬hGß_A–L&ömÛt‚ÈóÏ?ÍfãüóÏçŠ+®`Ó¦M<ûì³Üu×]†ÁUW]Ž÷ÞÛög¹Ýnþû¿ÿ›+¯¼rA÷ñàÁƒœqÆÔjµ9FØl6¾øÅ/rûí·/è~@=™âÆoäÈ‘#T*, ëÖ­cÏž= þZGÃŽ;¸ä’K°X,¸\.î¿ÿ~.¿üòÅÞ­allŒO|â<ùä“T*•¶Ûx<\.ùÈG¸æšk¸òÊ+›ÒmA8:jÔ8Âöš†×xÝìæ-Þ¢D V±Šõ¬çÎálÎÖÓýô/ö[„%ê–Û*t´›m’*Íéh4J?6›$øG‡ú=UIæù™æíZwêwXu©Ÿ-•£u;ù½„“•~S"†’)”Da.Tª…ú;a9”Œ¡Ä %_À”t1SúÆ|h—†Ñ*[(A£U¶0 ­²…4ZÓ7” a~=A„ãA.—ÓG2™dhhHß³œœdbb‚‰‰ -zd³Yý0‹3]ÃkÅn·k™C¥søý~áp˜®®®¦†‚Ñh”eË–Ñ××G(’ë‚ B†aðâ‹/ê‘;vðÆoP«ÕèëëcÓ¦M\pÁ\xá…lÚ´‰3Ï<ó´mø{*1<<Ì?ýÓ?qÇwP*•¸é¦›¸é¦›Nú0BAáte®rÉlÛLL@±8óë,„\âñÔÇ‚F‰!æ ó´f@[‰Au&i]ÖÓÓsZ~)1 cZËv,ÕcdddÚgív»› Ú>˜;ÄÈÍáx'Þ”.¢¦Õx/{I’ÔÛ»q7%Œ´& 2ÈJVbG: ‚ptŒñØciAdß¾}x<6oÞŒÇãáñǧP(tLê¸öÚkùÑ~´`i Åb‘;mÛ¶ñÔSOqäÈ\.åryÆ‹?üáù¾° û`¦P(ðÿðüÝßý†aP«Õxúé§Ù¼yó¼~Ž*:é„9±N„Ì„ÍfãàOþäOðù|lݺ•w½ë]óÚ§“•Çœ«¯¾zÆ®Îf{ì1Þóž÷œ =„¥C•*‡8Ä^öò&o6É{Ù‹Aýï•kYË:Öq6gsç°¾1xñ.ò»„¥C.—#‹éëLªQÆLJòù|ÓóÕµ$U0ÕßßOoo/ýýýMÓ’ %Ì™ù æG,›ñ<®“Ä1›äÑÝÝ-€‚pŒ(yBuco'f(yB%bt3̉*Y£“˜¡^w¾¨ïÈJ–0K*½B¥d˜å %]Ì$[À” Ñš¾¡ž£~® ÂÉD"‘ —Ë‘ÍfI¥RŒ111Áää$£££LLLè5%zd³Ù¦Ô£b±H¹\žsZ‘ÍfÃn·ãt:q¹\ø|>¼^/>Ÿ¿ß¯eu_»¯¯›ÒÙAáx“L&yá…xñÅyþùçyñÅyõÕW)—ËÎ?ÿ|6mÚ¤6lD¼“„§žzŠï}ï{üò—¿$ qà 7pÓM7ÑÕյػ6'DAáØÉd ®S)H&§æÓéú|*Õ¼]<>5­ž—HÀL§&N'øýC0XŸêãH|¾º8¢Öy½õq0X_îóÕŸëñœ–‚I»îä†v]ðÔ Ñv"C»å§cÇ»|>ßT8aNi-®PËZoPû|¾&1Ç,í¨$s:IWW—D F‚G8Ò”.2ÄG8Â!1Ì0cŒéííØé£hcè£e,k«u<‹øÎA8xë­·øíoËoû[}ôQFGGçô¼î¾ûnÞûÞ÷.ø>MLLðì³Ïòì³Ï²cǶmÛ¦÷ËívS,©V«Øl6¶nÝÚ6!#›Í’ÏçI&“ºøÄ\À–L&) d2Òé4…BT*ÕT¨266Æë¯¿Îðð0}}}¬X±BÉÀTáŒ*˜Y,T÷Qh–JT‹*~©¢UH£:˜†Ãa}S7 âr¹ø|>Ün7¡P¨©Xo¡¨ÕjÜ~ûíüÕ_ýÀ¬]N'7Þx#·Þzë‚íƒ ,5†b7»Ù×2ìaYê«”„|.ç² ¬1 «Y•Ó¯1  µZ­íµˆÖkª±H,#—Ë5ý §Ó©“O£Ñ¨îŽF›Ä•T+7¬…*ÌV…ê‘J¥ˆÇãÓ–%“Iâñ8‰DB?O·Ã\È …¦Í«eêѺN ª…Ó•N©ê{g§T‹vbFkšF'1ÃüºóA%bÀT†úžiþv3T‚†úÞi2”ˆ¡Ò5Ì?[½® ©L6›Õ)êúd:fdd„X,¦ÏÁÆÇÇÉd2úe:Æ0 …†ahc®IV«›Í†ÓéÔƒ=>Ÿ@ @$! ‰Dèêꢧ§‡ÁÁA}ÿ3 õßjAA8UÉçóìÚµ‹^x矞^x]»va‡ƒ³Ï>›7ò¶·½7²qãFV¯^-ßGNäg?û?ýéOyùå—yûÛßÎ7Þȵ×^‹Ûí^ìÝ›"„‚ ‚pr‘ÍN‰"J$1Ë%‰D}ܺ,›­§–$“õå†Q_× §.Ž„BG'”Ìôü%@©Tj*P­É#Jp˜˜˜˜V|h·Û§¥ŒôööNK%éêꢫ«K‹$§›DbîdØNØi]6666íbk§î…rU˜7 i ‡9Ì(£ 1ÄHcf˜QFg¼éyA‚ 2¨e‘~ú‰e 1D‰ÒO?}ô-Ò;ádâ®»îâsŸûÜ4AµŸûÜç¸í¶ÛtáÆBQ,›Î8ÀÎ;Ù³g{÷îåðáÃäóy|>›7o&‘H袶D"A¹\îøóg’ÔMRµÍf#‘H‰D‡ÃZŠ€©sÁŒs!ÌL¨âhN i‡aúF°¹K²*‚©Î­æâUSHªðÈ,˨Ÿ£ ‰:a.öSÓ3-S%تߕT*Åõ×_ϯ~õ«yýÎ 044$ä…Ó– ö³Ÿ·xKßl oñEêÒZ„gÍ0ȹŸ t&ŸÏ3999kb©ZÖ®ñ„ùšÁ\LåZÁéG¡P˜Uܘ‹ä¡ºü·#‰tQ_ è(o´;§„S‰N©æïHsIµPbF»4Nb†zÝùÒšjaþžÙIžh3Ú¥it3Z_Wát$—Ëé2¹\N_S÷iS©ZªUòF&“!ŸÏëç—J%J¥’n&3_l6.—K§©k—J΃ºÁJTçnê{EWW—ü=A„”ËeöìÙÃK/½Ä®]»Øµk¯¼ò û÷ïêß‘6lØÐ$‰œF~oa IDATwÞytww/îŽ/<Èý÷ßÏ/~ñ žxâ "‘ŸøÄ'¸þúëyÇ;ޱػwÔˆ"‚ ÂÒÆ0 Ÿ¯ãñùO·[—LB§B1·».‡¸ÝSâÈ|¦Û­ á$%Ú)t*T8räHÛŽG+7ôõõIår¹©«§ºð«>{óØüï¡n@š±ÛíZÆ™i<Ó:Õñ[:'ÎC 3Ü4Ž×Ó‡9LŠæ."D‰2È`Ó8BDO¯`A–†„'ÂtÞþö·ó /Ì«8ßf³FùÉO~Ò6©CaÃÃà 7‰¯JrUËÌ7Z[ñz½M¢ê@ºbÅŠ¶…m‡`0¨‹f"‘ˆ~ŽÐUeNZQÅR­¥Í"N»e­¸Ýnº»»§X,b±X°Z­Ôj5-yÔjµŽ¿‡Ï<ó —\rÉq{ÿ‚°˜äÈñVch?ö³Ÿ8õBD ¢D9£1´JÝÈ *APtJpiY+‘HdV©C-[¾|¹|_¨s"s³õh·¼Ó¶3ÑzΜÔÖi™yùé˜",,R-” 1—T ³˜Ñš¦ÑIÌ8ÚÔÆv©J8ÚT •¦ÑIÌh÷º‚ Âì¨s*óù•šmºW—Ífµd›ÏçÉf³ºÑJ.—ÓÉ…B£)á³Z­8Nìv»þ›ï÷û›x½^/Á`þþ~~¿Ÿ•+WÒÓÓÓt.§Ò{¥ù‰ ‚ ,©TŠ—_~™—_~™]»véñÄĽ½½¬_¿žõë׳nÝ:Ö®]Ëúõë9묳ä:à  žyæ|ðAî¿ÿ~víÚ…ßï窫®âSŸúüà—D ²!‚ ‚ GC<¹ÜT*I6[ŸWé%†1•rbõu‰ÄÔsÔt.WOAÉd ¥ƒã4æšPÒ)áÄåªOàtÖŸ»ˆd2-4˜­bI;ѤÝi¬Ybh•:- .‘d—ÙhwÓ¿ÓC£´+ 8ZqGŠ„v¥5e”*SE¹nÜM’H¤1´HúèÃÎÒ—Æa)ðÆo°nÝ:ìv;µZmÖ„3êfå>ð¶lÙÂøø8CCCú˜¦Š,Íz{{u’™J1ëé顯¯¯iYww7ápxI\<Q‰J¾Sx€X,¦»(ª®Š­ç@6›MT9NÜn7üÇ̦M›X¶lÑh”eË–6ç–©O‰‡8Ä>öés®}¦a?ûõ¹W„kƒvÕüzÖãgaÓ™ád'—ËéF zlž6¯WÒiëyM0ÔçæG»eýýýrŒY"˜¯ÏÌWÜPËÛ¥Á(Tñø|ÅÖeªÛ¿ ÌF§T ³ 1—T %f´KÓè$f¨×/­©J˜kª…3Ú¥iÌ$f´{]AaaQ‚_*•"ŸÏë$T*Åää$cccd³Y}­È0 ‰¹\Ž|>O:¦P(P(0 ƒr¹L¹\>êÄ ¨_WR2žJÞp»Ý<>ŸP(Ô”¤ÖÝÝMWW===tuuáóùðz½:­Cî· ‚ ÂéÃðð0/¿ü2¯¾ú*¯½ö¯¿þ:o¼ñ¤V«a³ÙX½zµDÖ¯_ÏÚµkY½z5+V¬Àår-ö[8a$ ¶oßÎO<Áã?ÎöíÛÉçóœuÖY\uÕU\uÕU¼ç=ïYrŸ‰!‚ ‚ ' ™db09 mR8¦ÑšTÒ.½d®ËZׇÃp:ÉD299Ù¶Ë·ÝnŸ—Hbîø­n .e Ãh›@2—që× «Õª?OsõÖÏTM·ÎK1ÂéM‘"cŒq„#Œš†qƉc„Æ#FŒqÆ©0UleÅJ/½ôÐC/½ 0@ocè£~úõü’>"‹H¡Pà_þå_Èd2|ë[ßÒ…Aó¥»»›åË—³|ùrX¶l™G£QéééYrû„…£V«‹ÅåÈ‘#ŒŒŒèñáÇõxtt´©  ²jÕ*V¯^ÍêÕ«õôªU«Xµj½½½‹ø®„Ó sZ›’<Ìó8 Ï—ܸ›$ópgB:F K“B¡ÐVà˜Mðh×8!ëB­®®.]¼¥ ¸Ú‰ÒµïÔ ]è£WÅîí0wfž¸a^&¿WKUà S‚E»eê÷ïx,Sò…ùuçC»T uÍo.©JÌh—¦ÑIÌh÷º‚ ÂâÐz~eN×H§ÓLLLèdX,†a¤Ói½m2™¤X,ji£T*‘Ïç©T*T*•y¥›±Z­Øl6\.—NÜðx<ú¸äóùt°’2‚Á ‘H„îînz{{ñûýøý~B¡^¯W' ‚ ‚  Ãàõ×_oz(aD}§·X,D£QV­ZÅÊ•+›Æ«W¯fåÊ•§d#šR©Ä[o½Åž={عs'/¾ø"/¾ø"ûöí`íÚµ\vÙe¼ç=ïáÝï~7gžyæ"ïññE„AA„¥N;Q$™„b±žf’ËÕ¥‘x¼¾,›­'–‹õ$õœTª¾,•šzN"³NZ,Sé$^os:‰GB¡ú²@ ž`âtÖŧ³>ï÷ן M‰&Á`}YãÆÞ|˜oR†zŒŒŒ´í4g¾ñ>ÓMûNë{{{—l'¸N²H"‘hz$“I’ɤžžé†òlÒˆšD"m·ñx<'øSƒ5ÆC«,2Æ#Œhqd”Q$šžïÂ5£,¢¦{è¡>z葮؂°@LLLðú믳gÏ^yån½õÖ¶Û9=== °jÕ*Ö®]˪U«8ï¼óذaÃ’=¶ ''cccŒŽŽrèÐ!>ÌØ¿¿ëb¯×«e‘3Î8ƒ³Ï>›uëÖ±nÝ:V®\‰Õj]äw#œ à‡ÃAN›Ï3u>%Ê Ó°šÕz¼šÕtѵˆïFõ]_%ƒí÷üÙ1‰F£§Í÷úS\.G6›%N“L&Éf³º˜0•Jéùd2I:Öó‰D‚L&£çãñ¸N8˜ ÕÉYª"AÕÙY͇Ãaü~?>Ÿ@  ¯S¨BBõN.jµ‰DýZ*>]èeÐYêh·ìhP)æ„ %PË2•¦ÑIÌh—¦!‚ œœ¨Ä'•ङԽ›d2Éää$¹\ŽññqŠÅ"ñx\'®¦ÓiJ¥’çóy …år™B¡ ecÁjµjQCI‚.— §ÓI Àétâ÷û x½Þ¦s®¾¾>ÝÝÝ„B!\.—–7\.—>– ‚ ‚ ,b±û÷ïçàÁƒÌðð0{÷îeïÞ½ìß¿Ÿr¹ŒÅbáŒ3Î`Ó¦M\pÁ\pÁ\tÑED£Ñãº'"„‚ ‚ ÇF©TH²Ù)I¤P¨K#ét}:•ªK%ù|³Œ’ÍÖ§Í2J:]ŸN&§d”ÙðzërH8\—HüþúÃél–Q‚Áú²`°YFq¹êÛ›eõõ³A_ÜVÂB*•"7] O¥RMÓ­²C©TjûÌâB0lšnMÐ0¯WóK59c.òNkÇMõˆÅb3~Þ³øt{¤Ðg颺hlj3̰žnbhš@!B”(‘–aÁ— ÂéH±XœÖ©fÏž=¼þúëú£Çãaݺuôõõ±|ùrÖ¯_Ϲ瞫SNÅ.5ÂéM±XÔØÕcÿþýìÝ»—×_X,ÔÏQ”¢JYªç{Âträ8Ä!†æ0‡9†j>Æ×Û{ñjÁ£UøPƒ ICN ‰„þN×iZ%vÄb±Ó@½^oSJGww·~´KñPóò}ïÄbî}4ɿ鉉‰Ž‡JèÔØ£Ó¼yZ’8UpZ­VuBà|—™…‹cYv´©€$`J~8–ef ãX– ‚ §63¥i˜ïCår9&''ÉçóŒcÙlVoŸËå(•J†A©TÒ¢ÆB¤j(l6›>Ùl6Øäv»õy˜JkRB¬Y¤íééѲF(Ò’‡ÏçÃårIʆ ‚ “Íf›$‘‘‘b±cccŒ‹ÅˆÅbŒO;W …BØívB¡NâTç|~¿‡Ãѱ1„º¶£®ÉÔjµ¦³êºÂãñ088Èàà kÖ¬aíÚµ¬]»–³Î:‹µk×J³DAAN”Lb›O溬ÝúÉɺ´2Üîº,¢d’ÙÆm¶5l6’•JýQ.“²Xˆçó$T¡@²ÑŲT’J¥f,nè$Œ(©$ êŽGj;5ï÷û—\¡¡YÖ1§˜§gJ(I&“är¹¶?×ü›o\´v÷TÛ¨ŽTæeª[•pj’%ËcŒ3ÎĈ1ÑT ‰y~‚ J4 J––J¥¶‰õóE%j¨‡ËåÂn·ãõz±Ûíø|>ª¡„ •´®Î¥ºººzl·Û‰D"ú|Í|LAA–µZMË!J‰Çã”Ëe‰¥R‰L&£Ï—Õù®=Úa±Xô55V×}Í [ûûû‰F£tuIÊùlˆ"‚ ‚ æD•N¢¤‘L¦ž‚’LB¥RO@)—§ÒOr¹ú£P¨/+—§¶I¥¦’OÔÏ› J Q‰%Á ØluÁÄfÃðùHI»”ÕJ²V#uÁH $«Õº`R,’4 ¹‰L†T6K&—Ãè°/fADIª˜b&‰¤ÝsT€Sõ%U "ªyY:ÖE-©TJǘ—̓®:\µÊ%ês4/ úóo·L N~’$g\ "f‰dŒ±iRÉThþÝñáÓ¢H=tu"Dô´ér,_jµ¯½öÏ<ó ;wî䥗^bçÎ:õ`ÅŠœwÞyœwÞyœþùœ{]»ö”?NÂñ¦Z­rðàAöìÙÃÎ;Ù¹s'»víbÏž=”ËeÜn76làüóÏç¼óÎãâ‹/æÂ /”ÿ[‹@ŠC 1Ê(‡9Ì#âCá‡a„"Srw˜0ËXÆr–%ÊJV%Ú´¬Ÿ~,Xñ §#’f{ŒŽŽ¶í,<[JãLIhœª°_uƒnÎd2äóy=m†þþÚnÚ,tBu ƒx<|>_Ót(ÂívÏIÚ…Bø|¾%s,3§M˜;z/Æ2u3þh0Ë椕ÅX¦:‹ ‚ §/Žwccc ÆÇÇ) LNNR(H$‹E’É$Åb‘t:M©T"›Íêä •˜¡ÄŒJ¥B¹\¦Z­R.—DÐP)6› »ÝŽÓéÔc§Ó‰ÛíÆétâñxp¹\ú¼¨»»»)µ,‰hI# ât: …BØl6Âá°>?AAáô@„AAA8‘­`R©ÔŸS*Õ†J8ÉfëÒI*Uß&¯;aq›¼ßa·w¹ˆÛíÄ­VòN§^/—ÉÛíÕ*ñr™x±H¾ZŨTˆÙ,ÅryÆ×q»\DÂa<æâus.ó§r!ê–n¤¿˜Å‘´)FuTí´l&TñŒ’DT§ÔÖeJ8iÝÎëõêe6›í~:B'&™$Ö̲ˆK&™$NœIÓ``Lû9M²H7Ý3Ê#æÁÍÒ(žd2É3Ï<ömÛô#ãñxšŠÓ•²Ô¨a±) ¼òÊ+ìÚµK XJÂr:lÚ´‰Í›7³yófÞõ®w±råʾ9È<ÀgøÌ)-&Ɖ3Äqâ 3ÌCÓÆG8B’æó´Ö°†(QÔcµl9Ë Z¤w%,e*•JSb*•jJ 4'(Æãq½LmÇ)´IÑt»Ý:ÜÔ1—é`0¸ŸÄÉmÌÒͱNw꺧0Îgz¦u‹™Æ¢Þ?ÔLT¡f"QOZR]»&B¥Ñ¨î…0•nèϱ\.kFuÿ†ú¹°* UëÕÏ7/›/ª#" ?ß…^èsó¹.A3J¼PÇQuì­Õj Q­VuZ†º¶],õ6Ôj5R©årY'm¨éJ¥‚aÔj5-{‹E}l^(ìv;‹‡ÃÍfÃétb³Ùp»ÝØív<‡ŸÏ‡Ãá àp8t"F(ÂårÑÕÕ…Ë墧§—ËE__Ÿ~žÓéœ6AAA8ˆ"‚ ‚ K•LÒnϼn>Ûäóñ8y’IãÑqÞf«‹'V+†ÅB¼V#^­’¯ÕH”Ëtú’â¶Û‰x½xœÎºlâ÷itu7:cy|>Ü>‘în<Á îH„HWWÛâ•îîîSª³d»ÂŸNÝ{gZ¦ Xfb>@³­[̡ӣIQƒG&˜h»>CfÚÏòâ1}¤›n"!LXÕ´ ‹–£££<òÈ#üîw¿cÛ¶m¼úê«T«UÎ8ã ÞùÎwêÂó .¸à”•÷a)ðæ›oòôÓOóÌ3ÏðôÓO³sçNÊå2Ñh”Í›7óîw¿›-[¶°qãÆã¶Oñ·qÿÉR¥Ê ¼À\pÜ^ïhÈ‘c”QFaœq†b„†Ã# 1Äc”˜ê¤îÁC´1 0À ƒôÓÏ2–ÑGËYN}’ê!5dŽV¡C¥¶ ª¸½Ç£ÓU¡’5Ìó3IªCÿé€*èW€¹‹´Z¦:Lg2-Äãq]i^V,Éf³d2] 9í¾_y<ÂápÓ´×ëÕÿ¶æiÕIZM{<ž£sŽF²PïÚKf £d¡RN 9]C¥oV«ÕŽÍæ‚JÙ´Ùlús1'Q¨F V«•P¨.ï¹\.¼^/@@‘*áB¥VÍ2³„!‚ 3a>nªã®ad³Y’É$Ùl–\.Çä䤖2*•Š>GQëK¥étZKµZl6KµZ¥P(P*•¨T*ú®Æ •¡0'e¨iuŒôù|@=ÍÜb±à÷û±Ûíø|>\.G'”¼^¯>ïQ "‘ˆ>–[­V}|—†H‚ ‚ ‚ ,EDAAáØ©ÕêI&sC]6i3®LN’*•HÆbd*2‰™J…x*E¶Z%ŸË‘¬VÉõ„¡.›¤£±,Õ˜Îβûn«ÍFÄéÄãtâ±Û {½xŽz¡Çƒ×çã j¼^<4 _#ý$ØÛ‹?ÄÓÝM &00€ý$.šRE6*…$—Ëéä’\.G6›%“ËåÚ®K$ú&¢ZשC›*ž‰D"x½^¼^/Á`Pß°óù|×…Ãa|>_Ó:»Ý~?±¥O‘â4yd¶!ÞÚ 0£,Ònlžöã?ÁïþÔ GŽÛ¹òQÎåÜãúZ†aðä“OòðóuëV^zé%ïxÇ;xç;ß©%㺂 ¹\ŽgŸ}–§Ÿ~š§Ÿ~š'Ÿ|’‰‰ Ù²e [¶láŠ+® ¿¿ÿ˜^§D‰{¹—ÿÃÿáE^ĉ“"õ¢¡­le [âítÄÀhJñ˜mÚŒwSŠG”("Ó–E‰Šè!ÌH:nJ÷‹Çãd2™¶ ídUÄ׎™dŽÖeæùp8L8ÖóK¥±”” :N'“ɶ˔ ŸJ¥(‹¤R)-´[6›8¯ƒ8N‚ª9A㻢êm^æt:õw—Ë5Mê°Z­X,Ün7µZ ·ÛÝ$B´›>‘ë%X˜E‹vÓǺ~>ÛªAA˜NÇÌD"A:¦P(J¥( ú\P‰Åb‘R©¤¯­†¡E‹|>O>Ÿ§P(P©T¨T*”J%ý¨T*Ôj5-B.t2€ÕjÕç V«»ÝŽÝnÇápàp8p¹\Øl6=v»Ýú¡®ßªkµ~¿—Ë…ßï×ç=ê|4´=N‡Ãa,ùn%‚ ‚ ‚°Pˆ"‚ ‚ ©ÍE”äð0F±Hnb‚D6‹‘H`‹Ä''1J%ò™ ñB#—Ã(—Iärä*Œb‘d¥B¶T¨ÕHU*MJ'€ßb!h±à¶ZñÛløív ùÄi·ãs:ñ¹Ý8m6"«(„ÏéÄéóöùpz½ø¼>®`p €#!àõB8  øýàp€Çnw}Úb‹êU'ØD"¡Å‘v"‰ºªd”l6;ã:Õm¶N§SË"^¯·ÛM8Æåráóùô Ç@  »Ç…Ãa}ó1 áv»õ¶.—‹`0(EBGA‚qâ$ƒšn·[VW»šqà˜³H"D°eZ„Oáøó{~Ïe\À¥\ÊÜÈñG8Y˜Ï¡¡!î¹çxàžxâ ÃàœsÎaË–-\y啼÷½ïÅ‚ÿ®‚°°T*žþy¶nÝÊÃ?ÌÓO?M©TâüóÏçø×\s ]tÑœ^’$ÿÊ¿ò¿ùߌ2Š+*z½ ?ãg\ÇuóÞWƒ1ÆC¬1Œ1¦“=Ì) M¯ÛÛúè#J´ã´éÊ~:s,©{jY,ëxÞjNÕk÷hMÞ3?N•´=•Ú ÒÔç£ "UºC<×rºúþ0SšF©Tj»l6|>N§“H$‚£!ùû|>l6~¿‡ÃÓéÄétjÃáp4CÚl6jµ‡‹Å¢‹0Ëå2v»ý˜%‹¹¾—™8™$ ó2÷A„¹¢Ž…æä§\.Çøø8“““är9}¡’¹”Ha>¾–J%=]­VÉår”Ëe*•ŠNµ*‹:é"ŸÏS«Õ¨V«MÇøãQ:c±X°ÙlMâ…ÅbÁétbµZõy‰ÕjÅívc·Ûq:¸\.ìv»>ÖªT u=TÉ¥ækžjyww7V«U'Q©óó1[AAAXZˆ"‚ ‚ ‚p4¤RP©ÆÈfÉŒŽ’J§1&'ë"D,†‘Ï“M&If³™ ¹|žT&C©\&™Í’/•0ŠEÒ…ÅJ…d±H¡Z%W.“®V)Ïáëšp!À àn,sX,Îú2·› Í†Óå"ètâv:ñ¸Ý].>!§w8ŒÇf#ÐÕ…Óé$ÔÕN'¸\àõ‚Ý@ýÅ#‘ú8›­¾ÞåšÚÖf«¯;TAY;Y$—Ë‘ÉdH&“ºÓžê ¬º3«N}¹\NwðS7ˆg#‰4É#ªÓßﯖ&y$‰è”`0ˆÛíÖÛº\.B¡¾+ðšÉ“?&¡¤Fûÿ'aÂM‚ˆyڼμ¾u‹“K ú ¿áC|+VjÔânàó|ž38cÞ?3‹ñÿñÜ}÷Ý<þøã>ô¡qå•W²eË–/_¾ÐoC„“ˆL&Ãc=ÆÖ­[¹ï¾ûØ·ok×®åÚk¯åºë®cÆ mŸ÷oð=¾ÇÜI¹1´ÃƒÛ¸¹‘ ÆgŒ1ÆׂÇ(£mç³-o.\ôÐC/½³ ½ôbG ’—*­²±9•C%n˜Ó:Tú†y;•ÎQ©TÚ¾†ÓéÄï÷ë„@ €ßï'èNÇêá÷û‰D"Mó@@/[Ìâø\.§;V—Ëe‰•JeNâÆlÛæóyÒé´Þv®x<¬V«.Ft¹\8=m±X°X,ØívªÕªî^]*•ô9t±XÔ’†’* ÃÐ…œª¨ôh ƒØl6l6ÁÆ÷™vÒƒÅbÑ…–ê»L]Bý;LÉì0%¬ÀTwl‡Ã¡Å[³  …°Z­Mû"‚ 0 ˆêºX6›¥\.ëó¡J¥B&“ÑÇÏt:M©Tj’.*• †ah¹Â0 …‚N¾(•J:áœtQ(ÔEm•t¡¦kµš–0Ž6› «Õ  …N›Í¦¹J…ú±Öf³a·Ûõñ7àp8°Ùltuuée»ÝNOO‹…`0¨ÏúúúôÏPÏUÇkóù€ ‚ ‚ ‚ oDAAA8I©Õj$ …‚–ŠÅ"‰‰ ŠÉd½Kîø8¥r™øèh½snã&o±P ®Š·²Y²…ÅBD>O±T"S,’+•(ÌPgÆm±à±XöZ`­† ÖÆØi¤¢P—T¼ÔÅ¢&‹¿×‹Ãn'èñ`s¹»ÝX¼^"N'ÖPˆÃ1=õÄœv ÕZOAñxêÛ5аðùêòŠYZQ²Š’TL$“Iòù<ÙlvVyd¦m³Ù,…B¡iÛD"1kGA³<âr¹ºð+ âp8…BºpL­ …BØíö¦uª Ì¼Î\¶Ô10ˆ'O^O·³­ë$–¸qiÂu×]Ç?øÁS¢ ¹ LJ;vp÷ÝwsÏ=÷pèÐ!6nÜÈu×]ÇŸÿùŸ388È“<É7ù&ñ)vüy.\¸qS¤8-Ê‹—zè§Ÿ^zéi }AÍ÷ÒK?ýÏ·.'Ì›Û%mÌwÝ|’8Ú¥n´.›iUÄ¿¨bK5V)ª+v¥Raxx€‰‰ à N“Ï盤Œl6K©TÒ?+—ËQ*•ÈårT*òù|“(1J6P"†Õj¥V«iù¢Z­êyUày,¨ÏšÅ%YX­VB¡zÚ[;ÉÂ,a«da·Û 4¾7˜÷KAÔñêÇÂÑÑQ}m\ƒƒz“µ<›Í6¥U¨ct±XÔR…:f—ËeªÕª)Ôv•J…r¹L±X¤Z­R­Võt­VÓò£yZíãñ,ûP瀖&-A¨T @'oY,-S8\..—K‹êz—ÓéÔÇi¿ß¯Ó1TÊ…ÏçÓ‚F»t*ó9€ ‚ ‚ ‚ œ®ˆ"‚ ‚ ‚pš“H$tQ™ê"œL&)‹¤Ói]È—L&©V«ÄãqjÕ*‰±±z!Ûäd½@­!Jär9Œ|ž|¡@.Ÿ§P*‘Éç)ÍA>°[,ìvœ€ÏjÕR‰ÇbÁ]©àœ• j;õ$+,tTì@  „‰&a‡‹ê¶Ô…³@¢ UŠ9ùDmïtÖ…€p˜r¥‚d+Šå2)‹¥þ¹Øl$²Yr¥i“ð“ÍfÉd2”J%âñ¸.ô¡qÝu×ñÑ~TS ‚ @½ ì÷¿ÿ=¿øÅ/ø÷ÿwùžÝRËSX°Ì(ѵâÄÉf6ó>3%xTzyñ‘¹ëŽ»x衇¸ï¾û¸òÊ+ó;fܲ–Édôù aZxÍf³är9Ä¡ÒÛâñ¸žN¥RM ‡Ãx<|>¡PHÄæõz‰D"zZ¥u¨s•Öá÷û;ʰæNÙ###äóyÆÇÇÉçóº;v<§Z­jÑW=2™ •JE§b¨nتs¶ê”­Š9U±g¥RÑ…œ3%‘,$ªØÒb±èBJ‹Å‚ËåÂãñè¢LU˜éñx´ a.ªlWh¹PÓÇC´ANNJ¥±XL3™ “““zýðð0år=enrrRo§q@=J¥>©Ô*•0¡ŽÏµZMK€>.ú˜\«Õtz…:6+BUÉÄñ(PÒ%L‰˜j¹YdPÇK5­ Ç£“&œN§ÞÎápt²V Ðr†ÏçÃívk¡Âï÷c±Xðù|¬V«>§r:Ün·¾6 ¢¤ ‚ ‚ ‚ œZˆ"‚ ‚ ‚ œPfê}Tã\Žx,F¾PÀhé)%“ËQj̷͆ÇnÇmµâ±ZëóPŸ¯Õ´œB¹\ON)—q×jx*Üår}ÛÆ63i] °Ù°Ï7õÄj¥ìóÕ ¬VrÔ‹òv;ÅZR©DÖ鬋=†AÖj­Ë'Õj]T)IT«ÄS)ŒB‰r¹^ü™Ï“(1 ƒÉÉÉ9w‚n-\ˆ±¹s·ylîð|2‘!CŠI’¤C‚DÓ¼y}»uYÚÏ:Šà,AÁå94¾´c§Fñ1>1ú ~÷ßñãÿ˜îîn>ÿùÏóùÏžh4ºÀŸÀ©Ç–-[xøá‡{7„ãˆü;…B_Üó þ&ÿ7~ûa,ç[ÀœB¬X¹–kù?cïÞ½üèG?â?ø.ìøô§?Íüããý6–í’4ZÏ:¥nÌ´m*•šUXh=>G"\.—.D´Ûí¸\.|>GËn·§ÓI­VÃáp`·Ûµ4Q©T´DSRŠJ½P¢±ê˜].—ÉçóÔj5ŠÅ"Åb‘ZãœGxªbO`A$Œvœ‹E[ª” §Ó‰ÕjÕ¯Õ2Ud©Š0€4\.—$Ô9×ëÕÒ†ùsŸiZŠ5AN=TB”Â0 FGGÈçóMD¡PÐÂf­VÓ²C.—ÓÉÅbQKŒf)B%FA]Î0 CÕ1U͋Ŧïþ*¡Â¼Ïæä)uÜš¤ 5¿Ð¨c±9ÍJÕ´ÃáÐ…ËåÒÒ…J¤€f™¦Ò§Ô´ºÎ ÄS¨sÍÉT===úçF"‡n¶¡ŽÍJbAAAA8Qˆ"‚ ‚ ‚ ,YtbI£ø±X,’ÍfuÊF­‘Òè”t:ÝTˆ¨Š&K¥™L†jµª‹3âñ8€.¤Ìf³ …úëÍQ¤P¸ív<«¿ÃÍb!h·C­FÈáÀZ­â²Zñ6Š."6‹x­V\Õ*¶r™ Õ …þZ G­†Ã0ð7~¾JR1‹)ªG³—zŠŠJVÑøýàpÔ ŸN2i-”T‰J8Qcµ^=OuÕ´Z­„B¡úçÔ(Ö ƒº¨äxQ¡BŠwüô¾qÛÿK5PÄJ|×Aa ”çëÂTaù¥ËùŸ×ýO¾ð…/,Já¨*àQx<V­ZÅe—]ÆW¿úUÖ­[wÂ÷ àýï?>ú輟g±XŽK¡Ó±¢>ç½o ñy¯Ïôhÿg¢õwÙívÓÝÝͦM›¸úê«ùä'?ÙÔå×Ì›o¾É·¿ým¶nÝÊðð0‡sÎ9‡Ë/¿œk¯½–óÎ;¨ÿûýÛ¿ýßÿþ÷yã7Èår¬[·Ž«®ºŠO~ò“lܸqÁÞÏ|yòÉ'ùæw¿Éo¿aõgWãø˜ƒ7\o`Æ e¦‹˜Ç6â¾ÊͳÏ>Ûög^uÕUüíßþ-]tÑñÞýã‚JñJ&“”Ëe’ɤ>gPçj›D"¡»[«sÃ0Èf³¤R)½JåPI¹\nÖýP…n·[D*1AýÞÚl6Ý[ýŸSµ•d¡¶UÒ†šVE ª{÷‰@u»†z¦ê¢mµZq8Z:Q‰ê½+ù" âp8ðù|8"‘‹¥)™ÄãñÐÕÕ…×륧§»ÝNoo/~¿–½As‚S*•"ŸÏëã^¥R!c†Þ>“É4‰¥•J…±±1=_«Õ´`¨H$ú˜èc¶"•JéóÐjµªe 5_(ô|¥R¡\.7Í«´ µ½$Ôþ˜ÇÇóyª%6›MÏ+ÁQ}·5 Ž€>—UÇY5ïv»›„HuÜU©Sê˜ìv»õ6Á`Pÿt8ôööê}êééÑrF(Òß¿½^/ýýýÇå3AAAAXªˆ"‚ ‚ ‚ lj£PÌݰ3 AB‰'j³”¢ŠLÍÛ«Ÿ=×"Òv<ìV+› £$ävc­Õê‰)6 ìp@¥‚×nÇU«a­V 9P*a«V V«P.ã°XðW*P*á´Xð•ËP,â*•ð6:ªÔ˜’T|€*¯ö–}-Ûí”J6ÅF±kÁb¡`µÖ?‹…\£Ø5[©`ÙJ…\£«x®ZÕ²‰OÌJë4O½C¸ÇCÅ餿rañzëÅ0~?åF·ôœÃA¸««þÙ…BX­VÝE] *J\Q’ŠR~ñ‹_ðÿ÷ÿR2_¶ùÿ€/›>Œ6XkVj–ús\/¹¨þ²Êgû>ËmŸ¹mÑSUTñq­V#“ɰwï^~õ«_ñÝï~—»ï¾›+¯¼rQ÷o>œ¬B,ξÌBÈñÀ¼¯†a022ÂöíÛùÑ~Äðð0¿üå/Y»vmÓszè!þ×ÿú_üÍßü ïz×»p: sß}÷ñõ¯d2©æ7¾ñ vìØÁ·¾õ-Ö­[G>Ÿç…^à+_ù /¾øâIñ9ýîw¿ã¦›nâõ×_çæïßÌòO/çË#<ÌÃĉc¯Ù©P©ÿ=z˜Åa±ÙlM…™óE_Uh<'NpøðajµétZ‹ãããT«U²„±L&C¥R!“ÉP,u'ír#AKU7nsÅl´ŠD õo¨Ò*jµZSѧú[ GŸ’a·ÛuÇk‡ÃÅbѶÍÉžÆ1QÇ””´¨‰Dtáh»ä/˜ž4¦ÄMA„SõÌŒúîgfrrR³ ÞÔ •J5 …BA¯}œ*—ËZ–Pß +•ŠÞNmS­Võ1Íüú*1Â|¬PÇ<ó2s’„B Õj•r¹ÜtŒ3‹‡æe‹u.£Ò%ÌÉO*]B¥M¨c«ÃáЄ•0aµZuJ–ú¹ª¹Ô…Š€Jø¤.B(ÙAÍw5¾'ªíÕ±RÍwuua³ÙôwFõóÝn·žw:'¤i ‚ ‚ ‚ ‚pò#Bˆ ‚ ‚ ‚ œ´J%•JEÅv’JTBÊÑü %ÄMrй ì±ôx°5ŠyBn7ª6ârA­VVTO­F¨Q`c¯V 4žç(•ð[­P­â,ñZ,ÔJ%<å2nÀR(à­ÕÚŠ*~Àј6‹*Aêi+>«•bcrV+%…Ëd•b­F²Q$•TÙ×=ÀEóþ!ð¦m`-AÕ4¬ß Ù'½¼qo‘?8çøÉO~B4úÿ·wïÁQÕ÷ÿÇ_»ÙÍæ¾ I¸È-D H[En¡†D~•2þ¨R™V­ŠVFF¡ØVn_mmm:Q±£T‘v€A„~X.‚Ø”R¹ ÃM¹“ »l’ýþçt7Ɇ„€àù˜ÙœœóÙÏùì²'Æ÷ë¼;µlao@ÿëׯ×K/½¤ýû÷aVצ-‡„ÜxMÍuÉ’%úío«½{÷š…gΜÑĉµmÛ6¿â@Ö-[”––fŽÙ¡C8p@íÛ·÷Û/77W)))mfjjj´páB½òÊ+š4i’&L˜ ì³µ.ʇ”Kã$¥J:.)åêãõíÛ×¼¶ÔÔÔø³z<óŽÛ¾Å¦õ m[Ã(5ŠB.¾ß÷í®q­A ƒ¶0ŠH;u•$EGG›¡¾„„sߘ˜s߈ˆIRTT”Y jt¸2ºfH2ƒF€Ãè˜aQDD„ÒÒÒ”••¥øøx¿ý<¨Y³fé³Ï>“$5J¿ÿýïýÎ_’:¤_|Q;vìPHHˆÆŒ£¬¬,uïÞ½Á:7gMs=Öãïÿ»^{í5íÙ³G;vTFF†,XàŒ¸Ú×:ÿÆ^ã–¾ÆmêWªO>ù¤Ú·o¯… J’fΜ©””=öØcW[ºR”_TTô®>ÍõÉ'ŸèÁl´èW]iýT~õq|ß­ý•µQd*ÉìÂd±X£ÐÐPY­VóNܱ±±fáªñ~5:XH2ïÈí¨0B’ü:\cQ ŠÛ•ïÏdøþìåËè¶'ɼÖÜn·ßÏhn·ÛüYÎ7Xf|íBðýÌ2:'\.—Yü_?¸`t\0:4øòz½r»Ý ‚ ¾ç¨Ruuu£A3£+QcƒúÝ|çQn¾ãÝlÿ[з£ƒï¶ú£Ãƒïvß®F Áø<–þn0:Bß7®Á¾ŠŒ‡Ão>Fg$#Da\ƒ‡ù<áááfÂèºd\[ìv»ÙÉèšd·ÛÍnKÆ6›M111 ®#¾×&·'!ÍTQQaÞ ªwWT©©©1ï~(¨ræÌmÍΖÛ÷×6Ë$ýXÒIÿ+i£¤ÓW¾e•T§+u×3Ÿ{N¯deÝ€³o@Eô6lМ9s´ÿ~;vL£FÒܹsõÐCÉjµ*;;[sçÎÕ§Ÿ~ª.]ºø—––¦yóæiРAÚ¾}»~ô£Éëõjâĉ4hžzê)ÅÆÆ*77W¿øÅ/ô¯ý«A8À÷ëk=®9çžžž®çŸ^©©©*--ÕŒ3ät:õ—¿üÅÜïØ±c=z´æÏŸ¯ñãÇKº¨˜7ož¶oß®ž={J’Ž?®Ñ£GkÞ¼y?~¼¬V«>þøc½ýöÛÊÉÉñ›[KÖ´¾ë±‹E‹/Ö”)StñâEÍž=[‹Eï½÷^ƒý£5ó4ns_–Œéëßÿþ·~ö³Ÿ™]oúöí« 6¨GW[’~ò“Ÿ¨C‡zùå—ÍmÝŽ;tß}÷µªÈ833Ó SDDD˜Å¸N§Ó,æ5Š`ÃÂÂÌ¢ÙÆ:b u|¯YÍá{-óUQQÑh»Ëåºj§0—Ëe´û𣤤¤YÈŒ®†™êÍÅ8ÚÚÚFÇ®­­õ &¸\®F‹ûÝn·ÙM ±ðƒ1–1^ ®FÁ 1 F‡«íÓTÇ›ºº:3DèßøÍ2¸VFx PèÖè˜ÐØ÷m6[À.<¾ê m4$hµZÍN?õçèt:l7BõÇ SHHˆ_€A’X°Z­æg®ÍfóëÞàp8Ìî¾á ãø˜˜˜Ý*ÂÃÃÕ¡C‡çS¿3 y„´1/¿ü²^ûŸÿÑeß_Ûµ{ßÖ~†Jº,)&<\N˜ CGŽ(::ZÛ¶mkPx×Eô^¯WUUU:vì˜Ö­[§7ÞxC+V¬PZZš¦L™¢þýûkÖ¬Y~Ç.]ºTûöíS–OÐÅb±hÛ¶mºï¾ûݯ˜»þ×zÜÕÚ¿þv›Í¦ªªªÁ—Ë¥èèhy<ž&÷klÌ–¬i}­]ÂÂBÍ™3Gü± ü‚Í „´fþÆmîëÑ’1}}ñÅzâ‰'´oß>IRŸ>}´qãF%''_uì@ªªª4mÚ4EEEéý÷ß¿æqn´*55UG«W¯VIII³­­­m“Ý€¶ŒÿÃÐÕx½ê” _<ÿ¼vîÜ©ó%%ÊzóM5ªAÑtçÎ%]é$q3KHHPII‰¼^oƒÇÕ ÿ}9Neffêøñã:q℞yæ­X±B“'O¾!Ç]/ f_çÎSBB‚ß~ ö+..ntÌk]ÓÖ®ÇÔ©S©þóŸr¹\æó¶ÄõzO|—/^¬‰'š_7N›7onÕ˜‘‘‘úÓŸþ¤µk×¶vz7ŒÇãÑ©S§Ô¯_?-^¼XÅÅÅÚ½{·222šÕ]û-G  ™={¶öìÙ£³.è™™>|¸,KÀý5bĽõÖ[ßá,¯¿ûï¿_Û·oo°ý³Ï>ÓàÁƒ›=ŽÅbÑ™3g$] <üðÃÚ°aƒ6mÚt]Žkêµh±cÇjÍš5 ¶ôÑG;v¬ùuzzz£Á€­[·6ØÖš5mízìܹSóçÏWRR’l6›$ qãz½'¾+þóŸµuëVÍœ9ÓÜ6sæL-Z´Heee³fÍ3ÆüÚb±(??¿Á~6›MQQQ×}Î×ËòåËUYY©xÀÜvÏ=÷hÍš5ª¬¬Ô¦M›4fÌ¿î2¾„-G  ILLÔàÁƒ[ÅfùꫯôüóÏë©§ž øï!--M[·nÕåË—µjÕ* 6L!!!ßñL€[ €[À˜1c´`Á=÷Üsm®Sˆl¹ZÀ%))I~ø¡V¬X¡;ï¼S:uÒ‚ ôæ›oj„ Ž×ؘ[¶lQxx¸~øÃ*&&FÇ—ËåÒûï¿ß䜚sœ$effêÑGUTT”žyæ™&C8ν±í½zõRvv¶>üðC%''«GZ½zµ²³³Õ³gOs¿äädeggkõêÕêÑ£‡ºwï®wß}WûÛߌÙÜ5mLk×cÙ²er¹\:t¨¢££•‘‘av¨¿ÆhÍü[ã–¼WÓb±(66V#GŽÔòåËõè£jÏž=êÕ«WƒãƯeË–iÕªUúÞ÷¾'§Ó©´´4=ðÀzúé§5tèP­[·N4ŠÐ‡ÖÔ©Su÷Ýw«¬¬L»víRVV–ìv{°§Ü–„Ü‚µdÉíÙ³G]ºtQFF†  %K–¨ªª*ØÓp“ðz½Úºu«222Ô¯_?ýç?ÿÑ| œœ 2$ØÓnk/·†¸åíß¿_™™™Z¹rßB{bIDAT¥ÂÂÂ4yòd=òÈ#JMM•ÕÊ=Cø;~ü¸V®\©åË—ëðáÃ5j”f̘¡ŒŒ >3€6‚@Àm¤¨¨HK—.Õ_ÿúWíß¿_;wÖ¤I“ôÈ#èÞ{ï•Åb öÉéÓ§µjÕ*­\¹R»wïVbb¢&Mš¤Ÿÿüç0`@°§ !·©ÜÜ\­\¹R+W®Ô‘#GÔ£GMœ8QéééúÁ~ ˆˆˆ`OÀ TWW§½{÷jÓ¦MZ¿~½vîÜ©ØØXeddèá‡Ö˜1cd³Ù‚=M€¾üòK­\¹R7nÔäp84räH¥§§+--M”Õj ö4´Ò™3g´yófmÚ´I[·nUaa¡:vì¨ôôtMš4Iééé ö44ø9wîœY0¾eË?^‰‰‰=z´FŒ¡aÆiРA7£Gj×®]Úµk—¶oß®ÜÜ\…‡‡+55Õ |õïß_‹%ØSÐBB×ëÕ¾}û´yófmß¾]»víRII‰ÂÂÂ4xð` 6LÇ×ðáÃÕ¹sç`O¸­UTTh÷îÝÊÉÉ1C EEEr84hRSS•––¦Q£F)<<<ØÓÐJBÐl^¯Wyyyf±yNNŽ<¨ÚÚZuéÒEƒVÿþýÕ¿ 0@={ö”Õj ö´[Îùóçµÿ~íÝ»Wû÷ï×¾}û”››«ÚÚZuíÚUÇ×°aÃ4lØ0 ƒ} À5©®®V~~¾òóóuòäI}ú¨W¯^æß{÷î-»Ýä³Ð–@й\.åååéèÑ£füÑ£G•ŸŸ¯sçΩ®®N’©¤¤$%%%™!‘¤¤$uíÚU]»vU‡(šGP\¸pA:uêTƒàG~~¾ Í}ãââ”””¤äädõîÝÛ/ijp3!€6ÍãñèôéÓúæ›otîÜ98qÂïqêÔ)ÕÔÔ˜ûÇÅÅ©S§NºãŽ;þÙµkW‚#h–K—.éܹsæû¯±?Ož<©ªª*óß÷`rr²ßãÎ;ïTlllÏÀ­‚@njGgÏžÕÙ³gUPPðÏââb¿ãÚµk§ÄÄD%$$(>>^ jß¾}ÀmQQQA:C\/n·[ÅÅÅ***Raa¡.\¸ ¢¢"©¸¸¸Á¶¢¢"¿°QDDD“A£N:©[·nŠŒŒ âY¸]ÀmÁår™‘ .¨  À Åÿ¾a€K—.ù*§Ói>âââë·Íétúm‹‰‰‘ÓéTXX˜"##-›Í¤¸¹•••Éív«ªªJª¬¬TYY™ÊËËÍGYY™ù¨¿½¼¼Ü¯‹‡$…„„(!!ÁïѾ}{¿¯;vìh>œNgÎ"4¢ªªÊ/$RRRâ2(--m<0ÂÕÕÕÇ QLLŒ"##åp8«°°0…‡‡û…GŒý$)<<\aaa’¤ØØXY,Ùív³kIdd¤BCC%]ébáp8>¿ï¾ÍUZZð{^¯Weee þîv»Íu¨¨¨PMMêêêT^^.éJ@çÒ¥Kº|ù²òp»Ýºxñ¢ªªªäv»UVVfîˆÝn÷ ãÄÅÅ éÄÅÅù…=âãã[´Ж®3Ç£òòr]¼xQååår»Ýª¬¬lzp¹\*//×¥K—är¹TZZj)Œ „$UVVÊãñ \´E+qqq’®t\‰ŒŒ”ÍfStt´¢¢¢äp8ät:ÍPK\\œ–‰•ÃáPdd¤bbbeî ·#!ÀMÎèÀ!IeeejêW~Muû$**Jv»=à÷£££e³Ù$ý·ƒ àÆ"p“±{h!7›¤ÁžšïÿˆASþ4IEND®B`‚patroni-1.4.2/docs/index.rst000066400000000000000000000031011323411135200157510ustar00rootroot00000000000000.. Patroni documentation master file, created by sphinx-quickstart on Mon Dec 19 16:54:09 2016. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Introduction ============ Patroni is a template for you to create your own customized, high-availability solution using Python and - for maximum accessibility - a distributed configuration store like `ZooKeeper `__, `etcd `__, `Consul `__ or `Kubernetes `__. Database engineers, DBAs, DevOps engineers, and SREs who are looking to quickly deploy HA PostgreSQL in the datacenter-or anywhere else-will hopefully find it useful. We call Patroni a "template" because it is far from being a one-size-fits-all or plug-and-play replication system. It will have its own caveats. Use wisely. There are many ways to run high availability with PostgreSQL; for a list, see the `PostgreSQL Documentation `__. **Note to Kubernetes users**: Patroni can run natively on top of Kubernetes. Take a look at the :ref:`Kubernetes ` chapter of the Patroni documentation. .. toctree:: :maxdepth: 2 :caption: Contents: README dynamic_configuration ENVIRONMENT SETTINGS replica_bootstrap replication_modes pause kubernetes watchdog releases CONTRIBUTING Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` patroni-1.4.2/docs/kubernetes.rst000066400000000000000000000055731323411135200170300ustar00rootroot00000000000000.. _kubernetes: Using Patroni with Kubernetes ============================= Patroni can use Kubernetes objects in order to store the state of the cluster and manage the leader key. That makes it capable of operating Postgres in Kubernetes environment without any consistency store, namely, one doesn't need to run an extra Etcd deployment. There are two different type of Kubernetes objects Patroni can use to store the leader and the configuration keys, they are configured with the `kubernetes.use_endpoints` or `PATRONI_KUBERNETES_USE_ENDPOINTS` environment variable. Use Endpoints ------------- Despite the fact that this is the recommended mode, it is turned off by default for compatibility reasons. When it is on, Patroni stores the cluster configuration and the leader key in the `metadata: annotations` fields of the respective `Endpoints` it creates. Changing the leader is safer than when using `ConfigMaps`, since both the annotations, containing the leader information, and the actual addresses pointing to the running leader pod are updated simultaneously in one go. Use ConfigMaps -------------- In this mode, Patroni will create ConfigMaps instead of Endpoints and store keys inside meta-data of those ConfigMaps. Changing the leader takes at least two updates, one to the leader ConfigMap and another to the respective Endpoint. There are two ways to direct the traffic to the Postgres master: - use the `callback script `_ provided by Patroni - configure the Kubernetes Postgres service to use the label selector with the `role_label` (configured in patroni configuration). Note that in some cases, for instance, when running on OpenShift, there is no alternative to using ConfigMaps. Configuration ------------- Patroni Kubernetes :ref:`settings ` and :ref:`environment variables ` are described in the general chapters of the documentation. Examples -------- - The `kubernetes `__ folder of the Patroni repository contains examples of the Docker image, the Kubernetes manifest and the callback script in order to test Patroni Kubernetes setup. Note that in the current state it will not be able to use PersistentVolumes because of permission issues. - You can find the full-featured Docker image that can use Persistent Volumes in the `Spilo Project `_. - There is also a `Helm chart `_ to deploy the Spilo image configured with Patroni running using Kubernetes. - In order to run your database clusters at scale using Patroni and Spilo, take a look at the `postgres-operator `_ project. It implements the operator pattern to manage Spilo clusters. patroni-1.4.2/docs/pause.rst000066400000000000000000000050601323411135200157650ustar00rootroot00000000000000.. _pause: Pause/Resume mode for the cluster ================================= The goal -------- Under certain circumstances Patroni needs to temporary step down from managing the cluster, while still retaining the cluster state in DCS. Possible use cases are uncommon activities on the cluster, such as major version upgrades or corruption recovery. During those activities nodes are often started and stopped for the reason unknown to Patroni, some nodes can be even temporary promoted, violating the assumption of running only one master. Therefore, Patroni needs to be able to "detach" from the running cluster, implementing an equivalent of the maintenance mode in Pacemaker. The implementation ------------------ When Patroni runs in a paused mode, it does not change the state of PostgreSQL, except for the following cases: - For each node, the member key in DCS is updated with the current information about the cluster. This causes Patroni to run read-only queries on a member node if the member is running. - For the Postgres master with the leader lock Patroni updates the lock. If the node with the leader lock stops being the master (i.e. is demoted manually), Patroni will release the lock instead of promoting the node back. - Manual unscheduled restart, reinitialize and manual failover are allowed. Manual failover is only allowed if the node to failover to is specified. In the paused mode, manual failover does not require a running master node. - If 'parallel' masters are detected by Patroni, it emits a warning, but does not demote the masters without the leader lock. - If there is no leader lock in the cluster, the running master acquires the lock. If there is more than one master node, then the first master to acquire the lock wins. If there are no masters altogether, Patroni does not try to promote any replicas. There is an exception in this rule: if there is no leader lock because the old master has demoted itself due to the manual promotion, then only the candidate node mentioned in the promotion request may take the leader lock. When the new leader lock is granted (i.e. after promoting a replica manually), Patroni makes sure the replicas that were streaming from the previous leader will switch to the new one. - When Postgres is stopped, Patroni does not try to start it. When Patroni is stopped, it does not to stop Postgres instance it is managing. User guide ---------- ``patronictl`` supports ``pause`` and ``resume`` commands. One can also issue a ``PATCH`` request to the ``{namespace}/{cluster}/config`` key with ``{"pause": true/false/null}`` patroni-1.4.2/docs/releases.rst000066400000000000000000001271621323411135200164630ustar00rootroot00000000000000.. _releases: Release notes ============= Version 1.4.2 ------------- **Improvements in patronictl** - Rename scheduled failover to scheduled switchover (Alexander Kukushkin) Failover and switchover functions were separated in version 1.4, but `patronictl list` was still reporting `Scheduled failover` instead of `Scheduled switchover`. - Show information about pending restarts (Alexander) In order to apply some configuration changes sometimes it is necessary to restart postgres. Patroni was already giving a hint about that in the REST API and when writing node status into DCS, but there were no easy way to display it. - Make show-config to work with cluster_name from config file (Alexander) It works similar to the `patronictl edit-config` **Stability improvements** - Avoid calling pg_controldata during bootstrap (Alexander) During initdb or custom bootstrap there is a time window when pgdata is not empty but pg_controldata has not been written yet. In such case pg_controldata call was failing with error messages. - Handle exceptions raised from psutil (Alexander) cmdline is read and parsed every time when `cmdline()` method is called. It could happen that the process being examined has already disappeared, in that case `NoSuchProcess` is raised. **Kubernetes support improvements** - Don't swallow errors from k8s API (Alexander) A call to Kubernetes API could fail for a different number of reasons. In some cases such call should be retried, in some other cases we should log the error message and the exception stack trace. The change here will help debug Kubernetes permission issues. - Update Kubernetes example Dockerfile to install Patroni from the master branch (Maciej Szulik) Before that it was using `feature/k8s`, which became outdated. - Add proper RBAC to run patroni on k8s (Maciej) Add the Service account that is assigned to the pods of the cluster, the role that holds only the necessary permissions, and the rolebinding that connects the Service account and the Role. Version 1.4.1 ------------- **Fixes in patronictl** - Don't show current leader in suggested list of members to failover to. (Alexander Kukushkin) patronictl failover could still work when there is leader in the cluster and it should be excluded from the list of member where it is possible to failover to. - Make patronictl switchover compatible with the old Patroni api (Alexander) In case if POST /switchover REST API call has failed with status code 501 it will do it once again, but to /failover endpoint. Version 1.4 ----------- This version adds support for using Kubernetes as a DCS, allowing to run Patroni as a cloud-native agent in Kubernetes without any additional deployments of Etcd, Zookeeper or Consul. **Upgrade notice** Installing Patroni via pip will no longer bring in dependencies for (such as libraries for Etcd, Zookeper, Consul or Kubernetes, or support for AWS). In order to enable them one need to list them in pip install command explicitely, for instance `pip install patroni[etcd,kubernetes]`. **Kubernetes support** Implement Kubernetes-based DCS. The endpoints meta-data is used in order to store the configuration and the leader key. The meta-data field inside the pods definition is used to store the member-related data. In addition to using Endpoints, Patroni supports ConfigMaps. You can find more information about this feature in the :ref:`Kubernetes chapter of the documentation ` **Stability improvements** - Factor out postmaster process into a separate object (Ants Aasma) This object identifies a running postmaster process via pid and start time and simplifies detection (and resolution) of situations when the postmaster was restarted behind our back or when postgres directory disappeared from the file system. - Minimize the amount of SELECT's issued by Patroni on every loop of HA cylce (Alexander Kukushkin) On every iteration of HA loop Patroni needs to know recovery status and absolute wal position. From now on Patroni will run only single SELECT to get this information instead of two on the replica and three on the master. - Remove leader key on shutdown only when we have the lock (Ants) Unconditional removal was generating unnecessary and missleading exceptions. **Improvements in patronictl** - Add version command to patronictl (Ants) It will show the version of installed Patroni and versions of running Patroni instances (if the cluster name is specified). - Make optional specifying cluster_name argument for some of patronictl commands (Alexander, Ants) It will work if patronictl is using usual Patroni configuration file with the ``scope`` defined. - Show information about scheduled switchover and maintenance mode (Alexander) Before that it was possible to get this information only from Patroni logs or directly from DCS. - Improve ``patronictl reinit`` (Alexander) Sometimes ``patronictl reinit`` refused to proceed when Patroni was busy with other actions, namely trying to start postgres. `patronictl` didn't provide any commands to cancel such long running actions and the only (dangerous) workarond was removing a data directory manually. The new implementation of `reinit` forcefully cancells other long-running actions before proceeding with reinit. - Implement ``--wait`` flag in ``patronictl pause`` and ``patronictl resume`` (Alexander) It will make ``patronictl`` wait until the requested action is acknowledged by all nodes in the cluster. Such behaviour is achieved by exposing the ``pause`` flag for every node in DCS and via the REST API. - Rename ``patronictl failover`` into ``patronictl switchover`` (Alexander) The previous ``failover`` was actually only capable of doing a switchover; it refused to proceed in a cluster without the leader. - Alter the behavior of ``patronictl failover`` (Alexander) It will work even if there is no leader, but in that case you will have to explicitely specify a node which should become the new leader. **Expose information about timeline and history** - Expose current timeline in DCS and via API (Alexander) Store information about the current timeline for each member of the cluster. This information is accessible via the API and is stored in the DCS - Store promotion history in the /history key in DCS (Alexander) In addition, store the timeline history enriched with the timestamp of the corresponding promotion in the /history key in DCS and update it with each promote. **Add endpoints for getting synchronous and asynchronous replicas** - Add new /sync and /async endpoints (Alexander, Oleksii Kliukin) Those endpoints (also accessible as /synchronous and /asynchronous) return 200 only for synchronous and asynchornous replicas correspondingly (exclusing those marked as `noloadbalance`). **Allow multiple hosts for Etcd** - Add a new `hosts` parameter to Etcd configuration (Alexander) This parameter should contain the initial list of hosts that will be used to discover and populate the list of the running etcd cluster members. If for some reason during work this list of discovered hosts is exhausted (no available hosts from that list), Patroni will return to the initial list from the `hosts` parameter. Version 1.3.6 ------------- **Stability improvements** - Verify process start time when checking if postgres is running. (Ants Aasma) After a crash that doesn't clean up postmaster.pid there could be a new process with the same pid, resulting in a false positive for is_running(), which will lead to all kinds of bad behavior. - Shutdown postgresql before bootstrap when we lost data directory (ainlolcat) When data directory on the master is forcefully removed, postgres process can still stay alive for some time and prevent the replica created in place of that former master from starting or replicating. The fix makes Patroni cache the postmaster pid and its start time and let it terminate the old postmaster in case it is still running after the corresponding data directory has been removed. - Perform crash recovery in a single user mode if postgres master dies (Alexander Kukushkin) It is unsafe to start immediately as a standby and not possible to run ``pg_rewind`` if postgres hasn't been shut down cleanly. The single user crash recovery only kicks in if ``pg_rewind`` is enabled or there is no master at the moment. **Consul improvements** - Make it possible to provide datacenter configuration for Consul (Vilius Okockis, Alexander) Before that Patroni was always communicating with datacenter of the host it runs on. - Always send a token in X-Consul-Token http header (Alexander) If ``consul.token`` is defined in Patroni configuration, we will always send it in the 'X-Consul-Token' http header. python-consul module tries to be "consistent" with Consul REST API, which doesn't accept token as a query parameter for `session API `__, but it still works with 'X-Consul-Token' header. - Adjust session TTL if supplied value is smaller than the minimum possible (Stas Fomin, Alexander) It could happen that the TTL provided in the Patroni configuration is smaller than the minimum one supported by Consul. In that case, Consul agent fails to create a new session. Without a session Patroni cannot create member and leader keys in the Consul KV store, resulting in an unhealthy cluster. **Other improvements** - Define custom log format via environment variable ``PATRONI_LOGFORMAT`` (Stas) Allow disabling timestamps and other similar fields in Patroni logs if they are already added by the system logger (usually when Patroni runs as a service). Version 1.3.5 ------------- **Bugfix** - Set role to 'uninitialized' if data directory was removed (Alexander Kukushkin) If the node was running as a master it was preventing from failover. **Stability improvement** - Try to run postmaster in a single-user mode if we tried and failed to start postgres (Alexander) Usually such problem happens when node running as a master was terminated and timelines were diverged. If ``recovery.conf`` has ``restore_command`` defined, there are really high chances that postgres will abort startup and leave controldata unchanged. It makes impossible to use ``pg_rewind``, which requires a clean shutdown. **Consul improvements** - Make it possible to specify health checks when creating session (Alexander) If not specified, Consul will use "serfHealth". From one side it allows fast detection of isolated master, but from another side it makes it impossible for Patroni to tolerate short network lags. **Bugfix** - Fix watchdog on Python 3 (Ants Aasma) A misunderstanding of the ioctl() call interface. If mutable=False then fcntl.ioctl() actually returns the arg buffer back. This accidentally worked on Python2 because int and str comparison did not return an error. Error reporting is actually done by raising IOError on Python2 and OSError on Python3. Version 1.3.4 ------------- **Different Consul improvements** - Pass the consul token as a header (Andrew Colin Kissa) Headers are now the prefered way to pass the token to the consul `API `__. - Advanced configuration for Consul (Alexander Kukushkin) possibility to specify ``scheme``, ``token``, client and ca certificates :ref:`details `. - compatibility with python-consul-0.7.1 and above (Alexander) new python-consul module has changed signature of some methods - "Could not take out TTL lock" message was never logged (Alexander) Not a critical bug, but lack of proper logging complicates investigation in case of problems. **Quote synchronous_standby_names using quote_ident** - When writing ``synchronous_standby_names`` into the ``postgresql.conf`` its value must be quoted (Alexander) If it is not quoted properly, PostgreSQL will effectively disable synchronous replication and continue to work. **Different bugfixes around pause state, mostly related to watchdog** (Alexander) - Do not send keepalives if watchdog is not active - Avoid activating watchdog in a pause mode - Set correct postgres state in pause mode - Do not try to run queries from API if postgres is stopped Version 1.3.3 ------------- **Bugfixes** - synchronous replication was disabled shortly after promotion even when synchronous_mode_strict was turned on (Alexander Kukushkin) - create empty ``pg_ident.conf`` file if it is missing after restoring from the backup (Alexander) - open access in ``pg_hba.conf`` to all databases, not only postgres (Franco Bellagamba) Version 1.3.2 ------------- **Bugfix** - patronictl edit-config didn't work with ZooKeeper (Alexander Kukushkin) Version 1.3.1 ------------- **Bugfix** - failover via API was broken due to change in ``_MemberStatus`` (Alexander Kukushkin) Version 1.3 ----------- Version 1.3 adds custom bootstrap possibility, significantly improves support for pg_rewind, enhances the synchronous mode support, adds configuration editing to patronictl and implements watchdog support on Linux. In addition, this is the first version to work correctly with PostgreSQL 10. **Upgrade notice** There are no known compatibility issues with the new version of Patroni. Configuration from version 1.2 should work without any changes. It is possible to upgrade by installing new packages and either restarting Patroni (will cause PostgreSQL restart), or by putting Patroni into a :ref:`pause mode ` first and then restarting Patroni on all nodes in the cluster (Patroni in a pause mode will not attempt to stop/start PostgreSQL), resuming from the pause mode at the end. **Custom bootstrap** - Make the process of bootstrapping the cluster configurable (Alexander Kukushkin) Allow custom bootstrap scripts instead of ``initdb`` when initializing the very first node in the cluster. The bootstrap command receives the name of the cluster and the path to the data directory. The resulting cluster can be configured to perform recovery, making it possible to bootstrap from a backup and do point in time recovery. Refer to the :ref:`documentaton page ` for more detailed description of this feature. **Smarter pg_rewind support** - Decide on whether to run pg_rewind by looking at the timeline differences from the current master (Alexander) Previously, Patroni had a fixed set of conditions to trigger pg_rewind, namely when starting a former master, when doing a switchover to the designated node for every other node in the cluster or when there is a replica with the nofailover tag. All those cases have in common a chance that some replica may be ahead of the new master. In some cases, pg_rewind did nothing, in some other ones it was not running when necessary. Instead of relying on this limited list of rules make Patroni compare the master and the replica WAL positions (using the streaming replication protocol) in order to reliably decide if rewind is necessary for the replica. **Synchronous replication mode strict** - Enhance synchronous replication support by adding the strict mode (James Sewell, Alexander) Normally, when ``synchronous_mode`` is enabled and there are no replicas attached to the master, Patroni will disable synchronous replication in order to keep the master available for writes. The ``synchronous_mode_strict`` option changes that, when it is set Patroni will not disable the synchronous replication in a lack of replicas, effectively blocking all clients writing data to the master. In addition to the synchronous mode guarantee of preventing any data loss due to automatic failover, the strict mode ensures that each write is either durably stored on two nodes or not happening altogether if there is only one node in the cluster. **Configuration editing with patronictl** - Add configuration editing to patronictl (Ants Aasma, Alexander) Add the ability to patronictl of editing dynamic cluster configuration stored in DCS. Support either specifying the parameter/values from the command-line, invoking the $EDITOR, or applying configuration from the yaml file. **Linux watchdog support** - Implement watchdog support for Linux (Ants) Support Linux software watchdog in order to reboot the node where Patroni is not running or not responding (e.g because of the high load) The Linux software watchdog reboots the non-responsive node. It is possible to configure the watchdog device to use (`/dev/watchdog` by default) and the mode (on, automatic, off) from the watchdog section of the Patroni configuration. You can get more information from the :ref:`watchdog documentation `. **Add support for PostgreSQL 10** - Patroni is compatible with all beta versions of PostgreSQL 10 released so far and we expect it to be compatible with the PostgreSQL 10 when it will be released. **PostgreSQL-related minor improvements** - Define pg_hba.conf via the Patroni configuration file or the dynamic configuration in DCS (Alexander) Allow to define the contents of ``pg_hba.conf`` in the ``pg_hba`` sub-section of the ``postgresql`` section of the configuration. This simplifies managing ``pg_hba.conf`` on multiple nodes, as one needs to define it only ones in DCS instead of logging to every node, changing it manually and reload the configuration. When defined, the contents of this section will replace the current ``pg_hba.conf`` completely. Patroni ignores it if ``hba_file`` PostgreSQL parameter is set. - Support connecting via a UNIX socket to the local PostgreSQL cluster (Alexander) Add the ``use_unix_socket`` option to the ``postgresql`` section of Patroni configuration. When set to true and the PostgreSQL ``unix_socket_directories`` option is not empty, enables Patroni to use the first value from it to connect to the local PostgreSQL cluster. If ``unix_socket_directories`` is not defined, Patroni will assume its default value and omit the ``host`` parameter in the PostgreSQL connection string altogether. - Support change of superuser and replication credentials on reload (Alexander) - Support storing of configuration files outside of PostgreSQL data directory (@jouir) Add the new configuration ``postgresql`` configuration directive ``config_dir``. It defaults to the data directory and must be writable by Patroni. **Bug fixes and stability improvements** - Handle EtcdEventIndexCleared and EtcdWatcherCleared exceptions (Alexander) Faster recovery when the watch operation is ended by Etcd by avoiding useless retries. - Remove error spinning on Etcd failure and reduce log spam (Ants) Avoid immediate retrying and emitting stack traces in the log on the second and subsequent Etcd connection failures. - Export locale variables when forking PostgreSQL processes (Oleksii Kliukin) Avoid the `postmaster became multithreaded during startup` fatal error on non-English locales for PostgreSQL built with NLS. - Extra checks when dropping the replication slot (Alexander) In some cases Patroni is prevented from dropping the replication slot by the WAL sender. - Truncate the replication slot name to 63 (NAMEDATALEN - 1) characters to comply with PostgreSQL naming rules (Nick Scott) - Fix a race condition resulting in extra connections being opened to the PostgreSQL cluster from Patroni (Alexander) - Release the leader key when the node restarts with an empty data directory (Alex Kerney) - Set asynchronous executor busy when running bootstrap without a leader (Alexander) Failure to do so could have resulted in errors stating the node belonged to a different cluster, as Patroni proceeded with the normal business while being bootstrapped by a bootstrap method that doesn't require a leader to be present in the cluster. - Improve WAL-E replica creation method (Joar Wandborg, Alexander). - Use csv.DictReader when parsing WAL-E base backup, accepting ISO dates with space-delimited date and time. - Support fetching current WAL position from the replica to estimate the amount of WAL to restore. Previously, the code used to call system information functions that were available only on the master node. Version 1.2 ----------- This version introduces significant improvements over the handling of synchronous replication, makes the startup process and failover more reliable, adds PostgreSQL 9.6 support and fixes plenty of bugs. In addition, the documentation, including these release notes, has been moved to https://patroni.readthedocs.io. **Synchronous replication** - Add synchronous replication support. (Ants Aasma) Adds a new configuration variable ``synchronous_mode``. When enabled, Patroni will manage ``synchronous_standby_names`` to enable synchronous replication whenever there are healthy standbys available. When synchronous mode is enabled, Patroni will automatically fail over only to a standby that was synchronously replicating at the time of the master failure. This effectively means that no user visible transaction gets lost in such a case. See the :ref:`feature documentation ` for the detailed description and implementation details. **Reliability improvements** - Do not try to update the leader position stored in the ``leader optime`` key when PostgreSQL is not 100% healthy. Demote immediately when the update of the leader key failed. (Alexander Kukushkin) - Exclude unhealthy nodes from the list of targets to clone the new replica from. (Alexander) - Implement retry and timeout strategy for Consul similar to how it is done for Etcd. (Alexander) - Make ``--dcs`` and ``--config-file`` apply to all options in ``patronictl``. (Alexander) - Write all postgres parameters into postgresql.conf. (Alexander) It allows starting PostgreSQL configured by Patroni with just ``pg_ctl``. - Avoid exceptions when there are no users in the config. (Kirill Pushkin) - Allow pausing an unhealthy cluster. Before this fix, ``patronictl`` would bail out if the node it tries to execute pause on is unhealthy. (Alexander) - Improve the leader watch functionality. (Alexander) Previously the replicas were always watching the leader key (sleeping until the timeout or the leader key changes). With this change, they only watch when the replica's PostgreSQL is in the ``running`` state and not when it is stopped/starting or restarting PostgreSQL. - Avoid running into race conditions when handling SIGCHILD as a PID 1. (Alexander) Previously a race condition could occur when running inside the Docker containers, since the same process inside Patroni both spawned new processes and handled SIGCHILD from them. This change uses fork/execs for Patroni and leaves the original PID 1 process responsible for handling signals from children. - Fix WAL-E restore. (Oleksii Kliukin) Previously WAL-E restore used the ``no_master`` flag to avoid consulting with the master altogether, making Patroni always choose restoring from WAL over the ``pg_basebackup``. This change reverts it to the original meaning of ``no_master``, namely Patroni WAL-E restore may be selected as a replication method if the master is not running. The latter is checked by examining the connection string passed to the method. In addition, it makes the retry mechanism more robust and handles other minutia. - Implement asynchronous DNS resolver cache. (Alexander) Avoid failing when DNS is temporary unavailable (for instance, due to an excessive traffic received by the node). - Implement starting state and master start timeout. (Ants, Alexander) Previously ``pg_ctl`` waited for a timeout and then happily trodded on considering PostgreSQL to be running. This caused PostgreSQL to show up in listings as running when it was actually not and caused a race condition that resulted in either a failover, or a crash recovery, or a crash recovery interrupted by failover and a missed rewind. This change adds a ``master_start_timeout`` parameter and introduces a new state for the main HA loop: ``starting``. When ``master_start_timeout`` is 0 we will failover immediately when the master crashes as soon as there is a failover candidate. Otherwise, Patroni will wait after attempting to start PostgreSQL on the master for the duration of the timeout; when it expires, it will failover if possible. Manual failover requests will be honored during the crash of the master even before the timeout expiration. Introduce the ``timeout`` parameter to the ``restart`` API endpoint and ``patronictl``. When it is set and restart takes longer than the timeout, PostgreSQL is considered unhealthy and the other nodes becomes eligible to take the leader lock. - Fix ``pg_rewind`` behavior in a pause mode. (Ants) Avoid unnecessary restart in a pause mode when Patroni thinks it needs to rewind but rewind is not possible (i.e. ``pg_rewind`` is not present). Fallback to default ``libpq`` values for the ``superuser`` (default OS user) if ``superuser`` authentication is missing from the ``pg_rewind`` related Patroni configuration section. - Serialize callback execution. Kill the previous callback of the same type when the new one is about to run. Fix the issue of spawning zombie processes when running callbacks. (Alexander) - Avoid promoting a former master when the leader key is set in DCS but update to this leader key fails. (Alexander) This avoids the issue of a current master continuing to keep its role when it is partitioned together with the minority of nodes in Etcd and other DCSs that allow "inconsistent reads". **Miscellaneous** - Add ``post_init`` configuration option on bootstrap. (Alejandro Martínez) Patroni will call the script argument of this option right after running ``initdb`` and starting up PostgreSQL for a new cluster. The script receives a connection URL with ``superuser`` and sets ``PGPASSFILE`` to point to the ``.pgpass`` file containing the password. If the script fails, Patroni initialization fails as well. It is useful for adding new users or creating extensions in the new cluster. - Implement PostgreSQL 9.6 support. (Alexander) Use ``wal_level = replica`` as a synonym for ``hot_standby``, avoiding pending_restart flag when it changes from one to another. (Alexander) **Documentation improvements** - Add a Patroni main `loop workflow diagram `__. (Alejandro, Alexander) - Improve README, adding the Helm chart and links to release notes. (Lauri Apple) - Move Patroni documentation to ``Read the Docs``. The up-to-date documentation is available at https://patroni.readthedocs.io. (Oleksii) Makes the documentation easily viewable from different devices (including smartphones) and searchable. - Move the package to the semantic versioning. (Oleksii) Patroni will follow the major.minor.patch version schema to avoid releasing the new minor version on small but critical bugfixes. We will only publish the release notes for the minor version, which will include all patches. Version 1.1 ----------- This release improves management of Patroni cluster by bring in pause mode, improves maintenance with scheduled and conditional restarts, makes Patroni interaction with Etcd or Zookeeper more resilient and greatly enhances patronictl. **Upgrade notice** When upgrading from releases below 1.0 read about changing of credentials and configuration format at 1.0 release notes. **Pause mode** - Introduce pause mode to temporary detach Patroni from managing PostgreSQL instance (Murat Kabilov, Alexander Kukushkin, Oleksii Kliukin). Previously, one had to send SIGKILL signal to Patroni to stop it without terminating PostgreSQL. The new pause mode detaches Patroni from PostgreSQL cluster-wide without terminating Patroni. It is similar to the maintenance mode in Pacemaker. Patroni is still responsible for updating member and leader keys in DCS, but it will not start, stop or restart PostgreSQL server in the process. There are a few exceptions, for instance, manual failovers, reinitializes and restarts are still allowed. You can read :ref:`a detailed description of this feature `. In addition, patronictl supports new ``pause`` and ``resume`` commands to toggle the pause mode. **Scheduled and conditional restarts** - Add conditions to the restart API command (Oleksii) This change enhances Patroni restarts by adding a couple of conditions that can be verified in order to do the restart. Among the conditions are restarting when PostgreSQL role is either a master or a replica, checking the PostgreSQL version number or restarting only when restart is necessary in order to apply configuration changes. - Add scheduled restarts (Oleksii) It is now possible to schedule a restart in the future. Only one scheduled restart per node is supported. It is possible to clear the scheduled restart if it is not needed anymore. A combination of scheduled and conditional restarts is supported, making it possible, for instance, to scheduled minor PostgreSQL upgrades in the night, restarting only the instances that are running the outdated minor version without adding postgres-specific logic to administration scripts. - Add support for conditional and scheduled restarts to patronictl (Murat). patronictl restart supports several new options. There is also patronictl flush command to clean the scheduled actions. **Robust DCS interaction** - Set Kazoo timeouts depending on the loop_wait (Alexander) Originally, ping_timeout and connect_timeout values were calculated from the negotiated session timeout. Patroni loop_wait was not taken into account. As a result, a single retry could take more time than the session timeout, forcing Patroni to release the lock and demote. This change set ping and connect timeout to half of the value of loop_wait, speeding up detection of connection issues and leaving enough time to retry the connection attempt before loosing the lock. - Update Etcd topology only after original request succeed (Alexander) Postpone updating the Etcd topology known to the client until after the original request. When retrieving the cluster topology, implement the retry timeouts depending on the known number of nodes in the Etcd cluster. This makes our client prefer to get the results of the request to having the up-to-date list of nodes. Both changes make Patroni connections to DCS more robust in the face of network issues. **Patronictl, monitoring and configuration** - Return information about streaming replicas via the API (Feike Steenbergen) Previously, there was no reliable way to query Patroni about PostgreSQL instances that fail to stream changes (for instance, due to connection issues). This change exposes the contents of pg_stat_replication via the /patroni endpoint. - Add patronictl scaffold command (Oleksii) Add a command to create cluster structure in Etcd. The cluster is created with user-specified sysid and leader, and both leader and member keys are made persistent. This command is useful to create so-called master-less configurations, where Patroni cluster consisting of only replicas replicate from the external master node that is unaware of Patroni. Subsequently, one may remove the leader key, promoting one of the Patroni nodes and replacing the original master with the Patroni-based HA cluster. - Add configuration option ``bin_dir`` to locate PostgreSQL binaries (Ants Aasma) It is useful to be able to specify the location of PostgreSQL binaries explicitly when Linux distros that support installing multiple PostgreSQL versions at the same time. - Allow configuration file path to be overridden using ``custom_conf`` of (Alejandro Martínez) Allows for custom configuration file paths, which will be unmanaged by Patroni, :ref:`details `. **Bug fixes and code improvements** - Make Patroni compatible with new version schema in PostgreSQL 10 and above (Feike) Make sure that Patroni understand 2-digits version numbers when doing conditional restarts based on the PostgreSQL version. - Use pkgutil to find DCS modules (Alexander) Use the dedicated python module instead of traversing directories manually in order to find DCS modules. - Always call on_start callback when starting Patroni (Alexander) Previously, Patroni did not call any callbacks when attaching to the already running node with the correct role. Since callbacks are often used to route client connections that could result in the failure to register the running node in the connection routing scheme. With this fix, Patroni calls on_start callback even when attaching to the already running node. - Do not drop active replication slots (Murat, Oleksii) Avoid dropping active physical replication slots on master. PostgreSQL cannot drop such slots anyway. This change makes possible to run non-Patroni managed replicas/consumers on the master. - Close Patroni connections during start of the PostgreSQL instance (Alexander) Forces Patroni to close all former connections when PostgreSQL node is started. Avoids the trap of reusing former connections if postmaster was killed with SIGKILL. - Replace invalid characters when constructing slot names from member names (Ants) Make sure that standby names that do not comply with the slot naming rules don't cause the slot creation and standby startup to fail. Replace the dashes in the slot names with underscores and all other characters not allowed in slot names with their unicode codepoints. Version 1.0 ----------- This release introduces the global dynamic configuration that allows dynamic changes of the PostgreSQL and Patroni configuration parameters for the entire HA cluster. It also delivers numerous bugfixes. **Upgrade notice** When upgrading from v0.90 or below, always upgrade all replicas before the master. Since we don't store replication credentials in DCS anymore, an old replica won't be able to connect to the new master. **Dynamic Configuration** - Implement the dynamic global configuration (Alexander Kukushkin) Introduce new REST API endpoint /config to provide PostgreSQL and Patroni configuration parameters that should be set globally for the entire HA cluster (master and all the replicas). Those parameters are set in DCS and in many cases can be applied without disrupting PostgreSQL or Patroni. Patroni sets a special flag called "pending restart" visible via the API when some of the values require the PostgreSQL restart. In that case, restart should be issued manually via the API. Patroni SIGHUP or POST to /reload will make it re-read the configuration file. See the :ref:`dynamic configuration ` for the details on which parameters can be changed and the order of processing difference configuration sources. The configuration file format *has changed* since the v0.90. Patroni is still compatible with the old configuration files, but in order to take advantage of the bootstrap parameters one needs to change it. Users are encourage to update them by referring to the :ref:`dynamic configuraton documentation page `. **More flexible configuration*** - Make postgresql configuration and database name Patroni connects to configurable (Misja Hoebe) Introduce `database` and `config_base_name` configuration parameters. Among others, it makes possible to run Patroni with PipelineDB and other PostgreSQL forks. - Implement possibility to configure some Patroni configuration parameters via environment (Alexander) Those include the scope, the node name and the namespace, as well as the secrets and makes it easier to run Patroni in a dynamic environment, i.e. Kubernetes Please, refer to the :ref:`supported environment variables ` for further details. - Update the built-in Patroni docker container to take advantage of environment-based configuration (Feike Steenbergen). - Add Zookeeper support to Patroni docker image (Alexander) - Split the Zookeeper and Exhibitor configuration options (Alexander) - Make patronictl reuse the code from Patroni to read configuration (Alexander) This allows patronictl to take advantage of environment-based configuration. - Set application name to node name in primary_conninfo (Alexander) This simplifies identification and configuration of synchronous replication for a given node. **Stability, security and usability improvements** - Reset sysid and do not call pg_controldata when restore of backup in progress (Alexander) This change reduces the amount of noise generated by Patroni API health checks during the lengthy initialization of this node from the backup. - Fix a bunch of pg_rewind corner-cases (Alexander) Avoid running pg_rewind if the source cluster is not the master. In addition, avoid removing the data directory on an unsuccessful rewind, unless the new parameter *remove_data_directory_on_rewind_failure* is set to true. By default it is false. - Remove passwords from the replication connection string in DCS (Alexander) Previously, Patroni always used the replication credentials from the Postgres URL in DCS. That is now changed to take the credentials from the patroni configuration. The secrets (replication username and password) and no longer exposed in DCS. - Fix the asynchronous machinery around the demote call (Alexander) Demote now runs totally asynchronously without blocking the DCS interactions. - Make patronictl always send the authorization header if it is configured (Alexander) This allows patronictl to issue "protected" requests, i.e. restart or reinitialize, when Patroni is configured to require authorization on those. - Handle the SystemExit exception correctly (Alexander) Avoids the issues of Patroni not stopping properly when receiving the SIGTERM - Sample haproxy templates for confd (Alexander) Generates and dynamically changes haproxy configuration from the patroni state in the DCS using confide - Improve and restructure the documentation to make it more friendly to the new users (Lauri Apple) - API must report role=master during pg_ctl stop (Alexander) Makes the callback calls more reliable, particularly in the cluster stop case. In addition, introduce the `pg_ctl_timeout` option to set the timeout for the start, stop and restart calls via the `pg_ctl`. - Fix the retry logic in etcd (Alexander) Make retries more predictable and robust. - Make Zookeeper code more resilient against short network hiccups (Alexander) Reduce the connection timeouts to make Zookeeper connection attempts more frequent. Version 0.90 ------------ This releases adds support for Consul, includes a new *noloadbalance* tag, changes the behavior of the *clonefrom* tag, improves *pg_rewind* handling and improves *patronictl* control program. **Consul support** - Implement Consul support (Alexander Kukushkin) Patroni runs against Consul, in addition to Etcd and Zookeeper. the connection parameters can be configured in the YAML file. **New and improved tags** - Implement *noloadbalance* tag (Alexander) This tag makes Patroni always return that the replica is not available to the load balancer. - Change the implementation of the *clonefrom* tag (Alexander) Previously, a node name had to be supplied to the *clonefrom*, forcing a tagged replica to clone from the specific node. The new implementation makes *clonefrom* a boolean tag: if it is set to true, the replica becomes a candidate for other replicas to clone from it. When multiple candidates are present, the replicas picks one randomly. **Stability and security improvements** - Numerous reliability improvements (Alexander) Removes some spurious error messages, improves the stability of the failover, addresses some corner cases with reading data from DCS, shutdown, demote and reattaching of the former leader. - Improve systems script to avoid killing Patroni children on stop (Jan Keirse, Alexander Kukushkin) Previously, when stopping Patroni, *systemd* also sent a signal to PostgreSQL. Since Patroni also tried to stop PostgreSQL by itself, it resulted in sending to different shutdown requests (the smart shutdown, followed by the fast shutdown). That resulted in replicas disconnecting too early and a former master not being able to rejoin after demote. Fix by Jan with prior research by Alexander. - Eliminate some cases where the former master was unable to call pg_rewind before rejoining as a replica (Oleksii Kliukin) Previously, we only called *pg_rewind* if the former master had crashed. Change this to always run pg_rewind for the former master as long as pg_rewind is present in the system. This fixes the case when the master is shut down before the replicas managed to get the latest changes (i.e. during the "smart" shutdown). - Numerous improvements to unit- and acceptance- tests, in particular, enable support for Zookeeper and Consul (Alexander). - Make Travis CI faster and implement support for running tests against Zookeeper (Exhibitor) and Consul (Alexander) Both unit and acceptance tests run automatically against Etcd, Zookeeper and Consul on each commit or pull-request. - Clear environment variables before calling PostgreSQL commands from Patroni (Feike Steenbergen) This prevents a possibility of reading system environment variables by connecting to the PostgreSQL cluster managed by Patroni. **Configuration and control changes** - Unify patronictl and Patroni configuration (Feike) patronictl can use the same configuration file as Patroni itself. - Enable Patroni to read the configuration from the environment variables (Oleksii) This simplifies generating configuration for Patroni automatically, or merging a single configuration from different sources. - Include database system identifier in the information returned by the API (Feike) - Implement *delete_cluster* for all available DCSs (Alexander) Enables support for DCSs other than Etcd in patronictl. Version 0.80 ------------ This release adds support for *cascading replication* and simplifies Patroni management by providing *scheduled failovers*. One may use older versions of Patroni (in particular, 0.78) combined with this one in order to migrate to the new release. Note that the scheduled failover and cascading replication related features will only work with Patroni 0.80 and above. **Cascading replication** - Add support for the *replicatefrom* and *clonefrom* tags for the patroni node (Oleksii Kliukin). The tag *replicatefrom* allows a replica to use an arbitrary node a source, not necessary the master. The *clonefrom* does the same for the initial backup. Together, they enable Patroni to fully support cascading replication. - Add support for running replication methods to initialize the replica even without a running replication connection (Oleksii). This is useful in order to create replicas from the snapshots stored on S3 or FTP. A replication method that does not require a running replication connection should supply *no_master: true* in the yaml configuration. Those scripts will still be called in order if the replication connection is present. **Patronictl, API and DCS improvements** - Implement scheduled failovers (Feike Steenbergen). Failovers can be scheduled to happen at a certain time in the future, using either patronictl, or API calls. - Add support for *dbuser* and *password* parameters in patronictl (Feike). - Add PostgreSQL version to the health check output (Feike). - Improve Zookeeper support in patronictl (Oleksandr Shulgin) - Migrate to python-etcd 0.43 (Alexander Kukushkin) **Configuration** - Add a sample systems configuration script for Patroni (Jan Keirse). - Fix the problem of Patroni ignoring the superuser name specified in the configuration file for DB connections (Alexander). - Fix the handling of CTRL-C by creating a separate session ID and process group for the postmaster launched by Patroni (Alexander). **Tests** - Add acceptance tests with *behave* in order to check real-world scenarios of running Patroni (Alexander, Oleksii). The tests can be launched manually using the *behave* command. They are also launched automatically for pull requests and after commits. Release notes for some older versions can be found on `project's github page `__. patroni-1.4.2/docs/replica_bootstrap.rst000066400000000000000000000116351323411135200203710ustar00rootroot00000000000000Replica imaging and bootstrap ============================= Patroni allows customizing creation of a new replica. It also supports defining what happens when the new empty cluster is being bootstrapped. The distinction between two is well defined: Patroni creates replicas only if the ``initialize`` key is present in Etcd for the cluster. If there is no ``initialize`` key - Patroni calls bootstrap exclusively on the first node that takes the initialize key lock. .. _custom_bootstrap: Bootstrap --------- PostgreSQL provides ``initdb`` command to initialize a new cluster and Patroni calls it by default. In certain cases, particularly when creating a new cluster as a copy of an existing one, it is necessary to replace a built-in method with custom actions. Patroni supports executing user-defined scripts to bootstrap new clusters, supplying some required arguments to them, i.e. the name of the cluster and the path to the data directory. This is configured in the ``bootstrap`` section of the Patroni configuration. For example: .. code:: YAML bootstrap: method: : command: [param1 [, ...]] recovery_conf: recovery_target_action: promote recovery_target_timeline: latest restore_command: Each bootstrap method must define at least a ``name`` and a ``command``. A special ``initdb`` method is available to trigger the default behavior, in which case ``method`` parameter can be omitted altogether. The ``command`` can be specified using either an absolute path, or the one relative to the ``patroni`` command location. In addition to the fixed parameters defined in the configuration files, Patroni supplies two cluster-specific ones: --scope Name of the cluster to be bootstrapped --datadir Path to the data directory of the cluster instance to be bootstrapped If the bootstrap script returns 0, Patroni tries to configure and start the PostgreSQL instance produced by it. If any of the intermediate steps fail, or the script returns a non-zero value, Patroni assumes that the bootstrap has failed, cleans up after itself and releases the initialize lock to give another node the opportunity to bootstrap. If a ``recovery_conf`` block is defined in the same section as the custom bootstrap method, Patroni will generate a ``recovery.conf`` before starting the newly bootstrapped instance. Typically, such recovery.conf should contain at least one of the ``recovery_target_*`` parameters, together with the ``recovery_target_timeline`` set to ``promote``. .. note:: Bootstrap methods are neither chained, nor fallen-back to the default one in case the primary one fails .. _custom_replica_creation: Building replicas ----------------- Patroni uses tried and proven ``pg_basebackup`` in order to create new replicas. One downside of it is that it requires a running master node. Another one is the lack of 'on-the-fly' compression for the backup data and no built-in cleanup for outdated backup files. Some people prefer other backup solutions, such as ``WAL-E``, ``pgBackRest``, ``Barman`` and others, or simply roll their own scripts. In order to accommodate all those use-cases Patroni supports running custom scripts to clone a new replica. Those are configured in the ``postgresql`` configuration block: .. code:: YAML postgresql: create_replica_method: - wal_e - basebackup wal_e: command: patroni_wale_restore no_master: 1 envdir: {{WALE_ENV_DIR}} use_iam: 1 The ``create_replica_method`` defines available replica creation methods and the order of executing them. Patroni will stop on the first one that returns 0. The basebackup is the built-in method and doesn't require any configuration. The rest of the methods should define a separate section in the configuration file, listing the command to execute and any custom parameters that should be passed to that command. All parameters will be passed in a ``--name=value`` format. Besides user-defined parameters, Patroni supplies a couple of cluster-specific ones: --scope Which cluster this replica belongs to --datadir Path to the data directory of the replica --role Always 'replica' --connstring Connection string to connect to the cluster member to clone from (master or other replica). The user in the connection string can execute SQL and replication protocol commands. A special ``no_master`` parameter, if defined, allows Patroni to call the replica creation method even if there is no running master or replicas. In that case, an empty string will be passed in a connection string. This is useful for restoring the formerly running cluster from the binary backup. If all replica creation methods fail, Patroni will try again all methods in order during the next event loop cycle. patroni-1.4.2/docs/replication_modes.rst000066400000000000000000000147161323411135200203600ustar00rootroot00000000000000.. _replication_modes: ================= Replication modes ================= Patroni uses PostgreSQL streaming replication. For more information about streaming replication, see the `Postgres documentation `__. By default Patroni configures PostgreSQL for asynchronous replication. Choosing your replication schema is dependent on your business considerations. Investigate both async and sync replication, as well as other HA solutions, to determine which solution is best for you. Asynchronous mode durability ---------------------------- In asynchronous mode the cluster is allowed to lose some committed transactions to ensure availability. When master server fails or becomes unavailable for any other reason Patroni will automatically promote a sufficiently healthy standby to master. Any transactions that have not been replicated to that standby remain in a "forked timeline" on the master, and are effectively unrecoverable [1]_. The amount of transactions that can be lost is controlled via ``maximum_lag_on_failover`` parameter. Because master transaction log position is not sampled in real time, in reality the amount of lost data on failover is worst case bounded by ``maximum_lag_on_failover`` bytes of transaction log plus the amount that is written in the last ``ttl`` seconds (``loop_wait``/2 seconds in the average case). However typical steady state replication delay is well under a second. PostgreSQL synchronous replication ---------------------------------- You can use Postgres's `synchronous replication `__ with Patroni. Synchronous replication ensures consistency across a cluster by confirming that writes are written to a secondary before returning to the connecting client with a success. The cost of synchronous replication: reduced throughput on writes. This throughput will be entirely based on network performance. In hosted datacenter environments (like AWS, Rackspace, or any network you do not control), synchronous replication significantly increases the variability of write performance. If followers become inaccessible from the leader, the leader effectively becomes read-only. To enable a simple synchronous replication test, add the follow lines to the ``parameters`` section of your YAML configuration files: .. code:: YAML synchronous_commit: "on" synchronous_standby_names: "*" When using PostgreSQL synchronous replication, use at least three Postgres data nodes to ensure write availability if one host fails. Using PostgreSQL synchronous replication does not guarantee zero lost transactions under all circumstances. When master and standby that is currently acting as synchronous fail simultaneously a third node that might not contain all transactions will be promoted. .. _synchronous_mode: Synchronous mode ---------------- For use cases where losing committed transactions is not permissible you can turn on Patronis ``synchronous_mode``. When ``synchronous_mode`` is turned on Patroni will not promote a standby unless it is certain that the standby contains all transactions that may have returned a successful commit status to client [2]_. This means that the system may be unavailable for writes even though some servers are available. System administrators can still use manual failover commmands to promote a standby even if it results in transaction loss. Turning on ``synchronous_mode`` does not guarantee multi node durability of commits under all circumstances. When no suitable standby is available, master server will still accept writes, but does not guarantee their replication. When the master fails in this mode no standby will be promote. When the host that used to be master comes back it will get promoted automatically, unless system administrator performed a manual failover. This behavior makes synchronous mode usable with 2 node clusters. When ``synchronous_mode`` is on and a standby crashes, commits will block until next iteration of Patroni runs and switches master to standalone mode (worst case delay for writes ``ttl`` seconds, average case ``loop_wait``/2 seconds). Manually shutting down or restarting a standby will not cause a commit service interruption. Standby will signal the master to release itself from synchronous standby duties before PostgreSQL shutdown is initiated. You can ensure that a standby never becomes the synchronous standby by setting ``nosync`` tag to true. This is recommended to set for standbys that are behind slow network connections and would cause performance degradation when becoming a synchronous standby. Synchronous mode can be switched on and off via Patroni REST interface. See :ref:`dynamic configuration ` for instructions. Synchronous mode implementation ------------------------------- When in synchronous mode Patroni maintains synchronization state in the DCS, containing the latest master and current synchronous standby. This state is updated with strict ordering constraints to ensure the following invariants: - A node must be marked as the latest leader whenever it can accept write transactions. Patroni crashing or PostgreSQL not shutting down can cause violations of this invariant. - A node must be set as the synchronous standby in PostgreSQL as long as it is published as the synchronous standby. - A node that is not the leader or current synchronous standby is not allowed to promote itself automatically. Patroni will only ever assign one standby to ``synchronous_standby_names`` because with multiple candidates it is not possible to know which node was acting as synchronous during the failure. On each HA loop iteration Patroni re-evaluates synchronous standby choice. If the current synchronous standby is connected and has not requested its synchronous status to be removed it remains picked. Otherwise the cluster member avaiable for sync that is furthest ahead in replication is picked. .. [1] The data is still there, but recovering it requires a manual recovery effort by data recovery specialists. When Patroni is allowed to rewind with ``use_pg_rewind`` the forked timeline will be automatically erased to rejoin the failed master with the cluster. .. [2] Clients can change the behavior per transaction using PostgreSQL's ``synchronous_commit`` setting. Transactions with ``synchronous_commit`` values of ``off`` and ``local`` may be lost on fail over, but will not be blocked by replication delays. patroni-1.4.2/docs/watchdog.rst000066400000000000000000000076231323411135200164570ustar00rootroot00000000000000.. _watchdog: Watchdog support ================ Having multiple PostgreSQL servers running as master can result in transactions lost due to diverging timelines. This situation is also called a split-brain problem. To avoid split-brain Patroni needs to ensure PostgreSQL will not accept any transaction commits after leader key expires in the DCS. Under normal circumstances Patroni will try to achieve this by stopping PostgreSQL when leader lock update fails for any reason. However, this may fail to happen due to various reasons: - Patroni has crashed due to a bug, out-of-memory condition or by being accidentally killed by a system administrator. - Shutting down PostgreSQL is too slow. - Patroni does not get to run due to high load on the system, th VM being paused by the hypervisor, or other infrastructure issues. To guarantee correct behavior under these conditions Patroni supports watchdog devices. Watchdog devices are software or hardware mechanisms that will reset the whole system when they do not get a keepalive heartbeat within a specified timeframe. This adds an additional layer of fail safe in case usual Patroni split-brain protection mechanisms fail. Patroni will try to activate the watchdog before promoting PostgreSQL to master. If watchdog activation fails and watchdog mode is ``required`` then the node will refuse to become master. When deciding to participate in leader election Patroni will also check that watchdog configuration will allow it to become leader at all. After demoting PostgreSQL (for example due to a manual failover) Patroni will disable the watchdog again. Watchdog will also be disabled while Patroni is in paused state. By default Patroni will set up the watchdog to expire 5 seconds before TTL expires. With the default setup of ``loop_wait=10`` and ``ttl=30`` this gives HA loop at least 15 seconds (``ttl`` - ``safety_margin`` - ``loop_wait``) to complete before the system gets forcefully reset. By default accessing DCS is configured to time out after 10 seconds. This means that when DCS is unavailable, for example due to network issues, Patroni and PostgreSQL will have at least 5 seconds (``ttl`` - ``safety_margin`` - ``loop_wait`` - ``retry_timeout``) to come to a state where all client connections are terminated. Safety margin is the amount of time that Patroni reserves for time between leader key update and watchdog keepalive. Patroni will try to send a keepalive immediately after confirmation of leader key update. If Patroni process is suspended for extended amount of time at exactly the right moment the keepalive may be delayed for more than the safety margin without triggering the watchdog. This results in a window of time where watchdog will not trigger before leader key expiration, invalidating the guarantee. To be absolutely sure that watchdog will trigger under all circumstances set up the watchdog to expire after half of TTL by setting ``safety_margin`` to -1 to set watchdog timeout to ``ttl // 2``. If you need this guarantee you probably should increase ``ttl`` and/or reduce ``loop_wait`` and ``retry_timeout``. Currently watchdogs are only supported using Linux watchdog device interface. Setting up software watchdog on Linux ------------------------------------- Default Patroni configuration will try to use ``/dev/watchdog`` on Linux if it is accessible to Patroni. For most use cases using software watchdog built into the Linux kernel is secure enough. To enable software watchdog issue the following commands as root before starting Patroni: .. code-block:: bash modprobe softdog # Replace postgres with the user you will be running patroni under chown postgres /dev/watchdog For testing it may be helpful to disable rebooting by adding ``soft_noboot=1`` to the modprobe command line. In this case the watchdog will just log a line in kernel ring buffer, visible via `dmesg`. Patroni will log information about the watchdog when it is successfully enabled. patroni-1.4.2/extras/000077500000000000000000000000001323411135200144735ustar00rootroot00000000000000patroni-1.4.2/extras/README.md000066400000000000000000000011461323411135200157540ustar00rootroot00000000000000### confd `confd` directory contains haproxy template files for the [confd](https://github.com/kelseyhightower/confd) -- lightweight configuration management tool You need to copy content of `confd` directory into /etcd/confd and run confd service: ```bash $ confd -prefix=/service/$PATRONI_SCOPE -backend etcd -node $PATRONI_ETCD_HOST -interval=10 ``` It will periodically update haproxy.cfg with the actual list of Patroni nodes from `etcd` and "reload" haproxy when it is necessary. ### startup-scripts `startup-scripts` directory contains startup scripts for various OSes and management tools for Patroni. patroni-1.4.2/extras/confd/000077500000000000000000000000001323411135200155645ustar00rootroot00000000000000patroni-1.4.2/extras/confd/conf.d/000077500000000000000000000000001323411135200167335ustar00rootroot00000000000000patroni-1.4.2/extras/confd/conf.d/haproxy.toml000066400000000000000000000004751323411135200213300ustar00rootroot00000000000000[template] #prefix = "/service/batman" #owner = "haproxy" #mode = "0644" src = "haproxy.tmpl" dest = "/etc/haproxy/haproxy.cfg" check_cmd = "/usr/sbin/haproxy -c -f {{ .src }}" reload_cmd = "haproxy -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -D -sf $(cat /var/run/haproxy.pid)" keys = [ "/members/", ] patroni-1.4.2/extras/confd/templates/000077500000000000000000000000001323411135200175625ustar00rootroot00000000000000patroni-1.4.2/extras/confd/templates/haproxy.tmpl000066400000000000000000000017421323411135200221560ustar00rootroot00000000000000global maxconn 100 defaults log global mode tcp retries 2 timeout client 30m timeout connect 4s timeout server 30m timeout check 5s listen stats mode http bind *:7000 stats enable stats uri / listen master bind *:5000 option httpchk OPTIONS /master http-check expect status 200 default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions {{range gets "/members/*"}} server {{base .Key}} {{$data := json .Value}}{{base (replace (index (split $data.conn_url "/") 2) "@" "/" -1)}} maxconn 100 check port {{index (split (index (split $data.api_url "/") 2) ":") 1}} {{end}} listen replicas bind *:5001 option httpchk OPTIONS /replica http-check expect status 200 default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions {{range gets "/members/*"}} server {{base .Key}} {{$data := json .Value}}{{base (replace (index (split $data.conn_url "/") 2) "@" "/" -1)}} maxconn 100 check port {{index (split (index (split $data.api_url "/") 2) ":") 1}} {{end}} patroni-1.4.2/extras/startup-scripts/000077500000000000000000000000001323411135200176625ustar00rootroot00000000000000patroni-1.4.2/extras/startup-scripts/README.md000066400000000000000000000021461323411135200211440ustar00rootroot00000000000000# startup scripts for Patroni This directory contains sample startup scripts for various OSes and management tools for Patroni. Scripts supplied: ### patroni.upstart.conf Upstart job for Ubuntu 12.04 or 14.04. Requires Upstart > 1.4. Intended for systems where Patroni has been installed on a base system, rather than in Docker. ### patroni.service Systemd service file, to be copied to /etc/systemd/system/patroni.service, tested on Centos 7.1 with Patroni installed from pip. ### patroni Init.d service file for Debian-like distributions. Copy it to /etc/init.d/, make executable: ```chmod 755 /etc/init.d/patroni``` and run with ```service patroni start```, or make it starting on boot with ```update-rc.d patroni defaults```. Also you might edit some configuration variables in it: PATRONI for patroni.py location CONF for configuration file LOGFILE for log (script creates it if does not exist) Note. If you have several versions of Postgres installed, please add to POSTGRES_VERSION the release number which you wish to run. Script uses this value to append PATH environment with correct path to Postgres bin. patroni-1.4.2/extras/startup-scripts/patroni000066400000000000000000000065311323411135200212660ustar00rootroot00000000000000#!/bin/sh # ### BEGIN INIT INFO # Provides: patroni # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Patroni init script # Description: Runners to orchestrate a high-availability PostgreSQL ### END INIT INFO ### BEGIN USER CONFIGURATION CONF="/etc/patroni/postgres.yml" LOGFILE="/var/log/patroni.log" USER="postgres" GROUP="postgres" NAME=patroni PATRONI="/opt/patroni/$NAME.py" PIDFILE="/var/run/$NAME.pid" # Set this parameter, if you have several Postgres versions installed # POSTGRES_VERSION="9.4" POSTGRES_VERSION="" ### END USER CONFIGURATION . /lib/lsb/init-functions # Loading this library for get_versions() function if test ! -e /usr/share/postgresql-common/init.d-functions; then log_failure_msg "Probably postgresql-common does not installed." exit 1 else . /usr/share/postgresql-common/init.d-functions fi # Is there Patroni executable? if test ! -e $PATRONI; then log_failure_msg "Patroni executable $PATRONI does not exist." exit 1 fi # Is there Patroni configuration file? if test ! -e $CONF; then log_failure_msg "Patroni configuration file $CONF does not exist." exit 1 fi # Create logfile if doesn't exist if test ! -e $LOGFILE; then log_action_msg "Creating logfile for Patroni..." touch $LOGFILE chown $USER:$GROUP $LOGFILE fi prepare_pgpath() { if [ "$POSTGRES_VERSION" != "" ]; then if [ -x /usr/lib/postgresql/$POSTGRES_VERSION/bin/pg_ctl ]; then PGPATH="/usr/lib/postgresql/$POSTGRES_VERSION/bin" else log_failure_msg "Postgres version incorrect, check POSTGRES_VERSION variable." exit 0 fi else get_versions if echo $versions | grep -q -e "\s"; then log_warning_msg "You have several Postgres versions installed. Please, use POSTGRES_VERSION to define correct environment." else versions=`echo $versions | sed -e 's/^[ \t]*//'` PGPATH="/usr/lib/postgresql/$versions/bin" fi fi } get_pid() { if test -e $PIDFILE; then PID=`cat $PIDFILE` CHILDPID=`ps --ppid $PID -o %p --no-headers` else log_failure_msg "Could not find PID file. Patroni probably down." exit 1 fi } case "$1" in start) prepare_pgpath PGPATH=$PATH:$PGPATH log_success_msg "Starting Patroni\n" exec start-stop-daemon --start --quiet \ --background \ --pidfile $PIDFILE --make-pidfile \ --chuid $USER:$GROUP \ --chdir `eval echo ~$USER` \ --exec $PATRONI \ --startas /bin/sh -- \ -c "/usr/bin/env PATH=$PGPATH /usr/bin/python $PATRONI $CONF >> $LOGFILE 2>&1" ;; stop) log_success_msg "Stopping Patroni" get_pid start-stop-daemon --stop --pid $CHILDPID start-stop-daemon --stop --pidfile $PIDFILE --remove-pidfile --quiet ;; reload) log_success_msg "Reloading Patroni configuration" get_pid kill -HUP $CHILDPID ;; status) get_pid if start-stop-daemon -T --pid $CHILDPID; then log_success_msg "Patroni is running\n" exit 0 else log_warning_msg "Patroni in not running\n" fi ;; restart) $0 stop $0 start ;; *) echo "Usage: /etc/init.d/$NAME {start|stop|restart|reload|status}" exit 1 ;; esac if [ $? -eq 0 ]; then echo . exit 0 else echo " failed" exit 1 fi patroni-1.4.2/extras/startup-scripts/patroni.service000066400000000000000000000014371323411135200227250ustar00rootroot00000000000000# This is an example systemd config file for Patroni # You can copy it to "/etc/systemd/system/patroni.service", [Unit] Description=Runners to orchestrate a high-availability PostgreSQL After=syslog.target network.target [Service] Type=simple User=postgres Group=postgres # Where to send early-startup messages from the server # This is normally controlled by the global default set by systemd # StandardOutput=syslog ExecStart=/bin/patroni /etc/patroni.yml # only kill the patroni process, not it's children, so it will gracefully stop postgres KillMode=process # Give a reasonable amount of time for the server to start up/shut down TimeoutSec=30 # Do not restart the service if it crashes, we want to manually inspect database on failure Restart=no [Install] WantedBy=multi-user.target patroni-1.4.2/extras/startup-scripts/patroni.upstart.conf000066400000000000000000000014641323411135200237130ustar00rootroot00000000000000# patroni - patroni daemon # # controls startup/shutdown of postgres # you should disable any postgres start jobs # # assumes that patroni has been installed into the # pythonpath by using setup.py install description "patroni start daemon" start on net-device-up stop on runlevel [06] respawn respawn limit 5 10 # set location of patroni env PATRONI=/usr/local/bin/patroni # virtualenv example # env PATRONI=/var/lib/postgresql/patronienv/bin/patroni # set location of config file env PATRONICONF=/etc/patroni/patroni.yml # set log dir for patroni logs # postgres user must have write permission env POSTGRESLOGDIR=/var/log/postgresql setuid postgres setgid postgres script exec start-stop-daemon --start \ --exec $PATRONI -- $PATRONICONF \ >> $POSTGRESLOGDIR/patroni.log 2>&1 end script patroni-1.4.2/features/000077500000000000000000000000001323411135200150035ustar00rootroot00000000000000patroni-1.4.2/features/backup_create.sh000077500000000000000000000010131323411135200201250ustar00rootroot00000000000000#!/bin/bash while getopts ":-:" optchar; do [[ "${optchar}" == "-" ]] || continue case "${OPTARG}" in datadir=* ) PGDATA=${OPTARG#*=} ;; dbname=* ) DBNAME=${OPTARG#*=} ;; walmethod=* ) WALMETHOD=${OPTARG#*=} ;; esac done [[ -z $PGDATA || -z $DBNAME || -z $WALMETHOD ]] && exit 1 [[ $WALMETHOD != "none" ]] && WALMETHOD="-X $WALMETHOD" || WALMETHOD="" exec pg_basebackup -D $PGDATA $WALMETHOD -c fast -d $DBNAME patroni-1.4.2/features/backup_restore.sh000077500000000000000000000005611323411135200203540ustar00rootroot00000000000000#!/bin/bash set -x while getopts ":-:" optchar; do [[ "${optchar}" == "-" ]] || continue case "${OPTARG}" in datadir=* ) PGDATA=${OPTARG#*=} ;; sourcedir=* ) SOURCE=${OPTARG#*=} ;; esac done [[ -z $PGDATA || -z $SOURCE ]] && exit 1 mkdir -p $(dirname $PGDATA) exec cp -af $SOURCE $PGDATA patroni-1.4.2/features/basic_replication.feature000066400000000000000000000045651323411135200220440ustar00rootroot00000000000000Feature: basic replication We should check that the basic bootstrapping, replication and failover works. Scenario: check replication of a single table Given I start postgres0 Then postgres0 is a leader after 10 seconds When I issue a PATCH request to http://127.0.0.1:8008/config with {"ttl": 20, "loop_wait": 2, "synchronous_mode": true} Then I receive a response code 200 When I start postgres1 And I configure and start postgres2 with a tag replicatefrom postgres0 And "sync" key in DCS has leader=postgres0 after 20 seconds And I add the table foo to postgres0 Then table foo is present on postgres1 after 20 seconds Then table foo is present on postgres2 after 20 seconds Scenario: check restart of sync replica Given I run patronictl.py restart batman postgres2 --force And "sync" key in DCS has sync_standby=postgres1 after 2 seconds And I run patronictl.py restart batman postgres1 --force Then I receive a response returncode 0 And "sync" key in DCS has sync_standby=postgres2 after 10 seconds And I sleep for 2 seconds When I issue a GET request to http://127.0.0.1:8010/sync Then I receive a response code 200 When I issue a GET request to http://127.0.0.1:8009/async Then I receive a response code 200 Scenario: check the basic failover in synchronous mode Given I run patronictl.py pause batman Then I receive a response returncode 0 When I sleep for 2 seconds And I shut down postgres0 And I run patronictl.py resume batman Then I receive a response returncode 0 And postgres2 role is the primary after 24 seconds When I issue a PATCH request to http://127.0.0.1:8010/config with {"synchronous_mode": null, "master_start_timeout": 0} Then I receive a response code 200 When I add the table bar to postgres2 Then table bar is present on postgres1 after 20 seconds Scenario: check immediate failover when master_start_timeout=0 Given I kill postmaster on postgres2 Then postgres1 is a leader after 10 seconds And postgres1 role is the primary after 10 seconds Scenario: check rejoin of the former master with pg_rewind Given I add the table splitbrain to postgres0 And I start postgres0 Then postgres0 role is the secondary after 20 seconds When I add the table buz to postgres1 Then table buz is present on postgres0 after 20 seconds patroni-1.4.2/features/cascading_replication.feature000066400000000000000000000014301323411135200226630ustar00rootroot00000000000000Feature: cascading replication We should check that patroni can do base backup and streaming from the replica Scenario: check a base backup and streaming replication from a replica Given I start postgres0 And postgres0 is a leader after 10 seconds And I configure and start postgres1 with a tag clonefrom true And replication works from postgres0 to postgres1 after 20 seconds And I create label with "postgres0" in postgres0 data directory And I create label with "postgres1" in postgres1 data directory And "members/postgres1" key in DCS has state=running after 12 seconds And I configure and start postgres2 with a tag replicatefrom postgres1 Then replication works from postgres0 to postgres2 after 30 seconds And there is a label with "postgres1" in postgres2 data directory patroni-1.4.2/features/custom_bootstrap.feature000066400000000000000000000014141323411135200217670ustar00rootroot00000000000000Feature: custom bootstrap We should check that patroni can bootstrap a new cluster from a backup Scenario: clone existing cluster using pg_basebackup Given I start postgres0 Then postgres0 is a leader after 10 seconds When I add the table foo to postgres0 And I start postgres1 in a cluster batman1 as a clone of postgres0 Then postgres1 is a leader of batman1 after 10 seconds Then table foo is present on postgres1 after 10 seconds Scenario: make a backup and do a restore into a new cluster Given I add the table bar to postgres1 And I do a backup of postgres1 When I start postgres2 in a cluster batman2 from backup Then postgres2 is a leader of batman2 after 10 seconds And table bar is present on postgres2 after 10 seconds patroni-1.4.2/features/environment.py000066400000000000000000000717061323411135200177340ustar00rootroot00000000000000import abc import consul import datetime import etcd import kazoo.client import kazoo.exceptions import os import psutil import psycopg2 import json import shutil import signal import six import subprocess import tempfile import threading import time import yaml @six.add_metaclass(abc.ABCMeta) class AbstractController(object): def __init__(self, context, name, work_directory, output_dir): self._context = context self._name = name self._work_directory = work_directory self._output_dir = output_dir self._handle = None self._log = None def _has_started(self): return self._handle and self._handle.pid and self._handle.poll() is None def _is_running(self): return self._has_started() @abc.abstractmethod def _is_accessible(self): """process is accessible for queries""" @abc.abstractmethod def _start(self): """start process""" def start(self, max_wait_limit=5): if self._is_running(): return True self._log = open(os.path.join(self._output_dir, self._name + '.log'), 'a') self._handle = self._start() assert self._has_started(), "Process {0} is not running after being started".format(self._name) max_wait_limit *= self._context.timeout_multiplier for _ in range(max_wait_limit): if self._is_accessible(): break time.sleep(1) else: assert False,\ "{0} instance is not available for queries after {1} seconds".format(self._name, max_wait_limit) def stop(self, kill=False, timeout=15, _=False): term = False start_time = time.time() timeout *= self._context.timeout_multiplier while self._handle and self._is_running(): if kill: self._handle.kill() elif not term: self._handle.terminate() term = True time.sleep(1) if not kill and time.time() - start_time > timeout: kill = True if self._log: self._log.close() def cancel_background(self): pass class PatroniController(AbstractController): __PORT = 5440 PATRONI_CONFIG = '{}.yml' """ starts and stops individual patronis""" def __init__(self, context, name, work_directory, output_dir, custom_config=None): super(PatroniController, self).__init__(context, 'patroni_' + name, work_directory, output_dir) PatroniController.__PORT += 1 self._data_dir = os.path.join(work_directory, 'data', name) self._connstring = None if custom_config and 'watchdog' in custom_config: self.watchdog = WatchdogMonitor(name, work_directory, output_dir) custom_config['watchdog'] = {'driver': 'testing', 'device': self.watchdog.fifo_path, 'mode': 'required'} else: self.watchdog = None self._scope = (custom_config or {}).get('scope', 'batman') self._config = self._make_patroni_test_config(name, custom_config) self._closables = [] self._conn = None self._curs = None def write_label(self, content): with open(os.path.join(self._data_dir, 'label'), 'w') as f: f.write(content) def read_label(self): try: with open(os.path.join(self._data_dir, 'label'), 'r') as f: return f.read().strip() except IOError: return None def add_tag_to_config(self, tag, value): with open(self._config) as r: config = yaml.safe_load(r) config['tags']['tag'] = value with open(self._config, 'w') as w: yaml.safe_dump(config, w, default_flow_style=False) def _start(self): if self.watchdog: self.watchdog.start() if isinstance(self._context.dcs_ctl, KubernetesController): self._context.dcs_ctl.create_pod(self._name[8:], self._scope) os.environ['PATRONI_KUBERNETES_POD_IP'] = '10.0.0.' + self._name[-1] return subprocess.Popen(['coverage', 'run', '--source=patroni', '-p', 'patroni.py', self._config], stdout=self._log, stderr=subprocess.STDOUT, cwd=self._work_directory) def stop(self, kill=False, timeout=15, postgres=False): if postgres: return subprocess.call(['pg_ctl', '-D', self._data_dir, 'stop', '-mi', '-w']) super(PatroniController, self).stop(kill, timeout) if isinstance(self._context.dcs_ctl, KubernetesController): self._context.dcs_ctl.delete_pod(self._name[8:]) if self.watchdog: self.watchdog.stop() def _is_accessible(self): cursor = self.query("SELECT 1", fail_ok=True) if cursor is not None: cursor.execute("SET synchronous_commit TO 'local'") return True def _make_patroni_test_config(self, name, custom_config): patroni_config_name = self.PATRONI_CONFIG.format(name) patroni_config_path = os.path.join(self._output_dir, patroni_config_name) with open(patroni_config_name) as f: config = yaml.safe_load(f) config.pop('etcd', None) host = config['postgresql']['listen'].split(':')[0] config['postgresql']['listen'] = config['postgresql']['connect_address'] = '{0}:{1}'.format(host, self.__PORT) config['name'] = name config['postgresql']['data_dir'] = self._data_dir config['postgresql']['use_unix_socket'] = True config['postgresql']['parameters'].update({ 'logging_collector': 'on', 'log_destination': 'csvlog', 'log_directory': self._output_dir, 'log_filename': name + '.log', 'log_statement': 'all', 'log_min_messages': 'debug1', 'unix_socket_directories': self._data_dir}) if 'bootstrap' in config: config['bootstrap']['post_bootstrap'] = 'psql -w -c "SELECT 1"' if 'initdb' in config['bootstrap']: config['bootstrap']['initdb'].extend([{'auth': 'md5'}, {'auth-host': 'md5'}]) if custom_config is not None: def recursive_update(dst, src): for k, v in src.items(): if k in dst and isinstance(dst[k], dict): recursive_update(dst[k], v) else: dst[k] = v recursive_update(config, custom_config) with open(patroni_config_path, 'w') as f: yaml.safe_dump(config, f, default_flow_style=False) user = config['postgresql'].get('authentication', config['postgresql']).get('superuser', {}) self._connkwargs = {k: user[n] for n, k in [('username', 'user'), ('password', 'password')] if n in user} self._connkwargs.update({'host': host, 'port': self.__PORT, 'database': 'postgres'}) self._replication = config['postgresql'].get('authentication', config['postgresql']).get('replication', {}) self._replication.update({'host': host, 'port': self.__PORT, 'database': 'postgres'}) return patroni_config_path def _connection(self): if not self._conn or self._conn.closed != 0: self._conn = psycopg2.connect(**self._connkwargs) self._conn.autocommit = True return self._conn def _cursor(self): if not self._curs or self._curs.closed or self._curs.connection.closed != 0: self._curs = self._connection().cursor() return self._curs def query(self, query, fail_ok=False): try: cursor = self._cursor() cursor.execute(query) return cursor except psycopg2.Error: if not fail_ok: raise def check_role_has_changed_to(self, new_role, timeout=10): bound_time = time.time() + timeout recovery_status = new_role != 'primary' while time.time() < bound_time: cur = self.query("SELECT pg_is_in_recovery()", fail_ok=True) if cur: row = cur.fetchone() if row and row[0] == recovery_status: return True time.sleep(1) return False def get_watchdog(self): return self.watchdog def _get_pid(self): try: pidfile = os.path.join(self._data_dir, 'postmaster.pid') if not os.path.exists(pidfile): return None return int(open(pidfile).readline().strip()) except Exception: return None def database_is_running(self): pid = self._get_pid() if not pid: return False try: os.kill(pid, 0) except OSError: return False return True def patroni_hang(self, timeout): hang = ProcessHang(self._handle.pid, timeout) self._closables.append(hang) hang.start() def checkpoint_hang(self, timeout): pid = self._get_pid() if not pid: return False proc = psutil.Process(pid) for child in proc.children(): if 'checkpoint' in child.cmdline()[0]: checkpointer = child break else: return False hang = ProcessHang(checkpointer.pid, timeout) self._closables.append(hang) hang.start() return True def cancel_background(self): for obj in self._closables: obj.close() self._closables = [] def terminate_backends(self): pid = self._get_pid() if not pid: return False proc = psutil.Process(pid) for p in proc.children(): if 'process' not in p.cmdline()[0]: p.terminate() @property def backup_source(self): return 'postgres://{username}:{password}@{host}:{port}/{database}'.format(**self._replication) def backup(self, dest='basebackup'): subprocess.call([PatroniPoolController.BACKUP_SCRIPT, '--walmethod=none', '--datadir=' + os.path.join(self._output_dir, dest), '--dbname=' + self.backup_source]) class ProcessHang(object): """A background thread implementing a cancelable process hang via SIGSTOP.""" def __init__(self, pid, timeout): self._cancelled = threading.Event() self._thread = threading.Thread(target=self.run) self.pid = pid self.timeout = timeout def start(self): self._thread.start() def run(self): os.kill(self.pid, signal.SIGSTOP) try: self._cancelled.wait(self.timeout) finally: os.kill(self.pid, signal.SIGCONT) def close(self): self._cancelled.set() self._thread.join() class AbstractDcsController(AbstractController): _CLUSTER_NODE = '/service/{0}' def __init__(self, context, mktemp=True): work_directory = mktemp and tempfile.mkdtemp() or None super(AbstractDcsController, self).__init__(context, self.name(), work_directory, context.pctl.output_dir) def _is_accessible(self): return self._is_running() def stop(self, kill=False, timeout=15): """ terminate process and wipe out the temp work directory, but only if we actually started it""" super(AbstractDcsController, self).stop(kill=kill, timeout=timeout) if self._work_directory: shutil.rmtree(self._work_directory) def path(self, key=None, scope='batman'): return self._CLUSTER_NODE.format(scope) + (key and '/' + key or '') @abc.abstractmethod def query(self, key, scope='batman'): """ query for a value of a given key """ @abc.abstractmethod def cleanup_service_tree(self): """ clean all contents stored in the tree used for the tests """ @classmethod def get_subclasses(cls): for subclass in cls.__subclasses__(): for subsubclass in subclass.get_subclasses(): yield subsubclass yield subclass @classmethod def name(cls): return cls.__name__[:-10].lower() class ConsulController(AbstractDcsController): def __init__(self, context): super(ConsulController, self).__init__(context) os.environ['PATRONI_CONSUL_HOST'] = 'localhost:8500' self._client = consul.Consul() self._config_file = None def _start(self): self._config_file = self._work_directory + '.json' with open(self._config_file, 'wb') as f: f.write(b'{"session_ttl_min":"5s","server":true,"bootstrap":true,"advertise_addr":"127.0.0.1"}') return subprocess.Popen(['consul', 'agent', '-config-file', self._config_file, '-data-dir', self._work_directory], stdout=self._log, stderr=subprocess.STDOUT) def stop(self, kill=False, timeout=15): super(ConsulController, self).stop(kill=kill, timeout=timeout) if self._config_file: os.unlink(self._config_file) def _is_running(self): try: return bool(self._client.status.leader()) except Exception: return False def path(self, key=None, scope='batman'): return super(ConsulController, self).path(key, scope)[1:] def query(self, key, scope='batman'): _, value = self._client.kv.get(self.path(key, scope)) return value and value['Value'].decode('utf-8') def cleanup_service_tree(self): self._client.kv.delete(self.path(scope=''), recurse=True) def start(self, max_wait_limit=15): super(ConsulController, self).start(max_wait_limit) class EtcdController(AbstractDcsController): """ handles all etcd related tasks, used for the tests setup and cleanup """ def __init__(self, context): super(EtcdController, self).__init__(context) os.environ['PATRONI_ETCD_HOST'] = 'localhost:2379' self._client = etcd.Client(port=2379) def _start(self): return subprocess.Popen(["etcd", "--debug", "--data-dir", self._work_directory], stdout=self._log, stderr=subprocess.STDOUT) def query(self, key, scope='batman'): try: return self._client.get(self.path(key, scope)).value except etcd.EtcdKeyNotFound: return None def cleanup_service_tree(self): try: self._client.delete(self.path(scope=''), recursive=True) except (etcd.EtcdKeyNotFound, etcd.EtcdConnectionFailed): return except Exception as e: assert False, "exception when cleaning up etcd contents: {0}".format(e) def _is_running(self): # if etcd is running, but we didn't start it try: return bool(self._client.machines) except Exception: return False class KubernetesController(AbstractDcsController): def __init__(self, context): super(KubernetesController, self).__init__(context) self._namespace = 'default' self._labels = {"application": "patroni"} self._label_selector = ','.join('{0}={1}'.format(k, v) for k, v in self._labels.items()) os.environ['PATRONI_KUBERNETES_LABELS'] = json.dumps(self._labels) os.environ['PATRONI_KUBERNETES_USE_ENDPOINTS'] = 'true' from kubernetes import client as k8s_client, config as k8s_config k8s_config.load_kube_config(context='local') self._client = k8s_client self._api = self._client.CoreV1Api() def _start(self): pass def create_pod(self, name, scope): labels = self._labels.copy() labels['cluster-name'] = scope metadata = self._client.V1ObjectMeta(namespace=self._namespace, name=name, labels=labels) spec = self._client.V1PodSpec(containers=[self._client.V1Container(name=name, image='empty')]) body = self._client.V1Pod(metadata=metadata, spec=spec) self._api.create_namespaced_pod(self._namespace, body) def delete_pod(self, name): try: self._api.delete_namespaced_pod(name, self._namespace, self._client.V1DeleteOptions()) except: pass while True: try: self._api.read_namespaced_pod(name, self._namespace) except: break def query(self, key, scope='batman'): if key.startswith('members/'): pod = self._api.read_namespaced_pod(key[8:], self._namespace) return (pod.metadata.annotations or {}).get('status', '') else: try: e = self._api.read_namespaced_endpoints(scope + ('' if key == 'leader' else '-' + key), self._namespace) if key == 'leader': return e.metadata.annotations[key] else: return json.dumps(e.metadata.annotations) except: return None def cleanup_service_tree(self): try: self._api.delete_collection_namespaced_pod(self._namespace, label_selector=self._label_selector) except: pass try: self._api.delete_collection_namespaced_endpoints(self._namespace, label_selector=self._label_selector) except: pass while True: result = self._api.list_namespaced_pod(self._namespace, label_selector=self._label_selector) if len(result.items) < 1: break def _is_running(self): return True class ZooKeeperController(AbstractDcsController): """ handles all zookeeper related tasks, used for the tests setup and cleanup """ def __init__(self, context, export_env=True): super(ZooKeeperController, self).__init__(context, False) if export_env: os.environ['PATRONI_ZOOKEEPER_HOSTS'] = "'localhost:2181'" self._client = kazoo.client.KazooClient() def _start(self): pass # TODO: implement later def query(self, key, scope='batman'): try: return self._client.get(self.path(key, scope))[0].decode('utf-8') except kazoo.exceptions.NoNodeError: return None def cleanup_service_tree(self): try: self._client.delete(self.path(scope=''), recursive=True) except (kazoo.exceptions.NoNodeError): return except Exception as e: assert False, "exception when cleaning up zookeeper contents: {0}".format(e) def _is_running(self): # if zookeeper is running, but we didn't start it if self._client.connected: return True try: return self._client.start(1) or True except Exception: return False class ExhibitorController(ZooKeeperController): def __init__(self, context): super(ExhibitorController, self).__init__(context, False) os.environ.update({'PATRONI_EXHIBITOR_HOSTS': 'localhost', 'PATRONI_EXHIBITOR_PORT': '8181'}) class PatroniPoolController(object): BACKUP_SCRIPT = 'features/backup_create.sh' def __init__(self, context): self._context = context self._dcs = None self._output_dir = None self._patroni_path = None self._processes = {} self.create_and_set_output_directory('') self.known_dcs = {subclass.name(): subclass for subclass in AbstractDcsController.get_subclasses()} @property def patroni_path(self): if self._patroni_path is None: cwd = os.path.realpath(__file__) while True: cwd, entry = os.path.split(cwd) if entry == 'features' or cwd == '/': break self._patroni_path = cwd return self._patroni_path @property def output_dir(self): return self._output_dir def start(self, name, max_wait_limit=20, custom_config=None): if name not in self._processes: self._processes[name] = PatroniController(self._context, name, self.patroni_path, self._output_dir, custom_config) self._processes[name].start(max_wait_limit) def __getattr__(self, func): if func not in ['stop', 'query', 'write_label', 'read_label', 'check_role_has_changed_to', 'add_tag_to_config', 'get_watchdog', 'database_is_running', 'checkpoint_hang', 'patroni_hang', 'terminate_backends', 'backup']: raise AttributeError("PatroniPoolController instance has no attribute '{0}'".format(func)) def wrapper(name, *args, **kwargs): return getattr(self._processes[name], func)(*args, **kwargs) return wrapper def stop_all(self): for ctl in self._processes.values(): ctl.cancel_background() ctl.stop() self._processes.clear() def create_and_set_output_directory(self, feature_name): feature_dir = os.path.join(self.patroni_path, 'features/output', feature_name.replace(' ', '_')) if os.path.exists(feature_dir): shutil.rmtree(feature_dir) os.makedirs(feature_dir) self._output_dir = feature_dir def clone(self, from_name, cluster_name, to_name): f = self._processes[from_name] custom_config = { 'scope': cluster_name, 'bootstrap': { 'method': 'pg_basebackup', 'pg_basebackup': { 'command': self.BACKUP_SCRIPT + ' --walmethod=stream --dbname=' + f.backup_source } }, 'postgresql': { 'parameters': { 'archive_mode': 'on', 'archive_command': 'mkdir -p {0} && test ! -f {0}/%f && cp %p {0}/%f'.format( os.path.join(self._output_dir, 'wal_archive')) }, 'authentication': { 'superuser': {'password': 'zalando1'}, 'replication': {'password': 'rep-pass1'} } } } self.start(to_name, custom_config=custom_config) def bootstrap_from_backup(self, name, cluster_name): custom_config = { 'scope': cluster_name, 'bootstrap': { 'method': 'backup_restore', 'backup_restore': { 'command': 'features/backup_restore.sh --sourcedir=' + os.path.join(self._output_dir, 'basebackup'), 'recovery_conf': { 'recovery_target_action': 'promote', 'recovery_target_timeline': 'latest', 'restore_command': 'cp {0}/wal_archive/%f %p'.format(self._output_dir) } } }, 'postgresql': { 'authentication': { 'superuser': {'password': 'zalando2'}, 'replication': {'password': 'rep-pass2'} } } } self.start(name, custom_config=custom_config) @property def dcs(self): if self._dcs is None: self._dcs = os.environ.pop('DCS', 'etcd') assert self._dcs in self.known_dcs, 'Unsupported dcs: ' + self._dcs return self._dcs class WatchdogMonitor(object): """Testing harness for emulating a watchdog device as a named pipe. Because we can't easily emulate ioctl's we require a custom driver on Patroni side. The device takes no action, only notes if it was pinged and/or triggered. """ def __init__(self, name, work_directory, output_dir): self.fifo_path = os.path.join(work_directory, 'data', 'watchdog.{0}.fifo'.format(name)) self.fifo_file = None self._stop_requested = False # Relying on bool setting being atomic self._thread = None self.last_ping = None self.was_pinged = False self.was_closed = False self._was_triggered = False self.timeout = 60 self._log_file = open(os.path.join(output_dir, 'watchdog.{0}.log'.format(name)), 'w') self._log("watchdog {0} initialized".format(name)) def _log(self, msg): tstamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S,%f") self._log_file.write("{0}: {1}\n".format(tstamp, msg)) def start(self): assert self._thread is None self._stop_requested = False self._log("starting fifo {0}".format(self.fifo_path)) fifo_dir = os.path.dirname(self.fifo_path) if os.path.exists(self.fifo_path): os.unlink(self.fifo_path) elif not os.path.exists(fifo_dir): os.mkdir(fifo_dir) os.mkfifo(self.fifo_path) self.last_ping = time.time() self._thread = threading.Thread(target=self.run) self._thread.start() def run(self): try: while not self._stop_requested: self._log("opening") self.fifo_file = os.open(self.fifo_path, os.O_RDONLY) try: self._log("Fifo {0} connected".format(self.fifo_path)) self.was_closed = False while not self._stop_requested: c = os.read(self.fifo_file, 1) if c == b'X': self._log("Stop requested") return elif c == b'': self._log("Pipe closed") break elif c == b'C': command = b'' c = os.read(self.fifo_file, 1) while c != b'\n' and c != b'': command += c c = os.read(self.fifo_file, 1) command = command.decode('utf8') if command.startswith('timeout='): self.timeout = int(command.split('=')[1]) self._log("timeout={0}".format(self.timeout)) elif c in [b'V', b'1']: cur_time = time.time() if cur_time - self.last_ping > self.timeout: self._log("Triggered") self._was_triggered = True if c == b'V': self._log("magic close") self.was_closed = True elif c == b'1': self.was_pinged = True self._log("ping after {0} seconds".format(cur_time - (self.last_ping or cur_time))) self.last_ping = cur_time else: self._log('Unknown command {0} received from fifo'.format(c)) finally: self.was_closed = True self._log("closing") os.close(self.fifo_file) except Exception as e: self._log("Error {0}".format(e)) finally: self._log("stopping") self._log_file.flush() if os.path.exists(self.fifo_path): os.unlink(self.fifo_path) def stop(self): self._log("Monitor stop") self._stop_requested = True try: if os.path.exists(self.fifo_path): fd = os.open(self.fifo_path, os.O_WRONLY) os.write(fd, b'X') os.close(fd) except Exception as e: self._log("err while closing: {0}".format(str(e))) if self._thread: self._thread.join() self._thread = None def reset(self): self._log("reset") self.was_pinged = self.was_closed = self._was_triggered = False @property def was_triggered(self): delta = time.time() - self.last_ping triggered = self._was_triggered or not self.was_closed and delta > self.timeout self._log("triggered={0}, {1}s left".format(triggered, self.timeout - delta)) return triggered # actions to execute on start/stop of the tests and before running invidual features def before_all(context): os.environ.update({'PATRONI_RESTAPI_USERNAME': 'username', 'PATRONI_RESTAPI_PASSWORD': 'password'}) context.ci = 'TRAVIS_BUILD_NUMBER' in os.environ or 'BUILD_NUMBER' in os.environ context.timeout_multiplier = 2 if context.ci else 1 context.pctl = PatroniPoolController(context) context.dcs_ctl = context.pctl.known_dcs[context.pctl.dcs](context) context.dcs_ctl.start() try: context.dcs_ctl.cleanup_service_tree() except AssertionError: # after_all handlers won't be executed in before_all context.dcs_ctl.stop() raise def after_all(context): context.dcs_ctl.stop() subprocess.call(['coverage', 'combine']) subprocess.call(['coverage', 'report']) def before_feature(context, feature): """ create per-feature output directory to collect Patroni and PostgreSQL logs """ context.pctl.create_and_set_output_directory(feature.name) def after_feature(context, feature): """ stop all Patronis, remove their data directory and cleanup the keys in etcd """ context.pctl.stop_all() shutil.rmtree(os.path.join(context.pctl.patroni_path, 'data')) context.dcs_ctl.cleanup_service_tree() patroni-1.4.2/features/patroni_api.feature000066400000000000000000000122121323411135200206630ustar00rootroot00000000000000Feature: patroni api We should check that patroni correctly responds to valid and not-valid API requests. Scenario: check API requests on a stand-alone server Given I start postgres0 And postgres0 is a leader after 10 seconds When I issue a GET request to http://127.0.0.1:8008/ Then I receive a response code 200 And I receive a response state running And I receive a response role master When I issue a GET request to http://127.0.0.1:8008/replica Then I receive a response code 503 When I run patronictl.py reinit batman postgres0 --force Then I receive a response returncode 0 And I receive a response output "Failed: reinitialize for member postgres0, status code=503, (I am the leader, can not reinitialize)" When I run patronictl.py switchover batman --master postgres0 --force Then I receive a response returncode 1 And I receive a response output "Error: No candidates found to switchover to" When I issue a POST request to http://127.0.0.1:8008/switchover with {"leader": "postgres0"} Then I receive a response code 412 And I receive a response text switchover is not possible: cluster does not have members except leader When I issue an empty POST request to http://127.0.0.1:8008/failover Then I receive a response code 400 When I issue a POST request to http://127.0.0.1:8008/failover with {"foo": "bar"} Then I receive a response code 400 And I receive a response text "Failover could be performed only to a specific candidate" Scenario: check local configuration reload Given I issue an empty POST request to http://127.0.0.1:8008/reload Then I receive a response code 200 And I receive a response text nothing changed When I add tag new_tag new_value to postgres0 config And I issue an empty POST request to http://127.0.0.1:8008/reload Then I receive a response code 202 Scenario: check dynamic configuration change via DCS Given I run patronictl.py edit-config -s 'ttl=10' -s 'loop_wait=2' -p 'max_connections=101' --force batman Then I receive a response returncode 0 And I receive a response output "+loop_wait: 2" And Response on GET http://127.0.0.1:8008/patroni contains pending_restart after 11 seconds When I issue a GET request to http://127.0.0.1:8008/config Then I receive a response code 200 And I receive a response loop_wait 2 When I issue a GET request to http://127.0.0.1:8008/patroni Then I receive a response code 200 And I receive a response tags {'tag': 'new_value'} Scenario: check API requests for the primary-replica pair in the pause mode Given I run patronictl.py pause batman Then I receive a response returncode 0 When I start postgres1 Then replication works from postgres0 to postgres1 after 20 seconds When I issue a GET request to http://127.0.0.1:8009/replica Then I receive a response code 200 And I receive a response state running And I receive a response role replica When I run patronictl.py reinit batman postgres1 --force Then I receive a response returncode 0 And I receive a response output "Success: reinitialize for member postgres1" When I run patronictl.py restart batman postgres0 --force Then I receive a response returncode 0 And I receive a response output "Success: restart on member postgres0" And postgres0 role is the primary after 5 seconds When I sleep for 10 seconds Then postgres1 role is the secondary after 15 seconds Scenario: check the switchover via the API in the pause mode Given I issue a POST request to http://127.0.0.1:8008/switchover with {"leader": "postgres0", "candidate": "postgres1"} Then I receive a response code 200 And postgres1 is a leader after 5 seconds And postgres1 role is the primary after 10 seconds And postgres0 role is the secondary after 10 seconds And replication works from postgres1 to postgres0 after 20 seconds Scenario: check the scheduled switchover Given I issue a scheduled switchover from postgres1 to postgres0 in 3 seconds Then I receive a response returncode 1 And I receive a response output "Can't schedule switchover in the paused state" When I run patronictl.py resume batman Then I receive a response returncode 0 Given I issue a scheduled switchover from postgres1 to postgres0 in 3 seconds Then I receive a response returncode 0 And postgres0 is a leader after 20 seconds And postgres0 role is the primary after 10 seconds And postgres1 role is the secondary after 10 seconds And replication works from postgres0 to postgres1 after 25 seconds Scenario: check the scheduled restart Given I issue a PATCH request to http://127.0.0.1:8008/config with {"postgresql": {"parameters": {"superuser_reserved_connections": "6"}}} Then I receive a response code 200 And Response on GET http://127.0.0.1:8008/patroni contains pending_restart after 5 seconds Given I issue a scheduled restart at http://127.0.0.1:8008 in 3 seconds with {"role": "replica"} Then I receive a response code 202 And I sleep for 4 seconds And Response on GET http://127.0.0.1:8008/patroni contains pending_restart after 10 seconds Given I issue a scheduled restart at http://127.0.0.1:8008 in 3 seconds with {"restart_pending": "True"} Then I receive a response code 202 And Response on GET http://127.0.0.1:8008/patroni does not contain pending_restart after 10 seconds patroni-1.4.2/features/steps/000077500000000000000000000000001323411135200161415ustar00rootroot00000000000000patroni-1.4.2/features/steps/basic_replication.py000066400000000000000000000044021323411135200221650ustar00rootroot00000000000000import psycopg2 as pg from behave import step, then from time import sleep, time @step('I start {name:w}') def start_patroni(context, name): return context.pctl.start(name) @step('I shut down {name:w}') def stop_patroni(context, name): return context.pctl.stop(name, timeout=60) @step('I kill {name:w}') def kill_patroni(context, name): return context.pctl.stop(name, kill=True) @step('I kill postmaster on {name:w}') def stop_postgres(context, name): return context.pctl.stop(name, postgres=True) @step('I add the table {table_name:w} to {pg_name:w}') def add_table(context, table_name, pg_name): # parse the configuration file and get the port try: context.pctl.query(pg_name, "CREATE TABLE {0}()".format(table_name)) except pg.Error as e: assert False, "Error creating table {0} on {1}: {2}".format(table_name, pg_name, e) @then('Table {table_name:w} is present on {pg_name:w} after {max_replication_delay:d} seconds') def table_is_present_on(context, table_name, pg_name, max_replication_delay): max_replication_delay *= context.timeout_multiplier for _ in range(int(max_replication_delay)): if context.pctl.query(pg_name, "SELECT 1 FROM {0}".format(table_name), fail_ok=True) is not None: break sleep(1) else: assert False,\ "Table {0} is not present on {1} after {2} seconds".format(table_name, pg_name, max_replication_delay) @then('{pg_name:w} role is the {pg_role:w} after {max_promotion_timeout:d} seconds') def check_role(context, pg_name, pg_role, max_promotion_timeout): max_promotion_timeout *= context.timeout_multiplier assert context.pctl.check_role_has_changed_to(pg_name, pg_role, timeout=int(max_promotion_timeout)),\ "{0} role didn't change to {1} after {2} seconds".format(pg_name, pg_role, max_promotion_timeout) @step('replication works from {master:w} to {replica:w} after {time_limit:d} seconds') @then('replication works from {master:w} to {replica:w} after {time_limit:d} seconds') def replication_works(context, master, replica, time_limit): context.execute_steps(u""" When I add the table test_{0} to {1} Then table test_{0} is present on {2} after {3} seconds """.format(int(time()), master, replica, time_limit)) patroni-1.4.2/features/steps/cascading_replication.py000066400000000000000000000023611323411135200230220ustar00rootroot00000000000000import json import time from behave import step, then @step('I configure and start {name:w} with a tag {tag_name:w} {tag_value:w}') def start_patroni_with_a_name_value_tag(context, name, tag_name, tag_value): return context.pctl.start(name, custom_config={'tags': {tag_name: tag_value}}) @then('There is a label with "{content:w}" in {name:w} data directory') def check_label(context, content, name): label = context.pctl.read_label(name) assert label == content, "{0} is not equal to {1}".format(label, content) @step('I create label with "{content:w}" in {name:w} data directory') def write_label(context, content, name): context.pctl.write_label(name, content) @step('"{name}" key in DCS has {key:w}={value:w} after {time_limit:d} seconds') def check_member(context, name, key, value, time_limit): time_limit *= context.timeout_multiplier max_time = time.time() + int(time_limit) while time.time() < max_time: try: response = json.loads(context.dcs_ctl.query(name)) if response.get(key) == value: return except Exception: pass time.sleep(1) assert False, "{0} does not have {1}={2} in dcs after {3} seconds".format(name, key, value, time_limit) patroni-1.4.2/features/steps/custom_bootstrap.py000066400000000000000000000016751323411135200221330ustar00rootroot00000000000000import time from behave import step, then @step('I start {name:w} in a cluster {cluster_name:w} as a clone of {name2:w}') def start_cluster_clone(context, name, cluster_name, name2): context.pctl.clone(name2, cluster_name, name) @step('I start {name:w} in a cluster {cluster_name:w} from backup') def start_cluster_from_backup(context, name, cluster_name): context.pctl.bootstrap_from_backup(name, cluster_name) @then('{name:w} is a leader of {cluster_name:w} after {time_limit:d} seconds') def is_a_leader(context, name, cluster_name, time_limit): time_limit *= context.timeout_multiplier max_time = time.time() + int(time_limit) while (context.dcs_ctl.query("leader", scope=cluster_name) != name): time.sleep(1) assert time.time() < max_time, "{0} is not a leader in dcs after {1} seconds".format(name, time_limit) @step('I do a backup of {name:w}') def do_backup(context, name): context.pctl.backup(name) patroni-1.4.2/features/steps/patroni_api.py000066400000000000000000000141421323411135200210220ustar00rootroot00000000000000import base64 import json import os import parse import requests import shlex import subprocess import time import yaml from behave import register_type, step, then from dateutil import tz from datetime import datetime, timedelta tzutc = tz.tzutc() @parse.with_pattern(r'https?://(?:\w|\.|:|/)+') def parse_url(text): return text register_type(url=parse_url) # there is no way we can find out if the node has already # started as a leader without checking the DCS. We cannot # just rely on the database availability, since there is # a short gap between the time PostgreSQL becomes available # and Patroni assuming the leader role. @step('{name:w} is a leader after {time_limit:d} seconds') @then('{name:w} is a leader after {time_limit:d} seconds') def is_a_leader(context, name, time_limit): time_limit *= context.timeout_multiplier max_time = time.time() + int(time_limit) while (context.dcs_ctl.query("leader") != name): time.sleep(1) assert time.time() < max_time, "{0} is not a leader in dcs after {1} seconds".format(name, time_limit) @step('I sleep for {value:d} seconds') def sleep_for_n_seconds(context, value): time.sleep(int(value)) def _set_response(context, response): context.status_code = response.status_code data = response.content.decode('utf-8') ct = response.headers.get('content-type', '') if ct.startswith('application/json') or\ ct.startswith('text/yaml') or\ ct.startswith('text/x-yaml') or\ ct.startswith('application/yaml') or\ ct.startswith('application/x-yaml'): try: context.response = yaml.safe_load(data) except ValueError: context.response = data else: context.response = data @step('I issue a GET request to {url:url}') def do_get(context, url): try: r = requests.get(url) except requests.exceptions.RequestException: context.status_code = None context.response = None else: _set_response(context, r) @step('I issue an empty POST request to {url:url}') def do_post_empty(context, url): do_request(context, 'POST', url, None) @step('I issue a {request_method:w} request to {url:url} with {data}') def do_request(context, request_method, url, data): data = data and json.loads(data) or {} headers = {'Authorization': 'Basic ' + base64.b64encode('username:password'.encode('utf-8')).decode('utf-8'), 'Content-Type': 'application/json'} try: if request_method == 'PATCH': r = requests.patch(url, headers=headers, json=data) else: r = requests.post(url, headers=headers, json=data) except requests.exceptions.RequestException: context.status_code = None context.response = None else: _set_response(context, r) @step('I run {cmd}') def do_run(context, cmd): cmd = ['coverage', 'run', '--source=patroni', '-p'] + shlex.split(cmd) try: # XXX: Dirty hack! We need to take name/passwd from the config! env = os.environ.copy() env.update({'PATRONI_RESTAPI_USERNAME': 'username', 'PATRONI_RESTAPI_PASSWORD': 'password'}) response = subprocess.check_output(cmd, stderr=subprocess.STDOUT, env=env) context.status_code = 0 except subprocess.CalledProcessError as e: response = e.output context.status_code = e.returncode context.response = response.decode('utf-8').strip() @then('I receive a response {component:w} {data}') def check_response(context, component, data): if component == 'code': assert context.status_code == int(data),\ "status code {0} != {1}, response: {2}".format(context.status_code, data, context.response) elif component == 'returncode': assert context.status_code == int(data), "return code {0} != {1}, {2}".format(context.status_code, data, context.response) elif component == 'text': assert context.response == data.strip('"'), "response {0} does not contain {1}".format(context.response, data) elif component == 'output': assert data.strip('"') in context.response, "response {0} does not contain {1}".format(context.response, data) else: assert component in context.response, "{0} is not part of the response".format(component) assert str(context.response[component]) == str(data), "{0} does not contain {1}".format(component, data) @step('I issue a scheduled switchover from {from_host:w} to {to_host:w} in {in_seconds:d} seconds') def scheduled_switchover(context, from_host, to_host, in_seconds): context.execute_steps(u""" Given I run patronictl.py switchover batman --master {0} --candidate {1} --scheduled "{2}" --force """.format(from_host, to_host, datetime.now(tzutc) + timedelta(seconds=int(in_seconds)))) @step('I issue a scheduled restart at {url:url} in {in_seconds:d} seconds with {data}') def scheduled_restart(context, url, in_seconds, data): data = data and json.loads(data) or {} data.update(schedule='{0}'.format((datetime.now(tzutc) + timedelta(seconds=int(in_seconds))).isoformat())) context.execute_steps(u"""Given I issue a POST request to {0}/restart with {1}""".format(url, json.dumps(data))) @step('I add tag {tag:w} {value:w} to {pg_name:w} config') def add_tag_to_config(context, tag, value, pg_name): context.pctl.add_tag_to_config(pg_name, tag, value) @then('Response on GET {url} contains {value} after {timeout:d} seconds') def check_http_response(context, url, value, timeout, negate=False): timeout *= context.timeout_multiplier for _ in range(int(timeout)): r = requests.get(url) if (value in r.content.decode('utf-8')) != negate: break time.sleep(1) else: assert False,\ "Value {0} is {1} present in response after {2} seconds".format(value, "not" if not negate else "", timeout) @then('Response on GET {url} does not contain {value} after {timeout:d} seconds') def check_not_in_http_response(context, url, value, timeout): check_http_response(context, url, value, timeout, negate=True) patroni-1.4.2/features/steps/watchdog.py000066400000000000000000000041611323411135200203150ustar00rootroot00000000000000from behave import step, then import time def polling_loop(timeout, interval=1): """Returns an iterator that returns values until timeout has passed. Timeout is measured from start of iteration.""" start_time = time.time() iteration = 0 end_time = start_time + timeout while time.time() < end_time: yield iteration iteration += 1 time.sleep(interval) @step('I start {name:w} with watchdog') def start_patroni_with_watchdog(context, name): return context.pctl.start(name, custom_config={'watchdog': True}) @step('{name:w} watchdog has been pinged after {timeout:d} seconds') def watchdog_was_pinged(context, name, timeout): for _ in polling_loop(timeout): if context.pctl.get_watchdog(name).was_pinged: return True return False @then('{name:w} watchdog has been closed') def watchdog_was_closed(context, name): assert context.pctl.get_watchdog(name).was_closed @step('I reset {name:w} watchdog state') def watchdog_reset_pinged(context, name): context.pctl.get_watchdog(name).reset() @then('{name:w} watchdog is triggered after {timeout:d} seconds') def watchdog_was_triggered(context, name, timeout): for _ in polling_loop(timeout): if context.pctl.get_watchdog(name).was_triggered: return True assert False @then('{name:w} watchdog was not triggered') def watchdog_was_not_triggered(context, name): assert not context.pctl.get_watchdog(name).was_triggered @step('{name:w} checkpoint takes {timeout:d} seconds') def checkpoint_hang(context, name, timeout): assert context.pctl.checkpoint_hang(name, timeout) @step('{name:w} hangs for {timeout:d} seconds') def patroni_hang(context, name, timeout): return context.pctl.patroni_hang(name, timeout) @step('I terminate {name:w} user processes') def terminate_backends(context, name): return context.pctl.terminate_backends(name) @step('Sleep for {timeout:d} seconds') def dcs_connection_lost(context, timeout): time.sleep(timeout) @then('{name:w} database is running') def database_is_running(context, name): assert context.pctl.database_is_running(name) patroni-1.4.2/features/watchdog.feature000066400000000000000000000023151323411135200201610ustar00rootroot00000000000000Feature: watchdog Verify that watchdog gets pinged and triggered under appropriate circumstances. Scenario: watchdog is opened and pinged Given I start postgres0 with watchdog Then postgres0 is a leader after 10 seconds And postgres0 role is the primary after 10 seconds And postgres0 watchdog has been pinged after 10 seconds Scenario: watchdog is disabled during pause Given I run patronictl.py pause batman Then I receive a response returncode 0 When I sleep for 2 seconds Then postgres0 watchdog has been closed Scenario: watchdog is opened and pinged after resume Given I reset postgres0 watchdog state And I run patronictl.py resume batman Then I receive a response returncode 0 And postgres0 watchdog has been pinged after 10 seconds Scenario: watchdog is disabled when shutting down Given I shut down postgres0 Then postgres0 watchdog has been closed Scenario: watchdog is triggered if patroni stops responding Given I reset postgres0 watchdog state And I start postgres0 with watchdog Then postgres0 role is the primary after 10 seconds When postgres0 hangs for 30 seconds Then postgres0 watchdog is triggered after 30 seconds patroni-1.4.2/haproxy.cfg000066400000000000000000000010601323411135200153350ustar00rootroot00000000000000global maxconn 100 defaults log global mode tcp retries 2 timeout client 30m timeout connect 4s timeout server 30m timeout check 5s listen stats mode http bind *:7000 stats enable stats uri / listen batman bind *:5000 option httpchk http-check expect status 200 default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions server postgresql_127.0.0.1_5432 127.0.0.1:5432 maxconn 100 check port 8008 server postgresql_127.0.0.1_5433 127.0.0.1:5433 maxconn 100 check port 8009 patroni-1.4.2/kubernetes/000077500000000000000000000000001323411135200153345ustar00rootroot00000000000000patroni-1.4.2/kubernetes/Dockerfile000066400000000000000000000022661323411135200173340ustar00rootroot00000000000000FROM postgres:9.6 MAINTAINER Alexander Kukushkin RUN export DEBIAN_FRONTEND=noninteractive \ && echo 'APT::Install-Recommends "0";\nAPT::Install-Suggests "0";' > /etc/apt/apt.conf.d/01norecommend \ && apt-get update -y \ && apt-get upgrade -y \ && apt-get install -y git curl jq python-psycopg2 python-yaml python-requests python-six python-pysocks \ python-dateutil python-pip python-prettytable python-wheel python-psutil python locales \ ## Make sure we have a en_US.UTF-8 locale available && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 \ && pip install setuptools pip --upgrade \ && pip install 'git+https://github.com/zalando/patroni.git#egg=patroni[kubernetes]' \ && mkdir -p /home/postgres \ && chown postgres:postgres /home/postgres \ # Clean up && apt-get remove -y git python-pip python-setuptools \ && apt-get autoremove -y \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* /root/.cache ADD entrypoint.sh callback.py / EXPOSE 5432 8008 ENV LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 USER postgres WORKDIR /home/postgres CMD ["/bin/bash", "/entrypoint.sh"] patroni-1.4.2/kubernetes/callback.py000077500000000000000000000036221323411135200174500ustar00rootroot00000000000000#!/usr/bin/env python import logging import os import socket import sys import time from kubernetes import client as k8s_client, config as k8s_config from urllib3.exceptions import HTTPError from six.moves.http_client import HTTPException logger = logging.getLogger(__name__) class CoreV1Api(k8s_client.CoreV1Api): def retry(func): def wrapped(*args, **kwargs): count = 0 while True: try: return func(*args, **kwargs) except (HTTPException, HTTPError, socket.error, socket.timeout): if count >= 10: raise logger.info('Throttling API requests...') time.sleep(2 ** count * 0.5) count += 1 return wrapped @retry def patch_namespaced_endpoints(self, *args, **kwargs): return super(CoreV1Api, self).patch_namespaced_endpoints(*args, **kwargs) def patch_master_endpoint(api, namespace, cluster): addresses = [k8s_client.V1EndpointAddress(ip=os.environ['POD_IP'])] ports = [k8s_client.V1EndpointPort(port=5432)] subsets = [k8s_client.V1EndpointSubset(addresses=addresses, ports=ports)] body = k8s_client.V1Endpoints(subsets=subsets) return api.patch_namespaced_endpoints(cluster, namespace, body) def main(): logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=logging.INFO) if len(sys.argv) != 4 or sys.argv[1] not in ('on_start', 'on_stop', 'on_role_change'): sys.exit('Usage: %s ', sys.argv[0]) action, role, cluster = sys.argv[1:4] k8s_config.load_incluster_config() k8s_api = CoreV1Api() namespace = os.environ['KUBERNETES_NAMESPACE'] if role == 'master' and action in ('on_start', 'on_role_change'): patch_master_endpoint(k8s_api, namespace, cluster) if __name__ == '__main__': main() patroni-1.4.2/kubernetes/entrypoint.sh000077500000000000000000000016221323411135200201070ustar00rootroot00000000000000#!/bin/bash cat > /home/postgres/patroni.yml <<__EOF__ bootstrap: dcs: postgresql: use_pg_rewind: true initdb: - auth-host: md5 - auth-local: trust - encoding: UTF8 - locale: en_US.UTF-8 - data-checksums pg_hba: - host all all 0.0.0.0/0 md5 - host replication ${PATRONI_REPLICATION_USERNAME} ${POD_IP}/16 md5 restapi: connect_address: '${POD_IP}:8008' postgresql: connect_address: '${POD_IP}:5432' authentication: superuser: password: '${PATRONI_SUPERUSER_PASSWORD}' replication: password: '${PATRONI_REPLICATION_PASSWORD}' callbacks: on_start: /callback.py on_stop: /callback.py on_role_change: /callback.py __EOF__ unset PATRONI_SUPERUSER_PASSWORD PATRONI_REPLICATION_PASSWORD export KUBERNETES_NAMESPACE=$PATRONI_KUBERNETES_NAMESPACE export POD_NAME=$PATRONI_NAME exec /usr/bin/python /usr/local/bin/patroni /home/postgres/patroni.yml patroni-1.4.2/kubernetes/patroni_k8s.yaml000066400000000000000000000074121323411135200204650ustar00rootroot00000000000000apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: &cluster_name patronidemo labels: application: patroni cluster-name: *cluster_name spec: replicas: 3 serviceName: *cluster_name template: metadata: labels: application: patroni cluster-name: *cluster_name spec: serviceAccountName: patronidemo containers: - name: *cluster_name image: patroni # docker build -t patroni . imagePullPolicy: IfNotPresent ports: - containerPort: 8008 protocol: TCP - containerPort: 5432 protocol: TCP volumeMounts: - mountPath: /home/postgres/pgdata name: pgdata env: - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: PATRONI_KUBERNETES_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: PATRONI_KUBERNETES_LABELS value: '{application: patroni, cluster-name: patronidemo}' - name: PATRONI_SUPERUSER_USERNAME value: postgres - name: PATRONI_SUPERUSER_PASSWORD valueFrom: secretKeyRef: name: *cluster_name key: superuser-password - name: PATRONI_REPLICATION_USERNAME value: standby - name: PATRONI_REPLICATION_PASSWORD valueFrom: secretKeyRef: name: *cluster_name key: replication-password - name: PATRONI_SCOPE value: *cluster_name - name: PATRONI_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: PATRONI_POSTGRESQL_DATA_DIR value: /home/postgres/pgdata/pgroot/data - name: PATRONI_POSTGRESQL_PGPASS value: /tmp/pgpass - name: PATRONI_POSTGRESQL_LISTEN value: '0.0.0.0:5432' - name: PATRONI_RESTAPI_LISTEN value: '0.0.0.0:8008' terminationGracePeriodSeconds: 0 volumes: - name: pgdata emptyDir: {} # volumeClaimTemplates: # - metadata: # labels: # application: spilo # spilo-cluster: *cluster_name # annotations: # volume.alpha.kubernetes.io/storage-class: anything # name: pgdata # spec: # accessModes: # - ReadWriteOnce # resources: # requests: # storage: 5Gi --- apiVersion: v1 kind: Endpoints metadata: name: &cluster_name patronidemo labels: application: patroni cluster-name: *cluster_name subsets: [] --- apiVersion: v1 kind: Service metadata: name: &cluster_name patronidemo labels: application: patroni cluster-name: *cluster_name spec: type: ClusterIP ports: - port: 5432 targetPort: 5432 --- apiVersion: v1 kind: Secret metadata: name: &cluster_name patronidemo labels: application: patroni cluster-name: *cluster_name type: Opaque data: superuser-password: emFsYW5kbw== replication-password: cmVwLXBhc3M= --- apiVersion: v1 kind: ServiceAccount metadata: name: patronidemo --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: patronidemo rules: - apiGroups: - "" resources: - configmaps verbs: - create - get - list - patch - update - watch - apiGroups: - "" resources: - endpoints verbs: - get - patch - update # the following three privileges are necessary only when using endpoints - create - list - watch - apiGroups: - "" resources: - pods verbs: - get - list - patch - update - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: patronidemo roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: patronidemo subjects: - kind: ServiceAccount name: patronidemo patroni-1.4.2/mkbinary.sh000077500000000000000000000001711323411135200153370ustar00rootroot00000000000000#!/bin/sh set -e pip install --ignore-installed setuptools==19.2 pyinstaller pyinstaller --clean --onefile patroni.spec patroni-1.4.2/patroni.py000077500000000000000000000001271323411135200152160ustar00rootroot00000000000000#!/usr/bin/env python from patroni import main if __name__ == '__main__': main() patroni-1.4.2/patroni.spec000066400000000000000000000014471323411135200155230ustar00rootroot00000000000000# -*- mode: python -*- block_cipher = None def hiddenimports(): import sys sys.path.insert(0, '.') try: import patroni.dcs return patroni.dcs.dcs_modules() finally: sys.path.pop(0) a = Analysis(['patroni/__main__.py'], pathex=[], binaries=None, datas=None, hiddenimports=hiddenimports(), hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, name='patroni', debug=False, strip=False, upx=True, console=True) patroni-1.4.2/patroni/000077500000000000000000000000001323411135200146415ustar00rootroot00000000000000patroni-1.4.2/patroni/__init__.py000066400000000000000000000146231323411135200167600ustar00rootroot00000000000000import logging import os import signal import sys import time logger = logging.getLogger(__name__) class Patroni(object): def __init__(self): from patroni.api import RestApiServer from patroni.config import Config from patroni.dcs import get_dcs from patroni.ha import Ha from patroni.postgresql import Postgresql from patroni.version import __version__ from patroni.watchdog import Watchdog self.setup_signal_handlers() self.version = __version__ self.config = Config() self.dcs = get_dcs(self.config) self.watchdog = Watchdog(self.config) self.load_dynamic_configuration() self.postgresql = Postgresql(self.config['postgresql']) self.api = RestApiServer(self, self.config['restapi']) self.ha = Ha(self) self.tags = self.get_tags() self.next_run = time.time() self.scheduled_restart = {} def load_dynamic_configuration(self): from patroni.exceptions import DCSError while True: try: cluster = self.dcs.get_cluster() if cluster and cluster.config and cluster.config.data: if self.config.set_dynamic_configuration(cluster.config): self.dcs.reload_config(self.config) self.watchdog.reload_config(self.config) elif not self.config.dynamic_configuration and 'bootstrap' in self.config: if self.config.set_dynamic_configuration(self.config['bootstrap']['dcs']): self.dcs.reload_config(self.config) break except DCSError: logger.warning('Can not get cluster from dcs') def get_tags(self): return {tag: value for tag, value in self.config.get('tags', {}).items() if tag not in ('clonefrom', 'nofailover', 'noloadbalance', 'nosync') or value} @property def nofailover(self): return bool(self.tags.get('nofailover', False)) @property def nosync(self): return bool(self.tags.get('nosync', False)) def reload_config(self): try: self.tags = self.get_tags() self.dcs.reload_config(self.config) self.watchdog.reload_config(self.config) self.api.reload_config(self.config['restapi']) self.postgresql.reload_config(self.config['postgresql']) except Exception: logger.exception('Failed to reload config_file=%s', self.config.config_file) @property def replicatefrom(self): return self.tags.get('replicatefrom') def sighup_handler(self, *args): self._received_sighup = True def sigterm_handler(self, *args): if not self._received_sigterm: self._received_sigterm = True sys.exit() @property def noloadbalance(self): return bool(self.tags.get('noloadbalance', False)) def schedule_next_run(self): self.next_run += self.dcs.loop_wait current_time = time.time() nap_time = self.next_run - current_time if nap_time <= 0: self.next_run = current_time # Release the GIL so we don't starve anyone waiting on async_executor lock time.sleep(0.001) # Warn user that Patroni is not keeping up logger.warning("Loop time exceeded, rescheduling immediately.") elif self.ha.watch(nap_time): self.next_run = time.time() def run(self): self.api.start() self.next_run = time.time() while not self._received_sigterm: if self._received_sighup: self._received_sighup = False if self.config.reload_local_configuration(): self.reload_config() logger.info(self.ha.run_cycle()) if self.dcs.cluster and self.dcs.cluster.config and self.dcs.cluster.config.data \ and self.config.set_dynamic_configuration(self.dcs.cluster.config): self.reload_config() if self.postgresql.role != 'uninitialized': self.config.save_cache() self.schedule_next_run() def setup_signal_handlers(self): self._received_sighup = False self._received_sigterm = False signal.signal(signal.SIGHUP, self.sighup_handler) signal.signal(signal.SIGTERM, self.sigterm_handler) def shutdown(self): self.api.shutdown() self.ha.shutdown() def patroni_main(): logformat = os.environ.get('PATRONI_LOGFORMAT', '%(asctime)s %(levelname)s: %(message)s') logging.basicConfig(format=logformat, level=logging.INFO) logging.getLogger('requests').setLevel(logging.WARNING) patroni = Patroni() try: patroni.run() except KeyboardInterrupt: pass finally: patroni.shutdown() def pg_ctl_start(args): import subprocess postmaster = subprocess.Popen(args) print(postmaster.pid) def call_self(args, **kwargs): """This function executes Patroni once again with provided arguments. :args: list of arguments to call Patroni with. :returns: `Popen` object""" exe = [sys.executable] if not getattr(sys, 'frozen', False): # Binary distribution? exe.append(sys.argv[0]) import subprocess return subprocess.Popen(exe + args, **kwargs) def main(): if os.getpid() != 1: if len(sys.argv) > 5 and sys.argv[1] == 'pg_ctl_start': return pg_ctl_start(sys.argv[2:]) return patroni_main() pid = 0 # Looks like we are in a docker, so we will act like init def sigchld_handler(signo, stack_frame): try: while True: ret = os.waitpid(-1, os.WNOHANG) if ret == (0, 0): break elif ret[0] != pid: logging.info('Reaped pid=%s, exit status=%s', *ret) except OSError: pass def passtochild(signo, stack_frame): if pid: os.kill(pid, signo) signal.signal(signal.SIGCHLD, sigchld_handler) signal.signal(signal.SIGHUP, passtochild) signal.signal(signal.SIGINT, passtochild) signal.signal(signal.SIGUSR1, passtochild) signal.signal(signal.SIGUSR2, passtochild) signal.signal(signal.SIGQUIT, passtochild) signal.signal(signal.SIGTERM, passtochild) patroni = call_self(sys.argv[1:]) pid = patroni.pid patroni.wait() patroni-1.4.2/patroni/__main__.py000066400000000000000000000001011323411135200167230ustar00rootroot00000000000000from patroni import main if __name__ == '__main__': main() patroni-1.4.2/patroni/api.py000066400000000000000000000560371323411135200157770ustar00rootroot00000000000000import base64 import fcntl import json import logging import psycopg2 import time import dateutil.parser import datetime from patroni.postgresql import PostgresConnectionException, PostgresException, Postgresql from patroni.utils import deep_compare, parse_bool, patch_config, Retry, RetryFailedError, parse_int, tzutc from six.moves.BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from six.moves.socketserver import ThreadingMixIn from threading import Thread logger = logging.getLogger(__name__) def check_auth(func): """Decorator function to check authorization header. Usage example: @check_auth def do_PUT_foo(): pass """ def wrapper(handler, *args, **kwargs): if handler.check_auth_header(): return func(handler, *args, **kwargs) return wrapper class RestApiHandler(BaseHTTPRequestHandler): def _write_response(self, status_code, body, content_type='text/html', headers=None): self.send_response(status_code) headers = headers or {} if content_type: headers['Content-Type'] = content_type for name, value in headers.items(): self.send_header(name, value) self.end_headers() self.wfile.write(body.encode('utf-8')) def _write_json_response(self, status_code, response): self._write_response(status_code, json.dumps(response), content_type='application/json') def send_auth_request(self, body): headers = {'WWW-Authenticate': 'Basic realm="' + self.server.patroni.__class__.__name__ + '"'} self._write_response(401, body, headers=headers) def check_auth_header(self): auth_header = self.headers.get('Authorization') status = self.server.check_auth_header(auth_header) return not status or self.send_auth_request(status) def _write_status_response(self, status_code, response): patroni = self.server.patroni tags = patroni.ha.get_effective_tags() if tags: response['tags'] = tags if patroni.postgresql.sysid: response['database_system_identifier'] = patroni.postgresql.sysid if patroni.postgresql.pending_restart: response['pending_restart'] = True response['patroni'] = {'version': patroni.version, 'scope': patroni.postgresql.scope} if patroni.scheduled_restart and isinstance(patroni.scheduled_restart, dict): response['scheduled_restart'] = patroni.scheduled_restart.copy() del response['scheduled_restart']['postmaster_start_time'] response['scheduled_restart']['schedule'] = (response['scheduled_restart']['schedule']).isoformat() if not patroni.ha.watchdog.is_healthy: response['watchdog_failed'] = True if patroni.ha.is_paused(): response['pause'] = True self._write_json_response(status_code, response) def do_GET(self, write_status_code_only=False): """Default method for processing all GET requests which can not be routed to other methods""" path = '/master' if self.path == '/' else self.path response = self.get_postgresql_status() patroni = self.server.patroni cluster = patroni.dcs.cluster def is_synchronous(): return (cluster.is_synchronous_mode() and cluster.sync and cluster.sync.sync_standby == patroni.postgresql.name) def is_balanceable_replica(): return response.get('role') == 'replica' and not patroni.noloadbalance if cluster: # dcs available if cluster.leader and cluster.leader.name == patroni.postgresql.name: # is_leader status_code = 200 if 'master' in path else 503 elif 'role' not in response: status_code = 503 elif response['role'] == 'master': # running as master but without leader lock!!!! status_code = 503 elif path in ('/sync', '/synchronous'): status_code = 200 if is_balanceable_replica() and is_synchronous() else 503 elif path in ('/async', '/asynchronous'): status_code = 200 if is_balanceable_replica() and not is_synchronous() else 503 elif response['role'] in path: # response['role'] != 'master' status_code = 503 if patroni.noloadbalance else 200 else: status_code = 503 elif 'role' in response and response['role'] in path: status_code = 503 if response['role'] != 'master' and patroni.noloadbalance else 200 elif patroni.ha.restart_scheduled() and patroni.postgresql.role == 'master' and 'master' in path: # exceptional case for master node when the postgres is being restarted via API status_code = 200 else: status_code = 503 if write_status_code_only: # when haproxy sends OPTIONS request it reads only status code and nothing more message = self.responses[status_code][0] self.wfile.write('{0} {1} {2}\r\n'.format(self.protocol_version, status_code, message).encode('utf-8')) else: self._write_status_response(status_code, response) def do_OPTIONS(self): self.do_GET(write_status_code_only=True) def do_GET_patroni(self): response = self.get_postgresql_status(True) self._write_status_response(200, response) def do_GET_config(self): cluster = self.server.patroni.dcs.cluster or self.server.patroni.dcs.get_cluster() if cluster.config: self._write_json_response(200, cluster.config.data) else: self.send_error(502) def _read_json_content(self, body_is_optional=False): if 'content-length' not in self.headers: return self.send_error(411) if not body_is_optional else {} try: content_length = int(self.headers.get('content-length')) if content_length == 0 and body_is_optional: return {} request = json.loads(self.rfile.read(content_length).decode('utf-8')) if isinstance(request, dict) and (request or body_is_optional): return request except Exception: logger.exception('Bad request') self.send_error(400) @check_auth def do_PATCH_config(self): request = self._read_json_content() if request: cluster = self.server.patroni.dcs.get_cluster() data = cluster.config.data.copy() if patch_config(data, request): value = json.dumps(data, separators=(',', ':')) if not self.server.patroni.dcs.set_config_value(value, cluster.config.index): return self.send_error(409) self.server.patroni.ha.wakeup() self._write_json_response(200, data) @check_auth def do_PUT_config(self): request = self._read_json_content() if request: cluster = self.server.patroni.dcs.get_cluster() if not deep_compare(request, cluster.config.data): value = json.dumps(request, separators=(',', ':')) if not self.server.patroni.dcs.set_config_value(value): return self.send_error(502) self._write_json_response(200, request) @check_auth def do_POST_reload(self): try: if self.server.patroni.config.reload_local_configuration(True): status_code = 202 response = 'reload scheduled' self.server.patroni.sighup_handler() else: status_code = 200 response = 'nothing changed' except Exception as e: status_code = 500 response = str(e) self._write_response(status_code, response) @staticmethod def parse_schedule(schedule, action): """ parses the given schedule and validates at """ error = None scheduled_at = None try: scheduled_at = dateutil.parser.parse(schedule) if scheduled_at.tzinfo is None: error = 'Timezone information is mandatory for the scheduled {0}'.format(action) status_code = 400 elif scheduled_at < datetime.datetime.now(tzutc): error = 'Cannot schedule {0} in the past'.format(action) status_code = 422 else: status_code = None except (ValueError, TypeError): logger.exception('Invalid scheduled %s time: %s', action, schedule) error = 'Unable to parse scheduled timestamp. It should be in an unambiguous format, e.g. ISO 8601' status_code = 422 return (status_code, error, scheduled_at) @check_auth def do_POST_restart(self): status_code = 500 data = 'restart failed' request = self._read_json_content(body_is_optional=True) cluster = self.server.patroni.dcs.get_cluster() if request is None: # failed to parse the json return if request: logger.debug("received restart request: {0}".format(request)) if cluster.is_paused() and 'schedule' in request: self._write_response(status_code, "Can't schedule restart in the paused state") return for k in request: if k == 'schedule': (_, data, request[k]) = self.parse_schedule(request[k], "restart") if _: status_code = _ break elif k == 'role': if request[k] not in ('master', 'replica'): status_code = 400 data = "PostgreSQL role should be either master or replica" break elif k == 'postgres_version': try: Postgresql.postgres_version_to_int(request[k]) except PostgresException as e: status_code = 400 data = e.value break elif k == 'timeout': request[k] = parse_int(request[k], 's') if request[k] is None or request[k] <= 0: status_code = 400 data = "Timeout should be a positive number of seconds" break elif k != 'restart_pending': status_code = 400 data = "Unknown filter for the scheduled restart: {0}".format(k) break else: if 'schedule' not in request: try: status, data = self.server.patroni.ha.restart(request) status_code = 200 if status else 503 except Exception: logger.exception('Exception during restart') status_code = 400 else: if self.server.patroni.ha.schedule_future_restart(request): data = "Restart scheduled" status_code = 202 else: data = "Another restart is already scheduled" status_code = 409 self._write_response(status_code, data) @check_auth def do_DELETE_restart(self): if self.server.patroni.ha.delete_future_restart(): data = "scheduled restart deleted" code = 200 else: data = "no restarts are scheduled" code = 404 self._write_response(code, data) @check_auth def do_POST_reinitialize(self): request = self._read_json_content(body_is_optional=True) if request: logger.debug('received reinitialize request: %s', request) force = isinstance(request, dict) and parse_bool(request.get('force')) or False data = self.server.patroni.ha.reinitialize(force) if data is None: status_code = 200 data = 'reinitialize started' else: status_code = 503 self._write_response(status_code, data) def poll_failover_result(self, leader, candidate): timeout = max(10, self.server.patroni.dcs.loop_wait) for _ in range(0, timeout*2): time.sleep(1) try: cluster = self.server.patroni.dcs.get_cluster() if cluster.leader and cluster.leader.name != leader: if not candidate or candidate == cluster.leader.name: return 200, 'Successfully failed over to "{0}"'.format(cluster.leader.name) else: return 200, 'Failed over to "{0}" instead of "{1}"'.format(cluster.leader.name, candidate) if not cluster.failover: return 503, 'Failover failed' except Exception as e: logger.debug('Exception occured during polling failover result: %s', e) return 503, 'Failover status unknown' def is_failover_possible(self, cluster, leader, candidate, action): if leader and (not cluster.leader or cluster.leader.name != leader): return 'leader name does not match' if candidate: if action == 'switchover' and cluster.is_synchronous_mode() and cluster.sync.sync_standby != candidate: return 'candidate name does not match with sync_standby' members = [m for m in cluster.members if m.name == candidate] if not members: return 'candidate does not exists' elif cluster.is_synchronous_mode(): members = [m for m in cluster.members if m.name == cluster.sync.sync_standby] if not members: return action + ' is not possible: can not find sync_standby' else: members = [m for m in cluster.members if m.name != cluster.leader.name and m.api_url] if not members: return action + ' is not possible: cluster does not have members except leader' for st in self.server.patroni.ha.fetch_nodes_statuses(members): if st.failover_limitation() is None: return None return action + ' is not possible: no good candidates have been found' @check_auth def do_POST_failover(self, action='failover'): request = self._read_json_content() (status_code, data) = (400, '') if not request: return leader = request.get('leader') candidate = request.get('candidate') or request.get('member') scheduled_at = request.get('scheduled_at') cluster = self.server.patroni.dcs.get_cluster() logger.info("received %s request with leader=%s candidate=%s scheduled_at=%s", action, leader, candidate, scheduled_at) if action == 'failover' and not candidate: data = 'Failover could be performed only to a specific candidate' elif action == 'switchover' and not leader: data = 'Switchover could be performed only from a specific leader' if not data and scheduled_at: if not leader: data = 'Scheduled {0} is possible only from a specific leader'.format(action) if not data and cluster.is_paused(): data = "Can't schedule {0} in the paused state".format(action) if not data: (status_code, data, scheduled_at) = self.parse_schedule(scheduled_at, action) if not data and cluster.is_paused() and not candidate: data = action.title() + ' is possible only to a specific candidate in a paused state' if not data and not scheduled_at: data = self.is_failover_possible(cluster, leader, candidate, action) if data: status_code = 412 if not data: if self.server.patroni.dcs.manual_failover(leader, candidate, scheduled_at=scheduled_at): self.server.patroni.ha.wakeup() if scheduled_at: data = action.title() + ' scheduled' status_code = 202 else: status_code, data = self.poll_failover_result(cluster.leader and cluster.leader.name, candidate) else: data = 'failed to write {0} key into DCS'.format(action) status_code = 503 self._write_response(status_code, data) def do_POST_switchover(self): self.do_POST_failover(action='switchover') def parse_request(self): """Override parse_request method to enrich basic functionality of `BaseHTTPRequestHandler` class Original class can only invoke do_GET, do_POST, do_PUT, etc method implementations if they are defined. But we would like to have at least some simple routing mechanism, i.e.: GET /uri1/part2 request should invoke `do_GET_uri1()` POST /other should invoke `do_POST_other()` If the `do__` method does not exists we'll fallback to original behavior.""" ret = BaseHTTPRequestHandler.parse_request(self) if ret: mname = self.path.lstrip('/').split('/')[0] mname = self.command + ('_' + mname if mname else '') if hasattr(self, 'do_' + mname): self.command = mname return ret def query(self, sql, *params, **kwargs): if not kwargs.get('retry', False): return self.server.query(sql, *params) retry = Retry(delay=1, retry_exceptions=PostgresConnectionException) return retry(self.server.query, sql, *params) def get_postgresql_status(self, retry=False): try: if self.server.patroni.postgresql.state not in ('running', 'restarting', 'starting'): raise RetryFailedError('') stmt = ("WITH replication_info AS (" "SELECT usename, application_name, client_addr, state, sync_state, sync_priority" " FROM pg_stat_replication) SELECT" " to_char(pg_postmaster_start_time(), 'YYYY-MM-DD HH24:MI:SS.MS TZ')," " CASE WHEN pg_is_in_recovery() THEN 0" " ELSE ('x' || SUBSTR(pg_{0}file_name(pg_current_{0}_{1}()), 1, 8))::bit(32)::int END," " CASE WHEN pg_is_in_recovery() THEN 0" " ELSE pg_{0}_{1}_diff(pg_current_{0}_{1}(), '0/0')::bigint END," " pg_{0}_{1}_diff(COALESCE(pg_last_{0}_receive_{1}(), pg_last_{0}_replay_{1}()), '0/0')::bigint," " pg_{0}_{1}_diff(pg_last_{0}_replay_{1}(), '0/0')::bigint," " to_char(pg_last_xact_replay_timestamp(), 'YYYY-MM-DD HH24:MI:SS.MS TZ')," " pg_is_in_recovery() AND pg_is_{0}_replay_paused()," " (SELECT array_to_json(array_agg(row_to_json(ri))) FROM replication_info ri)") row = self.query(stmt.format(self.server.patroni.postgresql.wal_name, self.server.patroni.postgresql.lsn_name), retry=retry)[0] result = { 'state': self.server.patroni.postgresql.state, 'postmaster_start_time': row[0], 'role': 'replica' if row[1] == 0 else 'master', 'server_version': self.server.patroni.postgresql.server_version, 'xlog': ({ 'received_location': row[3], 'replayed_location': row[4], 'replayed_timestamp': row[5], 'paused': row[6]} if row[1] == 0 else { 'location': row[2] }) } if row[1] > 0: result['timeline'] = row[1] else: cluster = self.server.patroni.dcs.cluster leader_timeline = None if not cluster or cluster.is_unlocked() else cluster.leader.timeline result['timeline'] = self.server.patroni.postgresql.replica_cached_timeline(leader_timeline) if row[7]: result['replication'] = row[7] return result except (psycopg2.Error, RetryFailedError, PostgresConnectionException): state = self.server.patroni.postgresql.state if state == 'running': logger.exception('get_postgresql_status') state = 'unknown' return {'state': state, 'role': self.server.patroni.postgresql.role} def log_message(self, fmt, *args): logger.debug("API thread: %s - - [%s] %s", self.client_address[0], self.log_date_time_string(), fmt % args) class RestApiServer(ThreadingMixIn, HTTPServer, Thread): def __init__(self, patroni, config): self.patroni = patroni self.__initialize(config) self.__set_config_parameters(config) self.daemon = True def query(self, sql, *params): cursor = None try: with self.patroni.postgresql.connection().cursor() as cursor: cursor.execute(sql, params) return [r for r in cursor] except psycopg2.Error as e: if cursor and cursor.connection.closed == 0: raise e raise PostgresConnectionException('connection problems') @staticmethod def _set_fd_cloexec(fd): flags = fcntl.fcntl(fd, fcntl.F_GETFD) fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC) def check_basic_auth_key(self, key): return self.__auth_key == key def check_auth_header(self, auth_header): if self.__auth_key: if auth_header is None: return 'no auth header received' if not auth_header.startswith('Basic ') or not self.check_basic_auth_key(auth_header[6:]): return 'not authenticated' @staticmethod def __get_ssl_options(config): return {option: config[option] for option in ['certfile', 'keyfile'] if option in config} def __set_connection_string(self, connect_address): self.connection_string = '{0}://{1}/patroni'.format(self.__protocol, connect_address or self.__listen) def __set_config_parameters(self, config): self.__auth_key = base64.b64encode(config['auth'].encode('utf-8')).decode('utf-8') if 'auth' in config else None self.__set_connection_string(config.get('connect_address')) def __initialize(self, config): self.__ssl_options = self.__get_ssl_options(config) self.__listen = config['listen'] host, port = config['listen'].rsplit(':', 1) HTTPServer.__init__(self, (host, int(port)), RestApiHandler) Thread.__init__(self, target=self.serve_forever) self._set_fd_cloexec(self.socket) self.__protocol = 'http' # wrap socket with ssl if 'certfile' is defined in a config.yaml # Sometime it's also needed to pass reference to a 'keyfile'. if self.__ssl_options.get('certfile'): import ssl self.socket = ssl.wrap_socket(self.socket, server_side=True, **self.__ssl_options) self.__protocol = 'https' self.__set_connection_string(config.get('connect_address')) def reload_config(self, config): self.__set_config_parameters(config) if self.__listen != config['listen'] or self.__ssl_options != self.__get_ssl_options(config): self.shutdown() self.__initialize(config) self.start() patroni-1.4.2/patroni/async_executor.py000066400000000000000000000105071323411135200202510ustar00rootroot00000000000000import logging from threading import Event, Lock, RLock, Thread logger = logging.getLogger(__name__) class CriticalTask(object): """Represents a critical task in a background process that we either need to cancel or get the result of. Fields of this object may be accessed only when holding a lock on it. To perform the critical task the background thread must, while holding lock on this object, check `is_cancelled` flag, run the task and mark the task as complete using `complete()`. The main thread must hold async lock to prevent the task from completing, hold lock on critical task object, call cancel. If the task has completed `cancel()` will return False and `result` field will contain the result of the task. When cancel returns True it is guaranteed that the background task will notice the `is_cancelled` flag. """ def __init__(self): self._lock = Lock() self.is_cancelled = False self.result = None def reset(self): """Must be called every time the background task is finished. Must be called from async thread. Caller must hold lock on async executor when calling.""" self.is_cancelled = False self.result = None def cancel(self): """Tries to cancel the task, returns True if the task has already run. Caller must hold lock on async executor and the task when calling.""" if self.result is not None: return False self.is_cancelled = True return True def complete(self, result): """Mark task as completed along with a result. Must be called from async thread. Caller must hold lock on task when calling.""" self.result = result def __enter__(self): self._lock.acquire() return self def __exit__(self, exc_type, exc_val, exc_tb): self._lock.release() class AsyncExecutor(object): def __init__(self, state_handler, ha_wakeup): self.state_handler = state_handler self._ha_wakeup = ha_wakeup self._thread_lock = RLock() self._scheduled_action = None self._scheduled_action_lock = RLock() self._is_cancelled = False self._finish_event = Event() self.critical_task = CriticalTask() @property def busy(self): return self.scheduled_action is not None def schedule(self, action): with self._scheduled_action_lock: if self._scheduled_action is not None: return self._scheduled_action self._scheduled_action = action self._is_cancelled = False self._finish_event.set() return None @property def scheduled_action(self): with self._scheduled_action_lock: return self._scheduled_action def reset_scheduled_action(self): with self._scheduled_action_lock: self._scheduled_action = None def run(self, func, args=()): wakeup = False try: with self: if self._is_cancelled: return self._finish_event.clear() self.state_handler.reset_is_cancelled() # if the func returned something (not None) - wake up main HA loop wakeup = func(*args) if args else func() return wakeup except Exception: logger.exception('Exception during execution of long running task %s', self.scheduled_action) finally: with self: self.reset_scheduled_action() self._finish_event.set() with self.critical_task: self.critical_task.reset() if wakeup is not None: self._ha_wakeup() def run_async(self, func, args=()): Thread(target=self.run, args=(func, args)).start() def cancel(self): with self: with self._scheduled_action_lock: if self._scheduled_action is None: return logger.warning('Cancelling long running task %s', self._scheduled_action) self._is_cancelled = True self.state_handler.cancel() self._finish_event.wait() with self: self.reset_scheduled_action() def __enter__(self): self._thread_lock.acquire() def __exit__(self, *args): self._thread_lock.release() patroni-1.4.2/patroni/callback_executor.py000066400000000000000000000021521323411135200206650ustar00rootroot00000000000000import logging import subprocess from threading import Event, Lock, Thread logger = logging.getLogger(__name__) class CallbackExecutor(Thread): def __init__(self): super(CallbackExecutor, self).__init__() self.daemon = True self._lock = Lock() self._cmd = None self._process = None self._callback_event = Event() self.start() def call(self, cmd): with self._lock: if self._process and self._process.poll() is None: self._process.kill() logger.warning('Killed the old callback process because it was still running: %s', self._cmd) self._cmd = cmd self._callback_event.set() def run(self): while True: self._callback_event.wait() self._callback_event.clear() with self._lock: try: self._process = subprocess.Popen(self._cmd, close_fds=True) except Exception: logger.exception('Failed to execute %s', self._cmd) continue self._process.wait() patroni-1.4.2/patroni/config.py000066400000000000000000000343551323411135200164720ustar00rootroot00000000000000import json import logging import os import sys import tempfile import yaml from collections import defaultdict from copy import deepcopy from patroni.dcs import ClusterConfig from patroni.postgresql import Postgresql from patroni.utils import deep_compare, parse_int, patch_config logger = logging.getLogger(__name__) class Config(object): """ This class is responsible for: 1) Building and giving access to `effective_configuration` from: * `Config.__DEFAULT_CONFIG` -- some sane default values * `dynamic_configuration` -- configuration stored in DCS * `local_configuration` -- configuration from `config.yml` or environment 2) Saving and loading `dynamic_configuration` into 'patroni.dynamic.json' file located in local_configuration['postgresql']['data_dir'] directory. This is necessary to be able to restore `dynamic_configuration` if DCS was accidentally wiped 3) Loading of configuration file in the old format and converting it into new format 4) Mimicking some of the `dict` interfaces to make it possible to work with it as with the old `config` object. """ PATRONI_ENV_PREFIX = 'PATRONI_' PATRONI_CONFIG_VARIABLE = PATRONI_ENV_PREFIX + 'CONFIGURATION' __CACHE_FILENAME = 'patroni.dynamic.json' __DEFAULT_CONFIG = { 'ttl': 30, 'loop_wait': 10, 'retry_timeout': 10, 'maximum_lag_on_failover': 1048576, 'master_start_timeout': 300, 'synchronous_mode': False, 'synchronous_mode_strict': False, 'postgresql': { 'bin_dir': '', 'use_slots': True, 'parameters': {p: v[0] for p, v in Postgresql.CMDLINE_OPTIONS.items()} }, 'watchdog': { 'mode': 'automatic', } } def __init__(self): self._modify_index = -1 self._dynamic_configuration = {} self.__environment_configuration = self._build_environment_configuration() # Patroni reads the configuration from the command-line argument if it exists, otherwise from the environment self._config_file = len(sys.argv) >= 2 and os.path.isfile(sys.argv[1]) and sys.argv[1] if self._config_file: self._local_configuration = self._load_config_file() else: config_env = os.environ.pop(self.PATRONI_CONFIG_VARIABLE, None) self._local_configuration = config_env and yaml.safe_load(config_env) or self.__environment_configuration if not self._local_configuration: print('Usage: {0} config.yml'.format(sys.argv[0])) print('\tPatroni may also read the configuration from the {0} environment variable'. format(self.PATRONI_CONFIG_VARIABLE)) sys.exit(1) self.__effective_configuration = self._build_effective_configuration({}, self._local_configuration) self._data_dir = self.__effective_configuration['postgresql']['data_dir'] self._cache_file = os.path.join(self._data_dir, self.__CACHE_FILENAME) self._load_cache() self._cache_needs_saving = False @property def config_file(self): return self._config_file @property def dynamic_configuration(self): return deepcopy(self._dynamic_configuration) def _load_config_file(self): """Loads config.yaml from filesystem and applies some values which were set via ENV""" with open(self._config_file) as f: config = yaml.safe_load(f) patch_config(config, self.__environment_configuration) return config def _load_cache(self): if os.path.isfile(self._cache_file): try: with open(self._cache_file) as f: self.set_dynamic_configuration(json.load(f)) except Exception: logger.exception('Exception when loading file: %s', self._cache_file) def save_cache(self): if self._cache_needs_saving: tmpfile = fd = None try: (fd, tmpfile) = tempfile.mkstemp(prefix=self.__CACHE_FILENAME, dir=self._data_dir) with os.fdopen(fd, 'w') as f: fd = None json.dump(self.dynamic_configuration, f) tmpfile = os.rename(tmpfile, self._cache_file) self._cache_needs_saving = False except Exception: logger.exception('Exception when saving file: %s', self._cache_file) if fd: try: os.close(fd) except Exception: logger.error('Can not close temporary file %s', tmpfile) if tmpfile and os.path.exists(tmpfile): try: os.remove(tmpfile) except Exception: logger.error('Can not remove temporary file %s', tmpfile) # configuration could be either ClusterConfig or dict def set_dynamic_configuration(self, configuration): if isinstance(configuration, ClusterConfig): if self._modify_index == configuration.modify_index: return False # If the index didn't changed there is nothing to do self._modify_index = configuration.modify_index configuration = configuration.data if not deep_compare(self._dynamic_configuration, configuration): try: self.__effective_configuration = self._build_effective_configuration(configuration, self._local_configuration) self._dynamic_configuration = configuration self._cache_needs_saving = True return True except Exception: logger.exception('Exception when setting dynamic_configuration') def reload_local_configuration(self, dry_run=False): if self.config_file: try: configuration = self._load_config_file() if not deep_compare(self._local_configuration, configuration): new_configuration = self._build_effective_configuration(self._dynamic_configuration, configuration) if dry_run: return not deep_compare(new_configuration, self.__effective_configuration) self._local_configuration = configuration self.__effective_configuration = new_configuration return True except Exception: logger.exception('Exception when reloading local configuration from %s', self.config_file) if dry_run: raise @staticmethod def _process_postgresql_parameters(parameters, is_local=False): ret = {} for name, value in (parameters or {}).items(): if name not in Postgresql.CMDLINE_OPTIONS or not is_local and Postgresql.CMDLINE_OPTIONS[name][1](value): ret[name] = value return ret def _safe_copy_dynamic_configuration(self, dynamic_configuration): config = deepcopy(self.__DEFAULT_CONFIG) for name, value in dynamic_configuration.items(): if name == 'postgresql': for name, value in (value or {}).items(): if name == 'parameters': config['postgresql'][name].update(self._process_postgresql_parameters(value)) elif name not in ('connect_address', 'listen', 'data_dir', 'pgpass', 'authentication'): config['postgresql'][name] = deepcopy(value) elif name in config: # only variables present in __DEFAULT_CONFIG allowed to be overriden from DCS if name in ('synchronous_mode', 'synchronous_mode_strict'): config[name] = value else: config[name] = int(value) return config @staticmethod def _build_environment_configuration(): ret = defaultdict(dict) def _popenv(name): return os.environ.pop(Config.PATRONI_ENV_PREFIX + name.upper(), None) for param in ('name', 'namespace', 'scope'): value = _popenv(param) if value: ret[param] = value def _set_section_values(section, params): for param in params: value = _popenv(section + '_' + param) if value: ret[section][param] = value _set_section_values('restapi', ['listen', 'connect_address', 'certfile', 'keyfile']) _set_section_values('postgresql', ['listen', 'connect_address', 'data_dir', 'pgpass', 'bin_dir']) def _get_auth(name): ret = {} for param in ('username', 'password'): value = _popenv(name + '_' + param) if value: ret[param] = value return ret restapi_auth = _get_auth('restapi') if restapi_auth: ret['restapi']['authentication'] = restapi_auth authentication = {} for user_type in ('replication', 'superuser'): entry = _get_auth(user_type) if entry: authentication[user_type] = entry if authentication: ret['postgresql']['authentication'] = authentication users = {} def _parse_list(value): if not (value.strip().startswith('-') or '[' in value): value = '[{0}]'.format(value) try: return yaml.safe_load(value) except Exception: logger.exception('Exception when parsing list %s', value) return None for param in list(os.environ.keys()): if param.startswith(Config.PATRONI_ENV_PREFIX): name, suffix = (param[8:].split('_', 1) + [''])[:2] if name and suffix: # PATRONI_(ETCD|CONSUL|ZOOKEEPER|EXHIBITOR|...)_(HOSTS?|PORT|..) if suffix in ('HOST', 'HOSTS', 'PORT', 'SRV', 'URL', 'PROXY', 'CACERT', 'CERT', 'KEY', 'VERIFY', 'TOKEN', 'CHECKS', 'DC', 'NAMESPACE', 'CONTEXT', 'USE_ENDPOINTS', 'SCOPE_LABEL', 'ROLE_LABEL', 'POD_IP', 'PORTS', 'LABELS'): value = os.environ.pop(param) if suffix == 'PORT': value = value and parse_int(value) elif suffix in ('HOSTS', 'PORTS', 'CHECKS'): value = value and _parse_list(value) elif suffix == 'LABELS': if not value.strip().startswith('{'): value = '{{{0}}}'.format(value) try: value = yaml.safe_load(value) except Exception: logger.exception('Exception when parsing dict %s', value) value = None if value: ret[name.lower()][suffix.lower()] = value # PATRONI__PASSWORD=, PATRONI__OPTIONS= # CREATE USER "" WITH PASSWORD '' elif suffix == 'PASSWORD': password = os.environ.pop(param) if password: users[name] = {'password': password} options = os.environ.pop(param[:-9] + '_OPTIONS', None) options = options and _parse_list(options) if options: users[name]['options'] = options if users: ret['bootstrap']['users'] = users return ret def _build_effective_configuration(self, dynamic_configuration, local_configuration): config = self._safe_copy_dynamic_configuration(dynamic_configuration) for name, value in local_configuration.items(): if name == 'postgresql': for name, value in (value or {}).items(): if name == 'parameters': config['postgresql'][name].update(self._process_postgresql_parameters(value, True)) elif name != 'use_slots': # replication slots must be enabled/disabled globally config['postgresql'][name] = deepcopy(value) elif name not in config or name in ['watchdog']: config[name] = deepcopy(value) if value else {} # restapi server expects to get restapi.auth = 'username:password' if 'authentication' in config['restapi']: config['restapi']['auth'] = '{username}:{password}'.format(**config['restapi']['authentication']) # special treatment for old config # 'exhibitor' inside 'zookeeper': if 'zookeeper' in config and 'exhibitor' in config['zookeeper']: config['exhibitor'] = config['zookeeper'].pop('exhibitor') config.pop('zookeeper') pg_config = config['postgresql'] # no 'authentication' in 'postgresql', but 'replication' and 'superuser' if 'authentication' not in pg_config: pg_config['use_pg_rewind'] = 'pg_rewind' in pg_config pg_config['authentication'] = {u: pg_config[u] for u in ('replication', 'superuser') if u in pg_config} # no 'superuser' in 'postgresql'.'authentication' if 'superuser' not in pg_config['authentication'] and 'pg_rewind' in pg_config: pg_config['authentication']['superuser'] = pg_config['pg_rewind'] # no 'name' in config if 'name' not in config and 'name' in pg_config: config['name'] = pg_config['name'] pg_config.update({p: config[p] for p in ('name', 'scope', 'retry_timeout', 'synchronous_mode', 'maximum_lag_on_failover') if p in config}) return config def get(self, key, default=None): return self.__effective_configuration.get(key, default) def __contains__(self, key): return key in self.__effective_configuration def __getitem__(self, key): return self.__effective_configuration[key] def copy(self): return deepcopy(self.__effective_configuration) patroni-1.4.2/patroni/ctl.py000066400000000000000000001250061323411135200160010ustar00rootroot00000000000000''' Patroni Control ''' import base64 import click import codecs import datetime import dateutil.parser import cdiff import copy import difflib import io import json import logging import os import psycopg2 import random import requests import subprocess import sys import tempfile import time import tzlocal import yaml from click import ClickException from contextlib import contextmanager from patroni.config import Config from patroni.dcs import get_dcs as _get_dcs from patroni.exceptions import PatroniException from patroni.postgresql import Postgresql from patroni.utils import patch_config, polling_loop from patroni.version import __version__ from prettytable import PrettyTable from six.moves.urllib_parse import urlparse from six import text_type CONFIG_DIR_PATH = click.get_app_dir('patroni') CONFIG_FILE_PATH = os.path.join(CONFIG_DIR_PATH, 'patronictl.yaml') DCS_DEFAULTS = {'zookeeper': {'port': 2181, 'template': "zookeeper:\n hosts: ['{host}:{port}']"}, 'exhibitor': {'port': 8181, 'template': "exhibitor:\n hosts: [{host}]\n port: {port}"}, 'consul': {'port': 8500, 'template': "consul:\n host: '{host}:{port}'"}, 'etcd': {'port': 2379, 'template': "etcd:\n host: '{host}:{port}'"}} class PatroniCtlException(ClickException): pass def parse_dcs(dcs): if dcs is None: return None parsed = urlparse(dcs) scheme = parsed.scheme if scheme == '' and parsed.netloc == '': parsed = urlparse('//' + dcs) port = int(parsed.port) if parsed.port else None if scheme == '': scheme = ([k for k, v in DCS_DEFAULTS.items() if v['port'] == port] or ['etcd'])[0] elif scheme not in DCS_DEFAULTS: raise PatroniCtlException('Unknown dcs scheme: {}'.format(scheme)) dcs_info = DCS_DEFAULTS[scheme] return yaml.load(dcs_info['template'].format(host=parsed.hostname or 'localhost', port=port or dcs_info['port'])) def load_config(path, dcs): logging.debug('Loading configuration from file %s', path) config = {} old_argv = list(sys.argv) try: sys.argv[1] = path if Config.PATRONI_CONFIG_VARIABLE not in os.environ: for p in ('PATRONI_RESTAPI_LISTEN', 'PATRONI_POSTGRESQL_DATA_DIR'): if p not in os.environ: os.environ[p] = '.' config = Config().copy() finally: sys.argv = old_argv dcs = parse_dcs(dcs) or parse_dcs(config.get('dcs_api')) or {} if dcs: for d in DCS_DEFAULTS: config.pop(d, None) config.update(dcs) return config def store_config(config, path): dir_path = os.path.dirname(path) if dir_path and not os.path.isdir(dir_path): os.makedirs(dir_path) with open(path, 'w') as fd: yaml.dump(config, fd) option_format = click.option('--format', '-f', 'fmt', help='Output format (pretty, json, yaml)', default='pretty') option_watchrefresh = click.option('-w', '--watch', type=float, help='Auto update the screen every X seconds') option_watch = click.option('-W', is_flag=True, help='Auto update the screen every 2 seconds') option_force = click.option('--force', is_flag=True, help='Do not ask for confirmation at any point') arg_cluster_name = click.argument('cluster_name', required=False, default=lambda: click.get_current_context().obj.get('scope')) @click.group() @click.option('--config-file', '-c', help='Configuration file', default=CONFIG_FILE_PATH) @click.option('--dcs', '-d', help='Use this DCS', envvar='DCS') @click.pass_context def ctl(ctx, config_file, dcs): logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', level=os.environ.get('LOGLEVEL', 'WARNING')) ctx.obj = load_config(config_file, dcs) def get_dcs(config, scope): config.update({'scope': scope, 'patronictl': True}) config.setdefault('name', scope) try: return _get_dcs(config) except PatroniException as e: raise PatroniCtlException(str(e)) def auth_header(config): if config.get('restapi', {}).get('auth', ''): return {'Authorization': 'Basic ' + base64.b64encode(config['restapi']['auth'].encode('utf-8')).decode('utf-8')} def request_patroni(member, request_type, endpoint, content=None, headers=None): headers = headers or {} url_parts = urlparse(member.api_url) logging.debug(url_parts) if 'Content-Type' not in headers: headers['Content-Type'] = 'application/json' url = '{0}://{1}/{2}'.format(url_parts.scheme, url_parts.netloc, endpoint) return getattr(requests, request_type)(url, headers=headers, data=json.dumps(content) if content else None, timeout=60) def print_output(columns, rows=None, alignment=None, fmt='pretty', header=True, delimiter='\t'): rows = rows or [] if fmt == 'pretty': t = PrettyTable(columns) for k, v in (alignment or {}).items(): t.align[k] = v for r in rows: t.add_row(r) click.echo(t) return if fmt in ['json', 'yaml', 'yml']: elements = [dict(zip(columns, r)) for r in rows] if fmt == 'json': click.echo(json.dumps(elements)) elif fmt in ('yaml', 'yml'): click.echo(yaml.safe_dump(elements, encoding=None, default_flow_style=False, allow_unicode=True, width=200)) if fmt == 'tsv': if columns is not None and header: click.echo(delimiter.join(columns) + '\n') for r in rows: c = [str(c) for c in r] click.echo(delimiter.join(c)) def watching(w, watch, max_count=None, clear=True): """ >>> len(list(watching(True, 1, 0))) 1 >>> len(list(watching(True, 1, 1))) 2 >>> len(list(watching(True, None, 0))) 1 """ if w and not watch: watch = 2 if watch and clear: click.clear() yield 0 if max_count is not None and max_count < 1: return counter = 1 while watch and counter <= (max_count or counter): time.sleep(watch) counter += 1 if clear: click.clear() yield 0 def get_all_members(cluster, role='master'): if role == 'master': if cluster.leader is not None: yield cluster.leader return leader_name = (cluster.leader.member.name if cluster.leader else None) for m in cluster.members: if role == 'any' or role == 'replica' and m.name != leader_name: yield m def get_any_member(cluster, role='master', member=None): members = get_all_members(cluster, role) for m in members: if member is None or m.name == member: return m def get_cursor(cluster, connect_parameters, role='master', member=None): member = get_any_member(cluster, role=role, member=member) if member is None: return None params = member.conn_kwargs(connect_parameters) params.update({'fallback_application_name': 'Patroni ctl', 'connect_timeout': '5'}) if 'database' in connect_parameters: params['database'] = connect_parameters['database'] else: params.pop('database') conn = psycopg2.connect(**params) conn.autocommit = True cursor = conn.cursor() if role == 'any': return cursor cursor.execute('SELECT pg_is_in_recovery()') in_recovery = cursor.fetchone()[0] if in_recovery and role == 'replica' or not in_recovery and role == 'master': return cursor conn.close() return None def get_members(cluster, cluster_name, member_names, role, force, action): candidates = {m.name: m for m in cluster.members} if not force or role: output_members(cluster, cluster_name) if role: role_names = [m.name for m in get_all_members(cluster, role)] if member_names: member_names = list(set(member_names) & set(role_names)) if not member_names: raise PatroniCtlException('No {0} among provided members'.format(role)) else: member_names = role_names if not member_names and not force: member_names = [click.prompt('Which member do you want to {0} [{1}]?'.format(action, ', '.join(candidates.keys())), type=str, default='')] for member_name in member_names: if member_name not in candidates: raise PatroniCtlException('{0} is not a member of cluster'.format(member_name)) if not force: confirm = click.confirm('Are you sure you want to {0} members {1}?'.format(action, ', '.join(member_names))) if not confirm: raise PatroniCtlException('Aborted {0}'.format(action)) return [candidates[n] for n in member_names] @ctl.command('dsn', help='Generate a dsn for the provided member, defaults to a dsn of the master') @click.option('--role', '-r', help='Give a dsn of any member with this role', type=click.Choice(['master', 'replica', 'any']), default=None) @click.option('--member', '-m', help='Generate a dsn for this member', type=str) @arg_cluster_name @click.pass_obj def dsn(obj, cluster_name, role, member): if role is not None and member is not None: raise PatroniCtlException('--role and --member are mutually exclusive options') if member is None and role is None: role = 'master' cluster = get_dcs(obj, cluster_name).get_cluster() m = get_any_member(cluster, role=role, member=member) if m is None: raise PatroniCtlException('Can not find a suitable member') params = m.conn_kwargs() click.echo('host={host} port={port}'.format(**params)) @ctl.command('query', help='Query a Patroni PostgreSQL member') @arg_cluster_name @option_format @click.option('--format', 'fmt', help='Output format (pretty, json)', default='tsv') @click.option('--file', '-f', 'p_file', help='Execute the SQL commands from this file', type=click.File('rb')) @click.option('--password', help='force password prompt', is_flag=True) @click.option('-U', '--username', help='database user name', type=str) @option_watch @option_watchrefresh @click.option('--role', '-r', help='The role of the query', type=click.Choice(['master', 'replica', 'any']), default=None) @click.option('--member', '-m', help='Query a specific member', type=str) @click.option('--delimiter', help='The column delimiter', default='\t') @click.option('--command', '-c', help='The SQL commands to execute') @click.option('-d', '--dbname', help='database name to connect to', type=str) @click.pass_obj def query( obj, cluster_name, role, member, w, watch, delimiter, command, p_file, password, username, dbname, fmt='tsv', ): if role is not None and member is not None: raise PatroniCtlException('--role and --member are mutually exclusive options') if member is None and role is None: role = 'master' if p_file is not None and command is not None: raise PatroniCtlException('--file and --command are mutually exclusive options') if p_file is None and command is None: raise PatroniCtlException('You need to specify either --command or --file') connect_parameters = {} if username: connect_parameters['username'] = username if password: connect_parameters['password'] = click.prompt('Password', hide_input=True, type=str) if dbname: connect_parameters['database'] = dbname if p_file is not None: command = p_file.read() dcs = get_dcs(obj, cluster_name) cursor = None for _ in watching(w, watch, clear=False): if cursor is None: cluster = dcs.get_cluster() output, cursor = query_member(cluster, cursor, member, role, command, connect_parameters) print_output(None, output, fmt=fmt, delimiter=delimiter) def query_member(cluster, cursor, member, role, command, connect_parameters): try: if cursor is None: cursor = get_cursor(cluster, connect_parameters, role=role, member=member) if cursor is None: if role is None: message = 'No connection to member {0} is available'.format(member) else: message = 'No connection to role={0} is available'.format(role) logging.debug(message) return [[timestamp(0), message]], None cursor.execute('SELECT pg_is_in_recovery()') in_recovery = cursor.fetchone()[0] if in_recovery and role == 'master' or not in_recovery and role == 'replica': cursor.connection.close() return None, None cursor.execute(command) return cursor.fetchall(), cursor except (psycopg2.OperationalError, psycopg2.DatabaseError) as oe: logging.debug(oe) if cursor is not None and not cursor.connection.closed: cursor.connection.close() message = oe.pgcode or oe.pgerror or str(oe) message = message.replace('\n', ' ') return [[timestamp(0), 'ERROR, SQLSTATE: {0}'.format(message)]], None @ctl.command('remove', help='Remove cluster from DCS') @click.argument('cluster_name') @option_format @click.pass_obj def remove(obj, cluster_name, fmt): dcs = get_dcs(obj, cluster_name) cluster = dcs.get_cluster() output_members(cluster, cluster_name, fmt=fmt) confirm = click.prompt('Please confirm the cluster name to remove', type=str) if confirm != cluster_name: raise PatroniCtlException('Cluster names specified do not match') message = 'Yes I am aware' confirm = \ click.prompt('You are about to remove all information in DCS for {0}, please type: "{1}"'.format(cluster_name, message), type=str) if message != confirm: raise PatroniCtlException('You did not exactly type "{0}"'.format(message)) if cluster.leader and cluster.leader.name: confirm = click.prompt('This cluster currently is healthy. Please specify the master name to continue') if confirm != cluster.leader.name: raise PatroniCtlException('You did not specify the current master of the cluster') dcs.delete_cluster() def check_response(response, member_name, action_name, silent_success=False): if response.status_code >= 400: click.echo('Failed: {0} for member {1}, status code={2}, ({3})'.format( action_name, member_name, response.status_code, response.text )) return False elif not silent_success: click.echo('Success: {0} for member {1}'.format(action_name, member_name)) return True def parse_scheduled(scheduled): if (scheduled or 'now') != 'now': try: scheduled_at = dateutil.parser.parse(scheduled) if scheduled_at.tzinfo is None: scheduled_at = tzlocal.get_localzone().localize(scheduled_at) except (ValueError, TypeError): message = 'Unable to parse scheduled timestamp ({0}). It should be in an unambiguous format (e.g. ISO 8601)' raise PatroniCtlException(message.format(scheduled)) return scheduled_at return None @ctl.command('restart', help='Restart cluster member') @click.argument('cluster_name') @click.argument('member_names', nargs=-1) @click.option('--role', '-r', help='Restart only members with this role', default='any', type=click.Choice(['master', 'replica', 'any'])) @click.option('--any', 'p_any', help='Restart a single member only', is_flag=True) @click.option('--scheduled', help='Timestamp of a scheduled restart in unambiguous format (e.g. ISO 8601)', default=None) @click.option('--pg-version', 'version', help='Restart if the PostgreSQL version is less than provided (e.g. 9.5.2)', default=None) @click.option('--pending', help='Restart if pending', is_flag=True) @click.option('--timeout', help='Return error and fail over if necessary when restarting takes longer than this.') @option_force @click.pass_obj def restart(obj, cluster_name, member_names, force, role, p_any, scheduled, version, pending, timeout): cluster = get_dcs(obj, cluster_name).get_cluster() members = get_members(cluster, cluster_name, member_names, role, force, 'restart') if p_any: random.shuffle(members) members = members[:1] if version is None and not force: version = click.prompt('Restart if the PostgreSQL version is less than provided (e.g. 9.5.2) ', type=str, default='') content = {} if pending: content['restart_pending'] = True if version: try: Postgresql.postgres_version_to_int(version) except PatroniException as e: raise PatroniCtlException(e.value) content['postgres_version'] = version if scheduled is None and not force: scheduled = click.prompt('When should the restart take place (e.g. 2015-10-01T14:30) ', type=str, default='now') scheduled_at = parse_scheduled(scheduled) if scheduled_at: if cluster.is_paused(): raise PatroniCtlException("Can't schedule restart in the paused state") content['schedule'] = scheduled_at.isoformat() if timeout is not None: content['timeout'] = timeout for member in members: if 'schedule' in content: if force and member.data.get('scheduled_restart'): r = request_patroni(member, 'delete', 'restart', headers=auth_header(obj)) check_response(r, member.name, 'flush scheduled restart', True) r = request_patroni(member, 'post', 'restart', content, auth_header(obj)) if r.status_code == 200: click.echo('Success: restart on member {0}'.format(member.name)) elif r.status_code == 202: click.echo('Success: restart scheduled on member {0}'.format(member.name)) elif r.status_code == 409: click.echo('Failed: another restart is already scheduled on member {0}'.format(member.name)) else: click.echo('Failed: restart for member {0}, status code={1}, ({2})'.format( member.name, r.status_code, r.text) ) @ctl.command('reinit', help='Reinitialize cluster member') @click.argument('cluster_name') @click.argument('member_names', nargs=-1) @option_force @click.pass_obj def reinit(obj, cluster_name, member_names, force): cluster = get_dcs(obj, cluster_name).get_cluster() members = get_members(cluster, cluster_name, member_names, None, force, 'reinitialize') for member in members: body = {'force': force} while True: r = request_patroni(member, 'post', 'reinitialize', body, auth_header(obj)) if not check_response(r, member.name, 'reinitialize') and r.text.endswith(' already in progress') \ and not force and click.confirm('Do you want to cancel it and reinitialize anyway?'): body['force'] = True continue break def _do_failover_or_switchover(obj, action, cluster_name, master, candidate, force, scheduled=None): """ We want to trigger a failover or switchover for the specified cluster name. We verify that the cluster name, master name and candidate name are correct. If so, we trigger an action and keep the client up to date. """ dcs = get_dcs(obj, cluster_name) cluster = dcs.get_cluster() if action == 'switchover' and cluster.leader is None: raise PatroniCtlException('This cluster has no master') if master is None: if force or action == 'failover': master = cluster.leader and cluster.leader.name else: master = click.prompt('Master', type=str, default=cluster.leader.member.name) if master is not None and cluster.leader and cluster.leader.member.name != master: raise PatroniCtlException('Member {0} is not the leader of cluster {1}'.format(master, cluster_name)) candidate_names = [str(m.name) for m in cluster.members if m.name != master] # We sort the names for consistent output to the client candidate_names.sort() if not candidate_names: raise PatroniCtlException('No candidates found to {0} to'.format(action)) if candidate is None and not force: candidate = click.prompt('Candidate ' + str(candidate_names), type=str, default='') if action == 'failover' and not candidate: raise PatroniCtlException('Failover could be performed only to a specific candidate') if candidate == master: raise PatroniCtlException(action.title() + ' target and source are the same.') if candidate and candidate not in candidate_names: raise PatroniCtlException('Member {0} does not exist in cluster {1}'.format(candidate, cluster_name)) scheduled_at_str = None if action == 'switchover': if scheduled is None and not force: scheduled = click.prompt('When should the switchover take place (e.g. 2015-10-01T14:30) ', type=str, default='now') scheduled_at = parse_scheduled(scheduled) if scheduled_at: if cluster.is_paused(): raise PatroniCtlException("Can't schedule switchover in the paused state") scheduled_at_str = scheduled_at.isoformat() failover_value = {'leader': master, 'candidate': candidate, 'scheduled_at': scheduled_at_str} logging.debug(failover_value) # By now we have established that the leader exists and the candidate exists click.echo('Current cluster topology') output_members(dcs.get_cluster(), cluster_name) if not force: demote_msg = ', demoting current master ' + master if master else '' if not click.confirm('Are you sure you want to {0} cluster {1}{2}?'.format(action, cluster_name, demote_msg)): raise PatroniCtlException('Aborting ' + action) r = None try: member = cluster.leader.member if cluster.leader else cluster.get_member(candidate, False) r = request_patroni(member, 'post', action, failover_value, auth_header(obj)) # probably old patroni, which doesn't support switchover yet if r.status_code == 501 and action == 'switchover' and 'Server does not support this operation' in r.text: r = request_patroni(member, 'post', 'failover', failover_value, auth_header(obj)) if r.status_code in (200, 202): logging.debug(r) cluster = dcs.get_cluster() logging.debug(cluster) click.echo('{0} {1}'.format(timestamp(), r.text)) else: click.echo('{0} failed, details: {1}, {2}'.format(action.title(), r.status_code, r.text)) return except Exception: logging.exception(r) logging.warning('Failing over to DCS') click.echo('{0} Could not {1} using Patroni api, falling back to DCS'.format(timestamp(), action)) dcs.manual_failover(master, candidate, scheduled_at=scheduled_at) output_members(cluster, cluster_name) @ctl.command('failover', help='Failover to a replica') @arg_cluster_name @click.option('--master', help='The name of the current master', default=None) @click.option('--candidate', help='The name of the candidate', default=None) @option_force @click.pass_obj def failover(obj, cluster_name, master, candidate, force): action = 'switchover' if master else 'failover' _do_failover_or_switchover(obj, action, cluster_name, master, candidate, force) @ctl.command('switchover', help='Switchover to a replica') @arg_cluster_name @click.option('--master', help='The name of the current master', default=None) @click.option('--candidate', help='The name of the candidate', default=None) @click.option('--scheduled', help='Timestamp of a scheduled switchover in unambiguous format (e.g. ISO 8601)', default=None) @option_force @click.pass_obj def switchover(obj, cluster_name, master, candidate, force, scheduled): _do_failover_or_switchover(obj, 'switchover', cluster_name, master, candidate, force, scheduled) def output_members(cluster, name, extended=False, fmt='pretty'): rows = [] logging.debug(cluster) leader_name = None if cluster.leader: leader_name = cluster.leader.name xlog_location_cluster = cluster.last_leader_operation or 0 # Mainly for consistent pretty printing and watching we sort the output cluster.members.sort(key=lambda x: x.name) has_scheduled_restarts = any(m.data.get('scheduled_restart') for m in cluster.members) has_pending_restarts = any(m.data.get('pending_restart') for m in cluster.members) for m in cluster.members: logging.debug(m) role = '' if m.name == leader_name: role = 'Leader' elif m.name == cluster.sync.sync_standby: role = 'Sync standby' xlog_location = m.data.get('xlog_location') lag = '' if xlog_location is None: lag = 'unknown' elif xlog_location_cluster >= xlog_location: lag = round((xlog_location_cluster - xlog_location)/1024/1024) row = [name, m.name, m.conn_kwargs()['host'], role, m.data.get('state', ''), lag] if extended or has_pending_restarts: row.append('*' if m.data.get('pending_restart') else '') if extended or has_scheduled_restarts: value = '' scheduled_restart = m.data.get('scheduled_restart') if scheduled_restart: value = scheduled_restart['schedule'] if 'postgres_version' in scheduled_restart: value += ' if version < {0}'.format(scheduled_restart['postgres_version']) row.append(value) rows.append(row) columns = ['Cluster', 'Member', 'Host', 'Role', 'State', 'Lag in MB'] alignment = {'Lag in MB': 'r'} if extended or has_pending_restarts: columns.append('Pending restart') if extended or has_scheduled_restarts: columns.append('Scheduled restart') print_output(columns, rows, alignment, fmt) service_info = [] if cluster.is_paused(): service_info.append('Maintenance mode: on') if cluster.failover and cluster.failover.scheduled_at: info = 'Switchover scheduled at: ' + cluster.failover.scheduled_at.isoformat() if cluster.failover.leader: info += '\n from: ' + cluster.failover.leader if cluster.failover.candidate: info += '\n to: ' + cluster.failover.candidate service_info.append(info) if service_info: click.echo(' ' + '\n '.join(service_info)) @ctl.command('list', help='List the Patroni members for a given Patroni') @click.argument('cluster_names', nargs=-1) @click.option('--extended', '-e', help='Show some extra information', is_flag=True) @click.option('--timestamp', '-t', 'ts', help='Print timestamp', is_flag=True) @option_format @option_watch @option_watchrefresh @click.pass_obj def members(obj, cluster_names, fmt, watch, w, extended, ts): if not cluster_names: if 'scope' in obj: cluster_names = [obj['scope']] if not cluster_names: return logging.warning('Listing members: No cluster names were provided') for cluster_name in cluster_names: dcs = get_dcs(obj, cluster_name) for _ in watching(w, watch): if ts: click.echo(timestamp(0)) cluster = dcs.get_cluster() output_members(cluster, cluster_name, extended, fmt) def timestamp(precision=6): return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:precision - 7] @ctl.command('configure', help='Create configuration file') @click.option('--config-file', '-c', help='Configuration file', prompt='Configuration file', default=CONFIG_FILE_PATH) @click.option('--dcs', '-d', help='The DCS connect url', prompt='DCS connect url', default='etcd://localhost:2379') @click.option('--namespace', '-n', help='The namespace', prompt='Namespace', default='/service/') def configure(config_file, dcs, namespace): store_config({'dcs_api': str(dcs), 'namespace': str(namespace)}, config_file) def touch_member(config, dcs): ''' Rip-off of the ha.touch_member without inter-class dependencies ''' p = Postgresql(config['postgresql']) p.set_state('running') p.set_role('master') def restapi_connection_string(config): protocol = 'https' if config.get('certfile') else 'http' connect_address = config.get('connect_address') listen = config['listen'] return '{0}://{1}/patroni'.format(protocol, connect_address or listen) data = { 'conn_url': p.connection_string, 'api_url': restapi_connection_string(config['restapi']), 'state': p.state, 'role': p.role } return dcs.touch_member(json.dumps(data, separators=(',', ':')), permanent=True) def set_defaults(config, cluster_name): """fill-in some basic configuration parameters if config file is not set """ config['postgresql'].setdefault('name', cluster_name) config['postgresql'].setdefault('scope', cluster_name) config['postgresql'].setdefault('listen', '127.0.0.1') config['postgresql']['authentication'] = {'replication': None} config['restapi']['listen'] = ':' in config['restapi']['listen'] and config['restapi']['listen'] or '127.0.0.1:8008' @ctl.command('scaffold', help='Create a structure for the cluster in DCS') @click.argument('cluster_name') @click.option('--sysid', '-s', help='System ID of the cluster to put into the initialize key', default="") @click.pass_obj def scaffold(obj, cluster_name, sysid): dcs = get_dcs(obj, cluster_name) cluster = dcs.get_cluster() if cluster and cluster.initialize is not None: raise PatroniCtlException("This cluster is already initialized") if not dcs.initialize(create_new=True, sysid=sysid): # initialize key already exists, don't touch this cluster raise PatroniCtlException("Initialize key for cluster {0} already exists".format(cluster_name)) set_defaults(obj, cluster_name) # make sure the leader keys will never expire if not (touch_member(obj, dcs) and dcs.attempt_to_acquire_leader(permanent=True)): # we did initialize this cluster, but failed to write the leader or member keys, wipe it down completely. dcs.delete_cluster() raise PatroniCtlException("Unable to install permanent leader for cluster {0}".format(cluster_name)) click.echo("Cluster {0} has been created successfully".format(cluster_name)) @ctl.command('flush', help='Flush scheduled events') @click.argument('cluster_name') @click.argument('member_names', nargs=-1) @click.argument('target', type=click.Choice(['restart'])) @click.option('--role', '-r', help='Flush only members with this role', default='any', type=click.Choice(['master', 'replica', 'any'])) @option_force @click.pass_obj def flush(obj, cluster_name, member_names, force, role, target): cluster = get_dcs(obj, cluster_name).get_cluster() members = get_members(cluster, cluster_name, member_names, role, force, 'flush') for member in members: if target == 'restart': if member.data.get('scheduled_restart'): r = request_patroni(member, 'delete', 'restart', None, auth_header(obj)) check_response(r, member.name, 'flush scheduled restart') else: click.echo('No scheduled restart for member {0}'.format(member.name)) def wait_until_pause_is_applied(dcs, paused, old_cluster): click.echo("'{0}' request sent, waiting until it is recognized by all nodes".format(paused and 'pause' or 'resume')) old = {m.name: m.index for m in old_cluster.members if m.api_url} loop_wait = old_cluster.config.data.get('loop_wait', dcs.loop_wait) for _ in polling_loop(loop_wait + 1): cluster = dcs.get_cluster() if all(m.data.get('pause', False) == paused for m in cluster.members if m.name in old): break else: remaining = [m.name for m in cluster.members if m.data.get('pause', False) != paused and m.name in old and old[m.name] != m.index] if remaining: return click.echo("{0} members didn't recognized pause state after {1} seconds" .format(', '.join(remaining), loop_wait)) return click.echo('Success: cluster management is {0}'.format(paused and 'paused' or 'resumed')) def toggle_pause(config, cluster_name, paused, wait): dcs = get_dcs(config, cluster_name) cluster = dcs.get_cluster() if cluster.is_paused() == paused: raise PatroniCtlException('Cluster is {0} paused'.format(paused and 'already' or 'not')) members = [] if cluster.leader: members.append(cluster.leader.member) members.extend([m for m in cluster.members if m.api_url and (not members or members[0].name != m.name)]) for member in members: try: r = request_patroni(member, 'patch', 'config', {'pause': paused or None}, auth_header(config)) except Exception: logging.warning('Member %s is not accessible', member.name) continue if r.status_code == 200: if wait: wait_until_pause_is_applied(dcs, paused, cluster) else: click.echo('Success: cluster management is {0}'.format(paused and 'paused' or 'resumed')) else: click.echo('Failed: {0} cluster management status code={1}, ({2})'.format( paused and 'pause' or 'resume', r.status_code, r.text)) break else: raise PatroniCtlException('Can not find accessible cluster member') @ctl.command('pause', help='Disable auto failover') @arg_cluster_name @click.pass_obj @click.option('--wait', help='Wait until pause is applied on all nodes', is_flag=True) def pause(obj, cluster_name, wait): return toggle_pause(obj, cluster_name, True, wait) @ctl.command('resume', help='Resume auto failover') @arg_cluster_name @click.option('--wait', help='Wait until pause is cleared on all nodes', is_flag=True) @click.pass_obj def resume(obj, cluster_name, wait): return toggle_pause(obj, cluster_name, False, wait) @contextmanager def temporary_file(contents, suffix='', prefix='tmp'): """Creates a temporary file with specified contents that persists for the context. :param contents: binary string that will be written to the file. :param prefix: will be prefixed to the filename. :param suffix: will be appended to the filename. :returns path of the created file. """ tmp = tempfile.NamedTemporaryFile(suffix=suffix, prefix=prefix, delete=False) with tmp: tmp.write(contents) try: yield tmp.name finally: os.unlink(tmp.name) def show_diff(before_editing, after_editing): """Shows a diff between two strings. If the output is to a tty the diff will be colored. Inputs are expected to be unicode strings. """ def listify(string): return [l+'\n' for l in string.rstrip('\n').split('\n')] unified_diff = difflib.unified_diff(listify(before_editing), listify(after_editing)) if sys.stdout.isatty(): buf = io.StringIO() for line in unified_diff: # Force cast to unicode as difflib on Python 2.7 returns a mix of unicode and str. buf.write(text_type(line)) buf.seek(0) class opts: side_by_side = False width = 80 tab_width = 8 cdiff.markup_to_pager(cdiff.PatchStream(buf), opts) else: for line in unified_diff: click.echo(line.rstrip('\n')) def format_config_for_editing(data): """Formats configuration as YAML for human consumption. :param data: configuration as nested dictionaries :returns unicode YAML of the configuration""" return yaml.safe_dump(data, default_flow_style=False, encoding=None, allow_unicode=True) def apply_config_changes(before_editing, data, kvpairs): """Applies config changes specified as a list of key-value pairs. Keys are interpreted as dotted paths into the configuration data structure. Except for paths beginning with `postgresql.parameters` where rest of the path is used directly to allow for PostgreSQL GUCs containing dots. Values are interpreted as YAML values. :param before_editing: human representation before editing :param data: configuration datastructure :param kvpairs: list of strings containing key value pairs separated by = :returns tuple of human readable and parsed datastructure after changes """ changed_data = copy.deepcopy(data) def set_path_value(config, path, value, prefix=()): # Postgresql GUCs can't be nested, but can contain dots so we re-flatten the structure for this case if prefix == ('postgresql', 'parameters'): path = ['.'.join(path)] key = path[0] if len(path) == 1: if value is None: config.pop(key, None) else: config[key] = value else: if not isinstance(config.get(key), dict): config[key] = {} set_path_value(config[key], path[1:], value, prefix + (key,)) if config[key] == {}: del config[key] for pair in kvpairs: if not pair or "=" not in pair: raise PatroniCtlException("Invalid parameter setting {0}".format(pair)) key_path, value = pair.split("=", 1) set_path_value(changed_data, key_path.strip().split("."), yaml.safe_load(value)) return format_config_for_editing(changed_data), changed_data def apply_yaml_file(data, filename): """Applies changes from a YAML file to configuration :param data: configuration datastructure :param filename: name of the YAML file, - is taken to mean standard input :returns tuple of human readable and parsed datastructure after changes """ changed_data = copy.deepcopy(data) if filename == '-': new_options = yaml.safe_load(sys.stdin) else: with open(filename) as fd: new_options = yaml.safe_load(fd) patch_config(changed_data, new_options) return format_config_for_editing(changed_data), changed_data def invoke_editor(before_editing, cluster_name): """Starts editor command to edit configuration in human readable format :param before_editing: human representation before editing :returns tuple of human readable and parsed datastructure after changes """ editor_cmd = os.environ.get('EDITOR') if not editor_cmd: raise PatroniCtlException('EDITOR environment variable is not set') with temporary_file(contents=before_editing.encode('utf-8'), suffix='.yaml', prefix='{0}-config-'.format(cluster_name)) as tmpfile: ret = subprocess.call([editor_cmd, tmpfile]) if ret: raise PatroniCtlException("Editor exited with return code {0}".format(ret)) with codecs.open(tmpfile, encoding='utf-8') as fd: after_editing = fd.read() return after_editing, yaml.safe_load(after_editing) @ctl.command('edit-config', help="Edit cluster configuration") @arg_cluster_name @click.option('--quiet', '-q', is_flag=True, help='Do not show changes') @click.option('--set', '-s', 'kvpairs', multiple=True, help='Set specific configuration value. Can be specified multiple times') @click.option('--pg', '-p', 'pgkvpairs', multiple=True, help='Set specific PostgreSQL parameter value. Shorthand for -s postgresql.parameters. ' 'Can be specified multiple times') @click.option('--apply', 'apply_filename', help='Apply configuration from file. Use - for stdin.') @click.option('--replace', 'replace_filename', help='Apply configuration from file, replacing existing configuration.' ' Use - for stdin.') @option_force @click.pass_obj def edit_config(obj, cluster_name, force, quiet, kvpairs, pgkvpairs, apply_filename, replace_filename): dcs = get_dcs(obj, cluster_name) cluster = dcs.get_cluster() before_editing = format_config_for_editing(cluster.config.data) after_editing = None # Serves as a flag if any changes were requested changed_data = cluster.config.data if replace_filename: after_editing, changed_data = apply_yaml_file({}, replace_filename) if apply_filename: after_editing, changed_data = apply_yaml_file(changed_data, apply_filename) if kvpairs or pgkvpairs: all_pairs = list(kvpairs) + ['postgresql.parameters.'+v.lstrip() for v in pgkvpairs] after_editing, changed_data = apply_config_changes(before_editing, changed_data, all_pairs) # If no changes were specified on the command line invoke editor if after_editing is None: after_editing, changed_data = invoke_editor(before_editing, cluster_name) if cluster.config.data == changed_data: if not quiet: click.echo("Not changed") return if not quiet: show_diff(before_editing, after_editing) if (apply_filename == '-' or replace_filename == '-') and not force: click.echo("Use --force option to apply changes") return if force or click.confirm('Apply these changes?'): if not dcs.set_config_value(json.dumps(changed_data), cluster.config.index): raise PatroniCtlException("Config modification aborted due to concurrent changes") click.echo("Configuration changed") @ctl.command('show-config', help="Show cluster configuration") @arg_cluster_name @click.pass_obj def show_config(obj, cluster_name): cluster = get_dcs(obj, cluster_name).get_cluster() click.echo(format_config_for_editing(cluster.config.data)) @ctl.command('version', help='Output version of patronictl command or a running Patroni instance') @click.argument('cluster_name', required=False) @click.argument('member_names', nargs=-1) @click.pass_obj def version(obj, cluster_name, member_names): click.echo("patronictl version {0}".format(__version__)) if not cluster_name: return click.echo("") cluster = get_dcs(obj, cluster_name).get_cluster() for m in cluster.members: if m.api_url: if not member_names or m.name in member_names: try: response = request_patroni(m, 'get', 'patroni') data = response.json() version = data.get('patroni', {}).get('version') pg_version = data.get('server_version') pg_version_str = " PostgreSQL {0}".format(format_pg_version(pg_version)) if pg_version else "" click.echo("{0}: Patroni {1}{2}".format(m.name, version, pg_version_str)) except Exception as e: click.echo("{0}: failed to get version: {1}".format(m.name, e)) def format_pg_version(version): if version < 100000: return "{0}.{1}.{2}".format(version // 10000, version // 100 % 100, version % 100) else: return "{0}.{1}".format(version // 10000, version % 100) patroni-1.4.2/patroni/dcs/000077500000000000000000000000001323411135200154125ustar00rootroot00000000000000patroni-1.4.2/patroni/dcs/__init__.py000066400000000000000000000534441323411135200175350ustar00rootroot00000000000000import abc import dateutil import importlib import inspect import json import logging import os import pkgutil import six import sys from collections import namedtuple from patroni.exceptions import PatroniException from random import randint from six.moves.urllib_parse import urlparse, urlunparse, parse_qsl from threading import Event, Lock logger = logging.getLogger(__name__) def parse_connection_string(value): """Original Governor stores connection strings for each cluster members if a following format: postgres://{username}:{password}@{connect_address}/postgres Since each of our patroni instances provides own REST API endpoint it's good to store this information in DCS among with postgresql connection string. In order to not introduce new keys and be compatible with original Governor we decided to extend original connection string in a following way: postgres://{username}:{password}@{connect_address}/postgres?application_name={api_url} This way original Governor could use such connection string as it is, because of feature of `libpq` library. This method is able to split connection string stored in DCS into two parts, `conn_url` and `api_url`""" scheme, netloc, path, params, query, fragment = urlparse(value) conn_url = urlunparse((scheme, netloc, path, params, '', fragment)) api_url = ([v for n, v in parse_qsl(query) if n == 'application_name'] or [None])[0] return conn_url, api_url def dcs_modules(): """Get names of DCS modules, depending on execution environment. If being packaged with PyInstaller, modules aren't discoverable dynamically by scanning source directory because `FrozenImporter` doesn't implement `iter_modules` method. But it is still possible to find all potential DCS modules by iterating through `toc`, which contains list of all "frozen" resources.""" dcs_dirname = os.path.dirname(__file__) module_prefix = __package__ + '.' if getattr(sys, 'frozen', False): importer = pkgutil.get_importer(dcs_dirname) return [module for module in list(importer.toc) if module.startswith(module_prefix) and module.count('.') == 2] else: return [module_prefix + name for _, name, is_pkg in pkgutil.iter_modules([dcs_dirname]) if not is_pkg] def get_dcs(config): available_implementations = set() for module_name in dcs_modules(): try: module = importlib.import_module(module_name) for name in filter(lambda name: not name.startswith('__'), dir(module)): # iterate through module content item = getattr(module, name) name = name.lower() # try to find implementation of AbstractDCS interface, class name must match with module_name if inspect.isclass(item) and issubclass(item, AbstractDCS) and __package__ + '.' + name == module_name: available_implementations.add(name) if name in config: # which has configuration section in the config file # propagate some parameters config[name].update({p: config[p] for p in ('namespace', 'name', 'scope', 'loop_wait', 'patronictl', 'ttl', 'retry_timeout') if p in config}) return item(config[name]) except ImportError: if not config.get('patronictl'): logger.info('Failed to import %s', module_name) raise PatroniException("""Can not find suitable configuration of distributed configuration store Available implementations: """ + ', '.join(available_implementations)) class Member(namedtuple('Member', 'index,name,session,data')): """Immutable object (namedtuple) which represents single member of PostgreSQL cluster. Consists of the following fields: :param index: modification index of a given member key in a Configuration Store :param name: name of PostgreSQL cluster member :param session: either session id or just ttl in seconds :param data: arbitrary data i.e. conn_url, api_url, xlog location, state, role, tags, etc... There are two mandatory keys in a data: conn_url: connection string containing host, user and password which could be used to access this member. api_url: REST API url of patroni instance""" @staticmethod def from_node(index, name, session, data): """ >>> Member.from_node(-1, '', '', '{"conn_url": "postgres://foo@bar/postgres"}') is not None True >>> Member.from_node(-1, '', '', '{') Member(index=-1, name='', session='', data={}) """ if data.startswith('postgres'): conn_url, api_url = parse_connection_string(data) data = {'conn_url': conn_url, 'api_url': api_url} else: try: data = json.loads(data) except (TypeError, ValueError): data = {} return Member(index, name, session, data) @property def conn_url(self): return self.data.get('conn_url') def conn_kwargs(self, auth=None): ret = self.data.get('conn_kwargs') if ret: ret = ret.copy() else: r = urlparse(self.conn_url) ret = { 'host': r.hostname, 'port': r.port or 5432, 'database': r.path[1:] } self.data['conn_kwargs'] = ret.copy() if auth and isinstance(auth, dict): if 'username' in auth: ret['user'] = auth['username'] if 'password' in auth: ret['password'] = auth['password'] return ret @property def api_url(self): return self.data.get('api_url') @property def tags(self): return self.data.get('tags', {}) @property def nofailover(self): return self.tags.get('nofailover', False) @property def replicatefrom(self): return self.tags.get('replicatefrom') @property def clonefrom(self): return self.tags.get('clonefrom', False) and bool(self.conn_url) @property def state(self): return self.data.get('state', 'unknown') @property def is_running(self): return self.state == 'running' class Leader(namedtuple('Leader', 'index,session,member')): """Immutable object (namedtuple) which represents leader key. Consists of the following fields: :param index: modification index of a leader key in a Configuration Store :param session: either session id or just ttl in seconds :param member: reference to a `Member` object which represents current leader (see `Cluster.members`)""" @property def name(self): return self.member.name def conn_kwargs(self, auth=None): return self.member.conn_kwargs(auth) @property def conn_url(self): return self.member.conn_url @property def timeline(self): return self.member.data.get('timeline') class Failover(namedtuple('Failover', 'index,leader,candidate,scheduled_at')): """ >>> 'Failover' in str(Failover.from_node(1, '{"leader": "cluster_leader"}')) True >>> 'Failover' in str(Failover.from_node(1, {"leader": "cluster_leader"})) True >>> 'Failover' in str(Failover.from_node(1, '{"leader": "cluster_leader", "member": "cluster_candidate"}')) True >>> Failover.from_node(1, 'null') is None False >>> n = '{"leader": "cluster_leader", "member": "cluster_candidate", "scheduled_at": "2016-01-14T10:09:57.1394Z"}' >>> 'tzinfo=' in str(Failover.from_node(1, n)) True >>> Failover.from_node(1, None) is None False >>> Failover.from_node(1, '{}') is None False >>> 'abc' in Failover.from_node(1, 'abc:def') True """ @staticmethod def from_node(index, value): if isinstance(value, dict): data = value elif value: try: data = json.loads(value) if not isinstance(data, dict): data = {} except ValueError: t = [a.strip() for a in value.split(':')] leader = t[0] candidate = t[1] if len(t) > 1 else None return Failover(index, leader, candidate, None) if leader or candidate else None else: data = {} if data.get('scheduled_at'): data['scheduled_at'] = dateutil.parser.parse(data['scheduled_at']) return Failover(index, data.get('leader'), data.get('member'), data.get('scheduled_at')) def __len__(self): return int(bool(self.leader)) + int(bool(self.candidate)) class ClusterConfig(namedtuple('ClusterConfig', 'index,data,modify_index')): @staticmethod def from_node(index, data, modify_index=None): """ >>> ClusterConfig.from_node(1, '{') is None True """ try: data = json.loads(data) except (TypeError, ValueError): return None return ClusterConfig(index, data, modify_index or index) class SyncState(namedtuple('SyncState', 'index,leader,sync_standby')): """Immutable object (namedtuple) which represents last observed synhcronous replication state :param index: modification index of a synchronization key in a Configuration Store :param leader: reference to member that was leader :param sync_standby: standby that was last synchronized to leader """ @staticmethod def from_node(index, value): """ >>> SyncState.from_node(1, None).leader is None True >>> SyncState.from_node(1, '{}').leader is None True >>> SyncState.from_node(1, '{').leader is None True >>> SyncState.from_node(1, '[]').leader is None True >>> SyncState.from_node(1, '{"leader": "leader"}').leader == "leader" True >>> SyncState.from_node(1, {"leader": "leader"}).leader == "leader" True """ if isinstance(value, dict): data = value elif value: try: data = json.loads(value) if not isinstance(data, dict): data = {} except (TypeError, ValueError): data = {} else: data = {} return SyncState(index, data.get('leader'), data.get('sync_standby')) def matches(self, name): """ Returns if a node name matches one of the nodes in the sync state >>> s = SyncState(1, 'foo', 'bar') >>> s.matches('foo') True >>> s.matches('bar') True >>> s.matches('baz') False >>> s.matches(None) False >>> SyncState(1, None, None).matches('foo') False """ return name is not None and name in (self.leader, self.sync_standby) class TimelineHistory(namedtuple('TimelineHistory', 'index,lines')): """Object representing timeline history file""" @staticmethod def from_node(index, value): """ >>> h = TimelineHistory.from_node(1, 2) >>> h.lines [] """ try: lines = json.loads(value) except (TypeError, ValueError): lines = None if not isinstance(lines, list): lines = [] return TimelineHistory(index, lines) class Cluster(namedtuple('Cluster', 'initialize,config,leader,last_leader_operation,members,failover,sync,history')): """Immutable object (namedtuple) which represents PostgreSQL cluster. Consists of the following fields: :param initialize: shows whether this cluster has initialization key stored in DC or not. :param config: global dynamic configuration, reference to `ClusterConfig` object :param leader: `Leader` object which represents current leader of the cluster :param last_leader_operation: int or long object containing position of last known leader operation. This value is stored in `/optime/leader` key :param members: list of Member object, all PostgreSQL cluster members including leader :param failover: reference to `Failover` object :param sync: reference to `SyncState` object, last observed synchronous replication state. :param history: reference to `TimelineHistory` object """ def is_unlocked(self): return not (self.leader and self.leader.name) def has_member(self, member_name): return any(m for m in self.members if m.name == member_name) def get_member(self, member_name, fallback_to_leader=True): return ([m for m in self.members if m.name == member_name] or [self.leader if fallback_to_leader else None])[0] def get_clone_member(self, exclude): exclude = [exclude] + [self.leader.name] if self.leader else [] candidates = [m for m in self.members if m.clonefrom and m.is_running and m.name not in exclude] return candidates[randint(0, len(candidates) - 1)] if candidates else self.leader def is_paused(self): return self.config and self.config.data.get('pause', False) or False def is_synchronous_mode(self): return bool(self.config and self.config.data.get('synchronous_mode')) def is_synchronous_mode_strict(self): return bool(self.config and self.config.data.get('synchronous_mode_strict')) @six.add_metaclass(abc.ABCMeta) class AbstractDCS(object): _INITIALIZE = 'initialize' _CONFIG = 'config' _LEADER = 'leader' _FAILOVER = 'failover' _HISTORY = 'history' _MEMBERS = 'members/' _OPTIME = 'optime' _LEADER_OPTIME = _OPTIME + '/' + _LEADER _SYNC = 'sync' def __init__(self, config): """ :param config: dict, reference to config section of selected DCS. i.e.: `zookeeper` for zookeeper, `etcd` for etcd, etc... """ self._name = config['name'] self._base_path = os.path.join('/', config.get('namespace', '/service/').strip('/'), config['scope']) self._set_loop_wait(config.get('loop_wait', 10)) self._ctl = bool(config.get('patronictl', False)) self._cluster = None self._cluster_thread_lock = Lock() self._last_leader_operation = '' self.event = Event() def client_path(self, path): return '/'.join([self._base_path, path.lstrip('/')]) @property def initialize_path(self): return self.client_path(self._INITIALIZE) @property def config_path(self): return self.client_path(self._CONFIG) @property def members_path(self): return self.client_path(self._MEMBERS) @property def member_path(self): return self.client_path(self._MEMBERS + self._name) @property def leader_path(self): return self.client_path(self._LEADER) @property def failover_path(self): return self.client_path(self._FAILOVER) @property def history_path(self): return self.client_path(self._HISTORY) @property def leader_optime_path(self): return self.client_path(self._LEADER_OPTIME) @property def sync_path(self): return self.client_path(self._SYNC) @abc.abstractmethod def set_ttl(self, ttl): """Set the new ttl value for leader key""" @abc.abstractmethod def set_retry_timeout(self, retry_timeout): """Set the new value for retry_timeout""" def _set_loop_wait(self, loop_wait): self._loop_wait = loop_wait def reload_config(self, config): self._set_loop_wait(config['loop_wait']) self.set_ttl(config['ttl']) self.set_retry_timeout(config['retry_timeout']) @property def loop_wait(self): return self._loop_wait @abc.abstractmethod def _load_cluster(self): """Internally this method should build `Cluster` object which represents current state and topology of the cluster in DCS. this method supposed to be called only by `get_cluster` method. raise `~DCSError` in case of communication or other problems with DCS. If the current node was running as a master and exception raised, instance would be demoted.""" def get_cluster(self): with self._cluster_thread_lock: try: self._load_cluster() except Exception: self._cluster = None raise return self._cluster @property def cluster(self): with self._cluster_thread_lock: return self._cluster def reset_cluster(self): with self._cluster_thread_lock: self._cluster = None @abc.abstractmethod def _write_leader_optime(self, last_operation): """write current xlog location into `/optime/leader` key in DCS :param last_operation: absolute xlog location in bytes :returns: `!True` on success.""" def write_leader_optime(self, last_operation): if self._last_leader_operation != last_operation and self._write_leader_optime(last_operation): self._last_leader_operation = last_operation @abc.abstractmethod def _update_leader(self): """Update leader key (or session) ttl :returns: `!True` if leader key (or session) has been updated successfully. If not, `!False` must be returned and current instance would be demoted. You have to use CAS (Compare And Swap) operation in order to update leader key, for example for etcd `prevValue` parameter must be used.""" def update_leader(self, last_operation): """Update leader key (or session) ttl and optime/leader :param last_operation: absolute xlog location in bytes :returns: `!True` if leader key (or session) has been updated successfully. If not, `!False` must be returned and current instance would be demoted.""" ret = self._update_leader() if ret and last_operation: self.write_leader_optime(last_operation) return ret @abc.abstractmethod def attempt_to_acquire_leader(self, permanent=False): """Attempt to acquire leader lock This method should create `/leader` key with value=`~self._name` :param permanent: if set to `!True`, the leader key will never expire. Used in patronictl for the external master :returns: `!True` if key has been created successfully. Key must be created atomically. In case if key already exists it should not be overwritten and `!False` must be returned""" @abc.abstractmethod def set_failover_value(self, value, index=None): """Create or update `/failover` key""" def manual_failover(self, leader, candidate, scheduled_at=None, index=None): failover_value = {} if leader: failover_value['leader'] = leader if candidate: failover_value['member'] = candidate if scheduled_at: failover_value['scheduled_at'] = scheduled_at.isoformat() return self.set_failover_value(json.dumps(failover_value, separators=(',', ':')), index) @abc.abstractmethod def set_config_value(self, value, index=None): """Create or update `/config` key""" @abc.abstractmethod def touch_member(self, data, ttl=None, permanent=False): """Update member key in DCS. This method should create or update key with the name = '/members/' + `~self._name` and value = data in a given DCS. :param data: information about instance (including connection strings) :param ttl: ttl for member key, optional parameter. If it is None `~self.member_ttl will be used` :param permanent: if set to `!True`, the member key will never expire. Used in patronictl for the external master. :returns: `!True` on success otherwise `!False` """ @abc.abstractmethod def take_leader(self): """This method should create leader key with value = `~self._name` and ttl=`~self.ttl` Since it could be called only on initial cluster bootstrap it could create this key regardless, overwriting the key if necessary.""" @abc.abstractmethod def initialize(self, create_new=True, sysid=""): """Race for cluster initialization. :param create_new: False if the key should already exist (in the case we are setting the system_id) :param sysid: PostgreSQL cluster system identifier, if specified, is written to the key :returns: `!True` if key has been created successfully. this method should create atomically initialize key and return `!True` otherwise it should return `!False`""" @abc.abstractmethod def delete_leader(self): """Voluntarily remove leader key from DCS This method should remove leader key if current instance is the leader""" @abc.abstractmethod def cancel_initialization(self): """ Removes the initialize key for a cluster """ @abc.abstractmethod def delete_cluster(self): """Delete cluster from DCS""" @staticmethod def sync_state(leader, sync_standby): """Build sync_state dict""" return {'leader': leader, 'sync_standby': sync_standby} def write_sync_state(self, leader, sync_standby, index=None): sync_value = self.sync_state(leader, sync_standby) return self.set_sync_state_value(json.dumps(sync_value, separators=(',', ':')), index) @abc.abstractmethod def set_history_value(self, value): """""" @abc.abstractmethod def set_sync_state_value(self, value, index=None): """""" @abc.abstractmethod def delete_sync_state(self, index=None): """""" def watch(self, leader_index, timeout): """If the current node is a master it should just sleep. Any other node should watch for changes of leader key with a given timeout :param leader_index: index of a leader key :param timeout: timeout in seconds :returns: `!True` if you would like to reschedule the next run of ha cycle""" self.event.wait(timeout) return self.event.isSet() patroni-1.4.2/patroni/dcs/consul.py000066400000000000000000000370121323411135200172720ustar00rootroot00000000000000from __future__ import absolute_import import json import logging import os import socket import ssl import time import urllib3 from consul import ConsulException, NotFound, base from patroni.dcs import AbstractDCS, ClusterConfig, Cluster, Failover, Leader, Member, SyncState, TimelineHistory from patroni.exceptions import DCSError from patroni.utils import deep_compare, parse_bool, Retry, RetryFailedError, split_host_port from urllib3.exceptions import HTTPError from six.moves.urllib.parse import urlencode, urlparse from six.moves.http_client import HTTPException logger = logging.getLogger(__name__) class ConsulError(DCSError): pass class ConsulInternalError(ConsulException): """An internal Consul server error occurred""" class InvalidSessionTTL(ConsulInternalError): """Session TTL is too small or too big""" class HTTPClient(object): def __init__(self, host='127.0.0.1', port=8500, token=None, scheme='http', verify=True, cert=None, ca_cert=None): self.token = token self._read_timeout = 10 self.base_uri = '{0}://{1}:{2}'.format(scheme, host, port) kwargs = {} if cert: if isinstance(cert, tuple): # Key and cert are separate kwargs['cert_file'] = cert[0] kwargs['key_file'] = cert[1] else: # combined certificate kwargs['cert_file'] = cert if ca_cert: kwargs['ca_certs'] = ca_cert if verify or ca_cert: kwargs['cert_reqs'] = ssl.CERT_REQUIRED self.http = urllib3.PoolManager(num_pools=10, **kwargs) self._ttl = None def set_read_timeout(self, timeout): self._read_timeout = timeout/3.0 @property def ttl(self): return self._ttl def set_ttl(self, ttl): ret = self._ttl != ttl self._ttl = ttl return ret @staticmethod def response(response): data = response.data.decode('utf-8') if response.status == 500: msg = '{0} {1}'.format(response.status, data) if data.startswith('Invalid Session TTL'): raise InvalidSessionTTL(msg) else: raise ConsulInternalError(msg) return base.Response(response.status, response.headers, data) def uri(self, path, params=None): return '{0}{1}{2}'.format(self.base_uri, path, params and '?' + urlencode(params) or '') def __getattr__(self, method): if method not in ('get', 'post', 'put', 'delete'): raise AttributeError("HTTPClient instance has no attribute '{0}'".format(method)) def wrapper(callback, path, params=None, data=''): # python-consul doesn't allow to specify ttl smaller then 10 seconds # because session_ttl_min defaults to 10s, so we have to do this ugly dirty hack... if method == 'put' and path == '/v1/session/create': ttl = '"ttl": "{0}s"'.format(self._ttl) if not data or data == '{}': data = '{' + ttl + '}' else: data = data[:-1] + ', ' + ttl + '}' kwargs = {'retries': 0, 'preload_content': False, 'body': data} if method == 'get' and isinstance(params, dict) and 'index' in params: kwargs['timeout'] = (float(params['wait'][:-1]) if 'wait' in params else 300) + 1 else: kwargs['timeout'] = self._read_timeout token = params.pop('token', self.token) if isinstance(params, dict) else self.token if token: kwargs['headers'] = {'X-Consul-Token': token} return callback(self.response(self.http.request(method.upper(), self.uri(path, params), **kwargs))) return wrapper class ConsulClient(base.Consul): def __init__(self, *args, **kwargs): self._cert = kwargs.pop('cert', None) self._ca_cert = kwargs.pop('ca_cert', None) self._token = kwargs.get('token') super(ConsulClient, self).__init__(*args, **kwargs) def connect(self, *args, **kwargs): kwargs.update(dict(zip(['host', 'port', 'scheme', 'verify'], args))) if self._cert: kwargs['cert'] = self._cert if self._ca_cert: kwargs['ca_cert'] = self._ca_cert if self._token: kwargs['token'] = self._token return HTTPClient(**kwargs) def catch_consul_errors(func): def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except (RetryFailedError, ConsulException, HTTPException, HTTPError, socket.error, socket.timeout): return False return wrapper class Consul(AbstractDCS): def __init__(self, config): super(Consul, self).__init__(config) self._scope = config['scope'] self._session = None self.__do_not_watch = False self._retry = Retry(deadline=config['retry_timeout'], max_delay=1, max_tries=-1, retry_exceptions=(ConsulInternalError, HTTPException, HTTPError, socket.error, socket.timeout)) self._my_member_data = {} kwargs = {} if 'url' in config: r = urlparse(config['url']) config.update({'scheme': r.scheme, 'host': r.hostname, 'port': r.port or 8500}) elif 'host' in config: host, port = split_host_port(config.get('host', '127.0.0.1:8500'), 8500) config['host'] = host if 'port' not in config: config['port'] = int(port) if config.get('cacert'): config['ca_cert'] = config.pop('cacert') if config.get('key') and config.get('cert'): config['cert'] = (config['cert'], config['key']) config_keys = ('host', 'port', 'token', 'scheme', 'cert', 'ca_cert', 'dc') kwargs = {p: config.get(p) for p in config_keys if config.get(p)} verify = config.get('verify') if not isinstance(verify, bool): verify = parse_bool(verify) if isinstance(verify, bool): kwargs['verify'] = verify self._client = ConsulClient(**kwargs) self.set_retry_timeout(config['retry_timeout']) self.set_ttl(config.get('ttl') or 30) self._last_session_refresh = 0 self.__session_checks = config.get('checks') if not self._ctl: self.create_session() def retry(self, *args, **kwargs): return self._retry.copy()(*args, **kwargs) def create_session(self): while not self._session: try: self.refresh_session() except ConsulError: logger.info('waiting on consul') time.sleep(5) def set_ttl(self, ttl): if self._client.http.set_ttl(ttl/2.0): # Consul multiplies the TTL by 2x self._session = None self.__do_not_watch = True def set_retry_timeout(self, retry_timeout): self._retry.deadline = retry_timeout self._client.http.set_read_timeout(retry_timeout) def adjust_ttl(self): try: settings = self._client.agent.self() min_ttl = (settings['Config']['SessionTTLMin'] or 10000000000)/1000000000.0 logger.warning('Changing Session TTL from %s to %s', self._client.http.ttl, min_ttl) self._client.http.set_ttl(min_ttl) except Exception: logger.exception('adjust_ttl') def _do_refresh_session(self): """:returns: `!True` if it had to create new session""" if self._session and self._last_session_refresh + self._loop_wait > time.time(): return False if self._session: try: self._client.session.renew(self._session) except NotFound: self._session = None ret = not self._session if ret: try: self._session = self._client.session.create(name=self._scope + '-' + self._name, checks=self.__session_checks, lock_delay=0.001, behavior='delete') except InvalidSessionTTL: logger.exception('session.create') self.adjust_ttl() raise self._last_session_refresh = time.time() return ret def refresh_session(self): try: return self.retry(self._do_refresh_session) except (ConsulException, RetryFailedError): logger.exception('refresh_session') raise ConsulError('Failed to renew/create session') def client_path(self, path): return super(Consul, self).client_path(path)[1:] @staticmethod def member(node): return Member.from_node(node['ModifyIndex'], os.path.basename(node['Key']), node.get('Session'), node['Value']) def _load_cluster(self): try: path = self.client_path('/') _, results = self.retry(self._client.kv.get, path, recurse=True) if results is None: raise NotFound nodes = {} for node in results: node['Value'] = (node['Value'] or b'').decode('utf-8') nodes[os.path.relpath(node['Key'], path)] = node # get initialize flag initialize = nodes.get(self._INITIALIZE) initialize = initialize and initialize['Value'] # get global dynamic configuration config = nodes.get(self._CONFIG) config = config and ClusterConfig.from_node(config['ModifyIndex'], config['Value']) # get timeline history history = nodes.get(self._HISTORY) history = history and TimelineHistory.from_node(history['ModifyIndex'], history['Value']) # get last leader operation last_leader_operation = nodes.get(self._LEADER_OPTIME) last_leader_operation = 0 if last_leader_operation is None else int(last_leader_operation['Value']) # get list of members members = [self.member(n) for k, n in nodes.items() if k.startswith(self._MEMBERS) and k.count('/') == 1] # get leader leader = nodes.get(self._LEADER) if not self._ctl and leader and leader['Value'] == self._name \ and self._session != leader.get('Session', 'x'): logger.info('I am leader but not owner of the session. Removing leader node') self._client.kv.delete(self.leader_path, cas=leader['ModifyIndex']) leader = None if leader: member = Member(-1, leader['Value'], None, {}) member = ([m for m in members if m.name == leader['Value']] or [member])[0] leader = Leader(leader['ModifyIndex'], leader.get('Session'), member) # failover key failover = nodes.get(self._FAILOVER) if failover: failover = Failover.from_node(failover['ModifyIndex'], failover['Value']) # get synchronization state sync = nodes.get(self._SYNC) sync = SyncState.from_node(sync and sync['ModifyIndex'], sync and sync['Value']) self._cluster = Cluster(initialize, config, leader, last_leader_operation, members, failover, sync, history) except NotFound: self._cluster = Cluster(None, None, None, None, [], None, None, None) except Exception: logger.exception('get_cluster') raise ConsulError('Consul is not responding properly') def touch_member(self, data, ttl=None, permanent=False): cluster = self.cluster member = cluster and cluster.get_member(self._name, fallback_to_leader=False) create_member = not permanent and self.refresh_session() if member and (create_member or member.session != self._session): try: self._client.kv.delete(self.member_path) create_member = True except Exception: return False if not create_member and member and deep_compare(data, self._my_member_data): return True try: args = {} if permanent else {'acquire': self._session} self._client.kv.put(self.member_path, json.dumps(data, separators=(',', ':')), **args) self._my_member_data = data return True except Exception: logger.exception('touch_member') return False @catch_consul_errors def _do_attempt_to_acquire_leader(self, kwargs): return self.retry(self._client.kv.put, self.leader_path, self._name, **kwargs) def attempt_to_acquire_leader(self, permanent=False): if not self._session and not permanent: self.refresh_session() ret = self._do_attempt_to_acquire_leader({} if permanent else {'acquire': self._session}) if not ret: logger.info('Could not take out TTL lock') return ret def take_leader(self): return self.attempt_to_acquire_leader() @catch_consul_errors def set_failover_value(self, value, index=None): return self._client.kv.put(self.failover_path, value, cas=index) @catch_consul_errors def set_config_value(self, value, index=None): return self._client.kv.put(self.config_path, value, cas=index) @catch_consul_errors def _write_leader_optime(self, last_operation): return self._client.kv.put(self.leader_optime_path, last_operation) @catch_consul_errors def _update_leader(self): if self._session: self.retry(self._client.session.renew, self._session) self._last_session_refresh = time.time() return bool(self._session) @catch_consul_errors def initialize(self, create_new=True, sysid=''): kwargs = {'cas': 0} if create_new else {} return self.retry(self._client.kv.put, self.initialize_path, sysid, **kwargs) @catch_consul_errors def cancel_initialization(self): return self.retry(self._client.kv.delete, self.initialize_path) @catch_consul_errors def delete_cluster(self): return self.retry(self._client.kv.delete, self.client_path(''), recurse=True) @catch_consul_errors def set_history_value(self, value): return self._client.kv.put(self.history_path, value) @catch_consul_errors def delete_leader(self): cluster = self.cluster if cluster and isinstance(cluster.leader, Leader) and cluster.leader.name == self._name: return self._client.kv.delete(self.leader_path, cas=cluster.leader.index) @catch_consul_errors def set_sync_state_value(self, value, index=None): return self.retry(self._client.kv.put, self.sync_path, value, cas=index) @catch_consul_errors def delete_sync_state(self, index=None): return self.retry(self._client.kv.delete, self.sync_path, cas=index) def watch(self, leader_index, timeout): if self.__do_not_watch: self.__do_not_watch = False return True if leader_index: end_time = time.time() + timeout while timeout >= 1: try: idx, _ = self._client.kv.get(self.leader_path, index=leader_index, wait=str(timeout) + 's') return str(idx) != str(leader_index) except (ConsulException, HTTPException, HTTPError, socket.error, socket.timeout): logging.exception('watch') timeout = end_time - time.time() try: return super(Consul, self).watch(None, timeout) finally: self.event.clear() patroni-1.4.2/patroni/dcs/etcd.py000066400000000000000000000573751323411135200167240ustar00rootroot00000000000000from __future__ import absolute_import import etcd import json import logging import os import urllib3.util.connection import random import requests import six import socket import time from dns.exception import DNSException from dns import resolver from patroni.dcs import AbstractDCS, ClusterConfig, Cluster, Failover, Leader, Member, SyncState, TimelineHistory from patroni.exceptions import DCSError from patroni.utils import Retry, RetryFailedError, split_host_port from urllib3.exceptions import HTTPError, ReadTimeoutError from requests.exceptions import RequestException from six.moves.queue import Queue from six.moves.http_client import HTTPException from six.moves.urllib_parse import urlparse from threading import Thread logger = logging.getLogger(__name__) def uri(protocol, host, port, endpoint=''): return '{0}://{1}:{2}{3}'.format(protocol, host, port, endpoint) class EtcdError(DCSError): pass class DnsCachingResolver(Thread): def __init__(self, cache_time=600.0, cache_fail_time=30.0): super(DnsCachingResolver, self).__init__() self._cache = {} self._cache_time = cache_time self._cache_fail_time = cache_fail_time self._resolve_queue = Queue() self.daemon = True self.start() def run(self): while True: (host, port), attempt = self._resolve_queue.get() response = self._do_resolve(host, port) if response: self._cache[(host, port)] = (time.time(), response) else: if attempt < 10: self.resolve_async(host, port, attempt + 1) time.sleep(1) def resolve(self, host, port): current_time = time.time() cached_time, response = self._cache.get((host, port), (0, [])) time_passed = current_time - cached_time if time_passed > self._cache_time or (not response and time_passed > self._cache_fail_time): new_response = self._do_resolve(host, port) if new_response: self._cache[(host, port)] = (current_time, new_response) response = new_response return response def resolve_async(self, host, port, attempt=0): self._resolve_queue.put(((host, port), attempt)) @staticmethod def _do_resolve(host, port): try: return socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP) except socket.gaierror: logger.warning('failed to resolve host %s', host) return [] class Client(etcd.Client): def __init__(self, config, dns_resolver, cache_ttl=300): self._dns_resolver = dns_resolver self.set_machines_cache_ttl(cache_ttl) self._machines_cache_updated = 0 args = {p: config.get(p) for p in ('host', 'port', 'protocol', 'use_proxies', 'username', 'password', 'cert', 'ca_cert') if config.get(p)} super(Client, self).__init__(read_timeout=config['retry_timeout'], **args) self._config = config self._load_machines_cache() self._allow_reconnect = not self._use_proxies def _build_request_parameters(self): kwargs = {'headers': self._get_headers(), 'redirect': self.allow_redirect} # calculate the number of retries and timeout *per node* # actual number of retries depends on the number of nodes etcd_nodes = len(self._machines_cache) + 1 kwargs['retries'] = 0 if etcd_nodes > 3 else (1 if etcd_nodes > 1 else 2) # if etcd_nodes > 3: # kwargs.update({'retries': 0, 'timeout': float(self.read_timeout)/etcd_nodes}) # elif etcd_nodes > 1: # kwargs.update({'retries': 1, 'timeout': self.read_timeout/2.0/etcd_nodes}) # else: # kwargs.update({'retries': 2, 'timeout': self.read_timeout/3.0}) kwargs['timeout'] = self.read_timeout/float(kwargs['retries'] + 1)/etcd_nodes return kwargs def set_machines_cache_ttl(self, cache_ttl): self._machines_cache_ttl = cache_ttl @property def machines(self): """Original `machines` method(property) of `etcd.Client` class raise exception when it failed to get list of etcd cluster members. This method is being called only when request failed on one of the etcd members during `api_execute` call. For us it's more important to execute original request rather then get new topology of etcd cluster. So we will catch this exception and return empty list of machines. Later, during next `api_execute` call we will forcefully update machines_cache. Also this method implements the same timeout-retry logic as `api_execute`, because the original method was retrying 2 times with the `read_timeout` on each node.""" kwargs = self._build_request_parameters() while True: try: response = self.http.request(self._MGET, self._base_uri + self.version_prefix + '/machines', **kwargs) machines = [n.strip() for n in self._handle_server_response(response).data.decode('utf-8').split(',')] logger.debug("Retrieved list of machines: %s", machines) random.shuffle(machines) for url in machines: r = urlparse(url) port = r.port or (443 if r.scheme == 'https' else 80) self._dns_resolver.resolve_async(r.hostname, port) return machines except Exception as e: # We can't get the list of machines, if one server is in the # machines cache, try on it logger.error("Failed to get list of machines from %s%s: %r", self._base_uri, self.version_prefix, e) if self._machines_cache: self._base_uri = self._machines_cache.pop(0) logger.info("Retrying on %s", self._base_uri) elif self._update_machines_cache: raise etcd.EtcdException("Could not get the list of servers, " "maybe you provided the wrong " "host(s) to connect to?") else: return [] def set_read_timeout(self, timeout): self._read_timeout = timeout def _do_http_request(self, request_executor, method, url, fields=None, **kwargs): try: response = request_executor(method, url, fields=fields, **kwargs) response.data.decode('utf-8') self._check_cluster_id(response) except (HTTPError, HTTPException, socket.error, socket.timeout) as e: if (isinstance(fields, dict) and fields.get("wait") == "true" and isinstance(e, ReadTimeoutError)): logger.debug("Watch timed out.") raise etcd.EtcdWatchTimedOut("Watch timed out: {0}".format(e), cause=e) logger.error("Request to server %s failed: %r", self._base_uri, e) logger.info("Reconnection allowed, looking for another server.") self._base_uri = self._next_server(cause=e) response = False return response def api_execute(self, path, method, params=None, timeout=None): if not path.startswith('/'): raise ValueError('Path does not start with /') kwargs = {'fields': params, 'preload_content': False} if method in [self._MGET, self._MDELETE]: request_executor = self.http.request elif method in [self._MPUT, self._MPOST]: request_executor = self.http.request_encode_body kwargs['encode_multipart'] = False else: raise etcd.EtcdException('HTTP method {0} not supported'.format(method)) # Update machines_cache if previous attempt of update has failed if self._update_machines_cache: self._load_machines_cache() elif time.time() - self._machines_cache_updated > self._machines_cache_ttl: self._machines_cache = self.machines if self._base_uri in self._machines_cache: self._machines_cache.remove(self._base_uri) self._machines_cache_updated = time.time() kwargs.update(self._build_request_parameters()) if timeout is not None: kwargs.update({'retries': 0, 'timeout': timeout}) response = False try: some_request_failed = False while not response: response = self._do_http_request(request_executor, method, self._base_uri + path, **kwargs) if response is False: some_request_failed = True if some_request_failed and not self._use_proxies: self._machines_cache = self.machines if self._base_uri in self._machines_cache: self._machines_cache.remove(self._base_uri) except etcd.EtcdConnectionFailed: self._update_machines_cache = True if not response: raise return self._handle_server_response(response) @staticmethod def get_srv_record(host): try: return [(r.target.to_text(True), r.port) for r in resolver.query(host, 'SRV')] except DNSException: return [] def _get_machines_cache_from_srv(self, srv): """Fetch list of etcd-cluster member by resolving _etcd-server._tcp. SRV record. This record should contain list of host and peer ports which could be used to run 'GET http://{host}:{port}/members' request (peer protocol)""" ret = [] for r in ['-client-ssl', '-client', '-ssl', '', '-server-ssl', '-server']: protocol = 'https' if '-ssl' in r else 'http' endpoint = '/members' if '-server' in r else '' for host, port in self.get_srv_record('_etcd{0}._tcp.{1}'.format(r, srv)): url = uri(protocol, host, port, endpoint) if endpoint: try: response = requests.get(url, timeout=self.read_timeout, verify=False) if response.ok: for member in response.json(): ret.extend(member['clientURLs']) break except RequestException: logger.exception('GET %s', url) else: ret.append(url) if ret: self._protocol = protocol break else: logger.warning('Can not resolve SRV for %s', srv) return list(set(ret)) def _get_machines_cache_from_dns(self, host, port): """One host might be resolved into multiple ip addresses. We will make list out of it""" if self.protocol == 'http': ret = [] for af, _, _, _, sa in self._dns_resolver.resolve(host, port): host, port = sa[:2] if af == socket.AF_INET6: host = '[{0}]'.format(host) ret.append(uri(self.protocol, host, port)) if ret: return list(set(ret)) return [uri(self.protocol, host, port)] def _load_machines_cache(self): """This method should fill up `_machines_cache` from scratch. It could happen only in two cases: 1. During class initialization 2. When all etcd members failed""" self._update_machines_cache = True if 'srv' not in self._config and 'host' not in self._config and 'hosts' not in self._config: raise Exception('Neither srv, hosts, host nor url are defined in etcd section of config') if self._use_proxies: self._machines_cache = [uri(self.protocol, self._config['host'], self._config['port'])] else: self._machines_cache = [] if 'srv' in self._config: self._machines_cache = self._get_machines_cache_from_srv(self._config['srv']) if not self._machines_cache and 'hosts' in self._config: self._machines_cache = list(self._config['hosts']) if not self._machines_cache and 'host' in self._config: self._machines_cache = self._get_machines_cache_from_dns(self._config['host'], self._config['port']) # Can not bootstrap list of etcd-cluster members, giving up if not self._machines_cache: raise etcd.EtcdException # After filling up initial list of machines_cache we should ask etcd-cluster about actual list self._base_uri = self._next_server() self._machines_cache = self.machines if self._base_uri in self._machines_cache: self._machines_cache.remove(self._base_uri) self._update_machines_cache = False self._machines_cache_updated = time.time() class Etcd(AbstractDCS): def __init__(self, config): super(Etcd, self).__init__(config) self._ttl = int(config.get('ttl') or 30) self._retry = Retry(deadline=config['retry_timeout'], max_delay=1, max_tries=-1, retry_exceptions=(etcd.EtcdLeaderElectionInProgress, etcd.EtcdWatcherCleared, etcd.EtcdEventIndexCleared)) self._client = self.get_etcd_client(config) self.__do_not_watch = False self._has_failed = False def retry(self, *args, **kwargs): return self._retry.copy()(*args, **kwargs) def _handle_exception(self, e, name='', do_sleep=False, raise_ex=None): if not self._has_failed: logger.exception(name) else: logger.error(e) if do_sleep: time.sleep(1) self._has_failed = True if isinstance(raise_ex, Exception): raise raise_ex def catch_etcd_errors(func): def wrapper(self, *args, **kwargs): try: retval = func(self, *args, **kwargs) is not None self._has_failed = False return retval except (RetryFailedError, etcd.EtcdException) as e: self._handle_exception(e) return False except Exception as e: self._handle_exception(e, raise_ex=EtcdError('unexpected error')) return wrapper @staticmethod def get_etcd_client(config): if 'proxy' in config: config['use_proxies'] = True config['url'] = config['proxy'] if 'url' in config: r = urlparse(config['url']) config.update({'protocol': r.scheme, 'host': r.hostname, 'port': r.port or 2379, 'username': r.username, 'password': r.password}) elif 'hosts' in config: hosts = config.pop('hosts') default_port = config.pop('port', 2379) protocol = config.get('protocol', 'http') if isinstance(hosts, six.string_types): hosts = hosts.split(',') config['hosts'] = [] for value in hosts: if isinstance(value, six.string_types): config['hosts'].append(uri(protocol, *split_host_port(value, default_port))) elif 'host' in config: host, port = split_host_port(config['host'], 2379) config['host'] = host if 'port' not in config: config['port'] = int(port) if config.get('cacert'): config['ca_cert'] = config.pop('cacert') if config.get('key') and config.get('cert'): config['cert'] = (config['cert'], config['key']) for p in ('discovery_srv', 'srv_domain'): if p in config: config['srv'] = config.pop(p) dns_resolver = DnsCachingResolver() def create_connection_patched(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None, socket_options=None): host, port = address if host.startswith('['): host = host.strip('[]') err = None for af, socktype, proto, _, sa in dns_resolver.resolve(host, port): sock = None try: sock = socket.socket(af, socktype, proto) if socket_options: for opt in socket_options: sock.setsockopt(*opt) if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT: sock.settimeout(timeout) if source_address: sock.bind(source_address) sock.connect(sa) return sock except socket.error as e: err = e if sock is not None: sock.close() sock = None if err is not None: raise err raise socket.error("getaddrinfo returns an empty list") urllib3.util.connection.create_connection = create_connection_patched client = None while not client: try: client = Client(config, dns_resolver) except etcd.EtcdException: logger.info('waiting on etcd') time.sleep(5) return client def set_ttl(self, ttl): ttl = int(ttl) self.__do_not_watch = self._ttl != ttl self._ttl = ttl self._client.set_machines_cache_ttl(ttl*10) def set_retry_timeout(self, retry_timeout): self._retry.deadline = retry_timeout self._client.set_read_timeout(retry_timeout) @staticmethod def member(node): return Member.from_node(node.modifiedIndex, os.path.basename(node.key), node.ttl, node.value) def _load_cluster(self): try: result = self.retry(self._client.read, self.client_path(''), recursive=True) nodes = {os.path.relpath(node.key, result.key): node for node in result.leaves} # get initialize flag initialize = nodes.get(self._INITIALIZE) initialize = initialize and initialize.value # get global dynamic configuration config = nodes.get(self._CONFIG) config = config and ClusterConfig.from_node(config.modifiedIndex, config.value) # get timeline history history = nodes.get(self._HISTORY) history = history and TimelineHistory.from_node(history.modifiedIndex, history.value) # get last leader operation last_leader_operation = nodes.get(self._LEADER_OPTIME) last_leader_operation = 0 if last_leader_operation is None else int(last_leader_operation.value) # get list of members members = [self.member(n) for k, n in nodes.items() if k.startswith(self._MEMBERS) and k.count('/') == 1] # get leader leader = nodes.get(self._LEADER) if leader: member = Member(-1, leader.value, None, {}) member = ([m for m in members if m.name == leader.value] or [member])[0] index = result.etcd_index if result.etcd_index > leader.modifiedIndex else leader.modifiedIndex + 1 leader = Leader(index, leader.ttl, member) # failover key failover = nodes.get(self._FAILOVER) if failover: failover = Failover.from_node(failover.modifiedIndex, failover.value) # get synchronization state sync = nodes.get(self._SYNC) sync = SyncState.from_node(sync and sync.modifiedIndex, sync and sync.value) self._cluster = Cluster(initialize, config, leader, last_leader_operation, members, failover, sync, history) except etcd.EtcdKeyNotFound: self._cluster = Cluster(None, None, None, None, [], None, None, None) except Exception as e: self._handle_exception(e, 'get_cluster', raise_ex=EtcdError('Etcd is not responding properly')) self._has_failed = False @catch_etcd_errors def touch_member(self, data, ttl=None, permanent=False): data = json.dumps(data, separators=(',', ':')) return self.retry(self._client.set, self.member_path, data, None if permanent else ttl or self._ttl) @catch_etcd_errors def take_leader(self): return self.retry(self._client.set, self.leader_path, self._name, self._ttl) def attempt_to_acquire_leader(self, permanent=False): try: return bool(self.retry(self._client.write, self.leader_path, self._name, ttl=None if permanent else self._ttl, prevExist=False)) except etcd.EtcdAlreadyExist: logger.info('Could not take out TTL lock') except (RetryFailedError, etcd.EtcdException): pass return False @catch_etcd_errors def set_failover_value(self, value, index=None): return self._client.write(self.failover_path, value, prevIndex=index or 0) @catch_etcd_errors def set_config_value(self, value, index=None): return self._client.write(self.config_path, value, prevIndex=index or 0) @catch_etcd_errors def _write_leader_optime(self, last_operation): return self._client.set(self.leader_optime_path, last_operation) @catch_etcd_errors def _update_leader(self): return self.retry(self._client.test_and_set, self.leader_path, self._name, self._name, self._ttl) @catch_etcd_errors def initialize(self, create_new=True, sysid=""): return self.retry(self._client.write, self.initialize_path, sysid, prevExist=(not create_new)) @catch_etcd_errors def delete_leader(self): return self._client.delete(self.leader_path, prevValue=self._name) @catch_etcd_errors def cancel_initialization(self): return self.retry(self._client.delete, self.initialize_path) @catch_etcd_errors def delete_cluster(self): return self.retry(self._client.delete, self.client_path(''), recursive=True) @catch_etcd_errors def set_history_value(self, value): return self._client.write(self.history_path, value) @catch_etcd_errors def set_sync_state_value(self, value, index=None): return self.retry(self._client.write, self.sync_path, value, prevIndex=index or 0) @catch_etcd_errors def delete_sync_state(self, index=None): return self.retry(self._client.delete, self.sync_path, prevIndex=index or 0) def watch(self, leader_index, timeout): if self.__do_not_watch: self.__do_not_watch = False return True if leader_index: end_time = time.time() + timeout while timeout >= 1: # when timeout is too small urllib3 doesn't have enough time to connect try: self._client.watch(self.leader_path, index=leader_index, timeout=timeout + 0.5) self._has_failed = False # Synchronous work of all cluster members with etcd is less expensive # than reestablishing http connection every time from every replica. return True except etcd.EtcdWatchTimedOut: self._client.http.clear() self._has_failed = False return False except (etcd.EtcdEventIndexCleared, etcd.EtcdWatcherCleared): # Watch failed self._has_failed = False return True # leave the loop, because watch with the same parameters will fail anyway except etcd.EtcdException as e: self._handle_exception(e, 'watch', True) timeout = end_time - time.time() try: return super(Etcd, self).watch(None, timeout) finally: self.event.clear() patroni-1.4.2/patroni/dcs/exhibitor.py000066400000000000000000000050521323411135200177630ustar00rootroot00000000000000import logging import random import requests import time from patroni.dcs.zookeeper import ZooKeeper from requests.exceptions import RequestException logger = logging.getLogger(__name__) class ExhibitorEnsembleProvider(object): TIMEOUT = 3.1 def __init__(self, hosts, port, uri_path='/exhibitor/v1/cluster/list', poll_interval=300): self._exhibitor_port = port self._uri_path = uri_path self._poll_interval = poll_interval self._exhibitors = hosts self._master_exhibitors = hosts self._zookeeper_hosts = '' self._next_poll = None while not self.poll(): logger.info('waiting on exhibitor') time.sleep(5) def poll(self): if self._next_poll and self._next_poll > time.time(): return False json = self._query_exhibitors(self._exhibitors) if not json: json = self._query_exhibitors(self._master_exhibitors) if isinstance(json, dict) and 'servers' in json and 'port' in json: self._next_poll = time.time() + self._poll_interval zookeeper_hosts = ','.join([h + ':' + str(json['port']) for h in sorted(json['servers'])]) if self._zookeeper_hosts != zookeeper_hosts: logger.info('ZooKeeper connection string has changed: %s => %s', self._zookeeper_hosts, zookeeper_hosts) self._zookeeper_hosts = zookeeper_hosts self._exhibitors = json['servers'] return True return False def _query_exhibitors(self, exhibitors): random.shuffle(exhibitors) for host in exhibitors: uri = 'http://{0}:{1}{2}'.format(host, self._exhibitor_port, self._uri_path) try: response = requests.get(uri, timeout=self.TIMEOUT) return response.json() except RequestException: pass return None @property def zookeeper_hosts(self): return self._zookeeper_hosts class Exhibitor(ZooKeeper): def __init__(self, config): interval = config.get('poll_interval', 300) self._ensemble_provider = ExhibitorEnsembleProvider(config['hosts'], config['port'], poll_interval=interval) config = config.copy() config['hosts'] = self._ensemble_provider.zookeeper_hosts super(Exhibitor, self).__init__(config) def _load_cluster(self): if self._ensemble_provider.poll(): self._client.set_hosts(self._ensemble_provider.zookeeper_hosts) return super(Exhibitor, self)._load_cluster() patroni-1.4.2/patroni/dcs/kubernetes.py000066400000000000000000000443731323411135200201460ustar00rootroot00000000000000from __future__ import absolute_import import datetime import functools import json import logging import socket import sys import time from kubernetes import client as k8s_client, config as k8s_config, watch as k8s_watch from patroni.dcs import AbstractDCS, ClusterConfig, Cluster, Failover, Leader, Member, SyncState, TimelineHistory from patroni.exceptions import DCSError from patroni.utils import deep_compare, tzutc, Retry, RetryFailedError from urllib3.exceptions import HTTPError from six.moves.http_client import HTTPException logger = logging.getLogger(__name__) class KubernetesError(DCSError): pass class KubernetesRetriableException(k8s_client.rest.ApiException): def __init__(self, orig): super(KubernetesRetriableException, self).__init__(orig.status, orig.reason) self.body = orig.body self.headers = orig.headers class CoreV1ApiProxy(object): def __init__(self, use_endpoints=False): self._api = k8s_client.CoreV1Api() self._request_timeout = None self._use_endpoints = use_endpoints def set_timeout(self, timeout): self._request_timeout = (1, timeout / 3.0) def __getattr__(self, func): if func.endswith('_kind'): func = func[:-4] + ('endpoints' if self._use_endpoints else 'config_map') def wrapper(*args, **kwargs): if '_request_timeout' not in kwargs: kwargs['_request_timeout'] = self._request_timeout try: return getattr(self._api, func)(*args, **kwargs) except k8s_client.rest.ApiException as e: if e.status in (502, 503, 504): # XXX raise KubernetesRetriableException(e) raise return wrapper def catch_kubernetes_errors(func): def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except k8s_client.rest.ApiException as e: if e.status == 403: logger.exception('Permission denied') elif e.status != 409: # Object exists or conflict in resource_version logger.exception('Unexpected error from Kubernetes API') return False except (RetryFailedError, HTTPException, HTTPError, socket.error, socket.timeout): return False return wrapper class Kubernetes(AbstractDCS): def __init__(self, config): self._labels = config['labels'] self._labels[config.get('scope_label', 'cluster-name')] = config['scope'] self._label_selector = ','.join('{0}={1}'.format(k, v) for k, v in self._labels.items()) self._namespace = config.get('namespace') or 'default' self._role_label = config.get('role_label', 'role') config['namespace'] = '' super(Kubernetes, self).__init__(config) self._retry = Retry(deadline=config['retry_timeout'], max_delay=1, max_tries=-1, retry_exceptions=(KubernetesRetriableException, HTTPException, HTTPError, socket.error, socket.timeout)) self._ttl = None try: k8s_config.load_incluster_config() except k8s_config.ConfigException: k8s_config.load_kube_config(context=config.get('context', 'local')) self.__subsets = None use_endpoints = config.get('use_endpoints') and (config.get('patronictl') or 'pod_ip' in config) if use_endpoints: addresses = [k8s_client.V1EndpointAddress(ip=config['pod_ip'])] ports = [] for p in config.get('ports', [{}]): port = {'port': int(p.get('port', '5432'))} port.update({n: p[n] for n in ('name', 'protocol') if p.get(n)}) ports.append(k8s_client.V1EndpointPort(**port)) self.__subsets = [k8s_client.V1EndpointSubset(addresses=addresses, ports=ports)] self._api = CoreV1ApiProxy(use_endpoints) self.set_retry_timeout(config['retry_timeout']) self.set_ttl(config.get('ttl') or 30) self._leader_observed_record = {} self._leader_observed_time = None self._leader_resource_version = None self._leader_observed_subsets = [] self.__do_not_watch = False def retry(self, *args, **kwargs): return self._retry.copy()(*args, **kwargs) def client_path(self, path): return super(Kubernetes, self).client_path(path)[1:].replace('/', '-') @property def leader_path(self): return self._base_path[1:] if self.__subsets else super(Kubernetes, self).leader_path def set_ttl(self, ttl): ttl = int(ttl) self.__do_not_watch = self._ttl != ttl self._ttl = ttl def set_retry_timeout(self, retry_timeout): self._retry.deadline = retry_timeout self._api.set_timeout(retry_timeout) @staticmethod def member(pod): annotations = pod.metadata.annotations or {} member = Member.from_node(pod.metadata.resource_version, pod.metadata.name, None, annotations.get('status', '')) member.data['pod_labels'] = pod.metadata.labels return member def _load_cluster(self): try: # get list of members response = self.retry(self._api.list_namespaced_pod, self._namespace, label_selector=self._label_selector) members = [self.member(pod) for pod in response.items] response = self.retry(self._api.list_namespaced_kind, self._namespace, label_selector=self._label_selector) nodes = {item.metadata.name: item for item in response.items} config = nodes.get(self.config_path) metadata = config and config.metadata annotations = metadata and metadata.annotations or {} # get initialize flag initialize = annotations.get(self._INITIALIZE) # get global dynamic configuration config = ClusterConfig.from_node(metadata and metadata.resource_version, annotations.get(self._CONFIG) or '{}') # get timeline history history = TimelineHistory.from_node(metadata and metadata.resource_version, annotations.get(self._HISTORY) or '[]') leader = nodes.get(self.leader_path) metadata = leader and leader.metadata self._leader_resource_version = metadata.resource_version if metadata else None self._leader_observed_subsets = leader.subsets if self.__subsets and leader else [] annotations = metadata and metadata.annotations or {} # get last leader operation last_leader_operation = annotations.get(self._OPTIME) last_leader_operation = 0 if last_leader_operation is None else int(last_leader_operation) # get leader leader_record = {n: annotations.get(n) for n in (self._LEADER, 'acquireTime', 'ttl', 'renewTime', 'transitions') if n in annotations} if (leader_record or self._leader_observed_record) and leader_record != self._leader_observed_record: self._leader_observed_record = leader_record self._leader_observed_time = time.time() leader = leader_record.get(self._LEADER) try: ttl = int(leader_record.get('ttl')) or self._ttl except (TypeError, ValueError): ttl = self._ttl if not metadata or not self._leader_observed_time or self._leader_observed_time + ttl < time.time(): leader = None if metadata: member = Member(-1, leader, None, {}) member = ([m for m in members if m.name == leader] or [member])[0] leader = Leader(response.metadata.resource_version, None, member) # failover key failover = nodes.get(self.failover_path) metadata = failover and failover.metadata failover = Failover.from_node(metadata and metadata.resource_version, metadata and metadata.annotations) # get synchronization state sync = nodes.get(self.sync_path) metadata = sync and sync.metadata sync = SyncState.from_node(metadata and metadata.resource_version, metadata and metadata.annotations) self._cluster = Cluster(initialize, config, leader, last_leader_operation, members, failover, sync, history) except Exception: logger.exception('get_cluster') raise KubernetesError('Kubernetes API is not responding properly') @staticmethod def compare_ports(p1, p2): return p1.name == p2.name and p1.port == p2.port and (p1.protocol or 'TCP') == (p2.protocol or 'TCP') @staticmethod def subsets_changed(last_observed_subsets, subsets): """ >>> Kubernetes.subsets_changed([], []) False >>> Kubernetes.subsets_changed([], [k8s_client.V1EndpointSubset()]) True >>> s1 = [k8s_client.V1EndpointSubset(addresses=[k8s_client.V1EndpointAddress(ip='1.2.3.4')])] >>> s2 = [k8s_client.V1EndpointSubset(addresses=[k8s_client.V1EndpointAddress(ip='1.2.3.5')])] >>> Kubernetes.subsets_changed(s1, s2) True >>> a = [k8s_client.V1EndpointAddress(ip='1.2.3.4')] >>> s1 = [k8s_client.V1EndpointSubset(addresses=a, ports=[k8s_client.V1EndpointPort(protocol='TCP', port=1)])] >>> s2 = [k8s_client.V1EndpointSubset(addresses=a, ports=[k8s_client.V1EndpointPort(port=5432)])] >>> Kubernetes.subsets_changed(s1, s2) True >>> p1 = k8s_client.V1EndpointPort(name='port1', port=1) >>> p2 = k8s_client.V1EndpointPort(name='port2', port=2) >>> p3 = k8s_client.V1EndpointPort(name='port3', port=3) >>> s1 = [k8s_client.V1EndpointSubset(addresses=a, ports=[p1, p2])] >>> s2 = [k8s_client.V1EndpointSubset(addresses=a, ports=[p2, p3])] >>> Kubernetes.subsets_changed(s1, s2) True >>> s2 = [k8s_client.V1EndpointSubset(addresses=a, ports=[p2, p1])] >>> Kubernetes.subsets_changed(s1, s2) False """ if len(last_observed_subsets) != len(subsets): return True if subsets == []: return False if len(last_observed_subsets[0].addresses or []) != 1 or \ last_observed_subsets[0].addresses[0].ip != subsets[0].addresses[0].ip or \ len(last_observed_subsets[0].ports) != len(subsets[0].ports): return True if len(subsets[0].ports) == 1: return not Kubernetes.compare_ports(last_observed_subsets[0].ports[0], subsets[0].ports[0]) observed_ports = {p.name: p for p in last_observed_subsets[0].ports} for p in subsets[0].ports: if p.name not in observed_ports or not Kubernetes.compare_ports(p, observed_ports.pop(p.name)): return True return False @catch_kubernetes_errors def patch_or_create(self, name, annotations, resource_version=None, patch=False, retry=True, subsets=None): metadata = {'namespace': self._namespace, 'name': name, 'labels': self._labels, 'annotations': annotations} if patch or resource_version: if resource_version is not None: metadata['resource_version'] = resource_version func = functools.partial(self._api.patch_namespaced_kind, name) else: func = functools.partial(self._api.create_namespaced_kind) # skip annotations with null values metadata['annotations'] = {k: v for k, v in metadata['annotations'].items() if v is not None} metadata = k8s_client.V1ObjectMeta(**metadata) if subsets is not None and self.__subsets: endpoints = {'metadata': metadata} if self.subsets_changed(self._leader_observed_subsets, subsets): endpoints['subsets'] = subsets body = k8s_client.V1Endpoints(**endpoints) else: body = k8s_client.V1ConfigMap(metadata=metadata) return self.retry(func, self._namespace, body) if retry else func(self._namespace, body) def _write_leader_optime(self, last_operation): """Unused""" def _update_leader(self): """Unused""" def update_leader(self, last_operation): now = datetime.datetime.now(tzutc).isoformat() annotations = {self._LEADER: self._name, 'ttl': str(self._ttl), 'renewTime': now, 'acquireTime': self._leader_observed_record.get('acquireTime') or now, 'transitions': self._leader_observed_record.get('transitions') or '0'} if last_operation: annotations[self._OPTIME] = last_operation ret = self.patch_or_create(self.leader_path, annotations, self._leader_resource_version, subsets=self.__subsets) if ret: self._leader_resource_version = ret.metadata.resource_version return ret def attempt_to_acquire_leader(self, permanent=False): now = datetime.datetime.now(tzutc).isoformat() annotations = {self._LEADER: self._name, 'ttl': str(sys.maxsize if permanent else self._ttl), 'renewTime': now, 'acquireTime': now, 'transitions': '0'} if self._leader_observed_record: try: transitions = int(self._leader_observed_record.get('transitions')) except (TypeError, ValueError): transitions = 0 if self._leader_observed_record.get(self._LEADER) != self._name: transitions += 1 else: annotations['acquireTime'] = self._leader_observed_record.get('acquireTime') or now annotations['transitions'] = str(transitions) ret = self.patch_or_create(self.leader_path, annotations, self._leader_resource_version, subsets=self.__subsets) if ret: self._leader_resource_version = ret.metadata.resource_version else: logger.info('Could not take out TTL lock') return ret def take_leader(self): return self.attempt_to_acquire_leader() def set_failover_value(self, value, index=None): """Unused""" def manual_failover(self, leader, candidate, scheduled_at=None, index=None): annotations = {'leader': leader or None, 'member': candidate or None, 'scheduled_at': scheduled_at} patch = bool(self.cluster and isinstance(self.cluster.failover, Failover) and self.cluster.failover.index) return self.patch_or_create(self.failover_path, annotations, index, bool(index or patch), False) def set_config_value(self, value, index=None): patch = bool(index or self.cluster and self.cluster.config and self.cluster.config.index) return self.patch_or_create(self.config_path, {self._CONFIG: value}, index, patch, False) @catch_kubernetes_errors def touch_member(self, data, ttl=None, permanent=False): cluster = self.cluster if cluster and cluster.leader and cluster.leader.name == self._name: role = 'master' elif data['state'] == 'running' and data['role'] != 'master': role = data['role'] else: role = None member = cluster and cluster.get_member(self._name, fallback_to_leader=False) pod_labels = member and member.data.pop('pod_labels', None) ret = pod_labels is not None and pod_labels.get(self._role_label) == role and deep_compare(data, member.data) if not ret: metadata = {'namespace': self._namespace, 'name': self._name, 'labels': {self._role_label: role}, 'annotations': {'status': json.dumps(data, separators=(',', ':'))}} body = k8s_client.V1Pod(metadata=k8s_client.V1ObjectMeta(**metadata)) ret = self._api.patch_namespaced_pod(self._name, self._namespace, body) return ret def initialize(self, create_new=True, sysid=""): cluster = self.cluster resource_version = cluster.config.index if cluster and cluster.config and cluster.config.index else None return self.patch_or_create(self.config_path, {self._INITIALIZE: sysid}, resource_version) def delete_leader(self): if self.cluster and isinstance(self.cluster.leader, Leader) and self.cluster.leader.name == self._name: self.patch_or_create(self.leader_path, {self._LEADER: None}, self._leader_resource_version, True, False, []) self.reset_cluster() def cancel_initialization(self): self.patch_or_create(self.config_path, {self._INITIALIZE: None}, self.cluster.config.index, True) @catch_kubernetes_errors def delete_cluster(self): self.retry(self._api.delete_collection_namespaced_kind, self._namespace, label_selector=self._label_selector) def set_history_value(self, value): patch = bool(self.cluster and self.cluster.config and self.cluster.config.index) return self.patch_or_create(self.config_path, {self._HISTORY: value}, None, patch, False) def set_sync_state_value(self, value, index=None): """Unused""" def write_sync_state(self, leader, sync_standby, index=None): return self.patch_or_create(self.sync_path, self.sync_state(leader, sync_standby), index, False) def delete_sync_state(self, index=None): return self.write_sync_state(None, None, index) def watch(self, leader_index, timeout): if self.__do_not_watch: self.__do_not_watch = False return True if leader_index: end_time = time.time() + timeout w = k8s_watch.Watch() while timeout >= 1: try: for event in w.stream(self._api.list_namespaced_kind, self._namespace, resource_version=leader_index, timeout_seconds=int(timeout + 0.5), field_selector='metadata.name=' + self.leader_path, _request_timeout=(1, timeout + 1)): return event['raw_object'].get('metadata', {}).get('resourceVersion') != leader_index return False except KeyboardInterrupt: raise except Exception: logging.exception('watch') timeout = end_time - time.time() try: return super(Kubernetes, self).watch(None, timeout) finally: self.event.clear() patroni-1.4.2/patroni/dcs/zookeeper.py000066400000000000000000000344641323411135200200020ustar00rootroot00000000000000import json import logging import time from kazoo.client import KazooClient, KazooState, KazooRetry from kazoo.exceptions import NoNodeError, NodeExistsError from kazoo.handlers.threading import SequentialThreadingHandler from patroni.dcs import AbstractDCS, ClusterConfig, Cluster, Failover, Leader, Member, SyncState, TimelineHistory from patroni.exceptions import DCSError from patroni.utils import deep_compare logger = logging.getLogger(__name__) class ZooKeeperError(DCSError): pass class PatroniSequentialThreadingHandler(SequentialThreadingHandler): def __init__(self, connect_timeout): super(PatroniSequentialThreadingHandler, self).__init__() self.set_connect_timeout(connect_timeout) def set_connect_timeout(self, connect_timeout): self._connect_timeout = max(1.0, connect_timeout/2.0) # try to connect to zookeeper node during loop_wait/2 def create_connection(self, *args, **kwargs): """This method is trying to establish connection with one of the zookeeper nodes. Somehow strategy "fail earlier and retry more often" works way better comparing to the original strategy "try to connect with specified timeout". Since we want to try connect to zookeeper more often (with the smaller connect_timeout), he have to override `create_connection` method in the `SequentialThreadingHandler` class (which is used by `kazoo.Client`). :param args: always contains `tuple(host, port)` as the first element and could contain `connect_timeout` (negotiated session timeout) as the second element.""" args = list(args) if len(args) == 1: args.append(self._connect_timeout) else: args[1] = max(self._connect_timeout, args[1]/10.0) return super(PatroniSequentialThreadingHandler, self).create_connection(*args, **kwargs) class ZooKeeper(AbstractDCS): def __init__(self, config): super(ZooKeeper, self).__init__(config) hosts = config.get('hosts', []) if isinstance(hosts, list): hosts = ','.join(hosts) self._client = KazooClient(hosts, handler=PatroniSequentialThreadingHandler(config['retry_timeout']), timeout=config['ttl'], connection_retry=KazooRetry(max_delay=1, max_tries=-1, sleep_func=time.sleep), command_retry=KazooRetry(deadline=config['retry_timeout'], max_delay=1, max_tries=-1, sleep_func=time.sleep)) self._client.add_listener(self.session_listener) self._my_member_data = {} self._fetch_cluster = True self._orig_kazoo_connect = self._client._connection._connect self._client._connection._connect = self._kazoo_connect self._client.start() def _kazoo_connect(self, host, port): """Kazoo is using Ping's to determine health of connection to zookeeper. If there is no response on Ping after Ping interval (1/2 from read_timeout) it will consider current connection dead and try to connect to another node. Without this "magic" it was taking up to 2/3 from session timeout (ttl) to figure out that connection was dead and we had only small time for reconnect and retry. This method is needed to return different value of read_timeout, which is not calculated from negotiated session timeout but from value of `loop_wait`. And it is 2 sec smaller than loop_wait, because we can spend up to 2 seconds when calling `touch_member()` and `write_leader_optime()` methods, which also may hang...""" ret = self._orig_kazoo_connect(host, port) return max(self.loop_wait - 2, 2)*1000, ret[1] def session_listener(self, state): if state in [KazooState.SUSPENDED, KazooState.LOST]: self.cluster_watcher(None) def cluster_watcher(self, event): self._fetch_cluster = True self.event.set() def reload_config(self, config): self.set_retry_timeout(config['retry_timeout']) loop_wait = config['loop_wait'] loop_wait_changed = self._loop_wait != loop_wait self._loop_wait = loop_wait self._client.handler.set_connect_timeout(loop_wait) # We need to reestablish connection to zookeeper if we want to change # read_timeout (and Ping interval respectively), because read_timeout # is calculated in `_kazoo_connect` method. If we are changing ttl at # the same time, set_ttl method will reestablish connection and return # `!True`, otherwise we will close existing connection and let kazoo # open the new one. if not self.set_ttl(int(config['ttl'] * 1000)) and loop_wait_changed: self._client._connection._socket.close() def set_ttl(self, ttl): """It is not possible to change ttl (session_timeout) in zookeeper without destroying old session and creating the new one. This method returns `!True` if session_timeout has been changed (`restart()` has been called).""" if self._client._session_timeout != ttl: self._client._session_timeout = ttl self._client.restart() return True def set_retry_timeout(self, retry_timeout): retry = self._client.retry if isinstance(self._client.retry, KazooRetry) else self._client._retry retry.deadline = retry_timeout def get_node(self, key, watch=None): try: ret = self._client.get(key, watch) return (ret[0].decode('utf-8'), ret[1]) except NoNodeError: return None @staticmethod def member(name, value, znode): return Member.from_node(znode.version, name, znode.ephemeralOwner, value) def get_children(self, key, watch=None): try: return self._client.get_children(key, watch) except NoNodeError: return [] def load_members(self, sync_standby): members = [] for member in self.get_children(self.members_path, self.cluster_watcher): watch = member == sync_standby and self.cluster_watcher or None data = self.get_node(self.members_path + member, watch) if data is not None: members.append(self.member(member, *data)) return members def _inner_load_cluster(self): self._fetch_cluster = False self.event.clear() nodes = set(self.get_children(self.client_path(''), self.cluster_watcher)) if not nodes: self._fetch_cluster = True # get initialize flag initialize = (self.get_node(self.initialize_path) or [None])[0] if self._INITIALIZE in nodes else None # get global dynamic configuration config = self.get_node(self.config_path, watch=self.cluster_watcher) if self._CONFIG in nodes else None config = config and ClusterConfig.from_node(config[1].version, config[0], config[1].mzxid) # get timeline history history = self.get_node(self.history_path, watch=self.cluster_watcher) if self._HISTORY in nodes else None history = history and TimelineHistory.from_node(history[1].mzxid, history[0]) # get last leader operation last_leader_operation = self._OPTIME in nodes and self._fetch_cluster and self.get_node(self.leader_optime_path) last_leader_operation = last_leader_operation and int(last_leader_operation[0]) or 0 # get synchronization state sync = self.get_node(self.sync_path, watch=self.cluster_watcher) if self._SYNC in nodes else None sync = SyncState.from_node(sync and sync[1].version, sync and sync[0]) # get list of members sync_standby = sync.leader == self._name and sync.sync_standby or None members = self.load_members(sync_standby) if self._MEMBERS[:-1] in nodes else [] # get leader leader = self.get_node(self.leader_path) if self._LEADER in nodes else None if leader: client_id = self._client.client_id if not self._ctl and leader[0] == self._name and client_id is not None \ and client_id[0] != leader[1].ephemeralOwner: logger.info('I am leader but not owner of the session. Removing leader node') self._client.delete(self.leader_path) leader = None if leader: member = Member(-1, leader[0], None, {}) member = ([m for m in members if m.name == leader[0]] or [member])[0] leader = Leader(leader[1].version, leader[1].ephemeralOwner, member) self._fetch_cluster = member.index == -1 # failover key failover = self.get_node(self.failover_path, watch=self.cluster_watcher) if self._FAILOVER in nodes else None failover = failover and Failover.from_node(failover[1].version, failover[0]) self._cluster = Cluster(initialize, config, leader, last_leader_operation, members, failover, sync, history) def _load_cluster(self): if self._fetch_cluster or self._cluster is None: try: self._client.retry(self._inner_load_cluster) except Exception: logger.exception('get_cluster') self.cluster_watcher(None) raise ZooKeeperError('ZooKeeper in not responding properly') def _create(self, path, value, **kwargs): try: self._client.retry(self._client.create, path, value.encode('utf-8'), **kwargs) return True except Exception: return False def attempt_to_acquire_leader(self, permanent=False): ret = self._create(self.leader_path, self._name, makepath=True, ephemeral=not permanent) if not ret: logger.info('Could not take out TTL lock') return ret def __set_failover_or_sync_state_value(self, key, value, index=None): try: self._client.retry(self._client.set, key, value.encode('utf-8'), version=index or -1) return True except NoNodeError: return value == '' or (index is None and self._create(key, value)) except Exception: logging.exception('set_failover_value') return False def set_failover_value(self, value, index=None): return self.__set_failover_or_sync_state_value(self.failover_path, value, index) def set_config_value(self, value, index=None): try: self._client.retry(self._client.set, self.config_path, value.encode('utf-8'), version=index or -1) return True except NoNodeError: return index is None and self._create(self.config_path, value) except Exception: logging.exception('set_config_value') return False def initialize(self, create_new=True, sysid=""): return self._create(self.initialize_path, sysid, makepath=True) if create_new \ else self._client.retry(self._client.set, self.initialize_path, sysid.encode("utf-8")) def touch_member(self, data, ttl=None, permanent=False): cluster = self.cluster member = cluster and cluster.get_member(self._name, fallback_to_leader=False) encoded_data = json.dumps(data, separators=(',', ':')).encode('utf-8') if member and self._client.client_id is not None and member.session != self._client.client_id[0]: try: self._client.delete_async(self.member_path).get(timeout=1) except NoNodeError: pass except Exception: return False member = None if member: if deep_compare(data, self._my_member_data): return True else: try: self._client.create_async(self.member_path, encoded_data, makepath=True, ephemeral=not permanent).get(timeout=1) self._my_member_data = data return True except Exception as e: if not isinstance(e, NodeExistsError): logger.exception('touch_member') return False try: self._client.set_async(self.member_path, encoded_data).get(timeout=1) self._my_member_data = data return True except Exception: logger.exception('touch_member') return False def take_leader(self): return self.attempt_to_acquire_leader() def __write_leader_optime_or_history_value(self, key, value): value = value.encode('utf-8') try: self._client.set_async(key, value).get(timeout=1) return True except NoNodeError: try: self._client.create_async(key, value, makepath=True).get(timeout=1) return True except Exception: logger.exception('Failed to create %s', key) except Exception: logger.exception('Failed to update %s', key) return False def _write_leader_optime(self, last_operation): return self.__write_leader_optime_or_history_value(self.leader_optime_path, last_operation) def _update_leader(self): return True def delete_leader(self): self._client.restart() self._my_member_data = None return True def _cancel_initialization(self): node = self.get_node(self.initialize_path) if node: self._client.delete(self.initialize_path, version=node[1].version) def cancel_initialization(self): try: self._client.retry(self._cancel_initialization) except Exception: logger.exception("Unable to delete initialize key") def delete_cluster(self): try: return self._client.retry(self._client.delete, self.client_path(''), recursive=True) except NoNodeError: return True def set_history_value(self, value): return self.__write_leader_optime_or_history_value(self.history_path, value) def set_sync_state_value(self, value, index=None): return self.__set_failover_or_sync_state_value(self.sync_path, value, index) def delete_sync_state(self, index=None): return self.set_sync_state_value("{}", index) def watch(self, leader_index, timeout): if super(ZooKeeper, self).watch(leader_index, timeout): self._fetch_cluster = True return self._fetch_cluster patroni-1.4.2/patroni/exceptions.py000066400000000000000000000010441323411135200173730ustar00rootroot00000000000000class PatroniException(Exception): """Parent class for all kind of exceptions related to selected distributed configuration store""" def __init__(self, value): self.value = value def __str__(self): """ >>> str(PatroniException('foo')) "'foo'" """ return repr(self.value) class PostgresException(PatroniException): pass class DCSError(PatroniException): pass class PostgresConnectionException(PostgresException): pass class WatchdogError(PatroniException): pass patroni-1.4.2/patroni/ha.py000066400000000000000000001632461323411135200156170ustar00rootroot00000000000000import datetime import functools import json import logging import psycopg2 import requests import sys import time from collections import namedtuple from multiprocessing.pool import ThreadPool from patroni.async_executor import AsyncExecutor, CriticalTask from patroni.exceptions import DCSError, PostgresConnectionException, PatroniException from patroni.postgresql import ACTION_ON_START from patroni.utils import polling_loop, tzutc from threading import RLock logger = logging.getLogger(__name__) class _MemberStatus(namedtuple('_MemberStatus', 'member,reachable,in_recovery,wal_position,tags,watchdog_failed')): """Node status distilled from API response: member - dcs.Member object of the node reachable - `!False` if the node is not reachable or is not responding with correct JSON in_recovery - `!True` if pg_is_in_recovery() == true wal_position - value of `replayed_location` or `location` from JSON, dependin on its role. tags - dictionary with values of different tags (i.e. nofailover) watchdog_failed - indicates that watchdog is required by configuration but not available or failed """ @classmethod def from_api_response(cls, member, json): is_master = json['role'] == 'master' wal = not is_master and max(json['xlog'].get('received_location', 0), json['xlog'].get('replayed_location', 0)) return cls(member, True, not is_master, wal, json.get('tags', {}), json.get('watchdog_failed', False)) @classmethod def unknown(cls, member): return cls(member, False, None, 0, {}, False) def failover_limitation(self): """Returns reason why this node can't promote or None if everything is ok.""" if not self.reachable: return 'not reachable' if self.tags.get('nofailover', False): return 'not allowed to promote' if self.watchdog_failed: return 'not watchdog capable' return None class Ha(object): def __init__(self, patroni): self.patroni = patroni self.state_handler = patroni.postgresql self.dcs = patroni.dcs self.cluster = None self.old_cluster = None self._leader_timeline = None self.recovering = False self._post_bootstrap_task = None self._crash_recovery_executed = False self._start_timeout = None self._async_executor = AsyncExecutor(self.state_handler, self.wakeup) self.watchdog = patroni.watchdog # Each member publishes various pieces of information to the DCS using touch_member. This lock protects # the state and publishing procedure to have consistent ordering and avoid publishing stale values. self._member_state_lock = RLock() # Count of concurrent sync disabling requests. Value above zero means that we don't want to be synchronous # standby. Changes protected by _member_state_lock. self._disable_sync = 0 def is_paused(self): return self.cluster and self.cluster.is_paused() def load_cluster_from_dcs(self): cluster = self.dcs.get_cluster() # We want to keep the state of cluster when it was healthy if not cluster.is_unlocked() or not self.old_cluster: self.old_cluster = cluster self.cluster = cluster self._leader_timeline = None if cluster.is_unlocked() else cluster.leader.timeline def acquire_lock(self): return self.dcs.attempt_to_acquire_leader() def update_lock(self, write_leader_optime=False): last_operation = None if write_leader_optime: try: last_operation = self.state_handler.last_operation() except Exception: logger.exception('Exception when called state_handler.last_operation()') ret = self.dcs.update_leader(last_operation) if ret: self.watchdog.keepalive() return ret def has_lock(self): lock_owner = self.cluster.leader and self.cluster.leader.name logger.info('Lock owner: %s; I am %s', lock_owner, self.state_handler.name) return lock_owner == self.state_handler.name def get_effective_tags(self): """Return configuration tags merged with dynamically applied tags.""" tags = self.patroni.tags.copy() # _disable_sync could be modified concurrently, but we don't care as attribute get and set are atomic. if self._disable_sync > 0: tags['nosync'] = True return tags def touch_member(self): with self._member_state_lock: data = { 'conn_url': self.state_handler.connection_string, 'api_url': self.patroni.api.connection_string, 'state': self.state_handler.state, 'role': self.state_handler.role } tags = self.get_effective_tags() if tags: data['tags'] = tags if self.state_handler.pending_restart: data['pending_restart'] = True if self._async_executor.scheduled_action in (None, 'promote') \ and data['state'] in ['running', 'restarting', 'starting']: try: timeline, wal_position = self.state_handler.timeline_wal_position() data['xlog_location'] = wal_position if not timeline: timeline = self.state_handler.replica_cached_timeline(self._leader_timeline) if timeline: data['timeline'] = timeline except Exception: pass if self.patroni.scheduled_restart: scheduled_restart_data = self.patroni.scheduled_restart.copy() scheduled_restart_data['schedule'] = scheduled_restart_data['schedule'].isoformat() data['scheduled_restart'] = scheduled_restart_data if self.is_paused(): data['pause'] = True return self.dcs.touch_member(data) def clone(self, clone_member=None, msg='(without leader)'): if self.state_handler.clone(clone_member): logger.info('bootstrapped %s', msg) cluster = self.dcs.get_cluster() node_to_follow = self._get_node_to_follow(cluster) return self.state_handler.follow(node_to_follow) else: logger.error('failed to bootstrap %s', msg) self.state_handler.remove_data_directory() def bootstrap(self): if not self.cluster.is_unlocked(): # cluster already has leader clone_member = self.cluster.get_clone_member(self.state_handler.name) member_role = 'leader' if clone_member == self.cluster.leader else 'replica' msg = "from {0} '{1}'".format(member_role, clone_member.name) self._async_executor.schedule('bootstrap {0}'.format(msg)) self._async_executor.run_async(self.clone, args=(clone_member, msg)) return 'trying to bootstrap {0}'.format(msg) # no initialize key and node is allowed to be master and has 'bootstrap' section in a configuration file elif self.cluster.initialize is None and not self.patroni.nofailover and 'bootstrap' in self.patroni.config: if self.dcs.initialize(create_new=True): # race for initialization self.state_handler.bootstrapping = True self._post_bootstrap_task = CriticalTask() self._async_executor.schedule('bootstrap') self._async_executor.run_async(self.state_handler.bootstrap, args=(self.patroni.config['bootstrap'],)) return 'trying to bootstrap a new cluster' else: return 'failed to acquire initialize lock' else: if self.state_handler.can_create_replica_without_replication_connection(): msg = 'bootstrap (without leader)' self._async_executor.schedule(msg) self._async_executor.run_async(self.clone) return 'trying to ' + msg return 'waiting for leader to bootstrap' def _handle_rewind(self): if self.state_handler.rewind_needed_and_possible(self.cluster.leader): self._async_executor.schedule('running pg_rewind from ' + self.cluster.leader.name) self._async_executor.run_async(self.state_handler.rewind, (self.cluster.leader,)) return True def _start_crash_recovery(self, msg): self._async_executor.schedule(msg) self._async_executor.run_async(self.state_handler.fix_cluster_state) return msg def recover(self): # Postgres is not running and we will restart in standby mode. Watchdog is not needed until we promote. self.watchdog.disable() if self.has_lock() and self.update_lock(): timeout = self.patroni.config['master_start_timeout'] if timeout == 0: # We are requested to prefer failing over to restarting master. But see first if there # is anyone to fail over to. members = self.cluster.members if self.is_synchronous_mode(): members = [m for m in members if self.cluster.sync.matches(m.name)] if self.is_failover_possible(members): logger.info("Master crashed. Failing over.") self.demote('immediate') return 'stopped PostgreSQL to fail over after a crash' else: timeout = None data = self.state_handler.controldata() if data.get('Database cluster state') == 'in production' and not self._crash_recovery_executed and \ (self.cluster.is_unlocked() or self.state_handler.can_rewind): self._crash_recovery_executed = True return self._start_crash_recovery('doing crash recovery in a single user mode') self.load_cluster_from_dcs() if self.has_lock(): msg = "starting as readonly because i had the session lock" node_to_follow = None else: if not self.state_handler.rewind_executed: self.state_handler.trigger_check_diverged_lsn() if self._handle_rewind(): return self._async_executor.scheduled_action msg = "starting as a secondary" node_to_follow = self._get_node_to_follow(self.cluster) # once we already tried to start postgres but failed, single user mode is a rescue in this case if self.recovering and not self.state_handler.rewind_executed \ and not self._crash_recovery_executed and self.state_handler.can_rewind \ and data.get('Database cluster state') not in ('shut down', 'shut down in recovery'): self.recovering = False return self._start_crash_recovery('fixing cluster state in a single user mode') self.recovering = True self._async_executor.schedule('restarting after failure') self._async_executor.run_async(self.state_handler.follow, (node_to_follow, timeout)) return msg def _get_node_to_follow(self, cluster): # determine the node to follow. If replicatefrom tag is set, # try to follow the node mentioned there, otherwise, follow the leader. if not self.patroni.replicatefrom or self.patroni.replicatefrom == self.state_handler.name: node_to_follow = cluster.leader else: node_to_follow = cluster.get_member(self.patroni.replicatefrom) return node_to_follow if node_to_follow and node_to_follow.name != self.state_handler.name else None def follow(self, demote_reason, follow_reason, refresh=True): if refresh: self.load_cluster_from_dcs() is_leader = self.state_handler.is_leader() node_to_follow = self._get_node_to_follow(self.cluster) if self.is_paused(): if not (self.state_handler.need_rewind and self.state_handler.can_rewind) or self.cluster.is_unlocked(): self.state_handler.set_role('master' if is_leader else 'replica') if is_leader: return 'continue to run as master without lock' elif not node_to_follow: return 'no action' elif is_leader: self.demote('immediate-nolock') return demote_reason if self._handle_rewind(): return self._async_executor.scheduled_action if not self.state_handler.check_recovery_conf(node_to_follow): self._async_executor.schedule('changing primary_conninfo and restarting') self._async_executor.run_async(self.state_handler.follow, (node_to_follow,)) return follow_reason def is_synchronous_mode(self): return bool(self.cluster and self.cluster.is_synchronous_mode()) def is_synchronous_mode_strict(self): return bool(self.cluster and self.cluster.is_synchronous_mode_strict()) def process_sync_replication(self): """Process synchronous standby beahvior. Synchronous standbys are registered in two places postgresql.conf and DCS. The order of updating them must be right. The invariant that should be kept is that if a node is master and sync_standby is set in DCS, then that node must have synchronous_standby set to that value. Or more simple, first set in postgresql.conf and then in DCS. When removing, first remove in DCS, then in postgresql.conf. This is so we only consider promoting standbys that were guaranteed to be replicating synchronously. """ if self.is_synchronous_mode(): current = self.cluster.sync.leader and self.cluster.sync.sync_standby picked, allow_promote = self.state_handler.pick_synchronous_standby(self.cluster) if picked != current: # We need to revoke privilege from current before replacing it in the config if current: logger.info("Removing synchronous privilege from %s", current) if not self.dcs.write_sync_state(self.state_handler.name, None, index=self.cluster.sync.index): logger.info('Synchronous replication key updated by someone else.') return if self.is_synchronous_mode_strict() and picked is None: picked = '*' logger.warning("No standbys available!") logger.info("Assigning synchronous standby status to %s", picked) self.state_handler.set_synchronous_standby(picked) if picked and picked != '*' and not allow_promote: # Wait for PostgreSQL to enable synchronous mode and see if we can immediately set sync_standby time.sleep(2) picked, allow_promote = self.state_handler.pick_synchronous_standby(self.cluster) if allow_promote: cluster = self.dcs.get_cluster() if cluster.sync.leader and cluster.sync.leader != self.state_handler.name: logger.info("Synchronous replication key updated by someone else") return if not self.dcs.write_sync_state(self.state_handler.name, picked, index=cluster.sync.index): logger.info("Synchronous replication key updated by someone else") return logger.info("Synchronous standby status assigned to %s", picked) else: if self.cluster.sync.leader and self.dcs.delete_sync_state(index=self.cluster.sync.index): logger.info("Disabled synchronous replication") self.state_handler.set_synchronous_standby(None) def is_sync_standby(self, cluster): return cluster.leader and cluster.sync.leader == cluster.leader.name \ and cluster.sync.sync_standby == self.state_handler.name def while_not_sync_standby(self, func): """Runs specified action while trying to make sure that the node is not assigned synchronous standby status. Tags us as not allowed to be a sync standby as we are going to go away, if we currently are wait for leader to notice and pick an alternative one or if the leader changes or goes away we are also free. If the connection to DCS fails we run the action anyway, as this is only a hint. There is a small race window where this function runs between a master picking us the sync standby and publishing it to the DCS. As the window is rather tiny consequences are holding up commits for one cycle period we don't worry about it here.""" if not self.is_synchronous_mode() or self.patroni.nosync: return func() with self._member_state_lock: self._disable_sync += 1 try: if self.touch_member(): # Master should notice the updated value during the next cycle. We will wait double that, if master # hasn't noticed the value by then not disabling sync replication is not likely to matter. for _ in polling_loop(timeout=self.dcs.loop_wait*2, interval=2): try: if not self.is_sync_standby(self.dcs.get_cluster()): break except DCSError: logger.warning("Could not get cluster state, skipping synchronous standby disable") break logger.info("Waiting for master to release us from synchronous standby") else: logger.warning("Updating member state failed, skipping synchronous standby disable") return func() finally: with self._member_state_lock: self._disable_sync -= 1 def update_cluster_history(self): master_timeline = self.state_handler.get_master_timeline() cluster_history = self.cluster.history and self.cluster.history.lines if master_timeline == 1: if cluster_history: self.dcs.set_history_value('[]') elif not cluster_history or cluster_history[-1][0] != master_timeline - 1 or len(cluster_history[-1]) != 4: cluster_history = {l[0]: l for l in cluster_history or []} history = self.state_handler.get_history(master_timeline) if history: for line in history: # enrich current history with promotion timestamps stored in DCS if len(line) == 3 and line[0] in cluster_history \ and len(cluster_history[line[0]]) == 4 \ and cluster_history[line[0]][1] == line[1]: line.append(cluster_history[line[0]][3]) self.dcs.set_history_value(json.dumps(history, separators=(',', ':'))) def enforce_master_role(self, message, promote_message): if not self.is_paused() and not self.watchdog.is_running and not self.watchdog.activate(): if self.state_handler.is_leader(): self.demote('immediate') return 'Demoting self because watchdog could not be activated' else: self.release_leader_key_voluntarily() return 'Not promoting self because watchdog could not be activated' if self.state_handler.is_leader(): # Inform the state handler about its master role. # It may be unaware of it if postgres is promoted manually. self.state_handler.set_role('master') self.process_sync_replication() self.update_cluster_history() return message elif self.state_handler.role == 'master': self.process_sync_replication() return message else: if self.is_synchronous_mode(): # Just set ourselves as the authoritative source of truth for now. We don't want to wait for standbys # to connect. We will try finding a synchronous standby in the next cycle. if not self.dcs.write_sync_state(self.state_handler.name, None, index=self.cluster.sync.index): # Somebody else updated sync state, it may be due to us losing the lock. To be safe, postpone # promotion until next cycle. TODO: trigger immediate retry of run_cycle return 'Postponing promotion because synchronous replication state was updated by somebody else' self.state_handler.set_synchronous_standby('*' if self.is_synchronous_mode_strict() else None) if self.state_handler.role != 'master': self._async_executor.schedule('promote') self._async_executor.run_async(self.state_handler.promote, args=(self.dcs.loop_wait,)) return promote_message @staticmethod def fetch_node_status(member): """This function perform http get request on member.api_url and fetches its status :returns: `_MemberStatus` object """ try: response = requests.get(member.api_url, timeout=2, verify=False) logger.info('Got response from %s %s: %s', member.name, member.api_url, response.content) return _MemberStatus.from_api_response(member, response.json()) except Exception as e: logger.warning("request failed: GET %s (%s)", member.api_url, e) return _MemberStatus.unknown(member) def fetch_nodes_statuses(self, members): pool = ThreadPool(len(members)) results = pool.map(self.fetch_node_status, members) # Run API calls on members in parallel pool.close() pool.join() return results def is_lagging(self, wal_position): """Returns if instance with an wal should consider itself unhealthy to be promoted due to replication lag. :param wal_position: Current wal position. :returns True when node is lagging """ lag = (self.cluster.last_leader_operation or 0) - wal_position return lag > self.state_handler.config.get('maximum_lag_on_failover', 0) def _is_healthiest_node(self, members, check_replication_lag=True): """This method tries to determine whether I am healthy enough to became a new leader candidate or not.""" _, my_wal_position = self.state_handler.timeline_wal_position() if check_replication_lag and self.is_lagging(my_wal_position): return False # Too far behind last reported wal position on master # Prepare list of nodes to run check against members = [m for m in members if m.name != self.state_handler.name and not m.nofailover and m.api_url] if members: for st in self.fetch_nodes_statuses(members): if st.failover_limitation() is None: if not st.in_recovery: logger.warning('Master (%s) is still alive', st.member.name) return False if my_wal_position < st.wal_position: return False return True def is_failover_possible(self, members): ret = False members = [m for m in members if m.name != self.state_handler.name and not m.nofailover and m.api_url] if members: for st in self.fetch_nodes_statuses(members): not_allowed_reason = st.failover_limitation() if not_allowed_reason: logger.info('Member %s is %s', st.member.name, not_allowed_reason) elif self.is_lagging(st.wal_position): logger.info('Member %s exceeds maximum replication lag', st.member.name) else: ret = True else: logger.warning('manual failover: members list is empty') return ret def manual_failover_process_no_leader(self): failover = self.cluster.failover if failover.candidate: # manual failover to specific member if failover.candidate == self.state_handler.name: # manual failover to me return True elif self.is_paused(): # Remove failover key if the node to failover has terminated to avoid waiting for it indefinitely # In order to avoid attempts to delete this key from all nodes only the master is allowed to do it. if (not self.cluster.get_member(failover.candidate, fallback_to_leader=False) and self.state_handler.is_leader()): logger.warning("manual failover: removing failover key because failover candidate is not running") self.dcs.manual_failover('', '', index=self.cluster.failover.index) return None return False # find specific node and check that it is healthy member = self.cluster.get_member(failover.candidate, fallback_to_leader=False) if member: st = self.fetch_node_status(member) not_allowed_reason = st.failover_limitation() if not_allowed_reason is None: # node is healthy logger.info('manual failover: to %s, i am %s', st.member.name, self.state_handler.name) return False # we wanted to failover to specific member but it is not healthy logger.warning('manual failover: member %s is %s', st.member.name, not_allowed_reason) # at this point we should consider all members as a candidates for failover # i.e. we assume that failover.candidate is None elif self.is_paused(): return False # try to pick some other members to failover and check that they are healthy if failover.leader: if self.state_handler.name == failover.leader: # I was the leader # exclude me and desired member which is unhealthy (failover.candidate can be None) members = [m for m in self.cluster.members if m.name not in (failover.candidate, failover.leader)] if self.is_failover_possible(members): # check that there are healthy members return False else: # I was the leader and it looks like currently I am the only healthy member return True # at this point we assume that our node is a candidate for a failover among all nodes except former leader # exclude former leader from the list (failover.leader can be None) members = [m for m in self.cluster.members if m.name != failover.leader] return self._is_healthiest_node(members, check_replication_lag=False) def is_healthiest_node(self): if self.is_paused() and not self.patroni.nofailover and \ self.cluster.failover and not self.cluster.failover.scheduled_at: ret = self.manual_failover_process_no_leader() if ret is not None: # continue if we just deleted the stale failover key as a master return ret if self.state_handler.is_starting(): # postgresql still starting up is unhealthy return False if self.state_handler.is_leader(): # leader is always the healthiest return True if self.is_paused(): return False if self.patroni.nofailover: # nofailover tag makes node always unhealthy return False if self.cluster.failover: return self.manual_failover_process_no_leader() if not self.watchdog.is_healthy: return False # When in sync mode, only last known master and sync standby are allowed to promote automatically. all_known_members = self.cluster.members + self.old_cluster.members if self.is_synchronous_mode() and self.cluster.sync.leader: if not self.cluster.sync.matches(self.state_handler.name): return False # pick between synchronous candidates so we minimize unnecessary failovers/demotions members = {m.name: m for m in all_known_members if self.cluster.sync.matches(m.name)} else: # run usual health check members = {m.name: m for m in all_known_members} return self._is_healthiest_node(members.values()) def release_leader_key_voluntarily(self): self.dcs.delete_leader() self.touch_member() self.dcs.reset_cluster() logger.info("Leader key released") def demote(self, mode): """Demote PostgreSQL running as master. :param mode: One of offline, graceful or immediate. offline is used when connection to DCS is not available. graceful is used when failing over to another node due to user request. May only be called running async. immediate is used when we determine that we are not suitable for master and want to failover quickly without regard for data durability. May only be called synchronously. immediate-nolock is used when find out that we have lost the lock to be master. Need to bring down PostgreSQL as quickly as possible without regard for data durability. May only be called synchronously. """ mode_control = { 'offline': dict(stop='fast', checkpoint=False, release=False, offline=True, async=False), 'graceful': dict(stop='fast', checkpoint=True, release=True, offline=False, async=False), 'immediate': dict(stop='immediate', checkpoint=False, release=True, offline=False, async=True), 'immediate-nolock': dict(stop='immediate', checkpoint=False, release=False, offline=False, async=True), }[mode] self.state_handler.trigger_check_diverged_lsn() self.state_handler.stop(mode_control['stop'], checkpoint=mode_control['checkpoint'], on_safepoint=self.watchdog.disable if self.watchdog.is_running else None) self.state_handler.set_role('demoted') if mode_control['release']: self.release_leader_key_voluntarily() time.sleep(2) # Give a time to somebody to take the leader lock if mode_control['offline']: node_to_follow, leader = None, None else: cluster = self.dcs.get_cluster() node_to_follow, leader = self._get_node_to_follow(cluster), cluster.leader # FIXME: with mode offline called from DCS exception handler and handle_long_action_in_progress # there could be an async action already running, calling follow from here will lead # to racy state handler state updates. if mode_control['async']: self._async_executor.schedule('starting after demotion') self._async_executor.run_async(self.state_handler.follow, (node_to_follow,)) else: if self.is_synchronous_mode(): self.state_handler.set_synchronous_standby(None) if self.state_handler.rewind_needed_and_possible(leader): return False # do not start postgres, but run pg_rewind on the next iteration self.state_handler.follow(node_to_follow) def should_run_scheduled_action(self, action_name, scheduled_at, cleanup_fn): if scheduled_at and not self.is_paused(): # If the scheduled action is in the far future, we shouldn't do anything and just return. # If the scheduled action is in the past, we consider the value to be stale and we remove # the value. # If the value is close to now, we initiate the scheduled action # Additionally, if the scheduled action cannot be executed altogether, i.e. there is an error # or the action is in the past - we take care of cleaning it up. now = datetime.datetime.now(tzutc) try: delta = (scheduled_at - now).total_seconds() if delta > self.dcs.loop_wait: logger.info('Awaiting %s at %s (in %.0f seconds)', action_name, scheduled_at.isoformat(), delta) return False elif delta < - int(self.dcs.loop_wait * 1.5): # This means that if run_cycle gets delayed for 2.5x loop_wait we skip the # scheduled action. Probably not a problem, if things are that bad we don't # want to be restarting or failing over anyway. logger.warning('Found a stale %s value, cleaning up: %s', action_name, scheduled_at.isoformat()) cleanup_fn() return False # The value is very close to now time.sleep(max(delta, 0)) logger.info('Manual scheduled {0} at %s'.format(action_name), scheduled_at.isoformat()) return True except TypeError: logger.warning('Incorrect value of scheduled_at: %s', scheduled_at) cleanup_fn() return False def process_manual_failover_from_leader(self): """Checks if manual failover is requested and takes action if appropriate. Cleans up failover key if failover conditions are not matched. :returns: action message if demote was initiated, None if no action was taken""" failover = self.cluster.failover if not failover or (self.is_paused() and not self.state_handler.is_leader()): return if (failover.scheduled_at and not self.should_run_scheduled_action("failover", failover.scheduled_at, lambda: self.dcs.manual_failover('', '', index=failover.index))): return if not failover.leader or failover.leader == self.state_handler.name: if not failover.candidate or failover.candidate != self.state_handler.name: if not failover.candidate and self.is_paused(): logger.warning('Failover is possible only to a specific candidate in a paused state') else: if self.is_synchronous_mode(): if failover.candidate and not self.cluster.sync.matches(failover.candidate): logger.warning('Failover candidate=%s does not match with sync_standby=%s', failover.candidate, self.cluster.sync.sync_standby) members = [] else: members = [m for m in self.cluster.members if self.cluster.sync.matches(m.name)] else: members = [m for m in self.cluster.members if not failover.candidate or m.name == failover.candidate] if self.is_failover_possible(members): # check that there are healthy members self._async_executor.schedule('manual failover: demote') self._async_executor.run_async(self.demote, ('graceful',)) return 'manual failover: demoting myself' else: logger.warning('manual failover: no healthy members found, failover is not possible') else: logger.warning('manual failover: I am already the leader, no need to failover') else: logger.warning('manual failover: leader name does not match: %s != %s', failover.leader, self.state_handler.name) logger.info('Cleaning up failover key') self.dcs.manual_failover('', '', index=failover.index) def process_unhealthy_cluster(self): """Cluster has no leader key""" if self.is_healthiest_node(): if self.acquire_lock(): failover = self.cluster.failover if failover: if self.is_paused() and failover.leader and failover.candidate: logger.info('Updating failover key after acquiring leader lock...') self.dcs.manual_failover('', failover.candidate, failover.scheduled_at, failover.index) else: logger.info('Cleaning up failover key after acquiring leader lock...') self.dcs.manual_failover('', '') self.load_cluster_from_dcs() return self.enforce_master_role('acquired session lock as a leader', 'promoted self to leader by acquiring session lock') else: return self.follow('demoted self after trying and failing to obtain lock', 'following new leader after trying and failing to obtain lock') else: # when we are doing manual failover there is no guaranty that new leader is ahead of any other node # node tagged as nofailover can be ahead of the new leader either, but it is always excluded from elections if bool(self.cluster.failover) or self.patroni.nofailover: self.state_handler.trigger_check_diverged_lsn() time.sleep(2) # Give a time to somebody to take the leader lock if self.patroni.nofailover: return self.follow('demoting self because I am not allowed to become master', 'following a different leader because I am not allowed to promote') return self.follow('demoting self because i am not the healthiest node', 'following a different leader because i am not the healthiest node') def process_healthy_cluster(self): if self.has_lock(): if self.is_paused() and not self.state_handler.is_leader(): if self.cluster.failover and self.cluster.failover.candidate == self.state_handler.name: return 'waiting to become master after promote...' self.dcs.delete_leader() self.dcs.reset_cluster() return 'removed leader lock because postgres is not running as master' if self.update_lock(True): msg = self.process_manual_failover_from_leader() if msg is not None: return msg return self.enforce_master_role('no action. i am the leader with the lock', 'promoted self to leader because i had the session lock') else: # Either there is no connection to DCS or someone else acquired the lock logger.error('failed to update leader lock') if self.state_handler.is_leader(): self.demote('immediate-nolock') return 'demoted self because failed to update leader lock in DCS' else: return 'not promoting because failed to update leader lock in DCS' else: logger.info('does not have lock') return self.follow('demoting self because i do not have the lock and i was a leader', 'no action. i am a secondary and i am following a leader', refresh=False) def evaluate_scheduled_restart(self): if self._async_executor.busy: # Restart already in progress return None # restart if we need to restart_data = self.future_restart_scheduled() if restart_data: recent_time = self.state_handler.postmaster_start_time() request_time = restart_data['postmaster_start_time'] # check if postmaster start time has changed since the last restart if recent_time and request_time and recent_time != request_time: logger.info("Cancelling scheduled restart: postgres restart has already happened at %s", recent_time) self.delete_future_restart() return None if (restart_data and self.should_run_scheduled_action('restart', restart_data['schedule'], self.delete_future_restart)): try: ret, message = self.restart(restart_data, run_async=True) if not ret: logger.warning("Scheduled restart: %s", message) return None return message finally: self.delete_future_restart() def restart_matches(self, role, postgres_version, pending_restart): reason_to_cancel = "" # checking the restart filters here seem to be less ugly than moving them into the # run_scheduled_action. if role and role != self.state_handler.role: reason_to_cancel = "host role mismatch" if (postgres_version and self.state_handler.postgres_version_to_int(postgres_version) <= int(self.state_handler.server_version)): reason_to_cancel = "postgres version mismatch" if pending_restart and not self.state_handler.pending_restart: reason_to_cancel = "pending restart flag is not set" if not reason_to_cancel: return True else: logger.info("not proceeding with the restart: %s", reason_to_cancel) return False def schedule_future_restart(self, restart_data): with self._async_executor: restart_data['postmaster_start_time'] = self.state_handler.postmaster_start_time() if not self.patroni.scheduled_restart: self.patroni.scheduled_restart = restart_data self.touch_member() return True return False def delete_future_restart(self): ret = False with self._async_executor: if self.patroni.scheduled_restart: self.patroni.scheduled_restart = {} self.touch_member() ret = True return ret def future_restart_scheduled(self): return self.patroni.scheduled_restart.copy() if (self.patroni.scheduled_restart and isinstance(self.patroni.scheduled_restart, dict)) else None def restart_scheduled(self): return self._async_executor.scheduled_action == 'restart' def restart(self, restart_data, run_async=False): """ conditional and unconditional restart """ assert isinstance(restart_data, dict) if (not self.restart_matches(restart_data.get('role'), restart_data.get('postgres_version'), ('restart_pending' in restart_data))): return (False, "restart conditions are not satisfied") with self._async_executor: prev = self._async_executor.schedule('restart') if prev is not None: return (False, prev + ' already in progress') # Make the main loop to think that we were recovering dead postgres. If we fail # to start postgres after a specified timeout (see below), we need to remove # leader key (if it belong to us) rather than trying to start postgres once again. self.recovering = True # Now that restart is scheduled we can set timeout for startup, it will get reset # once async executor runs and main loop notices PostgreSQL as up. timeout = restart_data.get('timeout', self.patroni.config['master_start_timeout']) self.set_start_timeout(timeout) # For non async cases we want to wait for restart to complete or timeout before returning. do_restart = functools.partial(self.state_handler.restart, timeout, self._async_executor.critical_task) if self.is_synchronous_mode() and not self.has_lock(): do_restart = functools.partial(self.while_not_sync_standby, do_restart) if run_async: self._async_executor.run_async(do_restart) return (True, 'restart initiated') else: res = self._async_executor.run(do_restart) if res: return (True, 'restarted successfully') elif res is None: return (False, 'postgres is still starting') else: return (False, 'restart failed') def _do_reinitialize(self, cluster): self.state_handler.stop('immediate') self.state_handler.remove_data_directory() clone_member = self.cluster.get_clone_member(self.state_handler.name) member_role = 'leader' if clone_member == self.cluster.leader else 'replica' return self.clone(clone_member, "from {0} '{1}'".format(member_role, clone_member.name)) def reinitialize(self, force=False): with self._async_executor: self.load_cluster_from_dcs() if self.cluster.is_unlocked(): return 'Cluster has no leader, can not reinitialize' if self.cluster.leader.name == self.state_handler.name: return 'I am the leader, can not reinitialize' if force: self._async_executor.cancel() with self._async_executor: action = self._async_executor.schedule('reinitialize') if action is not None: return '{0} already in progress'.format(action) self._async_executor.run_async(self._do_reinitialize, args=(self.cluster, )) def handle_long_action_in_progress(self): if self.has_lock() and self.update_lock(): return 'updated leader lock during ' + self._async_executor.scheduled_action elif not self.state_handler.bootstrapping: # Don't have lock, make sure we are not starting up a master in the background if self.state_handler.role == 'master': logger.info("Demoting master during " + self._async_executor.scheduled_action) if self._async_executor.scheduled_action == 'restart': # Restart needs a special interlocking cancel because postmaster may be just started in a # background thread and has not even written a pid file yet. with self._async_executor.critical_task as task: if not task.cancel(): self.state_handler.terminate_starting_postmaster(postmaster=task.result) self.demote('immediate-nolock') return 'lost leader lock during ' + self._async_executor.scheduled_action if self.cluster.is_unlocked(): logger.info('not healthy enough for leader race') return self._async_executor.scheduled_action + ' in progress' @staticmethod def sysid_valid(sysid): # sysid does tv_sec << 32, where tv_sec is the number of seconds sine 1970, # so even 1 << 32 would have 10 digits. sysid = str(sysid) return len(sysid) >= 10 and sysid.isdigit() def post_recover(self): if not self.state_handler.is_running(): self.watchdog.disable() if self.has_lock(): self.state_handler.set_role('demoted') self.dcs.delete_leader() self.dcs.reset_cluster() return 'removed leader key after trying and failing to start postgres' return 'failed to start postgres' self._crash_recovery_executed = False return None def cancel_initialization(self): logger.info('removing initialize key after failed attempt to bootstrap the cluster') self.dcs.cancel_initialization() self.state_handler.stop('immediate') self.state_handler.move_data_directory() raise PatroniException('Failed to bootstrap cluster') def post_bootstrap(self): # bootstrap has failed if postgres is not running if not self.state_handler.is_running() or self._post_bootstrap_task.result is False: self.cancel_initialization() if self._post_bootstrap_task.result is None: if not self.state_handler.is_leader(): return 'waiting for end of recovery after bootstrap' self.state_handler.set_role('master') self._async_executor.schedule('post_bootstrap') self._async_executor.run_async(self.state_handler.post_bootstrap, args=(self.patroni.config['bootstrap'], self._post_bootstrap_task)) return 'running post_bootstrap' self.state_handler.bootstrapping = False self.dcs.set_config_value(json.dumps(self.patroni.config.dynamic_configuration, separators=(',', ':'))) if not self.watchdog.activate(): logger.error('Cancelling bootstrap because watchdog activation failed') self.cancel_initialization() self.dcs.take_leader() self.state_handler.call_nowait(ACTION_ON_START) self.load_cluster_from_dcs() return 'initialized a new cluster' def handle_starting_instance(self): """Starting up PostgreSQL may take a long time. In case we are the leader we may want to fail over to.""" # Check if we are in startup, when paused defer to main loop for manual failovers. if not self.state_handler.check_for_startup() or self.is_paused(): self.set_start_timeout(None) if self.is_paused(): self.state_handler.set_state(self.state_handler.is_running() and 'running' or 'stopped') return None # state_handler.state == 'starting' here if self.has_lock(): if not self.update_lock(): logger.info("Lost lock while starting up. Demoting self.") self.demote('immediate-nolock') return 'stopped PostgreSQL while starting up because leader key was lost' timeout = self._start_timeout or self.patroni.config['master_start_timeout'] time_left = timeout - self.state_handler.time_in_state() if time_left <= 0: if self.is_failover_possible(self.cluster.members): logger.info("Demoting self because master startup is taking too long") self.demote('immediate') return 'stopped PostgreSQL because of startup timeout' else: return 'master start has timed out, but continuing to wait because failover is not possible' else: msg = self.process_manual_failover_from_leader() if msg is not None: return msg return 'PostgreSQL is still starting up, {0:.0f} seconds until timeout'.format(time_left) else: # Use normal processing for standbys logger.info("Still starting up as a standby.") return None def set_start_timeout(self, value): """Sets timeout for starting as master before eligible for failover. Must be called when async_executor is busy or in the main thread.""" self._start_timeout = value def _run_cycle(self): dcs_failed = False try: self.state_handler.reset_cluster_info_state() self.load_cluster_from_dcs() if self.is_paused(): self.watchdog.disable() if not self.cluster.has_member(self.state_handler.name): self.touch_member() # cluster has leader key but not initialize key if not (self.cluster.is_unlocked() or self.sysid_valid(self.cluster.initialize)) and self.has_lock(): self.dcs.initialize(create_new=(self.cluster.initialize is None), sysid=self.state_handler.sysid) if not (self.cluster.is_unlocked() or self.cluster.config and self.cluster.config.data) and self.has_lock(): self.dcs.set_config_value(json.dumps(self.patroni.config.dynamic_configuration, separators=(',', ':'))) self.cluster = self.dcs.get_cluster() if self._async_executor.busy: return self.handle_long_action_in_progress() msg = self.handle_starting_instance() if msg is not None: return msg # we've got here, so any async action has finished. if self.state_handler.bootstrapping: return self.post_bootstrap() if self.recovering and not self.state_handler.need_rewind: self.recovering = False # Check if we tried to recover and failed msg = self.post_recover() if msg is not None: return msg # is data directory empty? if self.state_handler.data_directory_empty(): self.state_handler.set_role('uninitialized') self.state_handler.stop('immediate') # In case datadir went away while we were master. self.watchdog.disable() # is this instance the leader? if self.has_lock(): self.release_leader_key_voluntarily() return 'released leader key voluntarily as data dir empty and currently leader' return self.bootstrap() # new node # "bootstrap", but data directory is not empty elif not self.sysid_valid(self.cluster.initialize) and self.cluster.is_unlocked() and not self.is_paused(): self.dcs.initialize(create_new=(self.cluster.initialize is None), sysid=self.state_handler.sysid) else: # check if we are allowed to join if self.sysid_valid(self.cluster.initialize) and self.cluster.initialize != self.state_handler.sysid: logger.fatal("system ID mismatch, node %s belongs to a different cluster: %s != %s", self.state_handler.name, self.cluster.initialize, self.state_handler.sysid) sys.exit(1) if not self.state_handler.is_healthy(): if self.is_paused(): if self.has_lock(): self.dcs.delete_leader() self.dcs.reset_cluster() return 'removed leader lock because postgres is not running' elif not (self.state_handler.rewind_executed or self.state_handler.need_rewind and self.state_handler.can_rewind): return 'postgres is not running' # try to start dead postgres return self.recover() try: if self.cluster.is_unlocked(): return self.process_unhealthy_cluster() else: msg = self.process_healthy_cluster() return self.evaluate_scheduled_restart() or msg finally: # we might not have a valid PostgreSQL connection here if another thread # stops PostgreSQL, therefore, we only reload replication slots if no # asynchronous processes are running (should be always the case for the master) if not self._async_executor.busy and not self.state_handler.is_starting(): if not self.state_handler.cb_called: if not self.state_handler.is_leader(): self.state_handler.trigger_check_diverged_lsn() self.state_handler.call_nowait(ACTION_ON_START) self.state_handler.sync_replication_slots(self.cluster) except DCSError: dcs_failed = True logger.error('Error communicating with DCS') if not self.is_paused() and self.state_handler.is_running() and self.state_handler.is_leader(): self.demote('offline') return 'demoted self because DCS is not accessible and i was a leader' return 'DCS is not accessible' except (psycopg2.Error, PostgresConnectionException): return 'Error communicating with PostgreSQL. Will try again later' finally: if not dcs_failed: self.touch_member() def run_cycle(self): with self._async_executor: info = self._run_cycle() return (self.is_paused() and 'PAUSE: ' or '') + info def shutdown(self): if self.is_paused(): logger.info('Leader key is not deleted and Postgresql is not stopped due paused state') self.watchdog.disable() else: # FIXME: If stop doesn't reach safepoint quickly enough keepalive is triggered. If shutdown checkpoint # takes longer than ttl, then leader key is lost and replication might not have sent out all xlog. # This might not be the desired behavior of users, as a graceful shutdown of the host can mean lost data. # We probably need to something smarter here. disable_wd = self.watchdog.disable if self.watchdog.is_running else None self.while_not_sync_standby(lambda: self.state_handler.stop(checkpoint=False, on_safepoint=disable_wd)) if not self.state_handler.is_running(): if self.has_lock(): self.dcs.delete_leader() else: # XXX: what about when Patroni is started as the wrong user that has access to the watchdog device # but cannot shut down PostgreSQL. Root would be the obvious example. Would be nice to not kill the # system due to a bad config. logger.error("PostgreSQL shutdown failed, leader key not removed." + (" Leaving watchdog running." if self.watchdog.is_running else "")) def watch(self, timeout): cluster = self.cluster # watch on leader key changes if the postgres is running and leader is known and current node is not lock owner if not self._async_executor.busy and cluster and cluster.leader \ and cluster.leader.name != self.state_handler.name: leader_index = cluster.leader.index else: leader_index = None return self.dcs.watch(leader_index, timeout) def wakeup(self): """Call of this method will trigger the next run of HA loop if there is no "active" leader watch request in progress. This usually happens on the master or if the node is running async action""" self.dcs.event.set() patroni-1.4.2/patroni/postgresql.py000066400000000000000000002400541323411135200174230ustar00rootroot00000000000000import logging import os import psycopg2 import re import shlex import shutil import socket import subprocess import tempfile import time from collections import defaultdict from contextlib import contextmanager from patroni.callback_executor import CallbackExecutor from patroni.exceptions import PostgresConnectionException, PostgresException from patroni.utils import compare_values, parse_bool, parse_int, Retry, RetryFailedError, polling_loop, split_host_port from patroni.postmaster import PostmasterProcess from six import string_types from six.moves.urllib.parse import quote_plus from threading import current_thread, Lock logger = logging.getLogger(__name__) ACTION_ON_START = "on_start" ACTION_ON_STOP = "on_stop" ACTION_ON_RESTART = "on_restart" ACTION_ON_RELOAD = "on_reload" ACTION_ON_ROLE_CHANGE = "on_role_change" STATE_RUNNING = 'running' STATE_REJECT = 'rejecting connections' STATE_NO_RESPONSE = 'not responding' STATE_UNKNOWN = 'unknown' STOP_POLLING_INTERVAL = 1 REWIND_STATUS = type('Enum', (), {'INITIAL': 0, 'CHECK': 1, 'NEED': 2, 'NOT_NEED': 3, 'SUCCESS': 4, 'FAILED': 5}) sync_standby_name_re = re.compile('^[A-Za-z_][A-Za-z_0-9\$]*$') cluster_info_query = ("SELECT CASE WHEN pg_is_in_recovery() THEN 0 " "ELSE ('x' || SUBSTR(pg_{0}file_name(pg_current_{0}_{1}()), 1, 8))::bit(32)::int END, " "CASE WHEN pg_is_in_recovery() THEN GREATEST(" " pg_{0}_{1}_diff(COALESCE(pg_last_{0}_receive_{1}(), '0/0'), '0/0')::bigint," " pg_{0}_{1}_diff(pg_last_{0}_replay_{1}(), '0/0')::bigint)" "ELSE pg_{0}_{1}_diff(pg_current_{0}_{1}(), '0/0')::bigint END") def quote_ident(value): """Very simplified version of quote_ident""" return value if sync_standby_name_re.match(value) else '"' + value + '"' def slot_name_from_member_name(member_name): """Translate member name to valid PostgreSQL slot name. PostgreSQL replication slot names must be valid PostgreSQL names. This function maps the wider space of member names to valid PostgreSQL names. Names are lowercased, dashes and periods common in hostnames are replaced with underscores, other characters are encoded as their unicode codepoint. Name is truncated to 64 characters. Multiple different member names may map to a single slot name.""" def replace_char(match): c = match.group(0) return '_' if c in '-.' else "u{:04d}".format(ord(c)) slot_name = re.sub('[^a-z0-9_]', replace_char, member_name.lower()) return slot_name[0:63] @contextmanager def null_context(): yield class Postgresql(object): # List of parameters which must be always passed to postmaster as command line options # to make it not possible to change them with 'ALTER SYSTEM'. # Some of these parameters have sane default value assigned and Patroni doesn't allow # to decrease this value. E.g. 'wal_level' can't be lower then 'hot_standby' and so on. # These parameters could be changed only globally, i.e. via DCS. # P.S. 'listen_addresses' and 'port' are added here just for convenience, to mark them # as a parameters which should always be passed through command line. # # Format: # key - parameter name # value - tuple(default_value, check_function, min_version) # default_value -- some sane default value # check_function -- if the new value is not correct must return `!False` # min_version -- major version of PostgreSQL when parameter was introduced CMDLINE_OPTIONS = { 'listen_addresses': (None, lambda _: False, 90100), 'port': (None, lambda _: False, 90100), 'cluster_name': (None, lambda _: False, 90500), 'wal_level': ('hot_standby', lambda v: v.lower() in ('hot_standby', 'replica', 'logical'), 90100), 'hot_standby': ('on', lambda _: False, 90100), 'max_connections': (100, lambda v: int(v) >= 100, 90100), 'max_wal_senders': (10, lambda v: int(v) >= 10, 90100), 'wal_keep_segments': (8, lambda v: int(v) >= 8, 90100), 'max_prepared_transactions': (0, lambda v: int(v) >= 0, 90100), 'max_locks_per_transaction': (64, lambda v: int(v) >= 64, 90100), 'track_commit_timestamp': ('off', lambda v: parse_bool(v) is not None, 90500), 'max_replication_slots': (10, lambda v: int(v) >= 10, 90400), 'max_worker_processes': (8, lambda v: int(v) >= 8, 90400), 'wal_log_hints': ('on', lambda _: False, 90400) } _CONFIG_WARNING_HEADER = '# Do not edit this file manually!\n# It will be overwritten by Patroni!\n' def __init__(self, config): self.config = config self.name = config['name'] self.scope = config['scope'] self._bin_dir = config.get('bin_dir') or '' self._database = config.get('database', 'postgres') self._data_dir = config['data_dir'] self._config_dir = os.path.abspath(config.get('config_dir') or self._data_dir) self._pending_restart = False self.bootstrapping = False self._running_custom_bootstrap = False self.__thread_ident = current_thread().ident self._version_file = os.path.join(self._data_dir, 'PG_VERSION') self._synchronous_standby_names = None self._configure_server_parameters() self._connect_address = config.get('connect_address') self._superuser = config['authentication'].get('superuser', {}) self.resolve_connection_addresses() self._rewind_state = REWIND_STATUS.INITIAL self._use_slots = config.get('use_slots', True) self._schedule_load_slots = self.use_slots self._pgpass = config.get('pgpass') or os.path.join(os.path.expanduser('~'), 'pgpass') self._callback_executor = CallbackExecutor() self.__cb_called = False self.__cb_pending = None config_base_name = config.get('config_base_name', 'postgresql') self._postgresql_conf = os.path.join(self._config_dir, config_base_name + '.conf') self._postgresql_base_conf_name = config_base_name + '.base.conf' self._postgresql_base_conf = os.path.join(self._config_dir, self._postgresql_base_conf_name) self._pg_hba_conf = os.path.join(self._config_dir, 'pg_hba.conf') self._recovery_conf = os.path.join(self._data_dir, 'recovery.conf') self._postmaster_pid = os.path.join(self._data_dir, 'postmaster.pid') self._trigger_file = config.get('recovery_conf', {}).get('trigger_file') or 'promote' self._trigger_file = os.path.abspath(os.path.join(self._data_dir, self._trigger_file)) self._is_cancelled = False self._cancellable = None self._cancellable_lock = Lock() self._connection_lock = Lock() self._connection = None self._cursor_holder = None self._sysid = None self._replication_slots = [] # list of already existing replication slots self.retry = Retry(max_tries=-1, deadline=config['retry_timeout']/2.0, max_delay=1, retry_exceptions=PostgresConnectionException) # Retry 'pg_is_in_recovery()' only once self._is_leader_retry = Retry(max_tries=1, deadline=config['retry_timeout']/2.0, max_delay=1, retry_exceptions=PostgresConnectionException) self._state_lock = Lock() self.set_state('stopped') self._role_lock = Lock() self.set_role(self.get_postgres_role_from_data_directory()) self._state_entry_timestamp = None self._cluster_info_state = {} self._cached_replica_timeline = None # Last known running process self._postmaster_proc = None if self.is_running(): self.set_state('running') self.set_role('master' if self.is_leader() else 'replica') self._write_postgresql_conf() # we are "joining" already running postgres if self._replace_pg_hba(): self.reload() @property def _configuration_to_save(self): configuration = [os.path.basename(self._postgresql_conf)] if 'custom_conf' not in self.config: configuration.append(os.path.basename(self._postgresql_base_conf)) if not self._server_parameters.get('hba_file'): configuration.append('pg_hba.conf') if not self._server_parameters.get('ident_file'): configuration.append('pg_ident.conf') return configuration @property def use_slots(self): return self._use_slots and self._major_version >= 90400 @property def _replication(self): return self.config['authentication']['replication'] @property def callback(self): return self.config.get('callbacks') or {} @staticmethod def _wal_name(version): return 'wal' if version >= 100000 else 'xlog' @property def wal_name(self): return self._wal_name(self._major_version) @property def lsn_name(self): return 'lsn' if self._major_version >= 100000 else 'location' def _version_file_exists(self): return not self.data_directory_empty() and os.path.isfile(self._version_file) def get_major_version(self): if self._version_file_exists(): try: with open(self._version_file) as f: return self.postgres_major_version_to_int(f.read().strip()) except Exception: logger.exception('Failed to read PG_VERSION from %s', self._data_dir) return 0 def get_server_parameters(self, config): parameters = config['parameters'].copy() listen_addresses, port = split_host_port(config['listen'], 5432) parameters.update({'cluster_name': self.scope, 'listen_addresses': listen_addresses, 'port': str(port)}) if config.get('synchronous_mode', False): if self._synchronous_standby_names is None: if config.get('synchronous_mode_strict', False): parameters['synchronous_standby_names'] = '*' else: parameters.pop('synchronous_standby_names', None) else: parameters['synchronous_standby_names'] = self._synchronous_standby_names if self._major_version >= 90600 and parameters['wal_level'] == 'hot_standby': parameters['wal_level'] = 'replica' ret = {k: v for k, v in parameters.items() if not self._major_version or self._major_version >= self.CMDLINE_OPTIONS.get(k, (0, 1, 90100))[2]} for k in ('hba_file', 'ident_file'): if k in ret: ret[k] = os.path.join(self._config_dir, ret[k]) return ret def resolve_connection_addresses(self): port = self._server_parameters['port'] tcp_local_address = self._get_tcp_local_address() local_address = {'port': port} if self.config.get('use_unix_socket'): unix_socket_directories = self._server_parameters.get('unix_socket_directories') if unix_socket_directories is not None: # fallback to tcp if unix_socket_directories is set, but there are no sutable values local_address['host'] = self._get_unix_local_address(unix_socket_directories) or tcp_local_address # if unix_socket_directories is not specified, but use_unix_socket is set to true - do our best # to use default value, i.e. don't specify a host neither in connection url nor arguments else: local_address['host'] = tcp_local_address self._local_address = local_address self._local_replication_address = {'host': tcp_local_address, 'port': port} self.connection_string = 'postgres://{0}/{1}'.format( self._connect_address or tcp_local_address + ':' + port, self._database) def _pgcommand(self, cmd): """Returns path to the specified PostgreSQL command""" return os.path.join(self._bin_dir, cmd) def pg_ctl(self, cmd, *args, **kwargs): """Builds and executes pg_ctl command :returns: `!True` when return_code == 0, otherwise `!False`""" pg_ctl = [self._pgcommand('pg_ctl'), cmd] return subprocess.call(pg_ctl + ['-D', self._data_dir] + list(args), **kwargs) == 0 def pg_isready(self): """Runs pg_isready to see if PostgreSQL is accepting connections. :returns: 'ok' if PostgreSQL is up, 'reject' if starting up, 'no_resopnse' if not up.""" cmd = [self._pgcommand('pg_isready'), '-p', self._local_address['port'], '-d', self._database] # Host is not set if we are connecting via default unix socket if 'host' in self._local_address: cmd.extend(['-h', self._local_address['host']]) # We only need the username because pg_isready does not try to authenticate if 'username' in self._superuser: cmd.extend(['-U', self._superuser['username']]) ret = subprocess.call(cmd) return_codes = {0: STATE_RUNNING, 1: STATE_REJECT, 2: STATE_NO_RESPONSE, 3: STATE_UNKNOWN} return return_codes.get(ret, STATE_UNKNOWN) def reload_config(self, config): self._superuser = config['authentication'].get('superuser', {}) server_parameters = self.get_server_parameters(config) conf_changed = hba_changed = local_connection_address_changed = pending_restart = False if self.state == 'running': changes = {p: v for p, v in server_parameters.items() if '.' not in p} changes.update({p: None for p, v in self._server_parameters.items() if not ('.' in p or p in changes)}) if changes: if 'wal_segment_size' not in changes: changes['wal_segment_size'] = '16384kB' # XXX: query can raise an exception for r in self.query("""SELECT name, setting, unit, vartype, context FROM pg_settings WHERE name IN (""" + ', '.join(['%s'] * len(changes)) + """) ORDER BY 1 DESC""", *(list(changes.keys()))): if r[4] == 'internal': if r[0] == 'wal_segment_size': server_parameters.pop(r[0], None) wal_segment_size = parse_int(r[2], 'kB') if wal_segment_size is not None: changes['wal_segment_size'] = '{0}kB'.format(int(r[1]) * wal_segment_size) elif r[0] in changes: unit = changes['wal_segment_size'] if r[0] in ('min_wal_size', 'max_wal_size') else r[2] new_value = changes.pop(r[0]) if new_value is None or not compare_values(r[3], unit, r[1], new_value): if r[4] == 'postmaster': pending_restart = True if config.get('use_unix_socket') and r[0] == 'unix_socket_directories'\ or r[0] in ('listen_addresses', 'port'): local_connection_address_changed = True else: conf_changed = True for param in changes: if param in server_parameters: logger.warning('Removing invalid parameter `%s` from postgresql.parameters', param) server_parameters.pop(param) # Check that user-defined-paramters have changed (parameters with period in name) if not conf_changed: for p, v in server_parameters.items(): if '.' in p and (p not in self._server_parameters or str(v) != str(self._server_parameters[p])): conf_changed = True break if not conf_changed: for p, v in self._server_parameters.items(): if '.' in p and (p not in server_parameters or str(v) != str(server_parameters[p])): conf_changed = True break if not server_parameters.get('hba_file') and config.get('pg_hba'): hba_changed = self.config.get('pg_hba', []) != config['pg_hba'] self.config = config self._pending_restart = pending_restart self._server_parameters = server_parameters self._connect_address = config.get('connect_address') if not local_connection_address_changed: self.resolve_connection_addresses() if conf_changed: self._write_postgresql_conf() if hba_changed: self._replace_pg_hba() if conf_changed or hba_changed: self.reload() self._is_leader_retry.deadline = self.retry.deadline = config['retry_timeout']/2.0 @property def pending_restart(self): return self._pending_restart @staticmethod def configuration_allows_rewind(data): return data.get('wal_log_hints setting', 'off') == 'on' \ or data.get('Data page checksum version', '0') != '0' @property def can_rewind(self): """ check if pg_rewind executable is there and that pg_controldata indicates we have either wal_log_hints or checksums turned on """ # low-hanging fruit: check if pg_rewind configuration is there if not (self.config.get('use_pg_rewind') and all(self._superuser.get(n) for n in ('username', 'password'))): return False cmd = [self._pgcommand('pg_rewind'), '--help'] try: ret = subprocess.call(cmd, stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT) if ret != 0: # pg_rewind is not there, close up the shop and go home return False except OSError: return False return self.configuration_allows_rewind(self.controldata()) @property def sysid(self): if not self._sysid: data = self.controldata() self._sysid = data.get('Database system identifier', "") return self._sysid @staticmethod def _get_unix_local_address(unix_socket_directories): for d in unix_socket_directories.split(','): d = d.strip() if d.startswith('/'): # Only absolute path can be used to connect via unix-socket return d return '' def _get_tcp_local_address(self): listen_addresses = self._server_parameters['listen_addresses'].split(',') for la in listen_addresses: if la.strip().lower() in ('*', '0.0.0.0', '127.0.0.1', 'localhost'): # we are listening on '*' or localhost return 'localhost' # connection via localhost is preferred return listen_addresses[0].strip() # can't use localhost, take first address from listen_addresses def get_postgres_role_from_data_directory(self): if self.data_directory_empty(): return 'uninitialized' elif os.path.exists(self._recovery_conf): return 'replica' else: return 'master' @property def _local_connect_kwargs(self): ret = self._local_address.copy() ret.update({'database': self._database, 'fallback_application_name': 'Patroni', 'connect_timeout': 3, 'options': '-c statement_timeout=2000'}) if 'username' in self._superuser: ret['user'] = self._superuser['username'] if 'password' in self._superuser: ret['password'] = self._superuser['password'] return ret def connection(self): with self._connection_lock: if not self._connection or self._connection.closed != 0: self._connection = psycopg2.connect(**self._local_connect_kwargs) self._connection.autocommit = True self.server_version = self._connection.server_version return self._connection def _cursor(self): if not self._cursor_holder or self._cursor_holder.closed or self._cursor_holder.connection.closed != 0: logger.info("establishing a new patroni connection to the postgres cluster") self._cursor_holder = self.connection().cursor() return self._cursor_holder def close_connection(self): if self._connection and self._connection.closed == 0: self._connection.close() logger.info("closed patroni connection to the postgresql cluster") self._cursor_holder = self._connection = None def _query(self, sql, *params): """We are always using the same cursor, therefore this method is not thread-safe!!! You can call it from different threads only if you are holding explicit `AsyncExecutor` lock, because the main thread is always holding this lock when running HA cycle.""" cursor = None try: cursor = self._cursor() cursor.execute(sql, params) return cursor except psycopg2.Error as e: if cursor and cursor.connection.closed == 0: # When connected via unix socket, psycopg2 can't recoginze 'connection lost' # and leaves `_cursor_holder.connection.closed == 0`, but psycopg2.OperationalError # is still raised (what is correct). It doesn't make sense to continiue with existing # connection and we will close it, to avoid its reuse by the `_cursor` method. if isinstance(e, psycopg2.OperationalError): self.close_connection() else: raise e if self.state == 'restarting': raise RetryFailedError('cluster is being restarted') raise PostgresConnectionException('connection problems') def query(self, sql, *params): try: return self.retry(self._query, sql, *params) except RetryFailedError as e: raise PostgresConnectionException(str(e)) def data_directory_empty(self): return not os.path.exists(self._data_dir) or os.listdir(self._data_dir) == [] @staticmethod def initdb_allowed_option(name): if name in ['pgdata', 'nosync', 'pwfile', 'sync-only']: raise Exception('{0} option for initdb is not allowed'.format(name)) return True def get_initdb_options(self, config): options = [] for o in config: if isinstance(o, string_types) and self.initdb_allowed_option(o): options.append('--{0}'.format(o)) elif isinstance(o, dict): keys = list(o.keys()) if len(keys) != 1 or not isinstance(keys[0], string_types) or not self.initdb_allowed_option(keys[0]): raise Exception('Invalid option: {0}'.format(o)) options.append('--{0}={1}'.format(keys[0], o[keys[0]])) else: raise Exception('Unknown type of initdb option: {0}'.format(o)) return options def _initdb(self, config): self.set_state('initalizing new cluster') options = self.get_initdb_options(config.get('initdb') or []) pwfile = None if self._superuser: if 'username' in self._superuser: options.append('--username={0}'.format(self._superuser['username'])) if 'password' in self._superuser: (fd, pwfile) = tempfile.mkstemp() os.write(fd, self._superuser['password'].encode('utf-8')) os.close(fd) options.append('--pwfile={0}'.format(pwfile)) options = ['-o', ' '.join(options)] if options else [] ret = self.pg_ctl('initdb', *options) if pwfile: os.remove(pwfile) if ret: if not self._server_parameters.get('hba_file') and not self.config.get('pg_hba'): self.write_pg_hba(config.get('pg_hba', [])) self._major_version = self.get_major_version() self._server_parameters = self.get_server_parameters(self.config) else: self.set_state('initdb failed') return ret def _custom_bootstrap(self, config): self.set_state('running custom bootstrap script') params = ['--scope=' + self.scope, '--datadir=' + self._data_dir] try: logger.info('Running custom bootstrap script: %s', config['command']) if self.cancellable_subprocess_call(shlex.split(config['command']) + params) != 0: self.set_state('custom bootstrap failed') return False except Exception: logger.exception('Exception during custom bootstrap') return False self._post_restore() self.save_configuration_files() if 'recovery_conf' in config: self.write_recovery_conf(config['recovery_conf']) elif os.path.isfile(self._recovery_conf) or os.path.islink(self._recovery_conf): os.unlink(self._recovery_conf) return True def run_bootstrap_post_init(self, config): """ runs a script after initdb or custom bootstrap script is called and waits until completion. """ cmd = config.get('post_bootstrap') or config.get('post_init') if cmd: r = self._local_connect_kwargs if 'host' in r: # '/tmp' => '%2Ftmp' for unix socket path host = quote_plus(r['host']) if r['host'].startswith('/') else r['host'] else: host = '' # https://www.postgresql.org/docs/current/static/libpq-pgpass.html # A host name of localhost matches both TCP (host name localhost) and Unix domain socket # (pghost empty or the default socket directory) connections coming from the local machine. r['host'] = 'localhost' # set it to localhost to write into pgpass if 'user' in r: user = r['user'] + '@' else: user = '' if 'password' in r: import getpass r.setdefault('user', os.environ.get('PGUSER', getpass.getuser())) connstring = 'postgres://{0}{1}:{2}/{3}'.format(user, host, r['port'], r['database']) env = self.write_pgpass(r) if 'password' in r else None try: ret = self.cancellable_subprocess_call(shlex.split(cmd) + [connstring], env=env) except OSError: logger.error('post_init script %s failed', cmd) return False if ret != 0: logger.error('post_init script %s returned non-zero code %d', cmd, ret) return False return True def delete_trigger_file(self): if os.path.exists(self._trigger_file): os.unlink(self._trigger_file) def write_pgpass(self, record): if 'user' not in record or 'password' not in record: return os.environ.copy() with open(self._pgpass, 'w') as f: os.fchmod(f.fileno(), 0o600) f.write('{host}:{port}:*:{user}:{password}\n'.format(**record)) env = os.environ.copy() env['PGPASSFILE'] = self._pgpass return env def replica_method_can_work_without_replication_connection(self, method): return method != 'basebackup' and self.config and self.config.get(method, {}).get('no_master') def can_create_replica_without_replication_connection(self): """ go through the replication methods to see if there are ones that does not require a working replication connection. """ replica_methods = self.config.get('create_replica_method', []) return any(self.replica_method_can_work_without_replication_connection(method) for method in replica_methods) def create_replica(self, clone_member): """ create the replica according to the replica_method defined by the user. this is a list, so we need to loop through all methods the user supplies """ self.set_state('creating replica') self._sysid = None # get list of replica methods from config. # If there is no configuration key, or no value is specified, use basebackup replica_methods = self.config.get('create_replica_method') or ['basebackup'] if clone_member and clone_member.conn_url: r = clone_member.conn_kwargs(self._replication) connstring = 'postgres://{user}@{host}:{port}/{database}'.format(**r) # add the credentials to connect to the replica origin to pgpass. env = self.write_pgpass(r) else: connstring = '' env = os.environ.copy() # if we don't have any source, leave only replica methods that work without it replica_methods = \ [r for r in replica_methods if self.replica_method_can_work_without_replication_connection(r)] # go through them in priority order ret = 1 for replica_method in replica_methods: with self._cancellable_lock: if self._is_cancelled: break # if the method is basebackup, then use the built-in if replica_method == "basebackup": ret = self.basebackup(connstring, env) if ret == 0: logger.info("replica has been created using basebackup") # if basebackup succeeds, exit with success break else: if not self.data_directory_empty(): self.remove_data_directory() cmd = replica_method method_config = {} # user-defined method; check for configuration # not required, actually if replica_method in self.config: method_config = self.config[replica_method].copy() # look to see if the user has supplied a full command path # if not, use the method name as the command cmd = method_config.pop('command', cmd) # add the default parameters method_config.update({"scope": self.scope, "role": "replica", "datadir": self._data_dir, "connstring": connstring}) params = ["--{0}={1}".format(arg, val) for arg, val in method_config.items()] try: # call script with the full set of parameters ret = self.cancellable_subprocess_call(shlex.split(cmd) + params, env=env) # if we succeeded, stop if ret == 0: logger.info('replica has been created using %s', replica_method) break else: logger.error('Error creating replica using method %s: %s exited with code=%s', replica_method, cmd, ret) except Exception: logger.exception('Error creating replica using method %s', replica_method) ret = 1 self.set_state('stopped') return ret def reset_cluster_info_state(self): self._cluster_info_state = {} def _cluster_info_state_get(self, name): if not self._cluster_info_state: stmt = cluster_info_query.format(self.wal_name, self.lsn_name) try: result = self._is_leader_retry(self._query, stmt).fetchone() self._cluster_info_state = dict(zip(['timeline', 'wal_position'], result)) except RetryFailedError as e: # SELECT failed two times self._cluster_info_state = {'error': str(e)} if not self.is_starting() and self.pg_isready() == STATE_REJECT: self.set_state('starting') if 'error' in self._cluster_info_state: raise PostgresConnectionException(self._cluster_info_state['error']) return self._cluster_info_state.get(name) def is_leader(self): return bool(self._cluster_info_state_get('timeline')) def is_running(self): """Returns PostmasterProcess if one is running on the data directory or None. If most recently seen process is running udpates the cached process based on pid file.""" if self._postmaster_proc: if self._postmaster_proc.is_running(): return self._postmaster_proc self._postmaster_proc = None self._postmaster_proc = PostmasterProcess.from_pidfile(self._read_pid_file()) return self._postmaster_proc def _read_pid_file(self): """Reads and parses postmaster.pid from the data directory :returns dictionary of values if successful, empty dictionary otherwise """ pid_line_names = ['pid', 'data_dir', 'start_time', 'port', 'socket_dir', 'listen_addr', 'shmem_key'] try: with open(self._postmaster_pid) as f: return {name: line.rstrip("\n") for name, line in zip(pid_line_names, f)} except IOError: return {} @property def cb_called(self): return self.__cb_called def call_nowait(self, cb_name): """ pick a callback command and call it without waiting for it to finish """ if self.bootstrapping: return if cb_name in (ACTION_ON_START, ACTION_ON_STOP, ACTION_ON_RESTART, ACTION_ON_ROLE_CHANGE): self.__cb_called = True if self.callback and cb_name in self.callback: cmd = self.callback[cb_name] try: cmd = shlex.split(self.callback[cb_name]) + [cb_name, self.role, self.scope] self._callback_executor.call(cmd) except Exception: logger.exception('callback %s %s %s %s failed', cmd, cb_name, self.role, self.scope) @property def role(self): with self._role_lock: return self._role def set_role(self, value): with self._role_lock: self._role = value @property def state(self): with self._state_lock: return self._state def set_state(self, value): with self._state_lock: self._state = value self._state_entry_timestamp = time.time() def time_in_state(self): return time.time() - self._state_entry_timestamp def is_starting(self): return self.state == 'starting' def wait_for_port_open(self, postmaster, timeout): """Waits until PostgreSQL opens ports.""" for _ in polling_loop(timeout): with self._cancellable_lock: if self._is_cancelled: return False if not postmaster.is_running(): logger.error('postmaster is not running') self.set_state('start failed') return False isready = self.pg_isready() if isready != STATE_NO_RESPONSE: if isready not in [STATE_REJECT, STATE_RUNNING]: logger.warning("Can't determine PostgreSQL startup status, assuming running") return True logger.warning("Timed out waiting for PostgreSQL to start") return False def start(self, timeout=None, block_callbacks=False, task=None): """Start PostgreSQL Waits for postmaster to open ports or terminate so pg_isready can be used to check startup completion or failure. :returns: True if start was initiated and postmaster ports are open, False if start failed""" # make sure we close all connections established against # the former node, otherwise, we might get a stalled one # after kill -9, which would report incorrect data to # patroni. self.close_connection() if self.is_running(): logger.error('Cannot start PostgreSQL because one is already running.') return True if not block_callbacks: self.__cb_pending = ACTION_ON_START self.set_role(self.get_postgres_role_from_data_directory()) self.set_state('starting') self._pending_restart = False self._write_postgresql_conf() self.resolve_connection_addresses() self._replace_pg_hba() options = ['--{0}={1}'.format(p, self._server_parameters[p]) for p in self.CMDLINE_OPTIONS if p in self._server_parameters and p != 'wal_keep_segments'] with self._cancellable_lock: if self._is_cancelled: return False with task or null_context(): if task and task.is_cancelled: logger.info("PostgreSQL start cancelled.") return False self._postmaster_proc = PostmasterProcess.start(self._pgcommand('postgres'), self._data_dir, self._postgresql_conf, options) if task: task.complete(self._postmaster_proc) start_timeout = timeout if not start_timeout: try: start_timeout = float(self.config.get('pg_ctl_timeout', 60)) except ValueError: start_timeout = 60 # We want postmaster to open ports before we continue if not self._postmaster_proc or not self.wait_for_port_open(self._postmaster_proc, start_timeout): return False ret = self.wait_for_startup(start_timeout) if ret is not None: return ret elif timeout is not None: return False else: return None def checkpoint(self, connect_kwargs=None): check_not_is_in_recovery = connect_kwargs is not None connect_kwargs = connect_kwargs or self._local_connect_kwargs for p in ['connect_timeout', 'options']: connect_kwargs.pop(p, None) try: with self._get_connection_cursor(**connect_kwargs) as cur: cur.execute("SET statement_timeout = 0") if check_not_is_in_recovery: cur.execute('SELECT pg_is_in_recovery()') if cur.fetchone()[0]: return 'is_in_recovery=true' return cur.execute('CHECKPOINT') except psycopg2.Error: logging.exception('Exception during CHECKPOINT') return 'not accessible or not healty' def stop(self, mode='fast', block_callbacks=False, checkpoint=None, on_safepoint=None): """Stop PostgreSQL Supports a callback when a safepoint is reached. A safepoint is when no user backend can return a successful commit to users. Currently this means we wait for user backends to close. But in the future alternate mechanisms could be added. :param on_safepoint: This callback is called when no user backends are running. """ if checkpoint is None: checkpoint = False if mode == 'immediate' else True success, pg_signaled = self._do_stop(mode, block_callbacks, checkpoint, on_safepoint) if success: # block_callbacks is used during restart to avoid # running start/stop callbacks in addition to restart ones if not block_callbacks: self.set_state('stopped') if pg_signaled: self.call_nowait(ACTION_ON_STOP) else: logger.warning('pg_ctl stop failed') self.set_state('stop failed') return success def _do_stop(self, mode, block_callbacks, checkpoint, on_safepoint): postmaster = self.is_running() if not postmaster: if on_safepoint: on_safepoint() return True, False if checkpoint and not self.is_starting(): self.checkpoint() if not block_callbacks: self.set_state('stopping') # Send signal to postmaster to stop success = postmaster.signal_stop(mode) if success is not None: if success and on_safepoint: on_safepoint() return success, True # We can skip safepoint detection if we don't have a callback if on_safepoint: # Wait for our connection to terminate so we can be sure that no new connections are being initiated self._wait_for_connection_close(postmaster) postmaster.wait_for_user_backends_to_close() on_safepoint() postmaster.wait() return True, True @staticmethod def terminate_starting_postmaster(postmaster): """Terminates a postmaster that has not yet opened ports or possibly even written a pid file. Blocks until the process goes away.""" postmaster.signal_stop('immediate') postmaster.wait() def _wait_for_connection_close(self, postmaster): try: with self.connection().cursor() as cur: while postmaster.is_running(): # Need a timeout here? cur.execute("SELECT 1") time.sleep(STOP_POLLING_INTERVAL) except psycopg2.Error: pass def reload(self): ret = self.pg_ctl('reload') if ret: self.call_nowait(ACTION_ON_RELOAD) return ret def check_for_startup(self): """Checks PostgreSQL status and returns if PostgreSQL is in the middle of startup.""" return self.is_starting() and not self.check_startup_state_changed() def check_startup_state_changed(self): """Checks if PostgreSQL has completed starting up or failed or still starting. Should only be called when state == 'starting' :returns: True iff state was changed from 'starting' """ ready = self.pg_isready() if ready == STATE_REJECT: return False elif ready == STATE_NO_RESPONSE: self.set_state('start failed') self._schedule_load_slots = False # TODO: can remove this? if not self._running_custom_bootstrap: self.save_configuration_files() # TODO: maybe remove this? return True else: if ready != STATE_RUNNING: # Bad configuration or unexpected OS error. No idea of PostgreSQL status. # Let the main loop of run cycle clean up the mess. logger.warning("%s status returned from pg_isready", "Unknown" if ready == STATE_UNKNOWN else "Invalid") self.set_state('running') self._schedule_load_slots = self.use_slots if not self._running_custom_bootstrap: self.save_configuration_files() # TODO: __cb_pending can be None here after PostgreSQL restarts on its own. Do we want to call the callback? # Previously we didn't even notice. action = self.__cb_pending or ACTION_ON_START self.call_nowait(action) self.__cb_pending = None return True def wait_for_startup(self, timeout=None): """Waits for PostgreSQL startup to complete or fail. :returns: True if start was successful, False otherwise""" if not self.is_starting(): # Should not happen logger.warning("wait_for_startup() called when not in starting state") while not self.check_startup_state_changed(): with self._cancellable_lock: if self._is_cancelled: return None if timeout and self.time_in_state() > timeout: return None time.sleep(1) return self.state == 'running' def restart(self, timeout=None, task=None): """Restarts PostgreSQL. When timeout parameter is set the call will block either until PostgreSQL has started, failed to start or timeout arrives. :returns: True when restart was successful and timeout did not expire when waiting. """ self.set_state('restarting') self.__cb_pending = ACTION_ON_RESTART ret = self.stop(block_callbacks=True) and self.start(timeout=timeout, block_callbacks=True, task=task) if not ret and not self.is_starting(): self.set_state('restart failed ({0})'.format(self.state)) return ret def _write_postgresql_conf(self): # rename the original configuration if it is necessary if 'custom_conf' not in self.config and not os.path.exists(self._postgresql_base_conf): os.rename(self._postgresql_conf, self._postgresql_base_conf) with open(self._postgresql_conf, 'w') as f: f.write(self._CONFIG_WARNING_HEADER) f.write("include '{0}'\n\n".format(self.config.get('custom_conf') or self._postgresql_base_conf_name)) for name, value in sorted(self._server_parameters.items()): if not self._running_custom_bootstrap or name != 'hba_file': f.write("{0} = '{1}'\n".format(name, value)) # when we are doing custom bootstrap we assume that we don't know superuser password # and in order to be able to change it, we are opening trust access from a certain address # therefore we need to make sure that hba_file is not overriden # after changing superuser password we will "revert" all these "changes" if self._running_custom_bootstrap or 'hba_file' not in self._server_parameters: f.write("hba_file = '{0}'\n".format(self._pg_hba_conf)) if 'ident_file' not in self._server_parameters: f.write("ident_file = '{0}'\n".format(os.path.join(self._config_dir, 'pg_ident.conf'))) def is_healthy(self): if not self.is_running(): logger.warning('Postgresql is not running.') return False return True def write_pg_hba(self, config): with open(self._pg_hba_conf, 'a') as f: f.write('\n{}\n'.format('\n'.join(config))) def _replace_pg_hba(self): """ Replace pg_hba.conf content in the PGDATA if hba_file is not defined in the `postgresql.parameters` and pg_hba is defined in `postgresql` configuration section. :returns: True if pg_hba.conf was rewritten. """ # when we are doing custom bootstrap we assume that we don't know superuser password # and in order to be able to change it, we are opening trust access from a certain address if self._running_custom_bootstrap: addresses = {'': 'local'} if 'host' in self._local_address and not self._local_address['host'].startswith('/'): for _, _, _, _, sa in socket.getaddrinfo(self._local_address['host'], self._local_address['port'], 0, socket.SOCK_STREAM, socket.IPPROTO_TCP): addresses[sa[0] + '/32'] = 'host' with open(self._pg_hba_conf, 'w') as f: f.write(self._CONFIG_WARNING_HEADER) for address, t in addresses.items(): f.write('{0}\t{1}\t{2}\t{3}\ttrust\n'.format(t, 'all', self._superuser.get('username') or 'all', address)) elif not self._server_parameters.get('hba_file') and self.config.get('pg_hba'): with open(self._pg_hba_conf, 'w') as f: f.write(self._CONFIG_WARNING_HEADER) for line in self.config['pg_hba']: f.write('{0}\n'.format(line)) return True def primary_conninfo(self, member): if not (member and member.conn_url) or member.name == self.name: return None r = member.conn_kwargs(self._replication) r.update({'application_name': self.name, 'sslmode': 'prefer', 'sslcompression': '1'}) keywords = 'user password host port sslmode sslcompression application_name'.split() return ' '.join('{0}={{{0}}}'.format(kw) for kw in keywords).format(**r) def check_recovery_conf(self, member): # TODO: recovery.conf could be stale, would be nice to detect that. primary_conninfo = self.primary_conninfo(member) if not os.path.isfile(self._recovery_conf): return False with open(self._recovery_conf, 'r') as f: for line in f: if line.startswith('primary_conninfo'): return primary_conninfo and (primary_conninfo in line) return not primary_conninfo def write_recovery_conf(self, recovery_params): with open(self._recovery_conf, 'w') as f: for name, value in recovery_params.items(): f.write("{0} = '{1}'\n".format(name, value)) def pg_rewind(self, r): # prepare pg_rewind connection env = self.write_pgpass(r) dsn_attrs = [ ('user', r.get('user')), ('host', r.get('host')), ('port', r.get('port')), ('dbname', r.get('database')), ('sslmode', 'prefer'), ('sslcompression', '1'), ] dsn = " ".join("{0}={1}".format(k, v) for k, v in dsn_attrs if v is not None) logger.info('running pg_rewind from %s', dsn) try: return self.cancellable_subprocess_call([self._pgcommand('pg_rewind'), '-D', self._data_dir, '--source-server', dsn], env=env) == 0 except OSError: return False def controldata(self): """ return the contents of pg_controldata, or non-True value if pg_controldata call failed """ result = {} # Don't try to call pg_controldata during backup restore if not self.bootstrapping and self._version_file_exists() and self.state != 'creating replica': try: data = subprocess.check_output([self._pgcommand('pg_controldata'), self._data_dir], env={'LANG': 'C', 'LC_ALL': 'C', 'PATH': os.environ['PATH']}) if data: data = data.decode('utf-8').splitlines() # pg_controldata output depends on major verion. Some of parameters are prefixed by 'Current ' result = {l.split(':')[0].replace('Current ', '', 1): l.split(':', 1)[1].strip() for l in data if l} except subprocess.CalledProcessError: logger.exception("Error when calling pg_controldata") return result @property def need_rewind(self): return self._rewind_state in (REWIND_STATUS.CHECK, REWIND_STATUS.NEED) @staticmethod @contextmanager def _get_connection_cursor(**kwargs): with psycopg2.connect(**kwargs) as conn: conn.autocommit = True with conn.cursor() as cur: yield cur @contextmanager def _get_replication_connection_cursor(self, host='localhost', port=5432, **kwargs): with self._get_connection_cursor(host=host, port=int(port), database=self._database, replication=1, user=self._replication['username'], password=self._replication['password'], connect_timeout=3, options='-c statement_timeout=2000') as cur: yield cur def check_leader_is_not_in_recovery(self, **kwargs): try: with self._get_connection_cursor(connect_timeout=3, options='-c statement_timeout=2000', **kwargs) as cur: cur.execute('SELECT pg_is_in_recovery()') if not cur.fetchone()[0]: return True logger.info('Leader is still in_recovery and therefore can\'t be used for rewind') except Exception: return logger.exception('Exception when working with leader') def _get_local_timeline_lsn_from_replication_connection(self): timeline = lsn = None try: with self._get_replication_connection_cursor(**self._local_replication_address) as cur: cur.execute('IDENTIFY_SYSTEM') timeline, lsn = cur.fetchone()[1:3] except Exception: logger.exception('Can not fetch local timeline and lsn from replication connection') return timeline, lsn def _get_local_timeline_lsn_from_controldata(self): timeline = lsn = None data = self.controldata() try: if data.get('Database cluster state') == 'shut down in recovery': lsn = data.get('Minimum recovery ending location') timeline = int(data.get("Min recovery ending loc's timeline")) if lsn == '0/0' or timeline == 0: # it was a master when it crashed data['Database cluster state'] = 'shut down' if data.get('Database cluster state') == 'shut down': lsn = data.get('Latest checkpoint location') timeline = int(data.get("Latest checkpoint's TimeLineID")) except (TypeError, ValueError): logger.exception('Failed to get local timeline and lsn from pg_controldata output') return timeline, lsn def _get_local_timeline_lsn(self): if self.is_running(): # if postgres is running - get timeline and lsn from replication connection timeline, lsn = self._get_local_timeline_lsn_from_replication_connection() else: # otherwise analyze pg_controldata output timeline, lsn = self._get_local_timeline_lsn_from_controldata() logger.info('Local timeline=%s lsn=%s', timeline, lsn) return timeline, lsn @staticmethod def parse_lsn(lsn): t = lsn.split('/') return int(t[0], 16) * 0x100000000 + int(t[1], 16) @staticmethod def parse_history(data): for line in data.split('\n'): values = line.strip().split('\t') if len(values) == 3: try: values[0] = int(values[0]) values[1] = Postgresql.parse_lsn(values[1]) yield values except (IndexError, ValueError): logger.exception('Exception when parsing timeline history line "%s"', values) def _check_timeline_and_lsn(self, leader): local_timeline, local_lsn = self._get_local_timeline_lsn() if local_timeline is None or local_lsn is None: return if not self.check_leader_is_not_in_recovery(**leader.conn_kwargs(self._superuser)): return history = need_rewind = None try: with self._get_replication_connection_cursor(**leader.conn_kwargs()) as cur: cur.execute('IDENTIFY_SYSTEM') master_timeline = cur.fetchone()[1] logger.info('master_timeline=%s', master_timeline) if local_timeline > master_timeline: # Not always supported by pg_rewind need_rewind = True elif master_timeline > 1: cur.execute('TIMELINE_HISTORY %s', (master_timeline,)) history = bytes(cur.fetchone()[1]).decode('utf-8') logger.info('master: history=%s', history) else: # local_timeline == master_timeline == 1 need_rewind = False except Exception: return logger.exception('Exception when working with master via replication connection') if history is not None: for parent_timeline, switchpoint, _ in self.parse_history(history): if parent_timeline == local_timeline: try: need_rewind = self.parse_lsn(local_lsn) >= switchpoint except (IndexError, ValueError): logger.exception('Exception when parsing lsn') break elif parent_timeline > local_timeline: break self._rewind_state = need_rewind and REWIND_STATUS.NEED or REWIND_STATUS.NOT_NEED def get_replica_timeline(self): return self._get_local_timeline_lsn_from_replication_connection()[0] def replica_cached_timeline(self, master_timeline): if not self._cached_replica_timeline or not master_timeline or self._cached_replica_timeline != master_timeline: self._cached_replica_timeline = self.get_replica_timeline() return self._cached_replica_timeline def get_master_timeline(self): return self._cluster_info_state_get('timeline') def get_history(self, timeline): history_path = 'pg_{0}/{1:08X}.history'.format(self.wal_name, timeline) try: cursor = self._cursor() cursor.execute('SELECT isdir, modification FROM pg_stat_file(%s)', (history_path,)) isdir, modification = cursor.fetchone() if not isdir: cursor.execute('SELECT pg_read_file(%s)', (history_path,)) history = list(self.parse_history(cursor.fetchone()[0])) if history[-1][0] == timeline - 1: history[-1].append(modification.isoformat()) return history except Exception: logger.exception('Failed to read and parse %s', (history_path,)) def rewind(self, leader): if self.is_running() and not self.stop(checkpoint=False): return logger.warning('Can not run pg_rewind because postgres is still running') # prepare pg_rewind connection r = leader.conn_kwargs(self._superuser) # first make sure that we are really trying to rewind # from the master and run a checkpoint on it in order to # make it store the new timeline (5540277D.8020309@iki.fi) leader_status = self.checkpoint(r) if leader_status: return logger.warning('Can not use %s for rewind: %s', leader.name, leader_status) if self.pg_rewind(r): self._rewind_state = REWIND_STATUS.SUCCESS elif not self.check_leader_is_not_in_recovery(**r): logger.warning('Failed to rewind because master %s become unreachable', leader.name) else: logger.error('Failed to rewind from healty master: %s', leader.name) if self.config.get('remove_data_directory_on_rewind_failure', False): logger.warning('remove_data_directory_on_rewind_failure is set. removing...') self.remove_data_directory() self._rewind_state = REWIND_STATUS.INITIAL else: self._rewind_state = REWIND_STATUS.FAILED return False def trigger_check_diverged_lsn(self): if self.can_rewind and self._rewind_state != REWIND_STATUS.NEED: self._rewind_state = REWIND_STATUS.CHECK def rewind_needed_and_possible(self, leader): if leader and leader.name != self.name and leader.conn_url and self._rewind_state == REWIND_STATUS.CHECK: self._check_timeline_and_lsn(leader) return leader and leader.conn_url and self._rewind_state == REWIND_STATUS.NEED @property def rewind_executed(self): return self._rewind_state > REWIND_STATUS.NOT_NEED def follow(self, member, timeout=None): primary_conninfo = self.primary_conninfo(member) change_role = self.role in ('master', 'demoted') recovery_params = self.config.get('recovery_conf', {}).copy() recovery_params.update({'standby_mode': 'on', 'recovery_target_timeline': 'latest'}) if primary_conninfo: recovery_params['primary_conninfo'] = primary_conninfo if self.use_slots: recovery_params['primary_slot_name'] = slot_name_from_member_name(self.name) self.write_recovery_conf(recovery_params) if self.is_running(): self.restart() else: self.start(timeout=timeout) self.set_role('replica') if change_role: # TODO: postpone this until start completes, or maybe do even earlier self.call_nowait(ACTION_ON_ROLE_CHANGE) return True def save_configuration_files(self): """ copy postgresql.conf to postgresql.conf.backup to be able to retrive configuration files - originally stored as symlinks, those are normally skipped by pg_basebackup - in case of WAL-E basebackup (see http://comments.gmane.org/gmane.comp.db.postgresql.wal-e/239) """ try: for f in self._configuration_to_save: config_file = os.path.join(self._config_dir, f) backup_file = os.path.join(self._data_dir, f + '.backup') if os.path.isfile(config_file): shutil.copy(config_file, backup_file) except IOError: logger.exception('unable to create backup copies of configuration files') def restore_configuration_files(self): """ restore a previously saved postgresql.conf """ try: for f in self._configuration_to_save: config_file = os.path.join(self._config_dir, f) backup_file = os.path.join(self._data_dir, f + '.backup') if not os.path.isfile(config_file): if os.path.isfile(backup_file): shutil.copy(backup_file, config_file) # Previously we didn't backup pg_ident.conf, if file is missing just create empty elif f == 'pg_ident.conf': open(config_file, 'w').close() except IOError: logger.exception('unable to restore configuration files from backup') def _wait_promote(self, wait_seconds): for _ in polling_loop(wait_seconds - 1): data = self.controldata() if data.get('Database cluster state') == 'in production': return True def promote(self, wait_seconds): if self.role == 'master': return True ret = self.pg_ctl('promote', '-W') if ret: self.set_role('master') logger.info("cleared rewind state after becoming the leader") self._rewind_state = REWIND_STATUS.INITIAL self.call_nowait(ACTION_ON_ROLE_CHANGE) ret = self._wait_promote(wait_seconds) return ret def create_or_update_role(self, name, password, options): options = list(map(str.upper, options)) if 'NOLOGIN' not in options and 'LOGIN' not in options: options.append('LOGIN') self.query("""DO $$ BEGIN SET local synchronous_commit = 'local'; PERFORM * FROM pg_authid WHERE rolname = %s; IF FOUND THEN ALTER ROLE "{0}" WITH {1} PASSWORD %s; ELSE CREATE ROLE "{0}" WITH {1} PASSWORD %s; END IF; END; $$""".format(name, ' '.join(options)), name, password, password) def timeline_wal_position(self): # This method could be called from different threads (simultaneously with some other `_query` calls). # If it is called not from main thread we will create a new cursor to execute statement. if current_thread().ident == self.__thread_ident: return self._cluster_info_state_get('timeline'), self._cluster_info_state_get('wal_position') with self.connection().cursor() as cursor: cursor.execute(cluster_info_query.format(self.wal_name, self.lsn_name)) return cursor.fetchone()[:2] def load_replication_slots(self): if self.use_slots and self._schedule_load_slots: cursor = self._query("SELECT slot_name FROM pg_replication_slots WHERE slot_type='physical'") self._replication_slots = [r[0] for r in cursor] self._schedule_load_slots = False def postmaster_start_time(self): try: cursor = self.query("""SELECT to_char(pg_postmaster_start_time(), 'YYYY-MM-DD HH24:MI:SS.MS TZ')""") return cursor.fetchone()[0] except psycopg2.Error: return None def sync_replication_slots(self, cluster): if self.use_slots: try: self.load_replication_slots() # if the replicatefrom tag is set on the member - we should not create the replication slot for it on # the current master, because that member would replicate from elsewhere. We still create the slot if # the replicatefrom destination member is currently not a member of the cluster (fallback to the # master), or if replicatefrom destination member happens to be the current master if self.role == 'master': slot_members = [m.name for m in cluster.members if m.name != self.name and (m.replicatefrom is None or m.replicatefrom == self.name or not cluster.has_member(m.replicatefrom))] else: # only manage slots for replicas that replicate from this one, except for the leader among them slot_members = [m.name for m in cluster.members if m.replicatefrom == self.name and m.name != cluster.leader.name] slots = set(slot_name_from_member_name(name) for name in slot_members) if len(slots) < len(slot_members): # Find which names are conflicting for a nicer error message slot_conflicts = defaultdict(list) for name in slot_members: slot_conflicts[slot_name_from_member_name(name)].append(name) logger.error("Following cluster members share a replication slot name: %s", "; ".join("{} map to {}".format(", ".join(v), k) for k, v in slot_conflicts.items() if len(v) > 1)) # drop unused slots for slot in set(self._replication_slots) - slots: cursor = self._query("""SELECT pg_drop_replication_slot(%s) WHERE EXISTS(SELECT 1 FROM pg_replication_slots WHERE slot_name = %s AND NOT active)""", slot, slot) if cursor.rowcount != 1: # Either slot doesn't exists or it is still active self._schedule_load_slots = True # schedule load_replication_slots on the next iteration # create new slots for slot in slots - set(self._replication_slots): self._query("""SELECT pg_create_physical_replication_slot(%s) WHERE NOT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = %s)""", slot, slot) self._replication_slots = slots except Exception: logger.exception('Exception when changing replication slots') self._schedule_load_slots = True def last_operation(self): return str(self._cluster_info_state_get('wal_position')) def _post_restore(self): self.delete_trigger_file() self.restore_configuration_files() def _configure_server_parameters(self): self._major_version = self.get_major_version() self._server_parameters = self.get_server_parameters(self.config) return True def clone(self, clone_member): """ - initialize the replica from an existing member (master or replica) - initialize the replica using the replica creation method that works without the replication connection (i.e. restore from on-disk base backup) """ ret = self.create_replica(clone_member) == 0 if ret: self._post_restore() self._configure_server_parameters() return ret def bootstrap(self, config): """ Initialize a new node from scratch and start it. """ method = config.get('method') or 'initdb' self._running_custom_bootstrap = method != 'initdb' and method in config and 'command' in config[method] if self._running_custom_bootstrap: do_initialize = self._custom_bootstrap config = config[method] else: do_initialize = self._initdb return do_initialize(config) and self._configure_server_parameters() and self.start() def post_bootstrap(self, config, task): try: self.create_or_update_role(self._superuser['username'], self._superuser['password'], ['SUPERUSER']) task.complete(self.run_bootstrap_post_init(config)) if task.result: self.create_or_update_role(self._replication['username'], self._replication['password'], ['REPLICATION']) for name, value in (config.get('users') or {}).items(): if name not in (self._superuser.get('username'), self._replication['username']): self.create_or_update_role(name, value['password'], value.get('options', [])) # We were doing a custom bootstrap instead of running initdb, therefore we opened trust # access from certain addresses to be able to reach cluster and change password if self._running_custom_bootstrap: self._running_custom_bootstrap = False # If we don't have custom configuration for pg_hba.conf we need to restore original file if not self.config.get('pg_hba'): os.unlink(self._pg_hba_conf) self.restore_configuration_files() self._write_postgresql_conf() if self._server_parameters.get('hba_file') and \ self._server_parameters['hba_file'] != self._pg_hba_conf: self.restart() else: self._replace_pg_hba() self.reload() time.sleep(1) # give a time to postgres to "reload" configuration files self.close_connection() # close connection to reconnect with a new password except Exception: logger.exception('post_bootstrap') task.complete(False) return task.result def move_data_directory(self): if os.path.isdir(self._data_dir) and not self.is_running(): try: new_name = '{0}_{1}'.format(self._data_dir, time.strftime('%Y-%m-%d-%H-%M-%S')) logger.info('renaming data directory to %s', new_name) os.rename(self._data_dir, new_name) except OSError: logger.exception("Could not rename data directory %s", self._data_dir) def remove_data_directory(self): self.set_role('uninitialized') logger.info('Removing data directory: %s', self._data_dir) try: if os.path.islink(self._data_dir): os.unlink(self._data_dir) elif not os.path.exists(self._data_dir): return elif os.path.isfile(self._data_dir): os.remove(self._data_dir) elif os.path.isdir(self._data_dir): shutil.rmtree(self._data_dir) except (IOError, OSError): logger.exception('Could not remove data directory %s', self._data_dir) self.move_data_directory() def basebackup(self, conn_url, env): # creates a replica data dir using pg_basebackup. # this is the default, built-in create_replica_method # tries twice, then returns failure (as 1) # uses "stream" as the xlog-method to avoid sync issues maxfailures = 2 ret = 1 for bbfailures in range(0, maxfailures): with self._cancellable_lock: if self._is_cancelled: break if not self.data_directory_empty(): self.remove_data_directory() try: ret = self.cancellable_subprocess_call([self._pgcommand('pg_basebackup'), '--pgdata=' + self._data_dir, '-X', 'stream', '--dbname=' + conn_url], env=env) if ret == 0: break else: logger.error('Error when fetching backup: pg_basebackup exited with code=%s', ret) except Exception as e: logger.error('Error when fetching backup with pg_basebackup: %s', e) if bbfailures < maxfailures - 1: logger.warning('Trying again in 5 seconds') time.sleep(5) return ret def pick_synchronous_standby(self, cluster): """Finds the best candidate to be the synchronous standby. Current synchronous standby is always preferred, unless it has disconnected or does not want to be a synchronous standby any longer. :returns tuple of candidate name or None, and bool showing if the member is the active synchronous standby. """ current = cluster.sync.sync_standby current = current.lower() if current else current members = {m.name.lower(): m for m in cluster.members} candidates = [] # Pick candidates based on who has flushed WAL farthest. # TODO: for synchronous_commit = remote_write we actually want to order on write_location for app_name, state, sync_state in self.query( """SELECT LOWER(application_name), state, sync_state FROM pg_stat_replication ORDER BY flush_{0} DESC""".format(self.lsn_name)): member = members.get(app_name) if state != 'streaming' or not member or member.tags.get('nosync', False): continue if sync_state == 'sync': return app_name, True if sync_state == 'potential' and app_name == current: # Prefer current even if not the best one any more to avoid indecisivness and spurious swaps. return current, False if sync_state == 'async': candidates.append(app_name) if candidates: return candidates[0], False return None, False def set_synchronous_standby(self, name): """Sets a node to be synchronous standby and if changed does a reload for PostgreSQL.""" if name and name != '*': name = quote_ident(name) if name != self._synchronous_standby_names: if name is None: self._server_parameters.pop('synchronous_standby_names', None) else: self._server_parameters['synchronous_standby_names'] = name self._synchronous_standby_names = name if self.state == 'running': self._write_postgresql_conf() self.reload() @staticmethod def postgres_version_to_int(pg_version): """Convert the server_version to integer >>> Postgresql.postgres_version_to_int('9.5.3') 90503 >>> Postgresql.postgres_version_to_int('9.3.13') 90313 >>> Postgresql.postgres_version_to_int('10.1') 100001 >>> Postgresql.postgres_version_to_int('10') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... PostgresException: 'Invalid PostgreSQL version format: X.Y or X.Y.Z is accepted: 10' >>> Postgresql.postgres_version_to_int('9.6') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... PostgresException: 'Invalid PostgreSQL version format: X.Y or X.Y.Z is accepted: 9.6' >>> Postgresql.postgres_version_to_int('a.b.c') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... PostgresException: 'Invalid PostgreSQL version: a.b.c' """ try: components = list(map(int, pg_version.split('.'))) except ValueError: raise PostgresException('Invalid PostgreSQL version: {0}'.format(pg_version)) if len(components) < 2 or len(components) == 2 and components[0] < 10 or len(components) > 3: raise PostgresException('Invalid PostgreSQL version format: X.Y or X.Y.Z is accepted: {0}' .format(pg_version)) if len(components) == 2: # new style verion numbers, i.e. 10.1 becomes 100001 components.insert(1, 0) return int(''.join('{0:02d}'.format(c) for c in components)) @staticmethod def postgres_major_version_to_int(pg_version): """ >>> Postgresql.postgres_major_version_to_int('10') 100000 >>> Postgresql.postgres_major_version_to_int('9.6') 90600 """ return Postgresql.postgres_version_to_int(pg_version + '.0') def read_postmaster_opts(self): """returns the list of option names/values from postgres.opts, Empty dict if read failed or no file""" result = {} try: with open(os.path.join(self._data_dir, 'postmaster.opts')) as f: data = f.read() for opt in data.split('" "'): if '=' in opt and opt.startswith('--'): name, val = opt.split('=', 1) result[name.strip('-')] = val.rstrip('"\n') except IOError: logger.exception('Error when reading postmaster.opts') return result def single_user_mode(self, command=None, options=None): """run a given command in a single-user mode. If the command is empty - then just start and stop""" cmd = [self._pgcommand('postgres'), '--single', '-D', self._data_dir] for opt, val in sorted((options or {}).items()): cmd.extend(['-c', '{0}={1}'.format(opt, val)]) # need a database name to connect cmd.append(self._database) return self.cancellable_subprocess_call(cmd, communicate_input=command) def cleanup_archive_status(self): status_dir = os.path.join(self._data_dir, 'pg_' + self.wal_name, 'archive_status') try: for f in os.listdir(status_dir): path = os.path.join(status_dir, f) try: if os.path.islink(path): os.unlink(path) elif os.path.isfile(path): os.remove(path) except OSError: logger.exception('Unable to remove %s', path) except OSError: logger.exception('Unable to list %s', status_dir) def fix_cluster_state(self): self.cleanup_archive_status() # Start in a single user mode and stop to produce a clean shutdown opts = self.read_postmaster_opts() opts.update({'archive_mode': 'on', 'archive_command': 'false'}) if os.path.isfile(self._recovery_conf) or os.path.islink(self._recovery_conf): os.unlink(self._recovery_conf) return self.single_user_mode(options=opts) == 0 or None def cancellable_subprocess_call(self, *args, **kwargs): communicate_input = kwargs.pop('communicate_input', None) for s in ('stdin', 'stdout', 'stderr'): kwargs.pop(s, None) try: with self._cancellable_lock: if self._is_cancelled: raise PostgresException('cancelled') self._is_cancelled = False self._cancellable = subprocess.Popen(*args, **kwargs) if communicate_input: kwargs['stdin'] = subprocess.PIPE if communicate_input[-1] != '\n': communicate_input += '\n' self._cancellable.communicate(communicate_input + '\n') self._cancellable.stdin.close() return self._cancellable.wait() finally: with self._cancellable_lock: self._cancellable = None def reset_is_cancelled(self): with self._cancellable_lock: self._is_cancelled = False def cancel(self): with self._cancellable_lock: self._is_cancelled = True if self._cancellable is None or self._cancellable.returncode is not None: return self._cancellable.terminate() for _ in polling_loop(10): with self._cancellable_lock: if self._cancellable is None or self._cancellable.returncode is not None: return with self._cancellable_lock: if self._cancellable is not None and self._cancellable.returncode is None: self._cancellable.kill() patroni-1.4.2/patroni/postmaster.py000066400000000000000000000121361323411135200174170ustar00rootroot00000000000000import logging import os import psutil import re import signal import subprocess from patroni import call_self logger = logging.getLogger(__name__) STOP_SIGNALS = { 'smart': signal.SIGTERM, 'fast': signal.SIGINT, 'immediate': signal.SIGQUIT, } class PostmasterProcess(psutil.Process): def __init__(self, pid): self.is_single_user = False if pid < 0: pid = -pid self.is_single_user = True super(PostmasterProcess, self).__init__(pid) @classmethod def from_pidfile(cls, pidfile): try: pid = int(pidfile.get('pid', 0)) if not pid: return None except ValueError: return None try: proc = cls(pid) except psutil.NoSuchProcess: return None try: start_time = int(pidfile.get('start_time', 0)) if start_time and abs(proc.create_time() - start_time) > 3: return None except ValueError: logger.warning("Garbage start time value in pid file: %r", pidfile.get('start_time')) # Extra safety check. The process can't be ourselves, our parent or our direct child. if proc.pid == os.getpid() or proc.pid == os.getppid() or proc.parent() == os.getpid(): return None return proc @classmethod def from_pid(cls, pid): try: return cls(pid) except psutil.NoSuchProcess: return None def signal_stop(self, mode): """Signal postmaster process to stop :returns None if signaled, True if process is already gone, False if error """ if self.is_single_user: logger.warning("Cannot stop server; single-user server is running (PID: {0})".format(self.pid)) return False try: self.send_signal(STOP_SIGNALS[mode]) except psutil.NoSuchProcess: return True except psutil.AccessDenied as e: logger.warning("Could not send stop signal to PostgreSQL (error: {0})".format(e)) return False return None def wait_for_user_backends_to_close(self): # These regexps are cross checked against versions PostgreSQL 9.1 .. 9.6 aux_proc_re = re.compile("(?:postgres:)( .*:)? (?:""(?:startup|logger|checkpointer|writer|wal writer|" "autovacuum launcher|autovacuum worker|stats collector|wal receiver|archiver|" "wal sender) process|bgworker: )") try: user_backends = [] user_backends_cmdlines = [] for child in self.children(): try: cmdline = child.cmdline()[0] if not aux_proc_re.match(cmdline): user_backends.append(child) user_backends_cmdlines.append(cmdline) except psutil.NoSuchProcess: pass if user_backends: logger.debug('Waiting for user backends %s to close', ', '.join(user_backends_cmdlines)) psutil.wait_procs(user_backends) logger.debug("Backends closed") except psutil.Error: logger.exception('wait_for_user_backends_to_close') @classmethod def start(cls, pgcommand, data_dir, conf, options): # Unfortunately `pg_ctl start` does not return postmaster pid to us. Without this information # it is hard to know the current state of postgres startup, so we had to reimplement pg_ctl start # in python. It will start postgres, wait for port to be open and wait until postgres will start # accepting connections. # Important!!! We can't just start postgres using subprocess.Popen, because in this case it # will be our child for the rest of our live and we will have to take care of it (`waitpid`). # So we will use the same approach as pg_ctl uses: start a new process, which will start postgres. # This process will write postmaster pid to stdout and exit immediately. Now it's responsibility # of init process to take care about postmaster. # In order to make everything portable we can't use fork&exec approach here, so we will call # ourselves and pass list of arguments which must be used to start postgres. proc = call_self(['pg_ctl_start', pgcommand, '-D', data_dir, '--config-file={}'.format(conf)] + options, close_fds=True, preexec_fn=os.setsid, stdout=subprocess.PIPE, env={p: os.environ[p] for p in ('PATH', 'LC_ALL', 'LANG') if p in os.environ}) pid = int(proc.stdout.readline().strip()) proc.wait() logger.info('postmaster pid=%s', pid) # TODO: In an extremely unlikely case, the process could have exited and the pid reassigned. The start # initiation time is not accurate enough to compare to create time as start time would also likely # be relatively close. We need the subprocess extract pid+start_time in a race free manner. return PostmasterProcess.from_pid(pid) patroni-1.4.2/patroni/scripts/000077500000000000000000000000001323411135200163305ustar00rootroot00000000000000patroni-1.4.2/patroni/scripts/__init__.py000066400000000000000000000000001323411135200204270ustar00rootroot00000000000000patroni-1.4.2/patroni/scripts/aws.py000077500000000000000000000054201323411135200175000ustar00rootroot00000000000000#!/usr/bin/env python import logging import requests from requests.exceptions import RequestException import sys import boto.ec2 from patroni.utils import Retry, RetryFailedError logger = logging.getLogger(__name__) class AWSConnection(object): def __init__(self, cluster_name): self.available = False self.cluster_name = cluster_name if cluster_name is not None else 'unknown' self._retry = Retry(deadline=300, max_delay=30, max_tries=-1, retry_exceptions=(boto.exception.StandardError,)) try: # get the instance id r = requests.get('http://169.254.169.254/latest/dynamic/instance-identity/document', timeout=2.1) except RequestException: logger.error('cannot query AWS meta-data') return if r.ok: try: content = r.json() self.instance_id = content['instanceId'] self.region = content['region'] except Exception: logger.exception('unable to fetch instance id and region from AWS meta-data') return self.available = True def retry(self, *args, **kwargs): return self._retry.copy()(*args, **kwargs) def aws_available(self): return self.available def _tag_ebs(self, conn, role): """ set tags, carrying the cluster name, instance role and instance id for the EBS storage """ tags = {'Name': 'spilo_' + self.cluster_name, 'Role': role, 'Instance': self.instance_id} volumes = conn.get_all_volumes(filters={'attachment.instance-id': self.instance_id}) conn.create_tags([v.id for v in volumes], tags) def _tag_ec2(self, conn, role): """ tag the current EC2 instance with a cluster role """ tags = {'Role': role} conn.create_tags([self.instance_id], tags) def on_role_change(self, new_role): if not self.available: return False try: conn = self.retry(boto.ec2.connect_to_region, self.region) self.retry(self._tag_ec2, conn, new_role) self.retry(self._tag_ebs, conn, new_role) except RetryFailedError: logger.warning("Unable to communicate to AWS " "when setting tags for the EC2 instance {0} " "and attached EBS volumes".format(self.instance_id)) return False return True def main(): logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=logging.INFO) if len(sys.argv) == 4 and sys.argv[1] in ('on_start', 'on_stop', 'on_role_change'): AWSConnection(cluster_name=sys.argv[3]).on_role_change(sys.argv[2]) else: sys.exit("Usage: {0} action role name".format(sys.argv[0])) if __name__ == '__main__': main() patroni-1.4.2/patroni/scripts/wale_restore.py000077500000000000000000000341511323411135200214040ustar00rootroot00000000000000#!/usr/bin/env python # sample script to clone new replicas using WAL-E restore # falls back to pg_basebackup if WAL-E restore fails, or if # WAL-E backup is too far behind # note that pg_basebackup still expects to use restore from # WAL-E for transaction logs # theoretically should work with SWIFT, but not tested on it # arguments are: # - cluster scope # - cluster role # - master connection string # - number of retries # - envdir for the WALE env # - WALE_BACKUP_THRESHOLD_MEGABYTES if WAL amount is above that - use pg_basebackup # - WALE_BACKUP_THRESHOLD_PERCENTAGE if WAL size exceeds a certain percentage of the # this script depends on an envdir defining the S3 bucket (or SWIFT dir),and login # credentials per WALE Documentation. # currently also requires that you configure the restore_command to use wal_e, example: # recovery_conf: # restore_command: envdir /etc/wal-e.d/env wal-e wal-fetch "%f" "%p" -p 1 import argparse import csv import logging import os import psycopg2 import subprocess import sys import time from collections import namedtuple logger = logging.getLogger(__name__) RETRY_SLEEP_INTERVAL = 1 si_prefixes = ['K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] # Meaningful names to the exit codes used by WALERestore ExitCode = type('Enum', (), { 'SUCCESS': 0, #: Succeeded 'RETRY_LATER': 1, #: External issue, retry later 'FAIL': 2 #: Don't try again unless configuration changes }) # We need to know the current PG version in order to figure out the correct WAL directory name def get_major_version(data_dir): version_file = os.path.join(data_dir, 'PG_VERSION') if os.path.isfile(version_file): # version file exists try: with open(version_file) as f: return float(f.read()) except Exception: logger.exception('Failed to read PG_VERSION from %s', data_dir) return 0.0 def repr_size(n_bytes): """ >>> repr_size(1000) '1000 Bytes' >>> repr_size(8257332324597) '7.5 TiB' """ if n_bytes < 1024: return '{0} Bytes'.format(n_bytes) i = -1 while n_bytes > 1023: n_bytes /= 1024.0 i += 1 return '{0} {1}iB'.format(round(n_bytes, 1), si_prefixes[i]) def size_as_bytes(size_, prefix): """ >>> size_as_bytes(7.5, 'T') 8246337208320 """ prefix = prefix.upper() assert prefix in si_prefixes exponent = si_prefixes.index(prefix) + 1 return int(size_ * (1024.0 ** exponent)) WALEConfig = namedtuple( 'WALEConfig', [ 'env_dir', 'threshold_mb', 'threshold_pct', 'cmd', ] ) class WALERestore(object): def __init__(self, scope, datadir, connstring, env_dir, threshold_mb, threshold_pct, use_iam, no_master, retries): self.scope = scope self.master_connection = connstring self.data_dir = datadir self.no_master = no_master wale_cmd = [ 'envdir', env_dir, 'wal-e', ] if use_iam == 1: wale_cmd += ['--aws-instance-profile'] self.wal_e = WALEConfig( env_dir=env_dir, threshold_mb=threshold_mb, threshold_pct=threshold_pct, cmd=wale_cmd, ) self.init_error = (not os.path.exists(self.wal_e.env_dir)) self.retries = retries def run(self): """ Creates a new replica using WAL-E Returns ------- ExitCode 0 = Success 1 = Error, try again 2 = Error, don't try again """ if self.init_error: logger.error('init error: %r did not exist at initialization time', self.wal_e.env_dir) return ExitCode.FAIL try: should_use_s3 = self.should_use_s3_to_create_replica() if should_use_s3 is None: # Need to retry return ExitCode.RETRY_LATER elif should_use_s3: return self.create_replica_with_s3() elif not should_use_s3: return ExitCode.FAIL except Exception: logger.exception("Unhandled exception when running WAL-E restore") return ExitCode.FAIL def should_use_s3_to_create_replica(self): """ determine whether it makes sense to use S3 and not pg_basebackup """ threshold_megabytes = self.wal_e.threshold_mb threshold_percent = self.wal_e.threshold_pct try: cmd = self.wal_e.cmd + ['backup-list', '--detail', 'LATEST'] logger.debug('calling %r', cmd) wale_output = subprocess.check_output(cmd) reader = csv.DictReader(wale_output.decode('utf-8').splitlines(), dialect='excel-tab') rows = list(reader) if not len(rows): logger.warning('wal-e did not find any backups') return False # This check might not add much, it was performed in the previous # version of this code. since the old version rolled CSV parsing the # check may have been part of the CSV parsing. if len(rows) > 1: logger.warning( 'wal-e returned more than one row of backups: %r', rows) return False backup_info = rows[0] except subprocess.CalledProcessError: logger.exception("could not query wal-e latest backup") return None try: backup_size = int(backup_info['expanded_size_bytes']) backup_start_segment = backup_info['wal_segment_backup_start'] backup_start_offset = backup_info['wal_segment_offset_backup_start'] except KeyError: logger.exception("unable to get some of WALE backup parameters") return None # WAL filename is XXXXXXXXYYYYYYYY000000ZZ, where X - timeline, Y - LSN logical log file, # ZZ - 2 high digits of LSN offset. The rest of the offset is the provided decimal offset, # that we have to convert to hex and 'prepend' to the high offset digits. lsn_segment = backup_start_segment[8:16] # first 2 characters of the result are 0x and the last one is L lsn_offset = hex((int(backup_start_segment[16:32], 16) << 24) + int(backup_start_offset))[2:-1] # construct the LSN from the segment and offset backup_start_lsn = '{0}/{1}'.format(lsn_segment, lsn_offset) diff_in_bytes = backup_size attempts_no = 0 while True: if self.master_connection: try: # get the difference in bytes between the current WAL location and the backup start offset with psycopg2.connect(self.master_connection) as con: if con.server_version >= 100000: wal_name = 'wal' lsn_name = 'lsn' else: wal_name = 'xlog' lsn_name = 'location' con.autocommit = True with con.cursor() as cur: cur.execute("""SELECT CASE WHEN pg_is_in_recovery() THEN GREATEST( pg_{0}_{1}_diff(COALESCE( pg_last_{0}_receive_{1}(), '0/0'), %s)::bigint, pg_{0}_{1}_diff(pg_last_{0}_replay_{1}(), %s)::bigint) ELSE pg_{0}_{1}_diff(pg_current_{0}_{1}(), %s)::bigint END""".format(wal_name, lsn_name), (backup_start_lsn, backup_start_lsn, backup_start_lsn)) diff_in_bytes = int(cur.fetchone()[0]) except psycopg2.Error: logger.exception('could not determine difference with the master location') if attempts_no < self.retries: # retry in case of a temporarily connection issue attempts_no = attempts_no + 1 time.sleep(RETRY_SLEEP_INTERVAL) continue else: if not self.no_master: return False # do no more retries on the outer level logger.info("continue with base backup from S3 since master is not available") diff_in_bytes = 0 break else: # always try to use WAL-E if master connection string is not available diff_in_bytes = 0 break # if the size of the accumulated WAL segments is more than a certan percentage of the backup size # or exceeds the pre-determined size - pg_basebackup is chosen instead. is_size_thresh_ok = diff_in_bytes < int(threshold_megabytes) * 1048576 threshold_pct_bytes = backup_size * threshold_percent / 100.0 is_percentage_thresh_ok = float(diff_in_bytes) < int(threshold_pct_bytes) are_thresholds_ok = is_size_thresh_ok and is_percentage_thresh_ok class Size(object): def __init__(self, n_bytes, prefix=None): self.n_bytes = n_bytes self.prefix = prefix def __repr__(self): if self.prefix is not None: n_bytes = size_as_bytes(self.n_bytes, self.prefix) else: n_bytes = self.n_bytes return repr_size(n_bytes) class HumanContext(object): def __init__(self, items): self.items = items def __repr__(self): return ', '.join('{}={!r}'.format(key, value) for key, value in self.items) human_context = repr(HumanContext([ ('threshold_size', Size(threshold_megabytes, 'M')), ('threshold_percent', threshold_percent), ('threshold_percent_size', Size(threshold_pct_bytes)), ('backup_size', Size(backup_size)), ('backup_diff', Size(diff_in_bytes)), ('is_size_thresh_ok', is_size_thresh_ok), ('is_percentage_thresh_ok', is_percentage_thresh_ok), ])) if not are_thresholds_ok: logger.info('wal-e backup size diff is over threshold, falling back ' 'to other means of restore: %s', human_context) else: logger.info('Thresholds are OK, using wal-e basebackup: %s', human_context) return are_thresholds_ok def fix_subdirectory_path_if_broken(self, dirname): # in case it is a symlink pointing to a non-existing location, remove it and create the actual directory path = os.path.join(self.data_dir, dirname) if not os.path.exists(path): if os.path.islink(path): # broken xlog symlink, to remove try: os.remove(path) except OSError: logger.exception("could not remove broken %s symlink pointing to %s", dirname, os.readlink(path)) return False try: os.mkdir(path) except OSError: logger.exception("coud not create missing %s directory path", dirname) return False return True def create_replica_with_s3(self): # if we're set up, restore the replica using fetch latest try: cmd = self.wal_e.cmd + ['backup-fetch', '{}'.format(self.data_dir), 'LATEST'] logger.debug('calling: %r', cmd) exit_code = subprocess.call(cmd) except Exception as e: logger.error('Error when fetching backup with WAL-E: {0}'.format(e)) return ExitCode.RETRY_LATER if (exit_code == 0 and not self.fix_subdirectory_path_if_broken('pg_xlog' if get_major_version(self.data_dir) < 10 else 'pg_wal')): return ExitCode.FAIL return exit_code def main(): logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=logging.INFO) parser = argparse.ArgumentParser(description='Script to image replicas using WAL-E') parser.add_argument('--scope', required=True) parser.add_argument('--role', required=False) parser.add_argument('--datadir', required=True) parser.add_argument('--connstring', required=True) parser.add_argument('--retries', type=int, default=1) parser.add_argument('--envdir', required=True) parser.add_argument('--threshold_megabytes', type=int, default=10240) parser.add_argument('--threshold_backup_size_percentage', type=int, default=30) parser.add_argument('--use_iam', type=int, default=0) parser.add_argument('--no_master', type=int, default=0) args = parser.parse_args() exit_code = None assert args.retries >= 0 # Retry cloning in a loop. We do separate retries for the master # connection attempt inside should_use_s3_to_create_replica, # because we need to differentiate between the last attempt and # the rest and make a decision when the last attempt fails on # whether to use WAL-E or not depending on the no_master flag. for _ in range(0, args.retries + 1): restore = WALERestore(scope=args.scope, datadir=args.datadir, connstring=args.connstring, env_dir=args.envdir, threshold_mb=args.threshold_megabytes, threshold_pct=args.threshold_backup_size_percentage, use_iam=args.use_iam, no_master=args.no_master, retries=args.retries) exit_code = restore.run() if not exit_code == ExitCode.RETRY_LATER: # only WAL-E failures lead to the retry logger.debug('exit_code is %r, not retrying', exit_code) break time.sleep(RETRY_SLEEP_INTERVAL) return exit_code if __name__ == '__main__': sys.exit(main()) patroni-1.4.2/patroni/utils.py000066400000000000000000000223721323411135200163610ustar00rootroot00000000000000import random import time from dateutil import tz from patroni.exceptions import PatroniException tzutc = tz.tzutc() def deep_compare(obj1, obj2): """ >>> deep_compare({'1': None}, {}) False >>> deep_compare({'1': {}}, {'1': None}) False >>> deep_compare({'1': [1]}, {'1': [2]}) False >>> deep_compare({'1': 2}, {'1': '2'}) True >>> deep_compare({'1': {'2': [3, 4]}}, {'1': {'2': [3, 4]}}) True """ if set(list(obj1.keys())) != set(list(obj2.keys())): # Objects have different sets of keys return False for key, value in obj1.items(): if isinstance(value, dict): if not (isinstance(obj2[key], dict) and deep_compare(value, obj2[key])): return False elif str(value) != str(obj2[key]): return False return True def patch_config(config, data): """recursively 'patch' `config` with `data` :returns: `!True` if the `config` was changed""" is_changed = False for name, value in data.items(): if value is None: if config.pop(name, None) is not None: is_changed = True elif name in config: if isinstance(value, dict): if isinstance(config[name], dict): if patch_config(config[name], value): is_changed = True else: config[name] = value is_changed = True elif str(config[name]) != str(value): config[name] = value is_changed = True else: config[name] = value is_changed = True return is_changed def parse_bool(value): """ >>> parse_bool(1) True >>> parse_bool('off') False >>> parse_bool('foo') """ value = str(value).lower() if value in ('on', 'true', 'yes', '1'): return True if value in ('off', 'false', 'no', '0'): return False def strtol(value, strict=True): """As most as possible close equivalent of strtol(3) function (with base=0), used by postgres to parse parameter values. >>> strtol(0) == (0, '') True >>> strtol(1) == (1, '') True >>> strtol(9) == (9, '') True >>> strtol(' +0x400MB') == (1024, 'MB') True >>> strtol(' -070d') == (-56, 'd') True >>> strtol(' d ') == (None, 'd') True >>> strtol('9s', False) == (9, 's') True >>> strtol(' s ', False) == (1, 's') True """ value = str(value).strip() ln = len(value) i = 0 # skip sign: if i < ln and value[i] in ('-', '+'): i += 1 # we always expect to get digit in the beginning if i < ln and value[i].isdigit(): if value[i] == '0': i += 1 if i < ln and value[i] in ('x', 'X'): # '0' followed by 'x': HEX base = 16 i += 1 else: # just starts with '0': OCT base = 8 else: # any other digit: DEC base = 10 ret = None while i <= ln: try: # try to find maximally long number i += 1 # by giving to `int` longer and longer strings ret = int(value[:i], base) except ValueError: # until we will not get an exception or end of the string i -= 1 break if ret is not None: # yay! there is a number in the beginning of the string return ret, value[i:].strip() # return the number and the "rest" return (None if strict else 1), value.strip() def parse_int(value, base_unit=None): """ >>> parse_int('1') == 1 True >>> parse_int(' 0x400 MB ', '16384kB') == 64 True >>> parse_int('1MB', 'kB') == 1024 True >>> parse_int('1000 ms', 's') == 1 True >>> parse_int('1GB', 'MB') is None True >>> parse_int(0) == 0 True """ convert = { 'kB': {'kB': 1, 'MB': 1024, 'GB': 1024 * 1024, 'TB': 1024 * 1024 * 1024}, 'ms': {'ms': 1, 's': 1000, 'min': 1000 * 60, 'h': 1000 * 60 * 60, 'd': 1000 * 60 * 60 * 24}, 's': {'ms': -1000, 's': 1, 'min': 60, 'h': 60 * 60, 'd': 60 * 60 * 24}, 'min': {'ms': -1000 * 60, 's': -60, 'min': 1, 'h': 60, 'd': 60 * 24} } value, unit = strtol(value) if value is not None: if not unit: return value if base_unit and base_unit not in convert: base_value, base_unit = strtol(base_unit, False) else: base_value = 1 if base_unit in convert and unit in convert[base_unit]: multiplier = convert[base_unit][unit] if multiplier < 0: value /= -multiplier else: value *= multiplier return int(value/base_value) def compare_values(vartype, unit, old_value, new_value): """ >>> compare_values('enum', None, 'remote_write', 'REMOTE_WRITE') True >>> compare_values('real', None, '1.23', 1.23) True """ # if the integer or bool new_value is not correct this function will return False if vartype == 'bool': old_value = parse_bool(old_value) new_value = parse_bool(new_value) elif vartype == 'integer': old_value = parse_int(old_value) new_value = parse_int(new_value, unit) elif vartype == 'enum': return str(old_value).lower() == str(new_value).lower() else: # ('string', 'real') return str(old_value) == str(new_value) return old_value is not None and new_value is not None and old_value == new_value def _sleep(interval): time.sleep(interval) class RetryFailedError(PatroniException): """Raised when retrying an operation ultimately failed, after retrying the maximum number of attempts.""" class Retry(object): """Helper for retrying a method in the face of retry-able exceptions""" def __init__(self, max_tries=1, delay=0.1, backoff=2, max_jitter=0.8, max_delay=3600, sleep_func=_sleep, deadline=None, retry_exceptions=PatroniException): """Create a :class:`Retry` instance for retrying function calls :param max_tries: How many times to retry the command. -1 means infinite tries. :param delay: Initial delay between retry attempts. :param backoff: Backoff multiplier between retry attempts. Defaults to 2 for exponential backoff. :param max_jitter: Additional max jitter period to wait between retry attempts to avoid slamming the server. :param max_delay: Maximum delay in seconds, regardless of other backoff settings. Defaults to one hour. :param retry_exceptions: single exception or tuple""" self.max_tries = max_tries self.delay = delay self.backoff = backoff self.max_jitter = int(max_jitter * 100) self.max_delay = float(max_delay) self._attempts = 0 self._cur_delay = delay self.deadline = deadline self._cur_stoptime = None self.sleep_func = sleep_func self.retry_exceptions = retry_exceptions def reset(self): """Reset the attempt counter""" self._attempts = 0 self._cur_delay = self.delay self._cur_stoptime = None def copy(self): """Return a clone of this retry manager""" return Retry(max_tries=self.max_tries, delay=self.delay, backoff=self.backoff, max_jitter=self.max_jitter / 100.0, max_delay=self.max_delay, sleep_func=self.sleep_func, deadline=self.deadline, retry_exceptions=self.retry_exceptions) def __call__(self, func, *args, **kwargs): """Call a function with arguments until it completes without throwing a `retry_exceptions` :param func: Function to call :param args: Positional arguments to call the function with :params kwargs: Keyword arguments to call the function with The function will be called until it doesn't throw one of the retryable exceptions""" self.reset() while True: try: if self.deadline is not None and self._cur_stoptime is None: self._cur_stoptime = time.time() + self.deadline return func(*args, **kwargs) except self.retry_exceptions: # Note: max_tries == -1 means infinite tries. if self._attempts == self.max_tries: raise RetryFailedError("Too many retry attempts") self._attempts += 1 sleeptime = self._cur_delay + (random.randint(0, self.max_jitter) / 100.0) if self._cur_stoptime is not None and time.time() + sleeptime >= self._cur_stoptime: raise RetryFailedError("Exceeded retry deadline") else: self.sleep_func(sleeptime) self._cur_delay = min(self._cur_delay * self.backoff, self.max_delay) def polling_loop(timeout, interval=1): """Returns an iterator that returns values until timeout has passed. Timeout is measured from start of iteration.""" start_time = time.time() iteration = 0 end_time = start_time + timeout while time.time() < end_time: yield iteration iteration += 1 time.sleep(interval) def split_host_port(value, default_port): t = value.rsplit(':', 1) t.append(default_port) return t[0], int(t[1]) patroni-1.4.2/patroni/version.py000066400000000000000000000000261323411135200166760ustar00rootroot00000000000000__version__ = '1.4.2' patroni-1.4.2/patroni/watchdog/000077500000000000000000000000001323411135200164415ustar00rootroot00000000000000patroni-1.4.2/patroni/watchdog/__init__.py000066400000000000000000000001421323411135200205470ustar00rootroot00000000000000from patroni.watchdog.base import WatchdogError, Watchdog __all__ = ['WatchdogError', 'Watchdog'] patroni-1.4.2/patroni/watchdog/base.py000066400000000000000000000266151323411135200177370ustar00rootroot00000000000000import abc import logging import platform import six import sys from threading import RLock from patroni.exceptions import WatchdogError __all__ = ['WatchdogError', 'Watchdog'] logger = logging.getLogger(__name__) MODE_REQUIRED = 'required' # Will not run if a watchdog is not available MODE_AUTOMATIC = 'automatic' # Will use a watchdog if one is available MODE_OFF = 'off' # Will not try to use a watchdog def parse_mode(mode): if mode is False: return MODE_OFF mode = mode.lower() if mode in ['require', 'required']: return MODE_REQUIRED elif mode in ['auto', 'automatic']: return MODE_AUTOMATIC else: if mode not in ['off', 'disable', 'disabled']: logger.warning("Watchdog mode {0} not recognized, disabling watchdog".format(mode)) return MODE_OFF def synchronized(func): def wrapped(self, *args, **kwargs): with self._lock: return func(self, *args, **kwargs) return wrapped class WatchdogConfig(object): """Helper to contain a snapshot of configuration""" def __init__(self, config): self.mode = parse_mode(config['watchdog'].get('mode', 'automatic')) self.ttl = config['ttl'] self.loop_wait = config['loop_wait'] self.safety_margin = config['watchdog'].get('safety_margin', 5) self.driver = config['watchdog'].get('driver', 'default') self.driver_config = dict((k, v) for k, v in config['watchdog'].items() if k not in ['mode', 'safety_margin', 'driver']) def __eq__(self, other): return isinstance(other, WatchdogConfig) and \ all(getattr(self, attr) == getattr(other, attr) for attr in ['mode', 'ttl', 'loop_wait', 'safety_margin', 'driver', 'driver_config']) def __ne__(self, other): return not self == other def get_impl(self): if self.driver == 'testing': from patroni.watchdog.linux import TestingWatchdogDevice return TestingWatchdogDevice.from_config(self.driver_config) elif platform.system() == 'Linux' and self.driver == 'default': from patroni.watchdog.linux import LinuxWatchdogDevice return LinuxWatchdogDevice.from_config(self.driver_config) else: return NullWatchdog() @property def timeout(self): if self.safety_margin == -1: return int(self.ttl // 2) else: return self.ttl - self.safety_margin @property def timing_slack(self): return self.timeout - self.loop_wait class Watchdog(object): """Facade to dynamically manage watchdog implementations and handle config changes. When activation fails underlying implementation will be switched to a Null implementation. To avoid log spam activation will only be retried when watchdog configuration is changed.""" def __init__(self, config): self.active_config = self.config = WatchdogConfig(config) self._lock = RLock() self.active = False if self.config.mode == MODE_OFF: self.impl = NullWatchdog() else: self.impl = self.config.get_impl() if self.config.mode == MODE_REQUIRED and self.impl.is_null: logger.error("Configuration requires a watchdog, but watchdog is not supported on this platform.") sys.exit(1) @synchronized def reload_config(self, config): self.config = WatchdogConfig(config) # Turning a watchdog off can always be done immediately if self.config.mode == MODE_OFF: if self.active: self._disable() self.active_config = self.config self.impl = NullWatchdog() # If watchdog is not active we can apply config immediately to show any warnings early. Otherwise we need to # delay until next time a keepalive is sent so timeout matches up with leader key update. if not self.active: if self.config.driver != self.active_config.driver or \ self.config.driver_config != self.active_config.driver_config: self.impl = self.config.get_impl() self.active_config = self.config @synchronized def activate(self): """Activates the watchdog device with suitable timeouts. While watchdog is active keepalive needs to be called every time loop_wait expires. :returns False if a safe watchdog could not be configured, but is required. """ self.active = True return self._activate() def _activate(self): self.active_config = self.config if self.config.timing_slack < 0: logger.warning('Watchdog not supported because leader TTL {0} is less than 2x loop_wait {1}' .format(self.config.ttl, self.config.loop_wait)) self.impl = NullWatchdog() try: self.impl.open() actual_timeout = self._set_timeout() except WatchdogError as e: logger.warning("Could not activate %s: %s", self.impl.describe(), e) self.impl = NullWatchdog() if self.impl.is_running and not self.impl.can_be_disabled: logger.warning("Watchdog implementation can't be disabled." " Watchdog will trigger after Patroni loses leader key.") if not self.impl.is_running or actual_timeout > self.config.timeout: if self.config.mode == MODE_REQUIRED: if self.impl.is_null: logger.error("Configuration requires watchdog, but watchdog could not be configured.") else: logger.error("Configuration requires watchdog, but a safe watchdog timeout {0} could" " not be configured. Watchdog timeout is {1}.".format( self.config.timeout, actual_timeout)) return False else: if not self.impl.is_null: logger.warning("Watchdog timeout {0} seconds does not ensure safe termination within {1} seconds" .format(actual_timeout, self.config.timeout)) if self.is_running: logger.info("{0} activated with {1} second timeout, timing slack {2} seconds" .format(self.impl.describe(), actual_timeout, self.config.timing_slack)) else: if self.config.mode == MODE_REQUIRED: logger.error("Configuration requires watchdog, but watchdog could not be activated") return False return True def _set_timeout(self): if self.impl.has_set_timeout(): self.impl.set_timeout(self.config.timeout) # Safety checks for watchdog implementations that don't support configurable timeouts actual_timeout = self.impl.get_timeout() if self.impl.is_running and actual_timeout < self.config.loop_wait: logger.error('loop_wait of {0} seconds is too long for watchdog {1} second timeout' .format(self.config.loop_wait, actual_timeout)) if self.impl.can_be_disabled: logger.info('Disabling watchdog due to unsafe timeout.') self.impl.close() self.impl = NullWatchdog() return None return actual_timeout @synchronized def disable(self): self._disable() self.active = False def _disable(self): try: if self.impl.is_running and not self.impl.can_be_disabled: # Give sysadmin some extra time to clean stuff up. self.impl.keepalive() logger.warning("Watchdog implementation can't be disabled. System will reboot after " "{0} seconds when watchdog times out.".format(self.impl.get_timeout())) self.impl.close() except WatchdogError as e: logger.error("Error while disabling watchdog: %s", e) @synchronized def keepalive(self): try: if self.active: self.impl.keepalive() # In case there are any pending configuration changes apply them now. if self.active and self.config != self.active_config: if self.config.mode != MODE_OFF and self.active_config.mode == MODE_OFF: self.impl = self.config.get_impl() self._activate() if self.config.driver != self.active_config.driver \ or self.config.driver_config != self.active_config.driver_config: self._disable() self.impl = self.config.get_impl() self._activate() if self.config.timeout != self.active_config.timeout: self.impl.set_timeout(self.config.timeout) except WatchdogError as e: logger.error("Error while sending keepalive: %s", e) @property @synchronized def is_running(self): return self.impl.is_running @property @synchronized def is_healthy(self): if self.config.mode != MODE_REQUIRED: return True return self.config.timing_slack >= 0 and self.impl.is_healthy @six.add_metaclass(abc.ABCMeta) class WatchdogBase(object): """A watchdog object when opened requires periodic calls to keepalive. When keepalive is not called within a timeout the system will be terminated.""" is_null = False @property def is_running(self): """Returns True when watchdog is activated and capable of performing it's task.""" return False @property def is_healthy(self): """Returns False when calling open() is known to fail.""" return False @property def can_be_disabled(self): """Returns True when watchdog will be disabled by calling close(). Some watchdog devices will keep running no matter what once activated. May raise WatchdogError if called without calling open() first.""" return True @abc.abstractmethod def open(self): """Open watchdog device. When watchdog is opened keepalive must be called. Returns nothing on success or raises WatchdogError if the device could not be opened.""" @abc.abstractmethod def close(self): """Gracefully close watchdog device.""" @abc.abstractmethod def keepalive(self): """Resets the watchdog timer. Watchdog must be open when keepalive is called.""" @abc.abstractmethod def get_timeout(self): """Returns the current keepalive timeout in effect.""" @staticmethod def has_set_timeout(): """Returns True if setting a timeout is supported.""" return False def set_timeout(self, timeout): """Set the watchdog timer timeout. :param timeout: watchdog timeout in seconds""" raise WatchdogError("Setting timeout is not supported on {0}".format(self.describe())) def describe(self): """Human readable name for this device""" return self.__class__.__name__ @classmethod def from_config(cls, config): return cls() class NullWatchdog(WatchdogBase): """Null implementation when watchdog is not supported.""" is_null = True def open(self): return def close(self): return def keepalive(self): return def get_timeout(self): # A big enough number to not matter return 1000000000 patroni-1.4.2/patroni/watchdog/linux.py000066400000000000000000000172461323411135200201640ustar00rootroot00000000000000import collections import ctypes import fcntl import os import platform from patroni.watchdog.base import WatchdogBase, WatchdogError # Pythonification of linux/ioctl.h IOC_NONE = 0 IOC_WRITE = 1 IOC_READ = 2 IOC_NRBITS = 8 IOC_TYPEBITS = 8 IOC_SIZEBITS = 14 IOC_DIRBITS = 2 # Non-generic platform special cases machine = platform.machine() if machine in ['mips', 'sparc', 'powerpc', 'ppc64']: IOC_SIZEBITS = 13 IOC_DIRBITS = 3 IOC_NONE, IOC_WRITE, IOC_READ = 1, 2, 4 elif machine == 'parisc': IOC_WRITE, IOC_READ = 2, 1 IOC_NRSHIFT = 0 IOC_TYPESHIFT = IOC_NRSHIFT + IOC_NRBITS IOC_SIZESHIFT = IOC_TYPESHIFT + IOC_TYPEBITS IOC_DIRSHIFT = IOC_SIZESHIFT + IOC_SIZEBITS def IOW(type_, nr, size): return IOC(IOC_WRITE, type_, nr, size) def IOR(type_, nr, size): return IOC(IOC_READ, type_, nr, size) def IOWR(type_, nr, size): return IOC(IOC_READ | IOC_WRITE, type_, nr, size) def IOC(dir_, type_, nr, size): return (dir_ << IOC_DIRSHIFT) \ | (ord(type_) << IOC_TYPESHIFT) \ | (nr << IOC_NRSHIFT) \ | (size << IOC_SIZESHIFT) # Pythonification of linux/watchdog.h WATCHDOG_IOCTL_BASE = 'W' class watchdog_info(ctypes.Structure): _fields_ = [ ('options', ctypes.c_uint32), # Options the card/driver supports ('firmware_version', ctypes.c_uint32), # Firmware version of the card ('identity', ctypes.c_uint8 * 32), # Identity of the board ] struct_watchdog_info_size = ctypes.sizeof(watchdog_info) int_size = ctypes.sizeof(ctypes.c_int) WDIOC_GETSUPPORT = IOR(WATCHDOG_IOCTL_BASE, 0, struct_watchdog_info_size) WDIOC_GETSTATUS = IOR(WATCHDOG_IOCTL_BASE, 1, int_size) WDIOC_GETBOOTSTATUS = IOR(WATCHDOG_IOCTL_BASE, 2, int_size) WDIOC_GETTEMP = IOR(WATCHDOG_IOCTL_BASE, 3, int_size) WDIOC_SETOPTIONS = IOR(WATCHDOG_IOCTL_BASE, 4, int_size) WDIOC_KEEPALIVE = IOR(WATCHDOG_IOCTL_BASE, 5, int_size) WDIOC_SETTIMEOUT = IOWR(WATCHDOG_IOCTL_BASE, 6, int_size) WDIOC_GETTIMEOUT = IOR(WATCHDOG_IOCTL_BASE, 7, int_size) WDIOC_SETPRETIMEOUT = IOWR(WATCHDOG_IOCTL_BASE, 8, int_size) WDIOC_GETPRETIMEOUT = IOR(WATCHDOG_IOCTL_BASE, 9, int_size) WDIOC_GETTIMELEFT = IOR(WATCHDOG_IOCTL_BASE, 10, int_size) WDIOF_UNKNOWN = -1 # Unknown flag error WDIOS_UNKNOWN = -1 # Unknown status error WDIOF = { "OVERHEAT": 0x0001, # Reset due to CPU overheat "FANFAULT": 0x0002, # Fan failed "EXTERN1": 0x0004, # External relay 1 "EXTERN2": 0x0008, # External relay 2 "POWERUNDER": 0x0010, # Power bad/power fault "CARDRESET": 0x0020, # Card previously reset the CPU "POWEROVER": 0x0040, # Power over voltage "SETTIMEOUT": 0x0080, # Set timeout (in seconds) "MAGICCLOSE": 0x0100, # Supports magic close char "PRETIMEOUT": 0x0200, # Pretimeout (in seconds), get/set "ALARMONLY": 0x0400, # Watchdog triggers a management or other external alarm not a reboot "KEEPALIVEPING": 0x8000, # Keep alive ping reply } WDIOS = { "DISABLECARD": 0x0001, # Turn off the watchdog timer "ENABLECARD": 0x0002, # Turn on the watchdog timer "TEMPPANIC": 0x0004, # Kernel panic on temperature trip } # Implementation class WatchdogInfo(collections.namedtuple('WatchdogInfo', 'options,version,identity')): """Watchdog descriptor from the kernel""" def __getattr__(self, name): """Convenience has_XYZ attributes for checking WDIOF bits in options""" if name.startswith('has_') and name[4:] in WDIOF: return bool(self.options & WDIOF[name[4:]]) raise AttributeError("WatchdogInfo instance has no attribute '{0}'".format(name)) class LinuxWatchdogDevice(WatchdogBase): DEFAULT_DEVICE = '/dev/watchdog' def __init__(self, device): self.device = device self._support_cache = None self._fd = None @classmethod def from_config(cls, config): device = config.get('device', cls.DEFAULT_DEVICE) return cls(device) @property def is_running(self): return self._fd is not None @property def is_healthy(self): return os.path.exists(self.device) and os.access(self.device, os.W_OK) def open(self): try: self._fd = os.open(self.device, os.O_WRONLY) except OSError as e: raise WatchdogError("Can't open watchdog device: {0}".format(e)) def close(self): if self.is_running: try: os.write(self._fd, b'V') os.close(self._fd) self._fd = None except OSError as e: raise WatchdogError("Error while closing {0}: {1}".format(self.describe(), e)) @property def can_be_disabled(self): return self.get_support().has_MAGICCLOSE def _ioctl(self, func, arg): """Runs the specified ioctl on the underlying fd. Raises WatchdogError if the device is closed. Raises OSError or IOError (Python 2) when the ioctl fails.""" if self._fd is None: raise WatchdogError("Watchdog device is closed") fcntl.ioctl(self._fd, func, arg, True) def get_support(self): if self._support_cache is None: info = watchdog_info() try: self._ioctl(WDIOC_GETSUPPORT, info) except (WatchdogError, OSError, IOError) as e: raise WatchdogError("Could not get information about watchdog device: {}".format(e)) self._support_cache = WatchdogInfo(info.options, info.firmware_version, bytearray(info.identity).decode(errors='ignore').rstrip('\x00')) return self._support_cache def describe(self): dev_str = " at {0}".format(self.device) if self.device != self.DEFAULT_DEVICE else "" ver_str = "" identity = "Linux watchdog device" if self._fd: try: _, version, identity = self.get_support() ver_str = " (firmware {0})".format(version) if version else "" except WatchdogError: pass return identity + ver_str + dev_str def keepalive(self): try: os.write(self._fd, b'1') except OSError as e: raise WatchdogError("Could not send watchdog keepalive: {0}".format(e)) def has_set_timeout(self): """Returns True if setting a timeout is supported.""" return self.get_support().has_SETTIMEOUT def set_timeout(self, timeout): timeout = int(timeout) if not 0 < timeout < 0xFFFF: raise WatchdogError("Invalid timeout {0}. Supported values are between 1 and 65535".format(timeout)) try: self._ioctl(WDIOC_SETTIMEOUT, ctypes.c_int(timeout)) except (WatchdogError, OSError, IOError) as e: raise WatchdogError("Could not set timeout on watchdog device: {}".format(e)) def get_timeout(self): timeout = ctypes.c_int() try: self._ioctl(WDIOC_GETTIMEOUT, timeout) except (WatchdogError, OSError, IOError) as e: raise WatchdogError("Could not get timeout on watchdog device: {}".format(e)) return timeout.value class TestingWatchdogDevice(LinuxWatchdogDevice): """Converts timeout ioctls to regular writes that can be intercepted from a named pipe.""" timeout = 60 def get_support(self): return WatchdogInfo(WDIOF['MAGICCLOSE'] | WDIOF['SETTIMEOUT'], 0, "Watchdog test harness") def set_timeout(self, timeout): buf = "Ctimeout={0}\n".format(timeout).encode('utf8') while len(buf): buf = buf[os.write(self._fd, buf):] self.timeout = timeout def get_timeout(self): return self.timeout patroni-1.4.2/patronictl.py000077500000000000000000000001341323411135200157170ustar00rootroot00000000000000#!/usr/bin/env python from patroni.ctl import ctl if __name__ == '__main__': ctl(None) patroni-1.4.2/postgres0.yml000066400000000000000000000046001323411135200156360ustar00rootroot00000000000000scope: batman #namespace: /service/ name: postgresql0 restapi: listen: 127.0.0.1:8008 connect_address: 127.0.0.1:8008 # certfile: /etc/ssl/certs/ssl-cert-snakeoil.pem # keyfile: /etc/ssl/private/ssl-cert-snakeoil.key # authentication: # username: username # password: password etcd: host: 127.0.0.1:2379 bootstrap: # this section will be written into Etcd:///config after initializing new cluster # and all other cluster members will use it as a `global configuration` dcs: ttl: 30 loop_wait: 10 retry_timeout: 10 maximum_lag_on_failover: 1048576 # master_start_timeout: 300 # synchronous_mode: false postgresql: use_pg_rewind: true # use_slots: true parameters: # wal_level: hot_standby # hot_standby: "on" # wal_keep_segments: 8 # max_wal_senders: 5 # max_replication_slots: 5 # wal_log_hints: "on" # archive_mode: "on" # archive_timeout: 1800s # archive_command: mkdir -p ../wal_archive && test ! -f ../wal_archive/%f && cp %p ../wal_archive/%f # recovery_conf: # restore_command: cp ../wal_archive/%f %p # some desired options for 'initdb' initdb: # Note: It needs to be a list (some options need values, others are switches) - encoding: UTF8 - data-checksums pg_hba: # Add following lines to pg_hba.conf after running 'initdb' - host replication replicator 127.0.0.1/32 md5 - host all all 0.0.0.0/0 md5 # - hostssl all all 0.0.0.0/0 md5 # Additional script to be launched after initial cluster creation (will be passed the connection URL as parameter) # post_init: /usr/local/bin/setup_cluster.sh # Some additional users users which needs to be created after initializing new cluster users: admin: password: admin options: - createrole - createdb postgresql: listen: 127.0.0.1:5432 connect_address: 127.0.0.1:5432 data_dir: data/postgresql0 # bin_dir: # config_dir: pgpass: /tmp/pgpass0 authentication: replication: username: replicator password: rep-pass superuser: username: postgres password: zalando parameters: unix_socket_directories: '.' #watchdog: # mode: automatic # Allowed values: off, automatic, required # device: /dev/watchdog # safety_margin: 5 tags: nofailover: false noloadbalance: false clonefrom: false nosync: false patroni-1.4.2/postgres1.yml000066400000000000000000000042721323411135200156440ustar00rootroot00000000000000scope: batman #namespace: /service/ name: postgresql1 restapi: listen: 127.0.0.1:8009 connect_address: 127.0.0.1:8009 # certfile: /etc/ssl/certs/ssl-cert-snakeoil.pem # keyfile: /etc/ssl/private/ssl-cert-snakeoil.key # authentication: # username: username # password: password etcd: host: 127.0.0.1:2379 bootstrap: # this section will be written into Etcd:///config after initializing new cluster # and all other cluster members will use it as a `global configuration` dcs: ttl: 30 loop_wait: 10 retry_timeout: 10 maximum_lag_on_failover: 1048576 postgresql: use_pg_rewind: true # use_slots: true parameters: # wal_level: hot_standby # hot_standby: "on" # wal_keep_segments: 8 # max_wal_senders: 5 # max_replication_slots: 5 # wal_log_hints: "on" # archive_mode: "on" # archive_timeout: 1800s # archive_command: mkdir -p ../wal_archive && test ! -f ../wal_archive/%f && cp %p ../wal_archive/%f # recovery_conf: # restore_command: cp ../wal_archive/%f %p # some desired options for 'initdb' initdb: # Note: It needs to be a list (some options need values, others are switches) - encoding: UTF8 - data-checksums pg_hba: # Add following lines to pg_hba.conf after running 'initdb' - host replication replicator 127.0.0.1/32 md5 - host all all 0.0.0.0/0 md5 # - hostssl all all 0.0.0.0/0 md5 # Additional script to be launched after initial cluster creation (will be passed the connection URL as parameter) # post_init: /usr/local/bin/setup_cluster.sh # Some additional users users which needs to be created after initializing new cluster users: admin: password: admin options: - createrole - createdb postgresql: listen: 127.0.0.1:5433 connect_address: 127.0.0.1:5433 data_dir: data/postgresql1 # bin_dir: # config_dir: pgpass: /tmp/pgpass1 authentication: replication: username: replicator password: rep-pass superuser: username: postgres password: zalando parameters: unix_socket_directories: '.' tags: nofailover: false noloadbalance: false clonefrom: false patroni-1.4.2/postgres2.yml000066400000000000000000000040611323411135200156410ustar00rootroot00000000000000scope: batman #namespace: /service/ name: postgresql2 restapi: listen: 127.0.0.1:8010 connect_address: 127.0.0.1:8010 # certfile: /etc/ssl/certs/ssl-cert-snakeoil.pem # keyfile: /etc/ssl/private/ssl-cert-snakeoil.key authentication: username: username password: password etcd: host: 127.0.0.1:2379 bootstrap: # this section will be written into Etcd:///config after initializing new cluster # and all other cluster members will use it as a `global configuration` dcs: ttl: 30 loop_wait: 10 retry_timeout: 10 maximum_lag_on_failover: 1048576 postgresql: use_pg_rewind: true # use_slots: true parameters: # wal_level: hot_standby # hot_standby: "on" # wal_keep_segments: 8 # max_wal_senders: 5 # max_replication_slots: 5 # wal_log_hints: "on" # archive_mode: "on" # archive_timeout: 1800s # archive_command: mkdir -p ../wal_archive && test ! -f ../wal_archive/%f && cp %p ../wal_archive/%f # recovery_conf: # restore_command: cp ../wal_archive/%f %p # some desired options for 'initdb' initdb: # Note: It needs to be a list (some options need values, others are switches) - encoding: UTF8 - data-checksums pg_hba: # Add following lines to pg_hba.conf after running 'initdb' - host replication replicator 127.0.0.1/32 md5 - host all all 0.0.0.0/0 md5 # - hostssl all all 0.0.0.0/0 md5 # Some additional users users which needs to be created after initializing new cluster users: admin: password: admin options: - createrole - createdb postgresql: listen: 127.0.0.1:5434 connect_address: 127.0.0.1:5434 data_dir: data/postgresql2 # bin_dir: # config_dir: pgpass: /tmp/pgpass2 authentication: replication: username: replicator password: rep-pass superuser: username: postgres password: zalando parameters: unix_socket_directories: '.' tags: nofailover: false noloadbalance: false clonefrom: false replicatefrom: postgres1 patroni-1.4.2/release.sh000077500000000000000000000010411323411135200151400ustar00rootroot00000000000000#!/bin/sh if [ $# -ne 1 ]; then >&2 echo "usage: $0 " exit 1 fi readonly VERSIONFILE="patroni/version.py" ## Bail out on any non-zero exitcode from the called processes set -xe python3 --version git --version version=$1 sed -i "s/__version__ = .*/__version__ = '${version}'/" "${VERSIONFILE}" python3 setup.py clean python3 setup.py test python3 setup.py flake8 git add "${VERSIONFILE}" git commit -m "Bumped version to $version" git push python3 setup.py sdist bdist_wheel upload git tag v${version} git push --tags patroni-1.4.2/requirements.txt000066400000000000000000000003241323411135200164500ustar00rootroot00000000000000urllib3>=1.19.1,!=1.21 boto psycopg2>=2.5.4 PyYAML requests six >= 1.7 kazoo>=1.3.1 python-etcd>=0.4.3,<0.5 python-consul>=0.7.0 click>=4.1 prettytable>=0.7 tzlocal python-dateutil psutil cdiff kubernetes==3.0.0 patroni-1.4.2/setup.py000066400000000000000000000131101323411135200146730ustar00rootroot00000000000000#!/usr/bin/env python """ Setup file for patroni """ import inspect import os import sys from setuptools.command.test import test as TestCommand from setuptools import find_packages, setup if sys.version_info < (2, 7, 0): sys.stderr.write('FATAL: patroni needs to be run with Python 2.7+\n') sys.exit(1) __location__ = os.path.join(os.getcwd(), os.path.dirname(inspect.getfile(inspect.currentframe()))) def read_version(package): data = {} with open(os.path.join(package, 'version.py'), 'r') as fd: exec(fd.read(), data) return data['__version__'] NAME = 'patroni' MAIN_PACKAGE = NAME SCRIPTS = 'scripts' VERSION = read_version(MAIN_PACKAGE) DESCRIPTION = 'PostgreSQL High-Available orchestrator and CLI' LICENSE = 'The MIT License' URL = 'https://github.com/zalando/patroni' AUTHOR = 'Alexander Kukushkin, Oleksii Kliukin, Feike Steenbergen' AUTHOR_EMAIL = 'alexander.kukushkin@zalando.de, oleksii.kliukin@zalando.de, feike.steenbergen@zalando.de' KEYWORDS = 'etcd governor patroni postgresql postgres ha haproxy confd zookeeper exhibitor consul streaming replication' COVERAGE_XML = True COVERAGE_HTML = False JUNIT_XML = True # Add here all kinds of additional classifiers as defined under # https://pypi.python.org/pypi?%3Aaction=list_classifiers CLASSIFIERS = [ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: MIT License', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: Implementation :: CPython', ] CONSOLE_SCRIPTS = ['patroni = patroni:main', 'patronictl = patroni.ctl:ctl', "patroni_wale_restore = patroni.scripts.wale_restore:main", "patroni_aws = patroni.scripts.aws:main"] class PyTest(TestCommand): user_options = [('cov=', None, 'Run coverage'), ('cov-xml=', None, 'Generate junit xml report'), ('cov-html=', None, 'Generate junit html report'), ('junitxml=', None, 'Generate xml of test results')] def initialize_options(self): TestCommand.initialize_options(self) self.cov_xml = False self.cov_html = False self.junitxml = None def finalize_options(self): TestCommand.finalize_options(self) if self.cov_xml or self.cov_html: self.cov = ['--cov', MAIN_PACKAGE, '--cov-report', 'term-missing'] if self.cov_xml: self.cov.extend(['--cov-report', 'xml']) if self.cov_html: self.cov.extend(['--cov-report', 'html']) if self.junitxml is not None: self.junitxml = ['--junitxml', self.junitxml] def run_tests(self): try: import pytest except Exception: raise RuntimeError('py.test is not installed, run: pip install pytest') params = {'args': self.test_args} if self.cov: params['args'] += self.cov if self.junitxml: params['args'] += self.junitxml params['args'] += ['--doctest-modules', MAIN_PACKAGE, '-vv'] import logging silence = logging.WARNING logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=os.getenv('LOGLEVEL', silence)) params['args'] += ['-s' if logging.getLogger().getEffectiveLevel() < silence else '--capture=fd'] errno = pytest.main(**params) sys.exit(errno) def get_install_requirements(path): content = open(os.path.join(__location__, path)).read() return [req for req in content.split('\n') if req != ''] def read(fname): return open(os.path.join(__location__, fname)).read() def setup_package(): # Assemble additional setup commands cmdclass = {'test': PyTest} # Some helper variables version = os.getenv('GO_PIPELINE_LABEL', VERSION) install_requires = [] extras_require = {'aws': ['boto'], 'etcd': ['python-etcd'], 'consul': ['python-consul'], 'exhibitor': ['kazoo'], 'zookeeper': ['kazoo'], 'kubernetes': ['kubernetes']} for r in get_install_requirements('requirements.txt'): extra = False for e, v in extras_require.items(): if r.startswith(v[0]): extras_require[e] = [r] extra = True if not extra: install_requires.append(r) command_options = {'test': {'test_suite': ('setup.py', 'tests')}} if JUNIT_XML: command_options['test']['junitxml'] = 'setup.py', 'junit.xml' if COVERAGE_XML: command_options['test']['cov_xml'] = 'setup.py', True if COVERAGE_HTML: command_options['test']['cov_html'] = 'setup.py', True setup( name=NAME, version=version, url=URL, author=AUTHOR, author_email=AUTHOR_EMAIL, description=DESCRIPTION, license=LICENSE, keywords=KEYWORDS, long_description=read('README.rst'), classifiers=CLASSIFIERS, test_suite='tests', packages=find_packages(exclude=['tests', 'tests.*']), package_data={MAIN_PACKAGE: ["*.json"]}, install_requires=install_requires, extras_require=extras_require, cmdclass=cmdclass, tests_require=['flake8', 'mock>=2.0.0', 'pytest-cov', 'pytest'], command_options=command_options, entry_points={'console_scripts': CONSOLE_SCRIPTS}, ) if __name__ == '__main__': setup_package() patroni-1.4.2/tests/000077500000000000000000000000001323411135200143275ustar00rootroot00000000000000patroni-1.4.2/tests/test_api.py000066400000000000000000000364051323411135200165210ustar00rootroot00000000000000import datetime import json import psycopg2 import unittest from mock import Mock, PropertyMock, patch from patroni.api import RestApiHandler, RestApiServer from patroni.dcs import ClusterConfig, Member from patroni.ha import _MemberStatus from patroni.utils import tzutc from six import BytesIO as IO from six.moves import BaseHTTPServer from test_postgresql import psycopg2_connect, MockCursor future_restart_time = datetime.datetime.now(tzutc) + datetime.timedelta(days=5) postmaster_start_time = datetime.datetime.now(tzutc) class MockPostgresql(object): name = 'test' state = 'running' role = 'master' server_version = '999999' sysid = 'dummysysid' scope = 'dummy' pending_restart = True wal_name = 'wal' lsn_name = 'lsn' @staticmethod def connection(): return psycopg2_connect() @staticmethod def postmaster_start_time(): return str(postmaster_start_time) @staticmethod def replica_cached_timeline(_): return 2 class MockWatchdog(object): is_healthy = False class MockHa(object): state_handler = MockPostgresql() watchdog = MockWatchdog() @staticmethod def reinitialize(_): return 'reinitialize' @staticmethod def restart(): return (True, '') @staticmethod def restart_scheduled(): return False @staticmethod def delete_future_restart(): return True @staticmethod def fetch_nodes_statuses(members): return [_MemberStatus(None, True, None, None, {}, False)] @staticmethod def schedule_future_restart(data): return True @staticmethod def is_lagging(wal): return False @staticmethod def get_effective_tags(): return {'nosync': True} @staticmethod def wakeup(): pass @staticmethod def is_paused(): return True class MockPatroni(object): ha = MockHa() config = Mock() postgresql = ha.state_handler dcs = Mock() tags = {} version = '0.00' noloadbalance = PropertyMock(return_value=False) scheduled_restart = {'schedule': future_restart_time, 'postmaster_start_time': postgresql.postmaster_start_time()} @staticmethod def sighup_handler(): pass class MockRequest(object): def __init__(self, request): self.request = request.encode('utf-8') def makefile(self, *args, **kwargs): return IO(self.request) def sendall(self, *args, **kwargs): pass class MockRestApiServer(RestApiServer): def __init__(self, Handler, request): self.socket = 0 self.serve_forever = Mock() BaseHTTPServer.HTTPServer.__init__ = Mock() MockRestApiServer._BaseServer__is_shut_down = Mock() MockRestApiServer._BaseServer__shutdown_request = True config = {'listen': '127.0.0.1:8008', 'auth': 'test:test'} super(MockRestApiServer, self).__init__(MockPatroni(), config) config['certfile'] = 'dumb' self.reload_config(config) Handler(MockRequest(request), ('0.0.0.0', 8080), self) @patch('ssl.wrap_socket', Mock(return_value=0)) class TestRestApiHandler(unittest.TestCase): _authorization = '\nAuthorization: Basic dGVzdDp0ZXN0' def test_do_GET(self): MockRestApiServer(RestApiHandler, 'GET /replica') with patch.object(RestApiHandler, 'get_postgresql_status', Mock(return_value={})): MockRestApiServer(RestApiHandler, 'GET /replica') with patch.object(RestApiHandler, 'get_postgresql_status', Mock(return_value={'role': 'master'})): MockRestApiServer(RestApiHandler, 'GET /replica') MockRestApiServer(RestApiHandler, 'GET /master') MockPatroni.dcs.cluster.sync.sync_standby = MockPostgresql.name MockPatroni.dcs.cluster.is_synchronous_mode = Mock(return_value=True) with patch.object(RestApiHandler, 'get_postgresql_status', Mock(return_value={'role': 'replica'})): MockRestApiServer(RestApiHandler, 'GET /synchronous') with patch.object(RestApiHandler, 'get_postgresql_status', Mock(return_value={'role': 'replica'})): MockRestApiServer(RestApiHandler, 'GET /asynchronous') MockPatroni.dcs.cluster.leader.name = MockPostgresql.name MockRestApiServer(RestApiHandler, 'GET /replica') MockPatroni.dcs.cluster = None with patch.object(RestApiHandler, 'get_postgresql_status', Mock(return_value={'role': 'master'})): MockRestApiServer(RestApiHandler, 'GET /master') with patch.object(MockHa, 'restart_scheduled', Mock(return_value=True)): MockRestApiServer(RestApiHandler, 'GET /master') self.assertIsNotNone(MockRestApiServer(RestApiHandler, 'GET /master')) with patch.object(RestApiServer, 'query', Mock(return_value=[('', 1, '', '', '', '', False, '')])): self.assertIsNotNone(MockRestApiServer(RestApiHandler, 'GET /patroni')) def test_do_OPTIONS(self): self.assertIsNotNone(MockRestApiServer(RestApiHandler, 'OPTIONS / HTTP/1.0')) @patch.object(MockPostgresql, 'state', PropertyMock(return_value='stopped')) def test_do_GET_patroni(self): self.assertIsNotNone(MockRestApiServer(RestApiHandler, 'GET /patroni')) def test_basicauth(self): self.assertIsNotNone(MockRestApiServer(RestApiHandler, 'POST /restart HTTP/1.0')) MockRestApiServer(RestApiHandler, 'POST /restart HTTP/1.0\nAuthorization:') @patch.object(MockPatroni, 'dcs') def test_do_GET_config(self, mock_dcs): mock_dcs.cluster.config.data = {} self.assertIsNotNone(MockRestApiServer(RestApiHandler, 'GET /config')) mock_dcs.cluster.config = None self.assertIsNotNone(MockRestApiServer(RestApiHandler, 'GET /config')) @patch.object(MockPatroni, 'dcs') def test_do_PATCH_config(self, mock_dcs): config = {'postgresql': {'use_slots': False, 'use_pg_rewind': True, 'parameters': {'wal_level': 'logical'}}} mock_dcs.get_cluster.return_value.config = ClusterConfig.from_node(1, json.dumps(config)) request = 'PATCH /config HTTP/1.0' + self._authorization self.assertIsNotNone(MockRestApiServer(RestApiHandler, request)) request += '\nContent-Length: ' self.assertIsNotNone(MockRestApiServer(RestApiHandler, request + '34\n\n{"postgresql":{"use_slots":false}}')) config['ttl'] = 5 config['postgresql'].update({'use_slots': {'foo': True}, "parameters": None}) config = json.dumps(config) request += str(len(config)) + '\n\n' + config MockRestApiServer(RestApiHandler, request) mock_dcs.set_config_value.return_value = False MockRestApiServer(RestApiHandler, request) @patch.object(MockPatroni, 'dcs') def test_do_PUT_config(self, mock_dcs): mock_dcs.get_cluster.return_value.config = ClusterConfig.from_node(1, '{}') request = 'PUT /config HTTP/1.0' + self._authorization + '\nContent-Length: ' self.assertIsNotNone(MockRestApiServer(RestApiHandler, request + '2\n\n{}')) config = '{"foo": "bar"}' request += str(len(config)) + '\n\n' + config MockRestApiServer(RestApiHandler, request) mock_dcs.set_config_value.return_value = False MockRestApiServer(RestApiHandler, request) mock_dcs.get_cluster.return_value.config = ClusterConfig.from_node(1, config) MockRestApiServer(RestApiHandler, request) @patch.object(MockPatroni, 'sighup_handler', Mock(side_effect=Exception)) def test_do_POST_reload(self): with patch.object(MockPatroni, 'config') as mock_config: mock_config.reload_local_configuration.return_value = False MockRestApiServer(RestApiHandler, 'POST /reload HTTP/1.0' + self._authorization) self.assertIsNotNone(MockRestApiServer(RestApiHandler, 'POST /reload HTTP/1.0' + self._authorization)) @patch.object(MockPatroni, 'dcs') def test_do_POST_restart(self, mock_dcs): mock_dcs.get_cluster.return_value.is_paused.return_value = False request = 'POST /restart HTTP/1.0' + self._authorization self.assertIsNotNone(MockRestApiServer(RestApiHandler, request)) with patch.object(MockHa, 'restart', Mock(side_effect=Exception)): MockRestApiServer(RestApiHandler, request) post = request + '\nContent-Length: ' def make_request(request=None, **kwargs): request = json.dumps(kwargs) if request is None else request return '{0}{1}\n\n{2}'.format(post, len(request), request) # empty request request = make_request('') MockRestApiServer(RestApiHandler, request) # invalid request request = make_request('foobar=baz') MockRestApiServer(RestApiHandler, request) # wrong role request = make_request(schedule=future_restart_time.isoformat(), role='unknown', postgres_version='9.5.3') MockRestApiServer(RestApiHandler, request) # wrong version request = make_request(schedule=future_restart_time.isoformat(), role='master', postgres_version='9.5.3.1') MockRestApiServer(RestApiHandler, request) # unknown filter request = make_request(schedule=future_restart_time.isoformat(), batman='lives') MockRestApiServer(RestApiHandler, request) # incorrect schedule request = make_request(schedule='2016-08-42 12:45TZ+1', role='master') MockRestApiServer(RestApiHandler, request) # everything fine, but the schedule is missing request = make_request(role='master', postgres_version='9.5.2') MockRestApiServer(RestApiHandler, request) for retval in (True, False): with patch.object(MockHa, 'schedule_future_restart', Mock(return_value=retval)): request = make_request(schedule=future_restart_time.isoformat()) MockRestApiServer(RestApiHandler, request) with patch.object(MockHa, 'restart', Mock(return_value=(retval, "foo"))): request = make_request(role='master', postgres_version='9.5.2') MockRestApiServer(RestApiHandler, request) mock_dcs.get_cluster.return_value.is_paused.return_value = True MockRestApiServer(RestApiHandler, make_request(schedule='2016-08-42 12:45TZ+1', role='master')) # Valid timeout MockRestApiServer(RestApiHandler, make_request(timeout='60s')) # Invalid timeout MockRestApiServer(RestApiHandler, make_request(timeout='42towels')) def test_do_DELETE_restart(self): for retval in (True, False): with patch.object(MockHa, 'delete_future_restart', Mock(return_value=retval)): request = 'DELETE /restart HTTP/1.0' + self._authorization self.assertIsNotNone(MockRestApiServer(RestApiHandler, request)) @patch.object(MockPatroni, 'dcs') def test_do_POST_reinitialize(self, mock_dcs): cluster = mock_dcs.get_cluster.return_value cluster.is_paused.return_value = False request = 'POST /reinitialize HTTP/1.0' + self._authorization + '\nContent-Length: 15\n\n{"force": true}' MockRestApiServer(RestApiHandler, request) with patch.object(MockHa, 'reinitialize', Mock(return_value=None)): MockRestApiServer(RestApiHandler, request) @patch('time.sleep', Mock()) def test_RestApiServer_query(self): with patch.object(MockCursor, 'execute', Mock(side_effect=psycopg2.OperationalError)): self.assertIsNotNone(MockRestApiServer(RestApiHandler, 'GET /patroni')) with patch.object(MockPostgresql, 'connection', Mock(side_effect=psycopg2.OperationalError)): self.assertIsNotNone(MockRestApiServer(RestApiHandler, 'GET /patroni')) @patch('time.sleep', Mock()) @patch.object(MockPatroni, 'dcs') def test_do_POST_switchover(self, dcs): dcs.loop_wait = 10 cluster = dcs.get_cluster.return_value cluster.is_synchronous_mode.return_value = False cluster.is_paused.return_value = False post = 'POST /switchover HTTP/1.0' + self._authorization + '\nContent-Length: ' MockRestApiServer(RestApiHandler, post + '7\n\n{"1":2}') request = post + '0\n\n' MockRestApiServer(RestApiHandler, request) cluster.leader.name = 'postgresql1' MockRestApiServer(RestApiHandler, request) request = post + '25\n\n{"leader": "postgresql1"}' cluster.is_paused.return_value = True MockRestApiServer(RestApiHandler, request) cluster.is_paused.return_value = False for cluster.is_synchronous_mode.return_value in (True, False): MockRestApiServer(RestApiHandler, request) cluster.leader.name = 'postgresql2' request = post + '53\n\n{"leader": "postgresql1", "candidate": "postgresql2"}' MockRestApiServer(RestApiHandler, request) cluster.leader.name = 'postgresql1' for cluster.is_synchronous_mode.return_value in (True, False): MockRestApiServer(RestApiHandler, request) cluster.members = [Member(0, 'postgresql0', 30, {'api_url': 'http'}), Member(0, 'postgresql2', 30, {'api_url': 'http'})] MockRestApiServer(RestApiHandler, request) cluster.failover = None MockRestApiServer(RestApiHandler, request) dcs.get_cluster.side_effect = [cluster] MockRestApiServer(RestApiHandler, request) cluster2 = cluster.copy() cluster2.leader.name = 'postgresql0' dcs.get_cluster.side_effect = [cluster, cluster2] MockRestApiServer(RestApiHandler, request) cluster2.leader.name = 'postgresql2' dcs.get_cluster.side_effect = [cluster, cluster2] MockRestApiServer(RestApiHandler, request) dcs.get_cluster.side_effect = None dcs.manual_failover.return_value = False MockRestApiServer(RestApiHandler, request) dcs.manual_failover.return_value = True with patch.object(MockHa, 'fetch_nodes_statuses', Mock(return_value=[])): MockRestApiServer(RestApiHandler, request) # Valid future date request = post + '103\n\n{"leader": "postgresql1", "member": "postgresql2",' +\ ' "scheduled_at": "6016-02-15T18:13:30.568224+01:00"}' MockRestApiServer(RestApiHandler, request) with patch.object(MockPatroni, 'dcs') as d: d.manual_failover.return_value = False MockRestApiServer(RestApiHandler, request) # Exception: No timezone specified request = post + '97\n\n{"leader": "postgresql1", "member": "postgresql2",' +\ ' "scheduled_at": "6016-02-15T18:13:30.568224"}' MockRestApiServer(RestApiHandler, request) # Exception: Scheduled in the past request = post + '103\n\n{"leader": "postgresql1", "member": "postgresql2", "scheduled_at": "' MockRestApiServer(RestApiHandler, request + '1016-02-15T18:13:30.568224+01:00"}') # Invalid date self.assertIsNotNone(MockRestApiServer(RestApiHandler, request + '2010-02-29T18:13:30.568224+01:00"}')) @patch.object(MockPatroni, 'dcs', Mock()) def test_do_POST_failover(self): post = 'POST /failover HTTP/1.0' + self._authorization + '\nContent-Length: ' MockRestApiServer(RestApiHandler, post + '14\n\n{"leader":"1"}') MockRestApiServer(RestApiHandler, post + '37\n\n{"candidate":"2","scheduled_at": "1"}') patroni-1.4.2/tests/test_async_executor.py000066400000000000000000000013641323411135200207770ustar00rootroot00000000000000import unittest from mock import Mock, patch from patroni.async_executor import AsyncExecutor, CriticalTask from threading import Thread class TestAsyncExecutor(unittest.TestCase): def setUp(self): self.a = AsyncExecutor(Mock(), Mock()) @patch.object(Thread, 'start', Mock()) def test_run_async(self): self.a.run_async(Mock(return_value=True)) def test_run(self): self.a.run(Mock(side_effect=Exception())) def test_cancel(self): self.a.cancel() self.a.schedule('foo') self.a.cancel() self.a.run(Mock()) class TestCriticalTask(unittest.TestCase): def test_completed_task(self): ct = CriticalTask() ct.complete(1) self.assertFalse(ct.cancel()) patroni-1.4.2/tests/test_aws.py000066400000000000000000000041001323411135200165250ustar00rootroot00000000000000import boto.ec2 import sys import unittest from mock import Mock, patch from collections import namedtuple from patroni.scripts.aws import AWSConnection, main as _main from requests.exceptions import RequestException class MockEc2Connection(object): @staticmethod def get_all_volumes(*args, **kwargs): oid = namedtuple('Volume', 'id') return [oid(id='a'), oid(id='b')] @staticmethod def create_tags(objects, *args, **kwargs): if len(objects) == 0: raise boto.exception.BotoServerError(503, 'Service Unavailable', 'Request limit exceeded') return True class MockResponse(object): ok = True def __init__(self, content): self.content = content def json(self): return self.content def requests_get(url, **kwargs): if url.split('/')[-1] == 'document': result = {"instanceId": "012345", "region": "eu-west-1"} else: result = 'foo' return MockResponse(result) @patch('boto.ec2.connect_to_region', Mock(return_value=MockEc2Connection())) class TestAWSConnection(unittest.TestCase): @patch('requests.get', requests_get) def setUp(self): self.conn = AWSConnection('test') def test_on_role_change(self): self.assertTrue(self.conn.on_role_change('master')) with patch.object(MockEc2Connection, 'get_all_volumes', Mock(return_value=[])): self.conn._retry.max_tries = 1 self.assertFalse(self.conn.on_role_change('master')) @patch('requests.get', Mock(side_effect=RequestException('foo'))) def test_non_aws(self): conn = AWSConnection('test') self.assertFalse(conn.on_role_change("master")) @patch('requests.get', Mock(return_value=MockResponse('foo'))) def test_aws_bizare_response(self): conn = AWSConnection('test') self.assertFalse(conn.aws_available()) @patch('requests.get', requests_get) @patch('sys.exit', Mock()) def test_main(self): self.assertIsNone(_main()) sys.argv = ['aws.py', 'on_start', 'replica', 'foo'] self.assertIsNone(_main()) patroni-1.4.2/tests/test_callback_executor.py000066400000000000000000000012531323411135200214130ustar00rootroot00000000000000import unittest from mock import Mock, patch from patroni.callback_executor import CallbackExecutor class TestCallbackExecutor(unittest.TestCase): @patch('subprocess.Popen') def test_callback_executor(self, mock_popen): mock_popen.return_value.wait.side_effect = Exception mock_popen.return_value.poll.return_value = None ce = CallbackExecutor() self.assertIsNone(ce.call([])) ce.join() self.assertIsNone(ce.call([])) mock_popen.side_effect = Exception ce = CallbackExecutor() ce._callback_event.wait = Mock(side_effect=[None, Exception]) self.assertIsNone(ce.call([])) ce.join() patroni-1.4.2/tests/test_config.py000066400000000000000000000076271323411135200172210ustar00rootroot00000000000000import os import unittest import sys from mock import MagicMock, Mock, patch from patroni.config import Config from six.moves import builtins class TestConfig(unittest.TestCase): @patch('os.path.isfile', Mock(return_value=True)) @patch('json.load', Mock(side_effect=Exception)) @patch.object(builtins, 'open', MagicMock()) def setUp(self): sys.argv = ['patroni.py'] os.environ[Config.PATRONI_CONFIG_VARIABLE] = 'restapi: {}\npostgresql: {data_dir: foo}' self.config = Config() def test_no_config(self): self.assertRaises(SystemExit, Config) def test_set_dynamic_configuration(self): with patch.object(Config, '_build_effective_configuration', Mock(side_effect=Exception)): self.assertIsNone(self.config.set_dynamic_configuration({'foo': 'bar'})) self.assertTrue(self.config.set_dynamic_configuration({'synchronous_mode': True})) def test_reload_local_configuration(self): os.environ.update({ 'PATRONI_NAME': 'postgres0', 'PATRONI_NAMESPACE': '/patroni/', 'PATRONI_SCOPE': 'batman2', 'PATRONI_RESTAPI_USERNAME': 'username', 'PATRONI_RESTAPI_PASSWORD': 'password', 'PATRONI_RESTAPI_LISTEN': '0.0.0.0:8008', 'PATRONI_RESTAPI_CONNECT_ADDRESS': '127.0.0.1:8008', 'PATRONI_RESTAPI_CERTFILE': '/certfile', 'PATRONI_RESTAPI_KEYFILE': '/keyfile', 'PATRONI_POSTGRESQL_LISTEN': '0.0.0.0:5432', 'PATRONI_POSTGRESQL_CONNECT_ADDRESS': '127.0.0.1:5432', 'PATRONI_POSTGRESQL_DATA_DIR': 'data/postgres0', 'PATRONI_POSTGRESQL_CONFIG_DIR': 'data/postgres0', 'PATRONI_POSTGRESQL_PGPASS': '/tmp/pgpass0', 'PATRONI_ETCD_HOST': '127.0.0.1:2379', 'PATRONI_ETCD_URL': 'https://127.0.0.1:2379', 'PATRONI_ETCD_PROXY': 'http://127.0.0.1:2379', 'PATRONI_ETCD_SRV': 'test', 'PATRONI_ETCD_CACERT': '/cacert', 'PATRONI_ETCD_CERT': '/cert', 'PATRONI_ETCD_KEY': '/key', 'PATRONI_CONSUL_HOST': '127.0.0.1:8500', 'PATRONI_KUBERNETES_LABELS': 'a:b:c', 'PATRONI_KUBERNETES_SCOPE_LABEL': 'a', 'PATRONI_KUBERNETES_PORTS': '[{"name": "postgresql"}]', 'PATRONI_ZOOKEEPER_HOSTS': "'host1:2181','host2:2181'", 'PATRONI_EXHIBITOR_HOSTS': 'host1,host2', 'PATRONI_EXHIBITOR_PORT': '8181', 'PATRONI_foo_HOSTS': '[host1,host2', # Exception in parse_list 'PATRONI_SUPERUSER_USERNAME': 'postgres', 'PATRONI_SUPERUSER_PASSWORD': 'zalando', 'PATRONI_REPLICATION_USERNAME': 'replicator', 'PATRONI_REPLICATION_PASSWORD': 'rep-pass', 'PATRONI_admin_PASSWORD': 'admin', 'PATRONI_admin_OPTIONS': 'createrole,createdb' }) sys.argv = ['patroni.py', 'postgres0.yml'] config = Config() with patch.object(Config, '_load_config_file', Mock(return_value={'restapi': {}})): with patch.object(Config, '_build_effective_configuration', Mock(side_effect=Exception)): self.assertRaises(Exception, config.reload_local_configuration, True) self.assertTrue(config.reload_local_configuration(True)) self.assertTrue(config.reload_local_configuration()) @patch('tempfile.mkstemp', Mock(return_value=[3000, 'blabla'])) @patch('os.path.exists', Mock(return_value=True)) @patch('os.remove', Mock(side_effect=IOError)) @patch('os.close', Mock(side_effect=IOError)) @patch('os.rename', Mock(return_value=None)) @patch('json.dump', Mock()) def test_save_cache(self): self.config.set_dynamic_configuration({'ttl': 30, 'postgresql': {'foo': 'bar'}}) with patch('os.fdopen', Mock(side_effect=IOError)): self.config.save_cache() with patch('os.fdopen', MagicMock()): self.config.save_cache() patroni-1.4.2/tests/test_consul.py000066400000000000000000000202351323411135200172450ustar00rootroot00000000000000import consul import unittest from consul import ConsulException, NotFound from mock import Mock, patch from patroni.dcs.consul import AbstractDCS, Cluster, Consul, ConsulInternalError, \ ConsulError, HTTPClient, InvalidSessionTTL from test_etcd import SleepException def kv_get(self, key, **kwargs): if key == 'service/test/members/postgresql1': return '1', {'Session': 'fd4f44fe-2cac-bba5-a60b-304b51ff39b7'} if key == 'service/test/': return None, None if key == 'service/good/leader': return '1', None if key == 'service/good/': return ('6429', [{'CreateIndex': 1334, 'Flags': 0, 'Key': key + 'failover', 'LockIndex': 0, 'ModifyIndex': 1334, 'Value': b''}, {'CreateIndex': 1334, 'Flags': 0, 'Key': key + 'initialize', 'LockIndex': 0, 'ModifyIndex': 1334, 'Value': b'postgresql0'}, {'CreateIndex': 2621, 'Flags': 0, 'Key': key + 'leader', 'LockIndex': 1, 'ModifyIndex': 2621, 'Session': 'fd4f44fe-2cac-bba5-a60b-304b51ff39b7', 'Value': b'postgresql1'}, {'CreateIndex': 6156, 'Flags': 0, 'Key': key + 'members/postgresql0', 'LockIndex': 1, 'ModifyIndex': 6156, 'Session': '782e6da4-ed02-3aef-7963-99a90ed94b53', 'Value': ('postgres://replicator:rep-pass@127.0.0.1:5432/postgres' + '?application_name=http://127.0.0.1:8008/patroni').encode('utf-8')}, {'CreateIndex': 2630, 'Flags': 0, 'Key': key + 'members/postgresql1', 'LockIndex': 1, 'ModifyIndex': 2630, 'Session': 'fd4f44fe-2cac-bba5-a60b-304b51ff39b7', 'Value': ('postgres://replicator:rep-pass@127.0.0.1:5433/postgres' + '?application_name=http://127.0.0.1:8009/patroni').encode('utf-8')}, {'CreateIndex': 1085, 'Flags': 0, 'Key': key + 'optime/leader', 'LockIndex': 0, 'ModifyIndex': 6429, 'Value': b'4496294792'}, {'CreateIndex': 1085, 'Flags': 0, 'Key': key + 'sync', 'LockIndex': 0, 'ModifyIndex': 6429, 'Value': b'{"leader": "leader", "sync_standby": null}'}]) raise ConsulException class TestHTTPClient(unittest.TestCase): def setUp(self): self.client = HTTPClient('127.0.0.1', '8500', 'http', False) self.client.http.request = Mock() def test_get(self): self.client.get(Mock(), '') self.client.get(Mock(), '', {'wait': '1s', 'index': 1, 'token': 'foo'}) self.client.http.request.return_value.status = 500 self.client.http.request.return_value.data = b'Foo' self.assertRaises(ConsulInternalError, self.client.get, Mock(), '') self.client.http.request.return_value.data = b"Invalid Session TTL '3000000000', must be between [10s=24h0m0s]" self.assertRaises(InvalidSessionTTL, self.client.get, Mock(), '') def test_unknown_method(self): try: self.client.bla(Mock(), '') self.assertFail() except Exception as e: self.assertTrue(isinstance(e, AttributeError)) def test_put(self): self.client.put(Mock(), '/v1/session/create') self.client.put(Mock(), '/v1/session/create', data='{"foo": "bar"}') @patch.object(consul.Consul.KV, 'get', kv_get) class TestConsul(unittest.TestCase): @patch.object(consul.Consul.Session, 'create', Mock(return_value='fd4f44fe-2cac-bba5-a60b-304b51ff39b7')) @patch.object(consul.Consul.Session, 'renew', Mock(side_effect=NotFound)) @patch.object(consul.Consul.KV, 'get', kv_get) @patch.object(consul.Consul.KV, 'delete', Mock()) def setUp(self): Consul({'ttl': 30, 'scope': 't', 'name': 'p', 'url': 'https://l:1', 'retry_timeout': 10, 'verify': 'on', 'key': 'foo', 'cert': 'bar', 'cacert': 'buz', 'token': 'asd', 'dc': 'dc1'}) Consul({'ttl': 30, 'scope': 't', 'name': 'p', 'url': 'https://l:1', 'retry_timeout': 10, 'verify': 'on', 'cert': 'bar', 'cacert': 'buz'}) self.c = Consul({'ttl': 30, 'scope': 'test', 'name': 'postgresql1', 'host': 'localhost:1', 'retry_timeout': 10}) self.c._base_path = '/service/good' self.c._load_cluster() @patch('time.sleep', Mock(side_effect=SleepException)) @patch.object(consul.Consul.Session, 'create', Mock(side_effect=ConsulException)) def test_create_session(self): self.c._session = None self.assertRaises(SleepException, self.c.create_session) @patch.object(consul.Consul.Session, 'renew', Mock(side_effect=NotFound)) @patch.object(consul.Consul.Session, 'create', Mock(side_effect=[InvalidSessionTTL, ConsulException])) @patch.object(consul.Consul.Agent, 'self', Mock(return_value={'Config': {'SessionTTLMin': 0}})) @patch.object(HTTPClient, 'set_ttl', Mock(side_effect=ValueError)) def test_referesh_session(self): self.c._session = '1' self.assertFalse(self.c.refresh_session()) self.c._last_session_refresh = 0 self.assertRaises(ConsulError, self.c.refresh_session) @patch.object(consul.Consul.KV, 'delete', Mock()) def test_get_cluster(self): self.c._base_path = '/service/test' self.assertIsInstance(self.c.get_cluster(), Cluster) self.assertIsInstance(self.c.get_cluster(), Cluster) self.c._base_path = '/service/fail' self.assertRaises(ConsulError, self.c.get_cluster) self.c._base_path = '/service/good' self.c._session = 'fd4f44fe-2cac-bba5-a60b-304b51ff39b8' self.assertIsInstance(self.c.get_cluster(), Cluster) @patch.object(consul.Consul.KV, 'delete', Mock(side_effect=[ConsulException, True, True])) @patch.object(consul.Consul.KV, 'put', Mock(side_effect=[True, ConsulException])) def test_touch_member(self): self.c.refresh_session = Mock(return_value=True) self.c.touch_member({'balbla': 'blabla'}) self.c.touch_member({'balbla': 'blabla'}) self.c.touch_member({'balbla': 'blabla'}) self.c.refresh_session = Mock(return_value=False) self.c.touch_member({'balbla': 'blabla'}) @patch.object(consul.Consul.KV, 'put', Mock(return_value=False)) def test_take_leader(self): self.c.set_ttl(20) self.c.refresh_session = Mock() self.c.take_leader() @patch.object(consul.Consul.KV, 'put', Mock(return_value=True)) def test_set_failover_value(self): self.c.set_failover_value('') @patch.object(consul.Consul.KV, 'put', Mock(return_value=True)) def test_set_config_value(self): self.c.set_config_value('') @patch.object(consul.Consul.KV, 'put', Mock(side_effect=ConsulException)) def test_write_leader_optime(self): self.c.write_leader_optime('1') @patch.object(consul.Consul.Session, 'renew', Mock()) def test_update_leader(self): self.c.update_leader(None) @patch.object(consul.Consul.KV, 'delete', Mock(return_value=True)) def test_delete_leader(self): self.c.delete_leader() @patch.object(consul.Consul.KV, 'put', Mock(return_value=True)) def test_initialize(self): self.c.initialize() @patch.object(consul.Consul.KV, 'delete', Mock(return_value=True)) def test_cancel_initialization(self): self.c.cancel_initialization() @patch.object(consul.Consul.KV, 'delete', Mock(return_value=True)) def test_delete_cluster(self): self.c.delete_cluster() @patch.object(AbstractDCS, 'watch', Mock()) def test_watch(self): self.c.watch(None, 1) self.c._name = '' self.c.watch(6429, 1) with patch.object(consul.Consul.KV, 'get', Mock(side_effect=ConsulException)): self.c.watch(6429, 1) def test_set_retry_timeout(self): self.c.set_retry_timeout(10) @patch.object(consul.Consul.KV, 'delete', Mock(return_value=True)) @patch.object(consul.Consul.KV, 'put', Mock(return_value=True)) def test_sync_state(self): self.assertTrue(self.c.set_sync_state_value('{}')) self.assertTrue(self.c.delete_sync_state()) @patch.object(consul.Consul.KV, 'put', Mock(return_value=True)) def test_set_history_value(self): self.assertTrue(self.c.set_history_value('{}')) patroni-1.4.2/tests/test_ctl.py000066400000000000000000000675451323411135200165430ustar00rootroot00000000000000import etcd import os import requests import sys import unittest from click.testing import CliRunner from datetime import datetime, timedelta from mock import patch, Mock from patroni.ctl import ctl, store_config, load_config, output_members, request_patroni, get_dcs, parse_dcs, \ get_all_members, get_any_member, get_cursor, query_member, configure, PatroniCtlException, apply_config_changes, \ format_config_for_editing, show_diff, invoke_editor, format_pg_version from patroni.dcs.etcd import Client, Failover from patroni.utils import tzutc from psycopg2 import OperationalError from test_etcd import etcd_read, requests_get, socket_getaddrinfo, MockResponse from test_ha import get_cluster_initialized_without_leader, get_cluster_initialized_with_leader, \ get_cluster_initialized_with_only_leader, get_cluster_not_initialized_without_leader, get_cluster, Member from test_postgresql import MockConnect, psycopg2_connect CONFIG_FILE_PATH = './test-ctl.yaml' def test_rw_config(): runner = CliRunner() with runner.isolated_filesystem(): store_config({'etcd': {'host': 'localhost:2379'}}, CONFIG_FILE_PATH + '/dummy') sys.argv = ['patronictl.py', ''] load_config(CONFIG_FILE_PATH + '/dummy', None) load_config(CONFIG_FILE_PATH + '/dummy', '0.0.0.0') os.remove(CONFIG_FILE_PATH + '/dummy') os.rmdir(CONFIG_FILE_PATH) @patch('patroni.ctl.load_config', Mock(return_value={'scope': 'alpha', 'postgresql': {'data_dir': '.', 'parameters': {}, 'retry_timeout': 5}, 'restapi': {'auth': 'u:p', 'listen': ''}, 'etcd': {'host': 'localhost:2379'}})) class TestCtl(unittest.TestCase): @patch('socket.getaddrinfo', socket_getaddrinfo) def setUp(self): with patch.object(Client, 'machines') as mock_machines: mock_machines.__get__ = Mock(return_value=['http://remotehost:2379']) self.runner = CliRunner() self.e = get_dcs({'etcd': {'ttl': 30, 'host': 'ok:2379', 'retry_timeout': 10}}, 'foo') @patch('psycopg2.connect', psycopg2_connect) def test_get_cursor(self): self.assertIsNone(get_cursor(get_cluster_initialized_without_leader(), {}, role='master')) self.assertIsNotNone(get_cursor(get_cluster_initialized_with_leader(), {}, role='master')) # MockCursor returns pg_is_in_recovery as false self.assertIsNone(get_cursor(get_cluster_initialized_with_leader(), {}, role='replica')) self.assertIsNotNone(get_cursor(get_cluster_initialized_with_leader(), {'database': 'foo'}, role='any')) def test_parse_dcs(self): assert parse_dcs(None) is None assert parse_dcs('localhost') == {'etcd': {'host': 'localhost:2379'}} assert parse_dcs('') == {'etcd': {'host': 'localhost:2379'}} assert parse_dcs('localhost:8500') == {'consul': {'host': 'localhost:8500'}} assert parse_dcs('zookeeper://localhost') == {'zookeeper': {'hosts': ['localhost:2181']}} assert parse_dcs('exhibitor://dummy') == {'exhibitor': {'hosts': ['dummy'], 'port': 8181}} assert parse_dcs('consul://localhost') == {'consul': {'host': 'localhost:8500'}} self.assertRaises(PatroniCtlException, parse_dcs, 'invalid://test') def test_output_members(self): scheduled_at = datetime.now(tzutc) + timedelta(seconds=600) cluster = get_cluster_initialized_with_leader(Failover(1, 'foo', 'bar', scheduled_at)) self.assertIsNone(output_members(cluster, name='abc', fmt='pretty')) self.assertIsNone(output_members(cluster, name='abc', fmt='json')) self.assertIsNone(output_members(cluster, name='abc', fmt='yaml')) self.assertIsNone(output_members(cluster, name='abc', fmt='tsv')) @patch('patroni.ctl.get_dcs') @patch('patroni.ctl.request_patroni', Mock(return_value=MockResponse())) def test_switchover(self, mock_get_dcs): mock_get_dcs.return_value = self.e mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader mock_get_dcs.return_value.set_failover_value = Mock() result = self.runner.invoke(ctl, ['switchover', 'dummy'], input='leader\nother\n\ny') assert 'leader' in result.output result = self.runner.invoke(ctl, ['switchover', 'dummy'], input='leader\nother\n2300-01-01T12:23:00\ny') assert result.exit_code == 0 with patch('patroni.dcs.Cluster.is_paused', Mock(return_value=True)): result = self.runner.invoke(ctl, ['switchover', 'dummy', '--force', '--scheduled', '2015-01-01T12:00:00']) assert result.exit_code == 1 # Aborting switchover, as we anser NO to the confirmation result = self.runner.invoke(ctl, ['switchover', 'dummy'], input='leader\nother\n\nN') assert result.exit_code == 1 # Target and source are equal result = self.runner.invoke(ctl, ['switchover', 'dummy'], input='leader\nleader\n\ny') assert result.exit_code == 1 # Reality is not part of this cluster result = self.runner.invoke(ctl, ['switchover', 'dummy'], input='leader\nReality\n\ny') assert result.exit_code == 1 result = self.runner.invoke(ctl, ['switchover', 'dummy', '--force']) assert 'Member' in result.output result = self.runner.invoke(ctl, ['switchover', 'dummy', '--force', '--scheduled', '2015-01-01T12:00:00+01:00']) assert result.exit_code == 0 # Invalid timestamp result = self.runner.invoke(ctl, ['switchover', 'dummy', '--force', '--scheduled', 'invalid']) assert result.exit_code != 0 # Invalid timestamp result = self.runner.invoke(ctl, ['switchover', 'dummy', '--force', '--scheduled', '2115-02-30T12:00:00+01:00']) assert result.exit_code != 0 # Specifying wrong leader result = self.runner.invoke(ctl, ['switchover', 'dummy'], input='dummy') assert result.exit_code == 1 with patch('patroni.ctl.request_patroni', Mock(side_effect=Exception)): # Non-responding patroni result = self.runner.invoke(ctl, ['switchover', 'dummy'], input='leader\nother\n2300-01-01T12:23:00\ny') assert 'falling back to DCS' in result.output with patch('patroni.ctl.request_patroni') as mocked: mocked.return_value.status_code = 500 result = self.runner.invoke(ctl, ['switchover', 'dummy'], input='leader\nother\n\ny') assert 'Switchover failed' in result.output mocked.return_value.status_code = 501 mocked.return_value.text = 'Server does not support this operation' result = self.runner.invoke(ctl, ['switchover', 'dummy'], input='leader\nother\n\ny') assert 'Switchover failed' in result.output # No members available mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_only_leader result = self.runner.invoke(ctl, ['switchover', 'dummy'], input='leader\nother\n\ny') assert result.exit_code == 1 # No master available mock_get_dcs.return_value.get_cluster = get_cluster_initialized_without_leader result = self.runner.invoke(ctl, ['switchover', 'dummy'], input='leader\nother\n\ny') assert result.exit_code == 1 @patch('patroni.ctl.get_dcs') @patch('patroni.ctl.request_patroni', Mock(return_value=MockResponse())) def test_failover(self, mock_get_dcs): mock_get_dcs.return_value = self.e mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader mock_get_dcs.return_value.set_failover_value = Mock() result = self.runner.invoke(ctl, ['failover', 'dummy'], input='\n') assert 'Failover could be performed only to a specific candidate' in result.output def test_get_dcs(self): self.assertRaises(PatroniCtlException, get_dcs, {'dummy': {}}, 'dummy') @patch('psycopg2.connect', psycopg2_connect) @patch('patroni.ctl.query_member', Mock(return_value=([['mock column']], None))) @patch('patroni.ctl.get_dcs') @patch.object(etcd.Client, 'read', etcd_read) def test_query(self, mock_get_dcs): mock_get_dcs.return_value = self.e # Mutually exclusive result = self.runner.invoke(ctl, ['query', 'alpha', '--member', 'abc', '--role', 'master']) assert result.exit_code == 1 with self.runner.isolated_filesystem(): with open('dummy', 'w') as dummy_file: dummy_file.write('SELECT 1') # Mutually exclusive result = self.runner.invoke(ctl, ['query', 'alpha', '--file', 'dummy', '--command', 'dummy']) assert result.exit_code == 1 result = self.runner.invoke(ctl, ['query', 'alpha', '--file', 'dummy']) assert result.exit_code == 0 os.remove('dummy') result = self.runner.invoke(ctl, ['query', 'alpha', '--command', 'SELECT 1']) assert 'mock column' in result.output # --command or --file is mandatory result = self.runner.invoke(ctl, ['query', 'alpha']) assert result.exit_code == 1 result = self.runner.invoke(ctl, ['query', 'alpha', '--command', 'SELECT 1', '--username', 'root', '--password', '--dbname', 'postgres'], input='ab\nab') assert 'mock column' in result.output def test_query_member(self): with patch('patroni.ctl.get_cursor', Mock(return_value=MockConnect().cursor())): rows = query_member(None, None, None, 'master', 'SELECT pg_is_in_recovery()', {}) self.assertTrue('False' in str(rows)) rows = query_member(None, None, None, 'replica', 'SELECT pg_is_in_recovery()', {}) self.assertEquals(rows, (None, None)) with patch('test_postgresql.MockCursor.execute', Mock(side_effect=OperationalError('bla'))): rows = query_member(None, None, None, 'replica', 'SELECT pg_is_in_recovery()', {}) with patch('patroni.ctl.get_cursor', Mock(return_value=None)): rows = query_member(None, None, None, None, 'SELECT pg_is_in_recovery()', {}) self.assertTrue('No connection to' in str(rows)) rows = query_member(None, None, None, 'replica', 'SELECT pg_is_in_recovery()', {}) self.assertTrue('No connection to' in str(rows)) with patch('patroni.ctl.get_cursor', Mock(side_effect=OperationalError('bla'))): rows = query_member(None, None, None, 'replica', 'SELECT pg_is_in_recovery()', {}) @patch('patroni.ctl.get_dcs') def test_dsn(self, mock_get_dcs): mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader result = self.runner.invoke(ctl, ['dsn', 'alpha']) assert 'host=127.0.0.1 port=5435' in result.output # Mutually exclusive options result = self.runner.invoke(ctl, ['dsn', 'alpha', '--role', 'master', '--member', 'dummy']) assert result.exit_code == 1 # Non-existing member result = self.runner.invoke(ctl, ['dsn', 'alpha', '--member', 'dummy']) assert result.exit_code == 1 @patch('requests.post', requests_get) @patch('patroni.ctl.get_dcs') def test_restart_reinit(self, mock_get_dcs): mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader result = self.runner.invoke(ctl, ['restart', 'alpha'], input='y\n\nnow') assert 'Failed: restart for' in result.output assert result.exit_code == 0 result = self.runner.invoke(ctl, ['reinit', 'alpha'], input='y') assert result.exit_code == 1 # successful reinit result = self.runner.invoke(ctl, ['reinit', 'alpha', 'other'], input='y\ny') assert result.exit_code == 0 # Aborted restart result = self.runner.invoke(ctl, ['restart', 'alpha'], input='N') assert result.exit_code == 1 result = self.runner.invoke(ctl, ['restart', 'alpha', '--pending', '--force']) assert result.exit_code == 0 # Not a member result = self.runner.invoke(ctl, ['restart', 'alpha', 'dummy', '--any'], input='y') assert result.exit_code == 1 # Wrong pg version result = self.runner.invoke(ctl, ['restart', 'alpha', '--any', '--pg-version', '9.1'], input='y') assert 'Error: Invalid PostgreSQL version format' in result.output assert result.exit_code == 1 result = self.runner.invoke(ctl, ['restart', 'alpha', '--pending', '--force', '--timeout', '10min']) assert result.exit_code == 0 with patch('requests.delete', Mock(return_value=MockResponse(500))): # normal restart, the schedule is actually parsed, but not validated in patronictl result = self.runner.invoke(ctl, ['restart', 'alpha', 'other', '--force', '--scheduled', '2300-10-01T14:30']) assert 'Failed: flush scheduled restart' in result.output with patch('patroni.dcs.Cluster.is_paused', Mock(return_value=True)): result = self.runner.invoke(ctl, ['restart', 'alpha', 'other', '--force', '--scheduled', '2300-10-01T14:30']) assert result.exit_code == 1 with patch('requests.post', Mock(return_value=MockResponse())): # normal restart, the schedule is actually parsed, but not validated in patronictl result = self.runner.invoke(ctl, ['restart', 'alpha', '--pg-version', '42.0.0', '--scheduled', '2300-10-01T14:30'], input='y') assert result.exit_code == 0 with patch('requests.post', Mock(return_value=MockResponse(204))): # get restart with the non-200 return code # normal restart, the schedule is actually parsed, but not validated in patronictl result = self.runner.invoke(ctl, ['restart', 'alpha', '--pg-version', '42.0', '--scheduled', '2300-10-01T14:30'], input='y') assert result.exit_code == 0 # force restart with restart already present with patch('patroni.ctl.request_patroni', Mock(return_value=MockResponse(204))): result = self.runner.invoke(ctl, ['restart', 'alpha', 'other', '--force', '--scheduled', '2300-10-01T14:30']) assert result.exit_code == 0 with patch('requests.post', Mock(return_value=MockResponse(202))): # get restart with the non-200 return code # normal restart, the schedule is actually parsed, but not validated in patronictl result = self.runner.invoke( ctl, ['restart', 'alpha', '--pg-version', '99.0.0', '--scheduled', '2300-10-01T14:30'], input='y' ) assert 'Success: restart scheduled' in result.output assert result.exit_code == 0 with patch('requests.post', Mock(return_value=MockResponse(409))): # get restart with the non-200 return code # normal restart, the schedule is actually parsed, but not validated in patronictl result = self.runner.invoke( ctl, ['restart', 'alpha', '--pg-version', '99.0.0', '--scheduled', '2300-10-01T14:30'], input='y' ) assert 'Failed: another restart is already' in result.output assert result.exit_code == 0 @patch('patroni.ctl.get_dcs') def test_remove(self, mock_get_dcs): mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader result = self.runner.invoke(ctl, ['remove', 'alpha'], input='alpha\nslave') assert 'Please confirm' in result.output assert 'You are about to remove all' in result.output # Not typing an exact confirmation assert result.exit_code == 1 # master specified does not match master of cluster result = self.runner.invoke(ctl, ['remove', 'alpha'], input='alpha\nYes I am aware\nslave') assert result.exit_code == 1 # cluster specified on cmdline does not match verification prompt result = self.runner.invoke(ctl, ['remove', 'alpha'], input='beta\nleader') assert result.exit_code == 1 result = self.runner.invoke(ctl, ['remove', 'alpha'], input='alpha\nYes I am aware\nleader') assert result.exit_code == 0 @patch('requests.post', Mock(side_effect=requests.exceptions.ConnectionError('foo'))) def test_request_patroni(self): member = get_cluster_initialized_with_leader().leader.member self.assertRaises(requests.exceptions.ConnectionError, request_patroni, member, 'post', 'dummy', {}) def test_ctl(self): self.runner.invoke(ctl, ['list']) result = self.runner.invoke(ctl, ['--help']) assert 'Usage:' in result.output def test_get_any_member(self): self.assertIsNone(get_any_member(get_cluster_initialized_without_leader(), role='master')) m = get_any_member(get_cluster_initialized_with_leader(), role='master') self.assertEquals(m.name, 'leader') def test_get_all_members(self): self.assertEquals(list(get_all_members(get_cluster_initialized_without_leader(), role='master')), []) r = list(get_all_members(get_cluster_initialized_with_leader(), role='master')) self.assertEquals(len(r), 1) self.assertEquals(r[0].name, 'leader') r = list(get_all_members(get_cluster_initialized_with_leader(), role='replica')) self.assertEquals(len(r), 1) self.assertEquals(r[0].name, 'other') self.assertEquals(len(list(get_all_members(get_cluster_initialized_without_leader(), role='replica'))), 2) @patch('patroni.ctl.get_dcs') def test_members(self, mock_get_dcs): mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader result = self.runner.invoke(ctl, ['list']) assert '127.0.0.1' in result.output assert result.exit_code == 0 with patch('patroni.ctl.load_config', Mock(return_value={})): self.runner.invoke(ctl, ['list']) def test_configure(self): result = self.runner.invoke(configure, ['--dcs', 'abc', '-c', 'dummy', '-n', 'bla']) assert result.exit_code == 0 @patch('patroni.ctl.get_dcs') def test_scaffold(self, mock_get_dcs): mock_get_dcs.return_value = self.e mock_get_dcs.return_value.get_cluster = get_cluster_not_initialized_without_leader mock_get_dcs.return_value.initialize = Mock(return_value=True) mock_get_dcs.return_value.touch_member = Mock(return_value=True) mock_get_dcs.return_value.attempt_to_acquire_leader = Mock(return_value=True) mock_get_dcs.return_value.delete_cluster = Mock() with patch.object(self.e, 'initialize', return_value=False): result = self.runner.invoke(ctl, ['scaffold', 'alpha']) assert result.exception with patch.object(mock_get_dcs.return_value, 'touch_member', Mock(return_value=False)): result = self.runner.invoke(ctl, ['scaffold', 'alpha']) assert result.exception result = self.runner.invoke(ctl, ['scaffold', 'alpha']) assert result.exit_code == 0 mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader result = self.runner.invoke(ctl, ['scaffold', 'alpha']) assert result.exception @patch('patroni.ctl.get_dcs') def test_list_extended(self, mock_get_dcs): mock_get_dcs.return_value = self.e cluster = get_cluster_initialized_with_leader(sync=('leader', 'other')) mock_get_dcs.return_value.get_cluster = Mock(return_value=cluster) result = self.runner.invoke(ctl, ['list', 'dummy', '--extended', '--timestamp']) assert '2100' in result.output assert 'Scheduled restart' in result.output @patch('patroni.ctl.get_dcs') @patch('requests.delete', Mock(return_value=MockResponse())) def test_flush(self, mock_get_dcs): mock_get_dcs.return_value = self.e mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader result = self.runner.invoke(ctl, ['flush', 'dummy', 'restart', '-r', 'master'], input='y') assert 'No scheduled restart' in result.output result = self.runner.invoke(ctl, ['flush', 'dummy', 'restart', '--force']) assert 'Success: flush scheduled restart' in result.output with patch.object(requests, 'delete', return_value=MockResponse(404)): result = self.runner.invoke(ctl, ['flush', 'dummy', 'restart', '--force']) assert 'Failed: flush scheduled restart' in result.output @patch('patroni.ctl.get_dcs') @patch('patroni.ctl.polling_loop', Mock(return_value=[1])) def test_pause_cluster(self, mock_get_dcs): mock_get_dcs.return_value = self.e mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader with patch('requests.patch', Mock(return_value=MockResponse(500))): result = self.runner.invoke(ctl, ['pause', 'dummy']) assert 'Failed' in result.output with patch('requests.patch', Mock(return_value=MockResponse(200))),\ patch('patroni.dcs.Cluster.is_paused', Mock(return_value=True)): result = self.runner.invoke(ctl, ['pause', 'dummy']) assert 'Cluster is already paused' in result.output with patch('requests.patch', Mock(return_value=MockResponse(200))): result = self.runner.invoke(ctl, ['pause', 'dummy', '--wait']) assert "'pause' request sent" in result.output mock_get_dcs.return_value.get_cluster = Mock(side_effect=[get_cluster_initialized_with_leader(), get_cluster(None, None, [], None, None)]) self.runner.invoke(ctl, ['pause', 'dummy', '--wait']) member = Member(1, 'other', 28, {}) mock_get_dcs.return_value.get_cluster = Mock(side_effect=[get_cluster_initialized_with_leader(), get_cluster(None, None, [member], None, None)]) self.runner.invoke(ctl, ['pause', 'dummy', '--wait']) @patch('patroni.ctl.get_dcs') def test_resume_cluster(self, mock_get_dcs): mock_get_dcs.return_value = self.e mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader with patch('patroni.dcs.Cluster.is_paused', Mock(return_value=True)): with patch('requests.patch', Mock(return_value=MockResponse(200))): result = self.runner.invoke(ctl, ['resume', 'dummy']) assert 'Success' in result.output with patch('requests.patch', Mock(return_value=MockResponse(500))): result = self.runner.invoke(ctl, ['resume', 'dummy']) assert 'Failed' in result.output with patch('requests.patch', Mock(return_value=MockResponse(200))),\ patch('patroni.dcs.Cluster.is_paused', Mock(return_value=False)): result = self.runner.invoke(ctl, ['resume', 'dummy']) assert 'Cluster is not paused' in result.output with patch('requests.patch', Mock(side_effect=Exception)): result = self.runner.invoke(ctl, ['resume', 'dummy']) assert 'Can not find accessible cluster member' in result.output def test_apply_config_changes(self): config = {"postgresql": {"parameters": {"work_mem": "4MB"}, "use_pg_rewind": True}, "ttl": 30} before_editing = format_config_for_editing(config) # Spaces are allowed and stripped, numbers and booleans are interpreted after_editing, changed_config = apply_config_changes(before_editing, config, ["postgresql.parameters.work_mem = 5MB", "ttl=15", "postgresql.use_pg_rewind=off", 'a.b=c']) self.assertEquals(changed_config, {"a": {"b": "c"}, "postgresql": {"parameters": {"work_mem": "5MB"}, "use_pg_rewind": False}, "ttl": 15}) # postgresql.parameters namespace is flattened after_editing, changed_config = apply_config_changes(before_editing, config, ["postgresql.parameters.work_mem.sub = x"]) self.assertEquals(changed_config, {"postgresql": {"parameters": {"work_mem": "4MB", "work_mem.sub": "x"}, "use_pg_rewind": True}, "ttl": 30}) # Setting to null deletes after_editing, changed_config = apply_config_changes(before_editing, config, ["postgresql.parameters.work_mem=null"]) self.assertEquals(changed_config, {"postgresql": {"use_pg_rewind": True}, "ttl": 30}) after_editing, changed_config = apply_config_changes(before_editing, config, ["postgresql.use_pg_rewind=null", "postgresql.parameters.work_mem=null"]) self.assertEquals(changed_config, {"ttl": 30}) self.assertRaises(PatroniCtlException, apply_config_changes, before_editing, config, ['a']) @patch('sys.stdout.isatty', return_value=False) @patch('cdiff.markup_to_pager') def test_show_diff(self, mock_markup_to_pager, mock_isatty): show_diff("foo:\n bar: 1\n", "foo:\n bar: 2\n") mock_markup_to_pager.assert_not_called() mock_isatty.return_value = True show_diff("foo:\n bar: 1\n", "foo:\n bar: 2\n") mock_markup_to_pager.assert_called_once() # Test that unicode handling doesn't fail with an exception show_diff(b"foo:\n bar: \xc3\xb6\xc3\xb6\n".decode('utf-8'), b"foo:\n bar: \xc3\xbc\xc3\xbc\n".decode('utf-8')) def test_invoke_editor(self): for e in ('', 'false'): os.environ['EDITOR'] = e self.assertRaises(PatroniCtlException, invoke_editor, 'foo: bar\n', 'test') @patch('patroni.ctl.get_dcs') def test_show_config(self, mock_get_dcs): mock_get_dcs.return_value = self.e mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader self.runner.invoke(ctl, ['show-config', 'dummy']) @patch('patroni.ctl.get_dcs') def test_edit_config(self, mock_get_dcs): mock_get_dcs.return_value = self.e mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader os.environ['EDITOR'] = 'true' self.runner.invoke(ctl, ['edit-config', 'dummy']) self.runner.invoke(ctl, ['edit-config', 'dummy', '-s', 'foo=bar']) self.runner.invoke(ctl, ['edit-config', 'dummy', '--replace', 'postgres0.yml']) self.runner.invoke(ctl, ['edit-config', 'dummy', '--apply', '-'], input='foo: bar') self.runner.invoke(ctl, ['edit-config', 'dummy', '--force', '--apply', '-'], input='foo: bar') mock_get_dcs.return_value.set_config_value = Mock(return_value=True) self.runner.invoke(ctl, ['edit-config', 'dummy', '--force', '--apply', '-'], input='foo: bar') @patch('patroni.ctl.get_dcs') def test_version(self, mock_get_dcs): mock_get_dcs.return_value = self.e mock_get_dcs.return_value.get_cluster = get_cluster_initialized_with_leader with patch('patroni.ctl.request_patroni') as mocked: result = self.runner.invoke(ctl, ['version']) assert 'patronictl version' in result.output mocked.return_value.json = lambda: {'patroni': {'version': '1.2.3'}, 'server_version': 100001} result = self.runner.invoke(ctl, ['version', 'dummy']) assert '1.2.3' in result.output with patch('requests.get', Mock(side_effect=Exception)): result = self.runner.invoke(ctl, ['version', 'dummy']) assert 'failed to get version' in result.output def test_format_pg_version(self): self.assertEquals(format_pg_version(100001), '10.1') self.assertEquals(format_pg_version(90605), '9.6.5') patroni-1.4.2/tests/test_etcd.py000066400000000000000000000340501323411135200166610ustar00rootroot00000000000000import etcd import json import urllib3.util.connection import requests import socket import unittest from dns.exception import DNSException from mock import Mock, patch from patroni.dcs.etcd import AbstractDCS, Client, Cluster, Etcd, EtcdError, DnsCachingResolver from patroni.exceptions import DCSError from urllib3.exceptions import ReadTimeoutError class MockResponse(object): def __init__(self, status_code=200): self.status_code = status_code self.content = '{}' self.ok = True def json(self): return json.loads(self.content) @property def data(self): return self.content.encode('utf-8') @property def text(self): return self.content @property def status(self): return self.status_code @staticmethod def getheader(*args): return '' def requests_get(url, **kwargs): members = '[{"id":14855829450254237642,"peerURLs":["http://localhost:2380","http://localhost:7001"],' +\ '"name":"default","clientURLs":["http://localhost:2379","http://localhost:4001"]}]' response = MockResponse() if url.startswith('http://local'): raise requests.exceptions.RequestException() elif ':8011/patroni' in url: response.content = '{"role": "replica", "xlog": {"received_location": 0}, "tags": {}}' elif url.endswith('/members'): response.content = '[{}]' if url.startswith('http://error') else members elif url.startswith('http://exhibitor'): response.content = '{"servers":["127.0.0.1","127.0.0.2","127.0.0.3"],"port":2181}' elif url.endswith(':8011/reinitialize'): data = kwargs.get('data', '') if ' false}' in data: response.status_code = 503 response.ok = False response.content = 'restarting after failure already in progress' else: response.status_code = 404 response.ok = False return response def etcd_watch(self, key, index=None, timeout=None, recursive=None): if timeout == 2.0: raise etcd.EtcdWatchTimedOut elif timeout == 5.0: return etcd.EtcdResult('delete', {}) elif 5 < timeout <= 10.0: raise etcd.EtcdException elif timeout == 20.0: raise etcd.EtcdEventIndexCleared def etcd_write(self, key, value, **kwargs): if key == '/service/exists/leader': raise etcd.EtcdAlreadyExist if key in ['/service/test/leader', '/patroni/test/leader'] and \ (kwargs.get('prevValue') == 'foo' or not kwargs.get('prevExist', True)): return True raise etcd.EtcdException def etcd_read(self, key, **kwargs): if key == '/service/noleader/': raise DCSError('noleader') elif key == '/service/nocluster/': raise etcd.EtcdKeyNotFound response = {"action": "get", "node": {"key": "/service/batman5", "dir": True, "nodes": [ {"key": "/service/batman5/config", "value": '{"foo": "bar"}', "modifiedIndex": 1582, "createdIndex": 1582}, {"key": "/service/batman5/failover", "value": "", "modifiedIndex": 1582, "createdIndex": 1582}, {"key": "/service/batman5/initialize", "value": "postgresql0", "modifiedIndex": 1582, "createdIndex": 1582}, {"key": "/service/batman5/leader", "value": "postgresql1", "expiration": "2015-05-15T09:11:00.037397538Z", "ttl": 21, "modifiedIndex": 20728, "createdIndex": 20434}, {"key": "/service/batman5/optime", "dir": True, "nodes": [ {"key": "/service/batman5/optime/leader", "value": "2164261704", "modifiedIndex": 20729, "createdIndex": 20729}], "modifiedIndex": 20437, "createdIndex": 20437}, {"key": "/service/batman5/sync", "value": '{"leader": "leader"}', "modifiedIndex": 1582, "createdIndex": 1582}, {"key": "/service/batman5/members", "dir": True, "nodes": [ {"key": "/service/batman5/members/postgresql1", "value": "postgres://replicator:rep-pass@127.0.0.1:5434/postgres" + "?application_name=http://127.0.0.1:8009/patroni", "expiration": "2015-05-15T09:10:59.949384522Z", "ttl": 21, "modifiedIndex": 20727, "createdIndex": 20727}, {"key": "/service/batman5/members/postgresql0", "value": "postgres://replicator:rep-pass@127.0.0.1:5433/postgres" + "?application_name=http://127.0.0.1:8008/patroni", "expiration": "2015-05-15T09:11:09.611860899Z", "ttl": 30, "modifiedIndex": 20730, "createdIndex": 20730}], "modifiedIndex": 1581, "createdIndex": 1581}], "modifiedIndex": 1581, "createdIndex": 1581}} result = etcd.EtcdResult(**response) result.etcd_index = 0 return result class SleepException(Exception): pass def dns_query(name, _): if '-server' not in name or '-ssl' in name: return [] if name == '_etcd-server._tcp.blabla': return [] elif name == '_etcd-server._tcp.exception': raise DNSException() srv = Mock() srv.port = 2380 srv.target.to_text.return_value = 'localhost' if name == '_etcd-server._tcp.foobar' else '127.0.0.1' return [srv] def socket_getaddrinfo(*args): if args[0] in ('ok', 'localhost', '127.0.0.1'): return [(socket.AF_INET, 1, 6, '', ('127.0.0.1', 0)), (socket.AF_INET6, 1, 6, '', ('::1', 0))] raise socket.gaierror def http_request(method, url, **kwargs): if url == 'http://localhost:2379/timeout': raise ReadTimeoutError(None, None, None) if url == 'http://localhost:2379/v2/machines': ret = MockResponse() ret.content = 'http://localhost:2379,http://localhost:4001' return ret if url == 'http://localhost:2379/': return MockResponse() raise socket.error class TestDnsCachingResolver(unittest.TestCase): @patch('time.sleep', Mock(side_effect=SleepException)) @patch('socket.getaddrinfo', Mock(side_effect=socket.gaierror)) def test_run(self): r = DnsCachingResolver() self.assertIsNone(r.resolve_async('', 0)) r.join() @patch('dns.resolver.query', dns_query) @patch('socket.getaddrinfo', socket_getaddrinfo) @patch('requests.get', requests_get) class TestClient(unittest.TestCase): @patch('dns.resolver.query', dns_query) @patch('socket.getaddrinfo', socket_getaddrinfo) @patch('requests.get', requests_get) def setUp(self): with patch.object(Client, 'machines') as mock_machines: mock_machines.__get__ = Mock(return_value=['http://localhost:2379', 'http://localhost:4001']) self.client = Client({'srv': 'test', 'retry_timeout': 3}, DnsCachingResolver()) self.client.http.request = http_request self.client.http.request_encode_body = http_request def test_machines(self): self.client._base_uri = 'http://localhost:4001' self.client._machines_cache = ['http://localhost:2379'] self.assertIsNotNone(self.client.machines) self.client._base_uri = 'http://localhost:4001' self.client._machines_cache = [] self.assertIsNotNone(self.client.machines) self.client._update_machines_cache = True machines = None try: machines = self.client.machines self.assertFail() except Exception: self.assertIsNone(machines) @patch.object(Client, 'machines') def test_api_execute(self, mock_machines): mock_machines.__get__ = Mock(return_value=['http://localhost:2379']) self.assertRaises(ValueError, self.client.api_execute, '', '') self.client._base_uri = 'http://localhost:4001' self.client._machines_cache = ['http://localhost:2379'] self.client.api_execute('/', 'POST', timeout=0) mock_machines.__get__ = Mock(return_value=['http://localhost:2379']) self.client._machines_cache_updated = 0 self.client.api_execute('/', 'POST', timeout=0) self.assertRaises(etcd.EtcdWatchTimedOut, self.client.api_execute, '/timeout', 'POST', params={'wait': 'true'}) self.assertRaises(etcd.EtcdException, self.client.api_execute, '/', '') self.client._update_machines_cache = True with patch.object(Client, '_load_machines_cache', Mock(side_effect=etcd.EtcdException)): self.assertRaises(etcd.EtcdException, self.client.api_execute, '/', 'GET') def test_get_srv_record(self): self.assertEquals(self.client.get_srv_record('_etcd-server._tcp.blabla'), []) self.assertEquals(self.client.get_srv_record('_etcd-server._tcp.exception'), []) def test__get_machines_cache_from_srv(self): self.client._get_machines_cache_from_srv('foobar') self.client.get_srv_record = Mock(return_value=[('localhost', 2380)]) self.client._get_machines_cache_from_srv('blabla') def test__get_machines_cache_from_dns(self): self.client._get_machines_cache_from_dns('error', 2379) @patch.object(Client, 'machines') def test__load_machines_cache(self, mock_machines): mock_machines.__get__ = Mock(return_value=['http://localhost:2379']) self.client._config = {} self.assertRaises(Exception, self.client._load_machines_cache) self.client._config = {'srv': 'blabla'} self.assertRaises(etcd.EtcdException, self.client._load_machines_cache) @patch.object(socket.socket, 'connect') def test_create_connection_patched(self, mock_connect): self.assertRaises(socket.error, urllib3.util.connection.create_connection, ('fail', 2379)) urllib3.util.connection.create_connection(('[localhost]', 2379)) mock_connect.side_effect = socket.error self.assertRaises(socket.error, urllib3.util.connection.create_connection, ('[localhost]', 2379), timeout=1, source_address=('localhost', 53333), socket_options=[(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)]) @patch('requests.get', requests_get) @patch('socket.getaddrinfo', socket_getaddrinfo) @patch.object(etcd.Client, 'write', etcd_write) @patch.object(etcd.Client, 'read', etcd_read) @patch.object(etcd.Client, 'delete', Mock(side_effect=etcd.EtcdException)) class TestEtcd(unittest.TestCase): @patch('socket.getaddrinfo', socket_getaddrinfo) def setUp(self): with patch.object(Client, 'machines') as mock_machines: mock_machines.__get__ = Mock(return_value=['http://localhost:2379', 'http://localhost:4001']) self.etcd = Etcd({'namespace': '/patroni/', 'ttl': 30, 'retry_timeout': 10, 'host': 'localhost:2379', 'scope': 'test', 'name': 'foo'}) def test_base_path(self): self.assertEquals(self.etcd._base_path, '/patroni/test') @patch('dns.resolver.query', dns_query) def test_get_etcd_client(self): with patch.object(Client, 'machines') as mock_machines: mock_machines.__get__ = Mock(side_effect=etcd.EtcdException) with patch('time.sleep', Mock(side_effect=SleepException)): self.assertRaises(SleepException, self.etcd.get_etcd_client, {'discovery_srv': 'test', 'retry_timeout': 10, 'cacert': '1', 'key': '1', 'cert': 1}) self.assertRaises(SleepException, self.etcd.get_etcd_client, {'url': 'https://test:2379', 'retry_timeout': 10}) self.assertRaises(SleepException, self.etcd.get_etcd_client, {'proxy': 'https://user:password@test:2379', 'retry_timeout': 10}) self.assertRaises(SleepException, self.etcd.get_etcd_client, {'hosts': 'foo:4001,bar', 'retry_timeout': 10}) def test_get_cluster(self): self.assertIsInstance(self.etcd.get_cluster(), Cluster) self.etcd._base_path = '/service/nocluster' cluster = self.etcd.get_cluster() self.assertIsInstance(cluster, Cluster) self.assertIsNone(cluster.leader) self.etcd._base_path = '/service/noleader' self.assertRaises(EtcdError, self.etcd.get_cluster) def test_touch_member(self): self.assertFalse(self.etcd.touch_member('', '')) def test_take_leader(self): self.assertFalse(self.etcd.take_leader()) def test_attempt_to_acquire_leader(self): self.etcd._base_path = '/service/exists' self.assertFalse(self.etcd.attempt_to_acquire_leader()) self.etcd._base_path = '/service/failed' self.assertFalse(self.etcd.attempt_to_acquire_leader()) def test_write_leader_optime(self): self.etcd.write_leader_optime('0') def test_update_leader(self): self.assertTrue(self.etcd.update_leader(None)) def test_initialize(self): self.assertFalse(self.etcd.initialize()) def test_cancel_initializion(self): self.assertFalse(self.etcd.cancel_initialization()) def test_delete_leader(self): self.assertFalse(self.etcd.delete_leader()) def test_delete_cluster(self): self.assertFalse(self.etcd.delete_cluster()) @patch('time.sleep', Mock(side_effect=SleepException)) @patch.object(etcd.Client, 'watch', etcd_watch) def test_watch(self): self.etcd.watch(None, 0) self.etcd.get_cluster() self.etcd.watch(20729, 1.5) self.etcd.watch(20729, 4.5) with patch.object(AbstractDCS, 'watch', Mock()): self.assertTrue(self.etcd.watch(20729, 19.5)) self.assertRaises(SleepException, self.etcd.watch, 20729, 9.5) def test_other_exceptions(self): self.etcd.retry = Mock(side_effect=AttributeError('foo')) self.assertRaises(EtcdError, self.etcd.cancel_initialization) def test_set_ttl(self): self.etcd.set_ttl(20) self.assertTrue(self.etcd.watch(None, 1)) def test_sync_state(self): self.assertFalse(self.etcd.write_sync_state('leader', None)) self.assertFalse(self.etcd.delete_sync_state()) def test_set_history_value(self): self.assertFalse(self.etcd.set_history_value('{}')) patroni-1.4.2/tests/test_exhibitor.py000066400000000000000000000021731323411135200177400ustar00rootroot00000000000000import unittest from mock import Mock, patch from patroni.dcs.exhibitor import ExhibitorEnsembleProvider, Exhibitor from patroni.dcs.zookeeper import ZooKeeperError from test_etcd import SleepException, requests_get from test_zookeeper import MockKazooClient @patch('requests.get', requests_get) @patch('time.sleep', Mock(side_effect=SleepException)) class TestExhibitorEnsembleProvider(unittest.TestCase): def test_init(self): self.assertRaises(SleepException, ExhibitorEnsembleProvider, ['localhost'], 8181) def test_poll(self): self.assertFalse(ExhibitorEnsembleProvider(['exhibitor'], 8181).poll()) class TestExhibitor(unittest.TestCase): @patch('requests.get', requests_get) @patch('patroni.dcs.zookeeper.KazooClient', MockKazooClient) def setUp(self): self.e = Exhibitor({'hosts': ['localhost', 'exhibitor'], 'port': 8181, 'scope': 'test', 'name': 'foo', 'ttl': 30, 'retry_timeout': 10}) @patch.object(ExhibitorEnsembleProvider, 'poll', Mock(return_value=True)) def test_get_cluster(self): self.assertRaises(ZooKeeperError, self.e.get_cluster) patroni-1.4.2/tests/test_ha.py000066400000000000000000001301551323411135200163350ustar00rootroot00000000000000import datetime import etcd import os import unittest from mock import Mock, MagicMock, PropertyMock, patch from patroni.config import Config from patroni.dcs import Cluster, ClusterConfig, Failover, Leader, Member, get_dcs, SyncState, TimelineHistory from patroni.dcs.etcd import Client from patroni.exceptions import DCSError, PostgresConnectionException, PatroniException from patroni.ha import Ha, _MemberStatus from patroni.postgresql import Postgresql from patroni.watchdog import Watchdog from patroni.utils import tzutc from test_etcd import socket_getaddrinfo, etcd_read, etcd_write, requests_get from test_postgresql import psycopg2_connect, MockPostmaster def true(*args, **kwargs): return True def false(*args, **kwargs): return False def get_cluster(initialize, leader, members, failover, sync): history = TimelineHistory(1, [(1, 67197376, 'no recovery target specified', datetime.datetime.now().isoformat())]) return Cluster(initialize, ClusterConfig(1, {1: 2}, 1), leader, 10, members, failover, sync, history) def get_cluster_not_initialized_without_leader(): return get_cluster(None, None, [], None, SyncState(None, None, None)) def get_cluster_initialized_without_leader(leader=False, failover=None, sync=None): m1 = Member(0, 'leader', 28, {'conn_url': 'postgres://replicator:rep-pass@127.0.0.1:5435/postgres', 'api_url': 'http://127.0.0.1:8008/patroni', 'xlog_location': 4}) leader = Leader(0, 0, m1) if leader else None m2 = Member(0, 'other', 28, {'conn_url': 'postgres://replicator:rep-pass@127.0.0.1:5436/postgres', 'api_url': 'http://127.0.0.1:8011/patroni', 'state': 'running', 'pause': True, 'tags': {'clonefrom': True}, 'scheduled_restart': {'schedule': "2100-01-01 10:53:07.560445+00:00", 'postgres_version': '99.0.0'}}) syncstate = SyncState(0 if sync else None, sync and sync[0], sync and sync[1]) return get_cluster(True, leader, [m1, m2], failover, syncstate) def get_cluster_initialized_with_leader(failover=None, sync=None): return get_cluster_initialized_without_leader(leader=True, failover=failover, sync=sync) def get_cluster_initialized_with_only_leader(failover=None): leader = get_cluster_initialized_without_leader(leader=True, failover=failover).leader return get_cluster(True, leader, [leader], failover, None) def get_node_status(reachable=True, in_recovery=True, wal_position=10, nofailover=False, watchdog_failed=False): def fetch_node_status(e): tags = {} if nofailover: tags['nofailover'] = True return _MemberStatus(e, reachable, in_recovery, wal_position, tags, watchdog_failed) return fetch_node_status future_restart_time = datetime.datetime.now(tzutc) + datetime.timedelta(days=5) postmaster_start_time = datetime.datetime.now(tzutc) class MockPatroni(object): def __init__(self, p, d): os.environ[Config.PATRONI_CONFIG_VARIABLE] = """ restapi: listen: 0.0.0.0:8008 bootstrap: users: replicator: password: rep-pass options: - replication postgresql: name: foo data_dir: data/postgresql0 pg_rewind: username: postgres password: postgres watchdog: mode: off zookeeper: exhibitor: hosts: [localhost] port: 8181 """ self.config = Config() self.postgresql = p self.dcs = d self.api = Mock() self.tags = {'foo': 'bar'} self.nofailover = None self.replicatefrom = None self.api.connection_string = 'http://127.0.0.1:8008' self.clonefrom = None self.nosync = False self.scheduled_restart = {'schedule': future_restart_time, 'postmaster_start_time': str(postmaster_start_time)} self.watchdog = Watchdog(self.config) def run_async(self, func, args=()): return func(*args) if args else func() @patch.object(Postgresql, 'is_running', Mock(return_value=MockPostmaster())) @patch.object(Postgresql, 'is_leader', Mock(return_value=True)) @patch.object(Postgresql, 'timeline_wal_position', Mock(return_value=(1, 10))) @patch.object(Postgresql, '_cluster_info_state_get', Mock(return_value=3)) @patch.object(Postgresql, 'call_nowait', Mock(return_value=True)) @patch.object(Postgresql, 'data_directory_empty', Mock(return_value=False)) @patch.object(Postgresql, 'controldata', Mock(return_value={'Database system identifier': '1234567890'})) @patch.object(Postgresql, 'sync_replication_slots', Mock()) @patch.object(Postgresql, 'write_pg_hba', Mock()) @patch.object(Postgresql, 'write_pgpass', Mock(return_value={})) @patch.object(Postgresql, 'write_recovery_conf', Mock()) @patch.object(Postgresql, 'query', Mock()) @patch.object(Postgresql, 'checkpoint', Mock()) @patch.object(Postgresql, 'call_nowait', Mock()) @patch.object(Postgresql, 'cancellable_subprocess_call', Mock(return_value=0)) @patch.object(etcd.Client, 'write', etcd_write) @patch.object(etcd.Client, 'read', etcd_read) @patch.object(etcd.Client, 'delete', Mock(side_effect=etcd.EtcdException)) @patch('patroni.postgresql.polling_loop', Mock(return_value=range(1))) @patch('patroni.async_executor.AsyncExecutor.busy', PropertyMock(return_value=False)) @patch('patroni.async_executor.AsyncExecutor.run_async', run_async) @patch('subprocess.call', Mock(return_value=0)) @patch('time.sleep', Mock()) class TestHa(unittest.TestCase): @patch('socket.getaddrinfo', socket_getaddrinfo) @patch('psycopg2.connect', psycopg2_connect) @patch('patroni.dcs.dcs_modules', Mock(return_value=['patroni.dcs.foo', 'patroni.dcs.etcd'])) @patch.object(etcd.Client, 'read', etcd_read) def setUp(self): with patch.object(Client, 'machines') as mock_machines: mock_machines.__get__ = Mock(return_value=['http://remotehost:2379']) self.p = Postgresql({'name': 'postgresql0', 'scope': 'dummy', 'listen': '127.0.0.1:5432', 'data_dir': 'data/postgresql0', 'retry_timeout': 10, 'maximum_lag_on_failover': 5, 'authentication': {'superuser': {'username': 'foo', 'password': 'bar'}, 'replication': {'username': '', 'password': ''}}, 'parameters': {'wal_level': 'hot_standby', 'max_replication_slots': 5, 'foo': 'bar', 'hot_standby': 'on', 'max_wal_senders': 5, 'wal_keep_segments': 8}}) self.p.set_state('running') self.p.set_role('replica') self.p.postmaster_start_time = MagicMock(return_value=str(postmaster_start_time)) self.p.can_create_replica_without_replication_connection = MagicMock(return_value=False) self.e = get_dcs({'etcd': {'ttl': 30, 'host': 'ok:2379', 'scope': 'test', 'name': 'foo', 'retry_timeout': 10}}) self.ha = Ha(MockPatroni(self.p, self.e)) self.ha.old_cluster = self.e.get_cluster() self.ha.cluster = get_cluster_not_initialized_without_leader() self.ha.load_cluster_from_dcs = Mock() def test_update_lock(self): self.p.last_operation = Mock(side_effect=PostgresConnectionException('')) self.assertTrue(self.ha.update_lock(True)) def test_touch_member(self): self.p.timeline_wal_position = Mock(return_value=(0, 1)) self.p.replica_cached_timeline = Mock(side_effect=Exception) self.ha.touch_member() def test_start_as_replica(self): self.p.is_healthy = false self.assertEquals(self.ha.run_cycle(), 'starting as a secondary') def test_recover_replica_failed(self): self.p.controldata = lambda: {'Database cluster state': 'in recovery'} self.p.is_running = false self.p.follow = false self.assertEquals(self.ha.run_cycle(), 'starting as a secondary') self.assertEquals(self.ha.run_cycle(), 'failed to start postgres') def test_recover_former_master(self): self.p.follow = false self.p.is_running = false self.p.name = 'leader' self.p.set_role('master') self.p.controldata = lambda: {'Database cluster state': 'shut down'} self.ha.cluster = get_cluster_initialized_with_leader() self.assertEquals(self.ha.run_cycle(), 'starting as readonly because i had the session lock') @patch.object(Postgresql, 'fix_cluster_state', Mock()) def test_crash_recovery(self): self.p.is_running = false self.p.controldata = lambda: {'Database cluster state': 'in production'} self.assertEquals(self.ha.run_cycle(), 'doing crash recovery in a single user mode') @patch.object(Postgresql, 'rewind_needed_and_possible', Mock(return_value=True)) def test_recover_with_rewind(self): self.p.is_running = false self.ha.cluster = get_cluster_initialized_with_leader() self.assertEquals(self.ha.run_cycle(), 'running pg_rewind from leader') @patch.object(Postgresql, 'can_rewind', PropertyMock(return_value=True)) @patch.object(Postgresql, 'fix_cluster_state', Mock()) def test_single_user_after_recover_failed(self): self.p.controldata = lambda: {'Database cluster state': 'in recovery'} self.p.is_running = false self.p.follow = false self.assertEquals(self.ha.run_cycle(), 'starting as a secondary') self.assertEquals(self.ha.run_cycle(), 'fixing cluster state in a single user mode') @patch('sys.exit', return_value=1) @patch('patroni.ha.Ha.sysid_valid', MagicMock(return_value=True)) def test_sysid_no_match(self, exit_mock): self.ha.run_cycle() exit_mock.assert_called_once_with(1) @patch.object(Cluster, 'is_unlocked', Mock(return_value=False)) def test_start_as_readonly(self): self.p.is_leader = false self.p.is_healthy = true self.ha.has_lock = true self.p.controldata = lambda: {'Database cluster state': 'in production'} self.assertEquals(self.ha.run_cycle(), 'promoted self to leader because i had the session lock') @patch('psycopg2.connect', psycopg2_connect) def test_acquire_lock_as_master(self): self.assertEquals(self.ha.run_cycle(), 'acquired session lock as a leader') def test_promoted_by_acquiring_lock(self): self.ha.is_healthiest_node = true self.p.is_leader = false self.assertEquals(self.ha.run_cycle(), 'promoted self to leader by acquiring session lock') def test_long_promote(self): self.ha.cluster.is_unlocked = false self.ha.has_lock = true self.p.is_leader = false self.p.set_role('master') self.assertEquals(self.ha.run_cycle(), 'no action. i am the leader with the lock') def test_demote_after_failing_to_obtain_lock(self): self.ha.acquire_lock = false self.assertEquals(self.ha.run_cycle(), 'demoted self after trying and failing to obtain lock') def test_follow_new_leader_after_failing_to_obtain_lock(self): self.ha.is_healthiest_node = true self.ha.acquire_lock = false self.p.is_leader = false self.assertEquals(self.ha.run_cycle(), 'following new leader after trying and failing to obtain lock') def test_demote_because_not_healthiest(self): self.ha.is_healthiest_node = false self.assertEquals(self.ha.run_cycle(), 'demoting self because i am not the healthiest node') def test_follow_new_leader_because_not_healthiest(self): self.ha.is_healthiest_node = false self.p.is_leader = false self.assertEquals(self.ha.run_cycle(), 'following a different leader because i am not the healthiest node') def test_promote_because_have_lock(self): self.ha.cluster.is_unlocked = false self.ha.has_lock = true self.p.is_leader = false self.assertEquals(self.ha.run_cycle(), 'promoted self to leader because i had the session lock') def test_promote_without_watchdog(self): self.ha.cluster.is_unlocked = false self.ha.has_lock = true self.p.is_leader = true with patch.object(Watchdog, 'activate', Mock(return_value=False)): self.assertEquals(self.ha.run_cycle(), 'Demoting self because watchdog could not be activated') self.p.is_leader = false self.assertEquals(self.ha.run_cycle(), 'Not promoting self because watchdog could not be activated') def test_leader_with_lock(self): self.ha.cluster.is_unlocked = false self.ha.has_lock = true self.assertEquals(self.ha.run_cycle(), 'no action. i am the leader with the lock') def test_demote_because_not_having_lock(self): self.ha.cluster.is_unlocked = false with patch.object(Watchdog, 'is_running', PropertyMock(return_value=True)): self.assertEquals(self.ha.run_cycle(), 'demoting self because i do not have the lock and i was a leader') def test_demote_because_update_lock_failed(self): self.ha.cluster.is_unlocked = false self.ha.has_lock = true self.ha.update_lock = false self.assertEquals(self.ha.run_cycle(), 'demoted self because failed to update leader lock in DCS') self.p.is_leader = false self.assertEquals(self.ha.run_cycle(), 'not promoting because failed to update leader lock in DCS') def test_follow(self): self.ha.cluster.is_unlocked = false self.p.is_leader = false self.assertEquals(self.ha.run_cycle(), 'no action. i am a secondary and i am following a leader') self.ha.patroni.replicatefrom = "foo" self.assertEquals(self.ha.run_cycle(), 'no action. i am a secondary and i am following a leader') def test_follow_in_pause(self): self.ha.cluster.is_unlocked = false self.ha.is_paused = true self.assertEquals(self.ha.run_cycle(), 'PAUSE: continue to run as master without lock') self.p.is_leader = false self.assertEquals(self.ha.run_cycle(), 'PAUSE: no action') @patch.object(Postgresql, 'rewind_needed_and_possible', Mock(return_value=True)) def test_follow_triggers_rewind(self): self.p.is_leader = false self.p.trigger_check_diverged_lsn() self.ha.cluster = get_cluster_initialized_with_leader() self.assertEquals(self.ha.run_cycle(), 'running pg_rewind from leader') def test_no_etcd_connection_master_demote(self): self.ha.load_cluster_from_dcs = Mock(side_effect=DCSError('Etcd is not responding properly')) self.assertEquals(self.ha.run_cycle(), 'demoted self because DCS is not accessible and i was a leader') @patch('time.sleep', Mock()) def test_bootstrap_from_another_member(self): self.ha.cluster = get_cluster_initialized_with_leader() self.assertEquals(self.ha.bootstrap(), 'trying to bootstrap from replica \'other\'') def test_bootstrap_waiting_for_leader(self): self.ha.cluster = get_cluster_initialized_without_leader() self.assertEquals(self.ha.bootstrap(), 'waiting for leader to bootstrap') def test_bootstrap_without_leader(self): self.ha.cluster = get_cluster_initialized_without_leader() self.p.can_create_replica_without_replication_connection = MagicMock(return_value=True) self.assertEquals(self.ha.bootstrap(), 'trying to bootstrap (without leader)') def test_bootstrap_initialize_lock_failed(self): self.ha.cluster = get_cluster_not_initialized_without_leader() self.assertEquals(self.ha.bootstrap(), 'failed to acquire initialize lock') def test_bootstrap_initialized_new_cluster(self): self.ha.cluster = get_cluster_not_initialized_without_leader() self.e.initialize = true self.assertEquals(self.ha.bootstrap(), 'trying to bootstrap a new cluster') self.p.is_leader = false self.assertEquals(self.ha.run_cycle(), 'waiting for end of recovery after bootstrap') self.p.is_leader = true self.assertEquals(self.ha.run_cycle(), 'running post_bootstrap') self.assertEquals(self.ha.run_cycle(), 'initialized a new cluster') def test_bootstrap_release_initialize_key_on_failure(self): self.ha.cluster = get_cluster_not_initialized_without_leader() self.e.initialize = true self.ha.bootstrap() self.p.is_running = false self.assertRaises(PatroniException, self.ha.post_bootstrap) def test_bootstrap_release_initialize_key_on_watchdog_failure(self): self.ha.cluster = get_cluster_not_initialized_without_leader() self.e.initialize = true self.ha.bootstrap() self.p.is_running.return_value = MockPostmaster() self.p.is_leader = true with patch.object(Watchdog, 'activate', Mock(return_value=False)): self.assertEquals(self.ha.post_bootstrap(), 'running post_bootstrap') self.assertRaises(PatroniException, self.ha.post_bootstrap) @patch('psycopg2.connect', psycopg2_connect) def test_reinitialize(self): self.assertIsNotNone(self.ha.reinitialize()) self.ha.cluster = get_cluster_initialized_with_leader() self.assertIsNone(self.ha.reinitialize(True)) self.assertIsNotNone(self.ha.reinitialize()) self.ha.state_handler.name = self.ha.cluster.leader.name self.assertIsNotNone(self.ha.reinitialize()) @patch('time.sleep', Mock()) def test_restart(self): self.assertEquals(self.ha.restart({}), (True, 'restarted successfully')) self.p.restart = Mock(return_value=None) self.assertEquals(self.ha.restart({}), (False, 'postgres is still starting')) self.p.restart = false self.assertEquals(self.ha.restart({}), (False, 'restart failed')) self.ha.cluster = get_cluster_initialized_with_leader() self.ha.reinitialize() self.assertEquals(self.ha.restart({}), (False, 'reinitialize already in progress')) with patch.object(self.ha, "restart_matches", return_value=False): self.assertEquals(self.ha.restart({'foo': 'bar'}), (False, "restart conditions are not satisfied")) @patch('os.kill', Mock()) def test_restart_in_progress(self): with patch('patroni.async_executor.AsyncExecutor.busy', PropertyMock(return_value=True)): self.ha.restart({}, run_async=True) self.assertTrue(self.ha.restart_scheduled()) self.assertEquals(self.ha.run_cycle(), 'restart in progress') self.ha.cluster = get_cluster_initialized_with_leader() self.assertEquals(self.ha.run_cycle(), 'restart in progress') self.ha.has_lock = true self.assertEquals(self.ha.run_cycle(), 'updated leader lock during restart') self.ha.update_lock = false self.p.set_role('master') with patch('patroni.async_executor.CriticalTask.cancel', Mock(return_value=False)): with patch('patroni.postgresql.Postgresql.terminate_starting_postmaster') as mock_terminate: self.assertEquals(self.ha.run_cycle(), 'lost leader lock during restart') mock_terminate.assert_called() @patch('requests.get', requests_get) def test_manual_failover_from_leader(self): self.ha.fetch_node_status = get_node_status() self.ha.has_lock = true self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, 'blabla', '', None)) self.assertEquals(self.ha.run_cycle(), 'no action. i am the leader with the lock') self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, '', self.p.name, None)) self.assertEquals(self.ha.run_cycle(), 'no action. i am the leader with the lock') self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, '', 'blabla', None)) self.assertEquals(self.ha.run_cycle(), 'no action. i am the leader with the lock') f = Failover(0, self.p.name, '', None) self.ha.cluster = get_cluster_initialized_with_leader(f) self.assertEquals(self.ha.run_cycle(), 'manual failover: demoting myself') self.p.rewind_needed_and_possible = true self.assertEquals(self.ha.run_cycle(), 'manual failover: demoting myself') self.ha.fetch_node_status = get_node_status(nofailover=True) self.assertEquals(self.ha.run_cycle(), 'no action. i am the leader with the lock') self.ha.fetch_node_status = get_node_status(watchdog_failed=True) self.assertEquals(self.ha.run_cycle(), 'no action. i am the leader with the lock') self.ha.fetch_node_status = get_node_status(wal_position=1) self.assertEquals(self.ha.run_cycle(), 'no action. i am the leader with the lock') # manual failover from the previous leader to us won't happen if we hold the nofailover flag self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, 'blabla', self.p.name, None)) self.assertEquals(self.ha.run_cycle(), 'no action. i am the leader with the lock') # Failover scheduled time must include timezone scheduled = datetime.datetime.now() self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, 'blabla', self.p.name, scheduled)) self.ha.run_cycle() scheduled = datetime.datetime.utcnow().replace(tzinfo=tzutc) self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, 'blabla', self.p.name, scheduled)) self.assertEquals('no action. i am the leader with the lock', self.ha.run_cycle()) scheduled = scheduled + datetime.timedelta(seconds=30) self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, 'blabla', self.p.name, scheduled)) self.assertEquals('no action. i am the leader with the lock', self.ha.run_cycle()) scheduled = scheduled + datetime.timedelta(seconds=-600) self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, 'blabla', self.p.name, scheduled)) self.assertEquals('no action. i am the leader with the lock', self.ha.run_cycle()) scheduled = None self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, 'blabla', self.p.name, scheduled)) self.assertEquals('no action. i am the leader with the lock', self.ha.run_cycle()) @patch('requests.get', requests_get) def test_manual_failover_from_leader_in_pause(self): self.ha.has_lock = true self.ha.is_paused = true scheduled = datetime.datetime.now() self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, 'blabla', self.p.name, scheduled)) self.assertEquals('PAUSE: no action. i am the leader with the lock', self.ha.run_cycle()) self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, self.p.name, '', None)) self.assertEquals('PAUSE: no action. i am the leader with the lock', self.ha.run_cycle()) @patch('requests.get', requests_get) def test_manual_failover_from_leader_in_synchronous_mode(self): self.p.is_leader = true self.ha.has_lock = true self.ha.is_synchronous_mode = true self.ha.is_failover_possible = false self.ha.process_sync_replication = Mock() self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, self.p.name, 'a', None), (self.p.name, None)) self.assertEquals('no action. i am the leader with the lock', self.ha.run_cycle()) self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, self.p.name, 'a', None), (self.p.name, 'a')) self.ha.is_failover_possible = true self.assertEquals('manual failover: demoting myself', self.ha.run_cycle()) @patch('requests.get', requests_get) def test_manual_failover_process_no_leader(self): self.p.is_leader = false self.ha.cluster = get_cluster_initialized_without_leader(failover=Failover(0, '', self.p.name, None)) self.assertEquals(self.ha.run_cycle(), 'promoted self to leader by acquiring session lock') self.ha.cluster = get_cluster_initialized_without_leader(failover=Failover(0, '', 'leader', None)) self.p.set_role('replica') self.assertEquals(self.ha.run_cycle(), 'promoted self to leader by acquiring session lock') self.ha.fetch_node_status = get_node_status() # accessible, in_recovery self.assertEquals(self.ha.run_cycle(), 'following a different leader because i am not the healthiest node') self.ha.cluster = get_cluster_initialized_without_leader(failover=Failover(0, self.p.name, '', None)) self.assertEquals(self.ha.run_cycle(), 'following a different leader because i am not the healthiest node') self.ha.fetch_node_status = get_node_status(reachable=False) # inaccessible, in_recovery self.p.set_role('replica') self.assertEquals(self.ha.run_cycle(), 'promoted self to leader by acquiring session lock') # set failover flag to True for all members of the cluster # this should elect the current member, as we are not going to call the API for it. self.ha.cluster = get_cluster_initialized_without_leader(failover=Failover(0, '', 'other', None)) self.ha.fetch_node_status = get_node_status(nofailover=True) # accessible, in_recovery self.p.set_role('replica') self.assertEquals(self.ha.run_cycle(), 'promoted self to leader by acquiring session lock') # same as previous, but set the current member to nofailover. In no case it should be elected as a leader self.ha.patroni.nofailover = True self.assertEquals(self.ha.run_cycle(), 'following a different leader because I am not allowed to promote') def test_manual_failover_process_no_leader_in_pause(self): self.ha.is_paused = true self.ha.cluster = get_cluster_initialized_without_leader(failover=Failover(0, '', 'other', None)) self.assertEquals(self.ha.run_cycle(), 'PAUSE: continue to run as master without lock') self.ha.cluster = get_cluster_initialized_without_leader(failover=Failover(0, 'leader', '', None)) self.assertEquals(self.ha.run_cycle(), 'PAUSE: continue to run as master without lock') self.ha.cluster = get_cluster_initialized_without_leader(failover=Failover(0, 'leader', 'blabla', None)) self.assertEquals('PAUSE: acquired session lock as a leader', self.ha.run_cycle()) self.p.is_leader = false self.p.set_role('replica') self.ha.cluster = get_cluster_initialized_without_leader(failover=Failover(0, 'leader', self.p.name, None)) self.assertEquals(self.ha.run_cycle(), 'PAUSE: promoted self to leader by acquiring session lock') def test_is_healthiest_node(self): self.ha.state_handler.is_leader = false self.ha.patroni.nofailover = False self.ha.fetch_node_status = get_node_status() self.assertTrue(self.ha.is_healthiest_node()) with patch.object(Watchdog, 'is_healthy', PropertyMock(return_value=False)): self.assertFalse(self.ha.is_healthiest_node()) with patch('patroni.postgresql.Postgresql.is_starting', return_value=True): self.assertFalse(self.ha.is_healthiest_node()) self.ha.is_paused = true self.assertFalse(self.ha.is_healthiest_node()) def test__is_healthiest_node(self): self.assertTrue(self.ha._is_healthiest_node(self.ha.old_cluster.members)) self.p.is_leader = false self.ha.fetch_node_status = get_node_status() # accessible, in_recovery self.assertTrue(self.ha._is_healthiest_node(self.ha.old_cluster.members)) self.ha.fetch_node_status = get_node_status(in_recovery=False) # accessible, not in_recovery self.assertFalse(self.ha._is_healthiest_node(self.ha.old_cluster.members)) self.ha.fetch_node_status = get_node_status(wal_position=11) # accessible, in_recovery, wal position ahead self.assertFalse(self.ha._is_healthiest_node(self.ha.old_cluster.members)) with patch('patroni.postgresql.Postgresql.timeline_wal_position', return_value=(1, 1)): self.assertFalse(self.ha._is_healthiest_node(self.ha.old_cluster.members)) self.ha.patroni.nofailover = True self.assertFalse(self.ha._is_healthiest_node(self.ha.old_cluster.members)) self.ha.patroni.nofailover = False @patch('requests.get', requests_get) def test_fetch_node_status(self): member = Member(0, 'test', 1, {'api_url': 'http://127.0.0.1:8011/patroni'}) self.ha.fetch_node_status(member) member = Member(0, 'test', 1, {'api_url': 'http://localhost:8011/patroni'}) self.ha.fetch_node_status(member) def test_post_recover(self): self.p.is_running = false self.ha.has_lock = true self.assertEqual(self.ha.post_recover(), 'removed leader key after trying and failing to start postgres') self.ha.has_lock = false self.assertEqual(self.ha.post_recover(), 'failed to start postgres') self.p.is_running = true self.assertIsNone(self.ha.post_recover()) def test_schedule_future_restart(self): self.ha.patroni.scheduled_restart = {} # do the restart 2 times. The first one should succeed, the second one should fail self.assertTrue(self.ha.schedule_future_restart({'schedule': future_restart_time})) self.assertFalse(self.ha.schedule_future_restart({'schedule': future_restart_time})) def test_delete_future_restarts(self): self.ha.delete_future_restart() def test_evaluate_scheduled_restart(self): self.p.postmaster_start_time = Mock(return_value=str(postmaster_start_time)) # restart already in progres with patch('patroni.async_executor.AsyncExecutor.busy', PropertyMock(return_value=True)): self.assertIsNone(self.ha.evaluate_scheduled_restart()) # restart while the postmaster has been already restarted, fails with patch.object(self.ha, 'future_restart_scheduled', Mock(return_value={'postmaster_start_time': str(postmaster_start_time - datetime.timedelta(days=1)), 'schedule': str(future_restart_time)})): self.assertIsNone(self.ha.evaluate_scheduled_restart()) with patch.object(self.ha, 'future_restart_scheduled', Mock(return_value={'postmaster_start_time': str(postmaster_start_time), 'schedule': str(future_restart_time)})): with patch.object(self.ha, 'should_run_scheduled_action', Mock(return_value=True)): # restart in the future, ok self.assertIsNotNone(self.ha.evaluate_scheduled_restart()) with patch.object(self.ha, 'restart', Mock(return_value=(False, "Test"))): # restart in the future, bit the actual restart failed self.assertIsNone(self.ha.evaluate_scheduled_restart()) def test_scheduled_restart(self): self.ha.cluster = get_cluster_initialized_with_leader() with patch.object(self.ha, "evaluate_scheduled_restart", Mock(return_value="restart scheduled")): self.assertEquals(self.ha.run_cycle(), "restart scheduled") def test_restart_matches(self): self.p._role = 'replica' self.p.server_version = 90500 self.p._pending_restart = True self.assertFalse(self.ha.restart_matches("master", "9.5.0", True)) self.assertFalse(self.ha.restart_matches("replica", "9.4.3", True)) self.p._pending_restart = False self.assertFalse(self.ha.restart_matches("replica", "9.5.2", True)) self.assertTrue(self.ha.restart_matches("replica", "9.5.2", False)) def test_process_healthy_cluster_in_pause(self): self.p.is_leader = false self.ha.is_paused = true self.p.name = 'leader' self.ha.cluster = get_cluster_initialized_with_leader() self.assertEquals(self.ha.run_cycle(), 'PAUSE: removed leader lock because postgres is not running as master') self.ha.cluster = get_cluster_initialized_with_leader(Failover(0, '', self.p.name, None)) self.assertEquals(self.ha.run_cycle(), 'PAUSE: waiting to become master after promote...') def test_postgres_unhealthy_in_pause(self): self.ha.is_paused = true self.p.is_healthy = false self.assertEquals(self.ha.run_cycle(), 'PAUSE: postgres is not running') self.ha.has_lock = true self.assertEquals(self.ha.run_cycle(), 'PAUSE: removed leader lock because postgres is not running') def test_no_etcd_connection_in_pause(self): self.ha.is_paused = true self.ha.load_cluster_from_dcs = Mock(side_effect=DCSError('Etcd is not responding properly')) self.assertEquals(self.ha.run_cycle(), 'PAUSE: DCS is not accessible') @patch('patroni.ha.Ha.update_lock', return_value=True) @patch('patroni.ha.Ha.demote') def test_starting_timeout(self, demote, update_lock): def check_calls(seq): for mock, called in seq: if called: mock.assert_called_once() else: mock.assert_not_called() mock.reset_mock() self.ha.has_lock = true self.ha.cluster = get_cluster_initialized_with_leader() self.p.check_for_startup = true self.p.time_in_state = lambda: 30 self.assertEquals(self.ha.run_cycle(), 'PostgreSQL is still starting up, 270 seconds until timeout') check_calls([(update_lock, True), (demote, False)]) self.p.time_in_state = lambda: 350 self.ha.fetch_node_status = get_node_status(reachable=False) # inaccessible, in_recovery self.assertEquals(self.ha.run_cycle(), 'master start has timed out, but continuing to wait because failover is not possible') check_calls([(update_lock, True), (demote, False)]) self.ha.fetch_node_status = get_node_status() # accessible, in_recovery self.assertEquals(self.ha.run_cycle(), 'stopped PostgreSQL because of startup timeout') check_calls([(update_lock, True), (demote, True)]) update_lock.return_value = False self.assertEquals(self.ha.run_cycle(), 'stopped PostgreSQL while starting up because leader key was lost') check_calls([(update_lock, True), (demote, True)]) self.ha.has_lock = false self.p.is_leader = false self.assertEquals(self.ha.run_cycle(), 'no action. i am a secondary and i am following a leader') check_calls([(update_lock, False), (demote, False)]) def test_manual_failover_while_starting(self): self.ha.has_lock = true self.p.check_for_startup = true f = Failover(0, self.p.name, '', None) self.ha.cluster = get_cluster_initialized_with_leader(f) self.ha.fetch_node_status = get_node_status() # accessible, in_recovery self.assertEquals(self.ha.run_cycle(), 'manual failover: demoting myself') @patch('patroni.ha.Ha.demote') def test_failover_immediately_on_zero_master_start_timeout(self, demote): self.p.is_running = false self.ha.cluster = get_cluster_initialized_with_leader(sync=(self.p.name, 'other')) self.ha.cluster.config.data['synchronous_mode'] = True self.ha.patroni.config.set_dynamic_configuration({'master_start_timeout': 0}) self.ha.has_lock = true self.ha.update_lock = true self.ha.fetch_node_status = get_node_status() # accessible, in_recovery self.assertEquals(self.ha.run_cycle(), 'stopped PostgreSQL to fail over after a crash') demote.assert_called_once() @patch('patroni.postgresql.Postgresql.follow') def test_demote_immediate(self, follow): self.ha.has_lock = true self.e.get_cluster = Mock(return_value=get_cluster_initialized_without_leader()) self.ha.demote('immediate') follow.assert_called_once_with(None) def test_process_sync_replication(self): self.ha.has_lock = true mock_set_sync = self.p.set_synchronous_standby = Mock() self.p.name = 'leader' # Test sync key removed when sync mode disabled self.ha.cluster = get_cluster_initialized_with_leader(sync=('leader', 'other')) with patch.object(self.ha.dcs, 'delete_sync_state') as mock_delete_sync: self.ha.run_cycle() mock_delete_sync.assert_called_once() mock_set_sync.assert_called_once_with(None) mock_set_sync.reset_mock() # Test sync key not touched when not there self.ha.cluster = get_cluster_initialized_with_leader() with patch.object(self.ha.dcs, 'delete_sync_state') as mock_delete_sync: self.ha.run_cycle() mock_delete_sync.assert_not_called() mock_set_sync.assert_called_once_with(None) mock_set_sync.reset_mock() self.ha.is_synchronous_mode = true # Test sync standby not touched when picking the same node self.p.pick_synchronous_standby = Mock(return_value=('other', True)) self.ha.cluster = get_cluster_initialized_with_leader(sync=('leader', 'other')) self.ha.run_cycle() mock_set_sync.assert_not_called() mock_set_sync.reset_mock() # Test sync standby is replaced when switching standbys self.p.pick_synchronous_standby = Mock(return_value=('other2', False)) self.ha.dcs.write_sync_state = Mock(return_value=True) self.ha.run_cycle() mock_set_sync.assert_called_once_with('other2') mock_set_sync.reset_mock() # Test sync standby is not disabled when updating dcs fails self.ha.dcs.write_sync_state = Mock(return_value=False) self.ha.run_cycle() mock_set_sync.assert_not_called() mock_set_sync.reset_mock() # Test changing sync standby self.ha.dcs.write_sync_state = Mock(return_value=True) self.ha.dcs.get_cluster = Mock(return_value=get_cluster_initialized_with_leader(sync=('leader', 'other'))) # self.ha.cluster = get_cluster_initialized_with_leader(sync=('leader', 'other')) self.p.pick_synchronous_standby = Mock(return_value=('other2', True)) self.ha.run_cycle() self.ha.dcs.get_cluster.assert_called_once() self.assertEquals(self.ha.dcs.write_sync_state.call_count, 2) # Test updating sync standby key failed due to race self.ha.dcs.write_sync_state = Mock(side_effect=[True, False]) self.ha.run_cycle() self.assertEquals(self.ha.dcs.write_sync_state.call_count, 2) # Test changing sync standby failed due to race self.ha.dcs.write_sync_state = Mock(return_value=True) self.ha.dcs.get_cluster = Mock(return_value=get_cluster_initialized_with_leader(sync=('somebodyelse', None))) self.ha.run_cycle() self.assertEquals(self.ha.dcs.write_sync_state.call_count, 1) # Test sync set to '*' when synchronous_mode_strict is enabled mock_set_sync.reset_mock() self.ha.is_synchronous_mode_strict = true self.p.pick_synchronous_standby = Mock(return_value=(None, False)) self.ha.run_cycle() mock_set_sync.assert_called_once_with('*') def test_sync_replication_become_master(self): self.ha.is_synchronous_mode = true mock_set_sync = self.p.set_synchronous_standby = Mock() self.p.is_leader = false self.p.set_role('replica') self.ha.has_lock = true mock_write_sync = self.ha.dcs.write_sync_state = Mock(return_value=True) self.p.name = 'leader' self.ha.cluster = get_cluster_initialized_with_leader(sync=('other', None)) # When we just became master nobody is sync self.assertEquals(self.ha.enforce_master_role('msg', 'promote msg'), 'promote msg') mock_set_sync.assert_called_once_with(None) mock_write_sync.assert_called_once_with('leader', None, index=0) mock_set_sync.reset_mock() # When we just became master nobody is sync self.p.set_role('replica') mock_write_sync.return_value = False self.assertTrue(self.ha.enforce_master_role('msg', 'promote msg') != 'promote msg') mock_set_sync.assert_not_called() def test_unhealthy_sync_mode(self): self.ha.is_synchronous_mode = true self.p.is_leader = false self.p.set_role('replica') self.p.name = 'other' self.ha.cluster = get_cluster_initialized_without_leader(sync=('leader', 'other2')) mock_write_sync = self.ha.dcs.write_sync_state = Mock(return_value=True) mock_acquire = self.ha.acquire_lock = Mock(return_value=True) mock_follow = self.p.follow = Mock() mock_promote = self.p.promote = Mock() # If we don't match the sync replica we are not allowed to acquire lock self.ha.run_cycle() mock_acquire.assert_not_called() mock_follow.assert_called_once() self.assertEquals(mock_follow.call_args[0][0], None) mock_write_sync.assert_not_called() mock_follow.reset_mock() # If we do match we will try to promote self.ha._is_healthiest_node = true self.ha.cluster = get_cluster_initialized_without_leader(sync=('leader', 'other')) self.ha.run_cycle() mock_acquire.assert_called_once() mock_follow.assert_not_called() mock_promote.assert_called_once() mock_write_sync.assert_called_once_with('other', None, index=0) def test_disable_sync_when_restarting(self): self.ha.is_synchronous_mode = true self.p.name = 'other' self.p.is_leader = false self.p.set_role('replica') mock_restart = self.p.restart = Mock(return_value=True) self.ha.cluster = get_cluster_initialized_with_leader(sync=('leader', 'other')) self.ha.touch_member = Mock(return_value=True) self.ha.dcs.get_cluster = Mock(side_effect=[ get_cluster_initialized_with_leader(sync=('leader', syncstandby)) for syncstandby in ['other', None]]) with patch('time.sleep') as mock_sleep: self.ha.restart({}) mock_restart.assert_called_once() mock_sleep.assert_called() # Restart is still called when DCS connection fails mock_restart.reset_mock() self.ha.dcs.get_cluster = Mock(side_effect=DCSError("foo")) self.ha.restart({}) mock_restart.assert_called_once() # We don't try to fetch the cluster state when touch_member fails mock_restart.reset_mock() self.ha.dcs.get_cluster.reset_mock() self.ha.touch_member = Mock(return_value=False) self.ha.restart({}) mock_restart.assert_called_once() self.ha.dcs.get_cluster.assert_not_called() def test_effective_tags(self): self.ha._disable_sync = True self.assertEquals(self.ha.get_effective_tags(), {'foo': 'bar', 'nosync': True}) self.ha._disable_sync = False self.assertEquals(self.ha.get_effective_tags(), {'foo': 'bar'}) def test_restore_cluster_config(self): self.ha.cluster.config.data.clear() self.ha.has_lock = true self.ha.cluster.is_unlocked = false self.assertEquals(self.ha.run_cycle(), 'no action. i am the leader with the lock') def test_watch(self): self.ha.cluster = get_cluster_initialized_with_leader() self.ha.watch(0) def test_wakup(self): self.ha.wakeup() def test_shutdown(self): self.p.is_running = false self.ha.has_lock = true self.ha.shutdown() @patch('time.sleep', Mock()) def test_leader_with_empty_directory(self): self.ha.cluster = get_cluster_initialized_with_leader() self.ha.has_lock = true self.p.data_directory_empty = true self.assertEquals(self.ha.run_cycle(), 'released leader key voluntarily as data dir empty and currently leader') self.assertEquals(self.p.role, 'uninitialized') # as has_lock is mocked out, we need to fake the leader key release self.ha.has_lock = false # will not say bootstrap from leader as replica can't self elect self.assertEquals(self.ha.run_cycle(), "trying to bootstrap from replica 'other'") def test_update_cluster_history(self): self.p.get_master_timeline = Mock(return_value=1) self.ha.has_lock = true self.ha.cluster.is_unlocked = false self.assertEquals(self.ha.run_cycle(), 'no action. i am the leader with the lock') patroni-1.4.2/tests/test_kubernetes.py000066400000000000000000000121371323411135200201130ustar00rootroot00000000000000import unittest from mock import Mock, patch from patroni.dcs.kubernetes import Kubernetes, KubernetesError, k8s_client, k8s_watch, RetryFailedError def mock_list_namespaced_config_map(self, *args, **kwargs): metadata = {'resource_version': '1', 'labels': {'f': 'b'}, 'name': 'test-config', 'annotations': {'initialize': '123', 'config': '{}'}} items = [k8s_client.V1ConfigMap(metadata=k8s_client.V1ObjectMeta(**metadata))] metadata.update({'name': 'test-leader', 'annotations': {'optime': '1234', 'leader': 'p-0', 'ttl': '30s'}}) items.append(k8s_client.V1ConfigMap(metadata=k8s_client.V1ObjectMeta(**metadata))) metadata.update({'name': 'test-failover', 'annotations': {'leader': 'p-0'}}) items.append(k8s_client.V1ConfigMap(metadata=k8s_client.V1ObjectMeta(**metadata))) metadata.update({'name': 'test-sync', 'annotations': {'leader': 'p-0'}}) items.append(k8s_client.V1ConfigMap(metadata=k8s_client.V1ObjectMeta(**metadata))) metadata = k8s_client.V1ObjectMeta(resource_version='1') return k8s_client.V1ConfigMapList(metadata=metadata, items=items) def mock_list_namespaced_pod(self, *args, **kwargs): metadata = k8s_client.V1ObjectMeta(resource_version='1', name='p-0', annotations={'status': '{}'}) items = [k8s_client.V1Pod(metadata=metadata)] return k8s_client.V1PodList(items=items) @patch.object(k8s_client.CoreV1Api, 'patch_namespaced_config_map', Mock()) @patch.object(k8s_client.CoreV1Api, 'create_namespaced_config_map', Mock()) class TestKubernetes(unittest.TestCase): @patch('kubernetes.config.load_kube_config', Mock()) @patch.object(k8s_client.CoreV1Api, 'list_namespaced_config_map', mock_list_namespaced_config_map) @patch.object(k8s_client.CoreV1Api, 'list_namespaced_pod', mock_list_namespaced_pod) def setUp(self): self.k = Kubernetes({'ttl': 30, 'scope': 'test', 'name': 'p-0', 'retry_timeout': 10, 'labels': {'f': 'b'}}) with patch('time.time', Mock(return_value=1)): self.k.get_cluster() @patch.object(k8s_client.CoreV1Api, 'list_namespaced_config_map', mock_list_namespaced_config_map) @patch.object(k8s_client.CoreV1Api, 'list_namespaced_pod', mock_list_namespaced_pod) def test_get_cluster(self): self.k.get_cluster() with patch.object(k8s_client.CoreV1Api, 'list_namespaced_pod', Mock(side_effect=Exception)): self.assertRaises(KubernetesError, self.k.get_cluster) @patch('kubernetes.config.load_kube_config', Mock()) @patch.object(k8s_client.CoreV1Api, 'create_namespaced_endpoints', Mock()) def test_update_leader(self): k = Kubernetes({'ttl': 30, 'scope': 'test', 'name': 'p-0', 'retry_timeout': 10, 'labels': {'f': 'b'}, 'use_endpoints': True, 'pod_ip': '10.0.0.0'}) self.assertIsNotNone(k.update_leader('123')) def test_take_leader(self): self.k.take_leader() self.k._leader_observed_record['leader'] = 'test' self.k.patch_or_create = Mock(return_value=False) self.k.take_leader() def test_manual_failover(self): with patch.object(k8s_client.CoreV1Api, 'patch_namespaced_config_map', Mock(side_effect=RetryFailedError(''))): self.k.manual_failover('foo', 'bar') def test_set_config_value(self): self.k.set_config_value('{}') @patch.object(k8s_client.CoreV1Api, 'patch_namespaced_pod', Mock(return_value=True)) def test_touch_member(self): self.k.touch_member({}) self.k._name = 'p-1' self.k.touch_member({'state': 'running', 'role': 'replica'}) self.k.touch_member({'state': 'stopped', 'role': 'master'}) def test_initialize(self): self.k.initialize() def test_delete_leader(self): self.k.delete_leader() def test_cancel_initialization(self): self.k.cancel_initialization() @patch.object(k8s_client.CoreV1Api, 'delete_collection_namespaced_config_map', Mock(side_effect=k8s_client.rest.ApiException(403, ''))) def test_delete_cluster(self): self.k.delete_cluster() @patch('kubernetes.config.load_kube_config', Mock()) @patch.object(k8s_client.CoreV1Api, 'create_namespaced_endpoints', Mock(side_effect=[k8s_client.rest.ApiException(502, ''), k8s_client.rest.ApiException(500, '')])) def test_delete_sync_state(self): k = Kubernetes({'ttl': 30, 'scope': 'test', 'name': 'p-0', 'retry_timeout': 10, 'labels': {'f': 'b'}, 'use_endpoints': True, 'pod_ip': '10.0.0.0'}) self.assertFalse(k.delete_sync_state()) def test_watch(self): self.k.set_ttl(10) self.k.watch(None, 0) self.k.watch(None, 0) with patch.object(k8s_watch.Watch, 'stream', Mock(side_effect=[Exception, [], KeyboardInterrupt, [{'raw_object': {'metadata': {'resourceVersion': '2'}}}]])): self.assertFalse(self.k.watch('1', 2)) self.assertRaises(KeyboardInterrupt, self.k.watch, '1', 2) self.assertTrue(self.k.watch('1', 2)) def test_set_history_value(self): self.k.set_history_value('{}') patroni-1.4.2/tests/test_patroni.py000066400000000000000000000137401323411135200174210ustar00rootroot00000000000000import etcd import signal import sys import time import unittest from mock import Mock, PropertyMock, patch from patroni.api import RestApiServer from patroni.async_executor import AsyncExecutor from patroni.dcs.etcd import Client from patroni.exceptions import DCSError from patroni import Patroni, main as _main, patroni_main from six.moves import BaseHTTPServer from test_etcd import SleepException, etcd_read, etcd_write from test_postgresql import Postgresql, psycopg2_connect, MockPostmaster class MockFrozenImporter(object): toc = set(['patroni.dcs.etcd']) @patch('time.sleep', Mock()) @patch('subprocess.call', Mock(return_value=0)) @patch('psycopg2.connect', psycopg2_connect) @patch.object(Postgresql, 'write_pg_hba', Mock()) @patch.object(Postgresql, '_write_postgresql_conf', Mock()) @patch.object(Postgresql, 'write_recovery_conf', Mock()) @patch.object(Postgresql, 'is_running', Mock(return_value=MockPostmaster())) @patch.object(Postgresql, 'call_nowait', Mock()) @patch.object(BaseHTTPServer.HTTPServer, '__init__', Mock()) @patch.object(AsyncExecutor, 'run', Mock()) @patch.object(etcd.Client, 'write', etcd_write) @patch.object(etcd.Client, 'read', etcd_read) class TestPatroni(unittest.TestCase): @patch('pkgutil.get_importer', Mock(return_value=MockFrozenImporter())) @patch('sys.frozen', Mock(return_value=True), create=True) @patch.object(etcd.Client, 'read', etcd_read) def setUp(self): RestApiServer._BaseServer__is_shut_down = Mock() RestApiServer._BaseServer__shutdown_request = True RestApiServer.socket = 0 with patch.object(Client, 'machines') as mock_machines: mock_machines.__get__ = Mock(return_value=['http://remotehost:2379']) sys.argv = ['patroni.py', 'postgres0.yml'] self.p = Patroni() @patch('patroni.dcs.AbstractDCS.get_cluster', Mock(side_effect=[None, DCSError('foo'), None])) def test_load_dynamic_configuration(self): self.p.config._dynamic_configuration = {} self.p.load_dynamic_configuration() self.p.load_dynamic_configuration() @patch('time.sleep', Mock(side_effect=SleepException)) @patch.object(etcd.Client, 'delete', Mock()) @patch.object(Client, 'machines') def test_patroni_patroni_main(self, mock_machines): with patch('subprocess.call', Mock(return_value=1)): sys.argv = ['patroni.py', 'postgres0.yml'] mock_machines.__get__ = Mock(return_value=['http://remotehost:2379']) with patch.object(Patroni, 'run', Mock(side_effect=SleepException)): self.assertRaises(SleepException, patroni_main) with patch.object(Patroni, 'run', Mock(side_effect=KeyboardInterrupt())): with patch('patroni.ha.Ha.is_paused', Mock(return_value=True)): patroni_main() @patch('os.getpid') @patch('subprocess.Popen', ) @patch('patroni.patroni_main', Mock()) def test_patroni_main(self, mock_popen, mock_getpid): mock_getpid.return_value = 2 _main() with patch('sys.frozen', Mock(return_value=True), create=True): sys.argv = ['/patroni', 'pg_ctl_start', 'postgres', '-D', '/data', '--max_connections=100'] _main() mock_getpid.return_value = 1 def mock_signal(signo, handler): handler(signo, None) with patch('signal.signal', mock_signal): with patch('os.waitpid', Mock(side_effect=[(1, 0), (0, 0)])): _main() with patch('os.waitpid', Mock(side_effect=OSError)): _main() ref = {'passtochild': lambda signo, stack_frame: 0} def mock_sighup(signo, handler): if signo == signal.SIGHUP: ref['passtochild'] = handler def mock_wait(): ref['passtochild'](0, None) mock_popen.return_value.wait = mock_wait with patch('signal.signal', mock_sighup), patch('os.kill', Mock()): self.assertIsNone(_main()) @patch('patroni.config.Config.save_cache', Mock()) @patch('patroni.config.Config.reload_local_configuration', Mock(return_value=True)) @patch.object(Postgresql, 'state', PropertyMock(return_value='running')) @patch.object(Postgresql, 'data_directory_empty', Mock(return_value=False)) def test_run(self): self.p.postgresql.set_role('replica') self.p.sighup_handler() self.p.ha.dcs.watch = Mock(side_effect=SleepException) self.p.api.start = Mock() self.p.config._dynamic_configuration = {} self.assertRaises(SleepException, self.p.run) with patch('patroni.config.Config.set_dynamic_configuration', Mock(return_value=True)): self.assertRaises(SleepException, self.p.run) with patch('patroni.postgresql.Postgresql.data_directory_empty', Mock(return_value=False)): self.assertRaises(SleepException, self.p.run) def test_sigterm_handler(self): self.assertRaises(SystemExit, self.p.sigterm_handler) def test_schedule_next_run(self): self.p.ha.dcs.watch = Mock(return_value=True) self.p.schedule_next_run() self.p.next_run = time.time() - self.p.dcs.loop_wait - 1 self.p.schedule_next_run() def test_noloadbalance(self): self.p.tags['noloadbalance'] = True self.assertTrue(self.p.noloadbalance) def test_nofailover(self): self.p.tags['nofailover'] = True self.assertTrue(self.p.nofailover) self.p.tags['nofailover'] = None self.assertFalse(self.p.nofailover) def test_replicatefrom(self): self.assertIsNone(self.p.replicatefrom) self.p.tags['replicatefrom'] = 'foo' self.assertEqual(self.p.replicatefrom, 'foo') def test_reload_config(self): self.p.reload_config() self.p.get_tags = Mock(side_effect=Exception) self.p.reload_config() def test_nosync(self): self.p.tags['nosync'] = True self.assertTrue(self.p.nosync) self.p.tags['nosync'] = None self.assertFalse(self.p.nosync) patroni-1.4.2/tests/test_postgresql.py000066400000000000000000001266551323411135200201620ustar00rootroot00000000000000import datetime import mock # for the mock.call method, importing it without a namespace breaks python3 import os import psycopg2 import shutil import subprocess import unittest from mock import Mock, MagicMock, PropertyMock, patch, mock_open from patroni.async_executor import CriticalTask from patroni.dcs import Cluster, Leader, Member, SyncState from patroni.exceptions import PostgresConnectionException, PostgresException from patroni.postgresql import Postgresql, STATE_REJECT, STATE_NO_RESPONSE from patroni.postmaster import PostmasterProcess from patroni.utils import RetryFailedError from six.moves import builtins from threading import Thread class MockCursor(object): def __init__(self, connection): self.connection = connection self.closed = False self.rowcount = 0 self.results = [] def execute(self, sql, *params): if sql.startswith('blabla'): raise psycopg2.ProgrammingError() elif sql == 'CHECKPOINT': raise psycopg2.OperationalError() elif sql.startswith('RetryFailedError'): raise RetryFailedError('retry') elif sql.startswith('SELECT slot_name'): self.results = [('blabla',), ('foobar',)] elif sql.startswith('SELECT CASE WHEN pg_is_in_recovery()'): self.results = [(1, 2)] elif sql.startswith('SELECT pg_is_in_recovery()'): self.results = [(False, 2)] elif sql.startswith('WITH replication_info AS ('): replication_info = '[{"application_name":"walreceiver","client_addr":"1.2.3.4",' +\ '"state":"streaming","sync_state":"async","sync_priority":0}]' self.results = [('', 0, '', '', '', '', False, replication_info)] elif sql.startswith('SELECT name, setting'): self.results = [('wal_segment_size', '2048', '8kB', 'integer', 'internal'), ('search_path', 'public', None, 'string', 'user'), ('port', '5433', None, 'integer', 'postmaster'), ('listen_addresses', '*', None, 'string', 'postmaster'), ('autovacuum', 'on', None, 'bool', 'sighup'), ('unix_socket_directories', '/tmp', None, 'string', 'postmaster')] elif sql.startswith('IDENTIFY_SYSTEM'): self.results = [('1', 2, '0/402EEC0', '')] elif sql.startswith('SELECT isdir, modification'): self.results = [(False, datetime.datetime.now())] elif sql.startswith('SELECT pg_read_file'): self.results = [('1\t0/40159C0\tno recovery target specified\n\n' + '2\t1/40159C0\tno recovery target specified\n',)] elif sql.startswith('TIMELINE_HISTORY '): self.results = [('', b'x\t0/40159C0\tno recovery target specified\n\n' + b'1\t0/40159C0\tno recovery target specified\n\n' + b'2\t0/402DD98\tno recovery target specified\n\n' + b'3\t0/403DD98\tno recovery target specified\n')] else: self.results = [(None, None, None, None, None, None, None, None, None, None)] def fetchone(self): return self.results[0] def fetchall(self): return self.results def __iter__(self): for i in self.results: yield i def __enter__(self): return self def __exit__(self, *args): pass class MockConnect(object): server_version = 99999 autocommit = False closed = 0 def cursor(self): return MockCursor(self) def __enter__(self): return self def __exit__(self, *args): pass @staticmethod def close(): pass class MockPostmaster(object): def __init__(self, is_running=True, is_single_master=False): self.is_running = Mock(return_value=is_running) self.is_single_master = Mock(return_value=is_single_master) self.wait_for_user_backends_to_close = Mock() self.signal_stop = Mock(return_value=None) self.wait = Mock() def pg_controldata_string(*args, **kwargs): return b""" pg_control version number: 942 Catalog version number: 201509161 Database system identifier: 6200971513092291716 Database cluster state: shut down in recovery pg_control last modified: Fri Oct 2 10:57:06 2015 Latest checkpoint location: 0/30000C8 Prior checkpoint location: 0/2000060 Latest checkpoint's REDO location: 0/3000090 Latest checkpoint's REDO WAL file: 000000020000000000000003 Latest checkpoint's TimeLineID: 2 Latest checkpoint's PrevTimeLineID: 2 Latest checkpoint's full_page_writes: on Latest checkpoint's NextXID: 0/943 Latest checkpoint's NextOID: 24576 Latest checkpoint's NextMultiXactId: 1 Latest checkpoint's NextMultiOffset: 0 Latest checkpoint's oldestXID: 931 Latest checkpoint's oldestXID's DB: 1 Latest checkpoint's oldestActiveXID: 943 Latest checkpoint's oldestMultiXid: 1 Latest checkpoint's oldestMulti's DB: 1 Latest checkpoint's oldestCommitTs: 0 Latest checkpoint's newestCommitTs: 0 Time of latest checkpoint: Fri Oct 2 10:56:54 2015 Fake LSN counter for unlogged rels: 0/1 Minimum recovery ending location: 0/30241F8 Min recovery ending loc's timeline: 2 Backup start location: 0/0 Backup end location: 0/0 End-of-backup record required: no wal_level setting: hot_standby Current wal_log_hints setting: on Current max_connections setting: 100 Current max_worker_processes setting: 8 Current max_prepared_xacts setting: 0 Current max_locks_per_xact setting: 64 Current track_commit_timestamp setting: off Maximum data alignment: 8 Database block size: 8192 Blocks per segment of large relation: 131072 WAL block size: 8192 Bytes per WAL segment: 16777216 Maximum length of identifiers: 64 Maximum columns in an index: 32 Maximum size of a TOAST chunk: 1996 Size of a large-object chunk: 2048 Date/time type storage: 64-bit integers Float4 argument passing: by value Float8 argument passing: by value Data page checksum version: 0 """ def psycopg2_connect(*args, **kwargs): return MockConnect() @patch('subprocess.call', Mock(return_value=0)) @patch('psycopg2.connect', psycopg2_connect) class TestPostgresql(unittest.TestCase): _PARAMETERS = {'wal_level': 'hot_standby', 'max_replication_slots': 5, 'f.oo': 'bar', 'search_path': 'public', 'hot_standby': 'on', 'max_wal_senders': 5, 'wal_keep_segments': 8, 'wal_log_hints': 'on', 'max_locks_per_transaction': 64, 'max_worker_processes': 8, 'max_connections': 100, 'max_prepared_transactions': 0, 'track_commit_timestamp': 'off', 'unix_socket_directories': '/tmp'} @patch('subprocess.call', Mock(return_value=0)) @patch('psycopg2.connect', psycopg2_connect) @patch('os.rename', Mock()) @patch.object(Postgresql, 'get_major_version', Mock(return_value=90600)) @patch.object(Postgresql, 'is_running', Mock(return_value=True)) def setUp(self): self.data_dir = 'data/test0' self.config_dir = self.data_dir if not os.path.exists(self.data_dir): os.makedirs(self.data_dir) self.p = Postgresql({'name': 'test0', 'scope': 'batman', 'data_dir': self.data_dir, 'config_dir': self.config_dir, 'retry_timeout': 10, 'pgpass': '/tmp/pgpass0', 'listen': '127.0.0.2, 127.0.0.3:5432', 'connect_address': '127.0.0.2:5432', 'authentication': {'superuser': {'username': 'test', 'password': 'test'}, 'replication': {'username': 'replicator', 'password': 'rep-pass'}}, 'remove_data_directory_on_rewind_failure': True, 'use_pg_rewind': True, 'pg_ctl_timeout': 'bla', 'parameters': self._PARAMETERS, 'recovery_conf': {'foo': 'bar'}, 'pg_hba': ['host all all 0.0.0.0/0 md5'], 'callbacks': {'on_start': 'true', 'on_stop': 'true', 'on_reload': 'true', 'on_restart': 'true', 'on_role_change': 'true'}}) self.p._callback_executor = Mock() self.leadermem = Member(0, 'leader', 28, {'conn_url': 'postgres://replicator:rep-pass@127.0.0.1:5435/postgres'}) self.leader = Leader(-1, 28, self.leadermem) self.other = Member(0, 'test-1', 28, {'conn_url': 'postgres://replicator:rep-pass@127.0.0.1:5433/postgres', 'tags': {'replicatefrom': 'leader'}}) self.me = Member(0, 'test0', 28, {'conn_url': 'postgres://replicator:rep-pass@127.0.0.1:5434/postgres'}) def tearDown(self): shutil.rmtree('data') def test_get_initdb_options(self): self.assertEquals(self.p.get_initdb_options([{'encoding': 'UTF8'}, 'data-checksums']), ['--encoding=UTF8', '--data-checksums']) self.assertRaises(Exception, self.p.get_initdb_options, [{'pgdata': 'bar'}]) self.assertRaises(Exception, self.p.get_initdb_options, [{'foo': 'bar', 1: 2}]) self.assertRaises(Exception, self.p.get_initdb_options, [1]) @patch('os.path.exists', Mock(return_value=True)) @patch('os.unlink', Mock()) def test_delete_trigger_file(self): self.p.delete_trigger_file() @patch('subprocess.Popen') @patch.object(Postgresql, 'wait_for_startup') @patch.object(Postgresql, 'wait_for_port_open') @patch.object(Postgresql, 'is_running') def test_start(self, mock_is_running, mock_wait_for_port_open, mock_wait_for_startup, mock_popen): mock_is_running.return_value = MockPostmaster() mock_wait_for_port_open.return_value = True mock_wait_for_startup.return_value = False mock_popen.return_value.stdout.readline.return_value = '123' self.assertTrue(self.p.start()) mock_is_running.return_value = None mock_postmaster = MockPostmaster() with patch.object(PostmasterProcess, 'start', return_value=mock_postmaster): pg_conf = os.path.join(self.data_dir, 'postgresql.conf') open(pg_conf, 'w').close() self.assertFalse(self.p.start(task=CriticalTask())) with open(pg_conf) as f: lines = f.readlines() self.assertTrue("f.oo = 'bar'\n" in lines) mock_wait_for_startup.return_value = None self.assertFalse(self.p.start(10)) self.assertIsNone(self.p.start()) mock_wait_for_port_open.return_value = False self.assertFalse(self.p.start()) task = CriticalTask() task.cancel() self.assertFalse(self.p.start(task=task)) self.p.cancel() self.assertFalse(self.p.start()) @patch.object(Postgresql, 'pg_isready') @patch('patroni.postgresql.polling_loop', Mock(return_value=range(1))) def test_wait_for_port_open(self, mock_pg_isready): mock_pg_isready.return_value = STATE_NO_RESPONSE mock_postmaster = MockPostmaster(is_running=False) # No pid file and postmaster death self.assertFalse(self.p.wait_for_port_open(mock_postmaster, 1)) mock_postmaster.is_running.return_value = True # timeout self.assertFalse(self.p.wait_for_port_open(mock_postmaster, 1)) # pg_isready failure mock_pg_isready.return_value = 'garbage' self.assertTrue(self.p.wait_for_port_open(mock_postmaster, 1)) # cancelled self.p.cancel() self.assertFalse(self.p.wait_for_port_open(mock_postmaster, 1)) @patch('time.sleep', Mock()) @patch.object(Postgresql, 'is_running') @patch.object(Postgresql, '_wait_for_connection_close', Mock()) def test_stop(self, mock_is_running): # Postmaster is not running mock_callback = Mock() mock_is_running.return_value = None self.assertTrue(self.p.stop(on_safepoint=mock_callback)) mock_callback.assert_called() # Is running, stopped successfully mock_is_running.return_value = mock_postmaster = MockPostmaster() mock_callback.reset_mock() self.assertTrue(self.p.stop(on_safepoint=mock_callback)) mock_callback.assert_called() mock_postmaster.signal_stop.assert_called() # Stop signal failed mock_postmaster.signal_stop.return_value = False self.assertFalse(self.p.stop()) # Stop signal failed to find process mock_postmaster.signal_stop.return_value = True mock_callback.reset_mock() self.assertTrue(self.p.stop(on_safepoint=mock_callback)) mock_callback.assert_called() def test_restart(self): self.p.start = Mock(return_value=False) self.assertFalse(self.p.restart()) self.assertEquals(self.p.state, 'restart failed (restarting)') @patch.object(builtins, 'open', MagicMock()) def test_write_pgpass(self): self.p.write_pgpass({'host': 'localhost', 'port': '5432', 'user': 'foo'}) self.p.write_pgpass({'host': 'localhost', 'port': '5432', 'user': 'foo', 'password': 'bar'}) def test_checkpoint(self): with patch.object(MockCursor, 'fetchone', Mock(return_value=(True, ))): self.assertEquals(self.p.checkpoint({'user': 'postgres'}), 'is_in_recovery=true') with patch.object(MockCursor, 'execute', Mock(return_value=None)): self.assertIsNone(self.p.checkpoint()) self.assertEquals(self.p.checkpoint(), 'not accessible or not healty') @patch.object(Postgresql, 'cancellable_subprocess_call') @patch('patroni.postgresql.Postgresql.write_pgpass', MagicMock(return_value=dict())) def test_pg_rewind(self, mock_cancellable_subprocess_call): r = {'user': '', 'host': '', 'port': '', 'database': '', 'password': ''} mock_cancellable_subprocess_call.return_value = 0 self.assertTrue(self.p.pg_rewind(r)) mock_cancellable_subprocess_call.side_effect = OSError self.assertFalse(self.p.pg_rewind(r)) def test_check_recovery_conf(self): self.p.write_recovery_conf({'primary_conninfo': 'foo'}) self.assertFalse(self.p.check_recovery_conf(None)) self.p.write_recovery_conf({}) self.assertTrue(self.p.check_recovery_conf(None)) @patch.object(Postgresql, 'start', Mock()) @patch.object(Postgresql, 'can_rewind', PropertyMock(return_value=True)) def test__get_local_timeline_lsn(self): self.p.trigger_check_diverged_lsn() with patch.object(Postgresql, 'controldata', Mock(return_value={'Database cluster state': 'shut down in recovery', 'Minimum recovery ending location': '0/0', "Min recovery ending loc's timeline": '0'})): self.p.rewind_needed_and_possible(self.leader) with patch.object(Postgresql, 'is_running', Mock(return_value=True)): with patch.object(MockCursor, 'fetchone', Mock(side_effect=[(False, ), Exception])): self.p.rewind_needed_and_possible(self.leader) @patch.object(Postgresql, 'start', Mock()) @patch.object(Postgresql, 'can_rewind', PropertyMock(return_value=True)) @patch.object(Postgresql, '_get_local_timeline_lsn', Mock(return_value=(2, '40159C1'))) @patch.object(Postgresql, 'check_leader_is_not_in_recovery') def test__check_timeline_and_lsn(self, mock_check_leader_is_not_in_recovery): mock_check_leader_is_not_in_recovery.return_value = False self.p.trigger_check_diverged_lsn() self.assertFalse(self.p.rewind_needed_and_possible(self.leader)) mock_check_leader_is_not_in_recovery.return_value = True self.assertFalse(self.p.rewind_needed_and_possible(self.leader)) self.p.trigger_check_diverged_lsn() with patch('psycopg2.connect', Mock(side_effect=Exception)): self.assertFalse(self.p.rewind_needed_and_possible(self.leader)) self.p.trigger_check_diverged_lsn() with patch.object(MockCursor, 'fetchone', Mock(side_effect=[('', 2, '0/0'), ('', b'3\t0/40159C0\tn\n')])): self.assertFalse(self.p.rewind_needed_and_possible(self.leader)) self.p.trigger_check_diverged_lsn() with patch.object(MockCursor, 'fetchone', Mock(return_value=('', 1, '0/0'))): with patch.object(Postgresql, '_get_local_timeline_lsn', Mock(return_value=(1, '0/0'))): self.assertFalse(self.p.rewind_needed_and_possible(self.leader)) self.p.trigger_check_diverged_lsn() self.assertTrue(self.p.rewind_needed_and_possible(self.leader)) @patch.object(MockCursor, 'fetchone', Mock(side_effect=[(True,), Exception])) def test_check_leader_is_not_in_recovery(self): self.p.check_leader_is_not_in_recovery() self.p.check_leader_is_not_in_recovery() @patch.object(Postgresql, 'cancellable_subprocess_call', Mock(return_value=0)) @patch.object(Postgresql, 'checkpoint', side_effect=['', '1']) @patch.object(Postgresql, 'stop', Mock(return_value=False)) @patch.object(Postgresql, 'start', Mock()) def test_rewind(self, mock_checkpoint): self.p.rewind(self.leader) with patch.object(Postgresql, 'pg_rewind', Mock(return_value=False)): mock_checkpoint.side_effect = ['1', '', '', ''] self.p.rewind(self.leader) self.p.rewind(self.leader) with patch.object(Postgresql, 'check_leader_is_not_in_recovery', Mock(return_value=False)): self.p.rewind(self.leader) self.p.config['remove_data_directory_on_rewind_failure'] = False self.p.trigger_check_diverged_lsn() self.p.rewind(self.leader) with patch.object(Postgresql, 'is_running', Mock(return_value=True)): self.p.rewind(self.leader) self.p.is_leader = Mock(return_value=False) self.p.rewind(self.leader) @patch.object(Postgresql, 'is_running', Mock(return_value=False)) @patch.object(Postgresql, 'start', Mock()) def test_follow(self): self.p.follow(None) @patch('subprocess.check_output', Mock(return_value=0, side_effect=pg_controldata_string)) def test_can_rewind(self): with patch('subprocess.call', MagicMock(return_value=1)): self.assertFalse(self.p.can_rewind) with patch('subprocess.call', side_effect=OSError): self.assertFalse(self.p.can_rewind) with patch.object(Postgresql, 'controldata', Mock(return_value={'wal_log_hints setting': 'on'})): self.assertTrue(self.p.can_rewind) self.p.config['use_pg_rewind'] = False self.assertFalse(self.p.can_rewind) @patch('time.sleep', Mock()) @patch.object(Postgresql, 'cancellable_subprocess_call') @patch.object(Postgresql, 'remove_data_directory', Mock(return_value=True)) def test_create_replica(self, mock_cancellable_subprocess_call): self.p.delete_trigger_file = Mock(side_effect=OSError) self.p.config['create_replica_method'] = ['wale', 'basebackup'] self.p.config['wale'] = {'command': 'foo'} mock_cancellable_subprocess_call.return_value = 0 self.assertEquals(self.p.create_replica(self.leader), 0) del self.p.config['wale'] self.assertEquals(self.p.create_replica(self.leader), 0) mock_cancellable_subprocess_call.return_value = 1 self.assertEquals(self.p.create_replica(self.leader), 1) mock_cancellable_subprocess_call.side_effect = Exception('foo') self.assertEquals(self.p.create_replica(self.leader), 1) mock_cancellable_subprocess_call.side_effect = [1, 0] self.assertEquals(self.p.create_replica(self.leader), 0) mock_cancellable_subprocess_call.side_effect = [Exception(), 0] self.assertEquals(self.p.create_replica(self.leader), 0) self.p.cancel() self.assertEquals(self.p.create_replica(self.leader), 1) def test_basebackup(self): self.p.cancel() self.p.basebackup(None, None) @patch.object(Postgresql, 'is_running', Mock(return_value=True)) def test_sync_replication_slots(self): self.p.start() cluster = Cluster(True, None, self.leader, 0, [self.me, self.other, self.leadermem], None, None, None) with mock.patch('patroni.postgresql.Postgresql._query', Mock(side_effect=psycopg2.OperationalError)): self.p.sync_replication_slots(cluster) self.p.sync_replication_slots(cluster) with mock.patch('patroni.postgresql.Postgresql.role', new_callable=PropertyMock(return_value='replica')): self.p.sync_replication_slots(cluster) with mock.patch('patroni.postgresql.logger.error', new_callable=Mock()) as errorlog_mock: self.p.query = Mock() alias1 = Member(0, 'test-3', 28, {'conn_url': 'postgres://replicator:rep-pass@127.0.0.1:5436/postgres'}) alias2 = Member(0, 'test.3', 28, {'conn_url': 'postgres://replicator:rep-pass@127.0.0.1:5436/postgres'}) cluster.members.extend([alias1, alias2]) self.p.sync_replication_slots(cluster) errorlog_mock.assert_called_once() assert "test-3" in errorlog_mock.call_args[0][1] assert "test.3" in errorlog_mock.call_args[0][1] @patch.object(MockCursor, 'execute', Mock(side_effect=psycopg2.OperationalError)) def test__query(self): self.assertRaises(PostgresConnectionException, self.p._query, 'blabla') self.p._state = 'restarting' self.assertRaises(RetryFailedError, self.p._query, 'blabla') def test_query(self): self.p.query('select 1') self.assertRaises(PostgresConnectionException, self.p.query, 'RetryFailedError') self.assertRaises(psycopg2.ProgrammingError, self.p.query, 'blabla') @patch.object(Postgresql, 'pg_isready', Mock(return_value=STATE_REJECT)) def test_is_leader(self): self.assertTrue(self.p.is_leader()) self.p.reset_cluster_info_state() with patch.object(Postgresql, '_query', Mock(side_effect=RetryFailedError(''))): self.assertRaises(PostgresConnectionException, self.p.is_leader) def test_reload(self): self.assertTrue(self.p.reload()) @patch.object(Postgresql, 'is_running') def test_is_healthy(self, mock_is_running): mock_is_running.return_value = True self.assertTrue(self.p.is_healthy()) mock_is_running.return_value = False self.assertFalse(self.p.is_healthy()) def test_promote(self): self.p.set_role('replica') self.assertIsNone(self.p.promote(0)) self.assertTrue(self.p.promote(0)) def test_timeline_wal_position(self): self.assertEquals(self.p.timeline_wal_position(), (1, 2)) Thread(target=self.p.timeline_wal_position).start() @patch.object(PostmasterProcess, 'from_pidfile') def test_is_running(self, mock_frompidfile): # Cached postmaster running mock_postmaster = self.p._postmaster_proc = MockPostmaster() self.assertEquals(self.p.is_running(), mock_postmaster) # Cached postmaster not running, no postmaster running mock_postmaster.is_running.return_value = False mock_frompidfile.return_value = None self.assertEquals(self.p.is_running(), None) self.assertEquals(self.p._postmaster_proc, None) # No cached postmaster, postmaster running mock_frompidfile.return_value = mock_postmaster2 = MockPostmaster() self.assertEquals(self.p.is_running(), mock_postmaster2) self.assertEquals(self.p._postmaster_proc, mock_postmaster2) @patch('shlex.split', Mock(side_effect=OSError)) def test_call_nowait(self): self.p.set_role('replica') self.assertIsNone(self.p.call_nowait('on_start')) self.p.bootstrapping = True self.assertIsNone(self.p.call_nowait('on_start')) def test_non_existing_callback(self): self.assertFalse(self.p.call_nowait('foobar')) @patch.object(Postgresql, 'is_running', Mock(return_value=MockPostmaster())) def test_is_leader_exception(self): self.p.start() self.p.query = Mock(side_effect=psycopg2.OperationalError("not supported")) self.assertTrue(self.p.stop()) @patch('os.rename', Mock()) @patch('os.path.isdir', Mock(return_value=True)) def test_move_data_directory(self): self.p.move_data_directory() with patch('os.rename', Mock(side_effect=OSError)): self.p.move_data_directory() @patch.object(Postgresql, 'is_running', Mock(return_value=True)) def test_bootstrap(self): with patch('subprocess.call', Mock(return_value=1)): self.assertFalse(self.p.bootstrap({})) config = {'users': {'replicator': {'password': 'rep-pass', 'options': ['replication']}}} self.p.bootstrap(config) with open(os.path.join(self.config_dir, 'pg_hba.conf')) as f: lines = f.readlines() self.assertTrue('host all all 0.0.0.0/0 md5\n' in lines) self.p.config.pop('pg_hba') config.update({'post_init': '/bin/false', 'pg_hba': ['host replication replicator 127.0.0.1/32 md5', 'hostssl all all 0.0.0.0/0 md5', 'host all all 0.0.0.0/0 md5']}) self.p.bootstrap(config) with open(os.path.join(self.data_dir, 'pg_hba.conf')) as f: lines = f.readlines() self.assertTrue('host replication replicator 127.0.0.1/32 md5\n' in lines) @patch.object(Postgresql, 'cancellable_subprocess_call') def test_custom_bootstrap(self, mock_cancellable_subprocess_call): config = {'method': 'foo', 'foo': {'command': 'bar'}} mock_cancellable_subprocess_call.return_value = 1 self.assertFalse(self.p.bootstrap(config)) mock_cancellable_subprocess_call.return_value = 0 with patch('subprocess.Popen', Mock(side_effect=Exception("42"))),\ patch('os.path.isfile', Mock(return_value=True)),\ patch('os.unlink', Mock()),\ patch.object(Postgresql, 'save_configuration_files', Mock()),\ patch.object(Postgresql, 'restore_configuration_files', Mock()),\ patch.object(Postgresql, 'write_recovery_conf', Mock()): with self.assertRaises(Exception) as e: self.p.bootstrap(config) self.assertEqual(str(e.exception), '42') config['foo']['recovery_conf'] = {'foo': 'bar'} with self.assertRaises(Exception) as e: self.p.bootstrap(config) self.assertEqual(str(e.exception), '42') mock_cancellable_subprocess_call.side_effect = Exception self.assertFalse(self.p.bootstrap(config)) @patch('time.sleep', Mock()) @patch('os.unlink', Mock()) @patch.object(Postgresql, 'run_bootstrap_post_init', Mock(return_value=True)) @patch.object(Postgresql, '_custom_bootstrap', Mock(return_value=True)) @patch.object(Postgresql, 'start', Mock(return_value=True)) def test_post_bootstrap(self): config = {'method': 'foo', 'foo': {'command': 'bar'}} self.p.bootstrap(config) task = CriticalTask() with patch.object(Postgresql, 'create_or_update_role', Mock(side_effect=Exception)): self.p.post_bootstrap({}, task) self.assertFalse(task.result) self.p.config.pop('pg_hba') self.p.post_bootstrap({}, task) self.assertTrue(task.result) self.p.bootstrap(config) self.p.set_state('stopped') self.p.reload_config({'authentication': {'superuser': {'username': 'p', 'password': 'p'}, 'replication': {'username': 'r', 'password': 'r'}}, 'listen': '*', 'retry_timeout': 10, 'parameters': {'hba_file': 'foo'}}) with patch.object(Postgresql, 'restart', Mock()) as mock_restart: self.p.post_bootstrap({}, task) mock_restart.assert_called_once() @patch.object(Postgresql, 'cancellable_subprocess_call') def test_run_bootstrap_post_init(self, mock_cancellable_subprocess_call): mock_cancellable_subprocess_call.return_value = 1 self.assertFalse(self.p.run_bootstrap_post_init({'post_init': '/bin/false'})) mock_cancellable_subprocess_call.return_value = 0 self.p._superuser.pop('username') self.assertTrue(self.p.run_bootstrap_post_init({'post_init': '/bin/false'})) mock_cancellable_subprocess_call.assert_called() args, kwargs = mock_cancellable_subprocess_call.call_args self.assertTrue('PGPASSFILE' in kwargs['env']) self.assertEquals(args[0], ['/bin/false', 'postgres://127.0.0.2:5432/postgres']) mock_cancellable_subprocess_call.reset_mock() self.p._local_address.pop('host') self.assertTrue(self.p.run_bootstrap_post_init({'post_init': '/bin/false'})) mock_cancellable_subprocess_call.assert_called() self.assertEquals(mock_cancellable_subprocess_call.call_args[0][0], ['/bin/false', 'postgres://:5432/postgres']) mock_cancellable_subprocess_call.side_effect = OSError self.assertFalse(self.p.run_bootstrap_post_init({'post_init': '/bin/false'})) @patch('patroni.postgresql.Postgresql.create_replica', Mock(return_value=0)) def test_clone(self): self.p.clone(self.leader) @patch('os.listdir', Mock(return_value=['recovery.conf'])) @patch('os.path.exists', Mock(return_value=True)) def test_get_postgres_role_from_data_directory(self): self.assertEquals(self.p.get_postgres_role_from_data_directory(), 'replica') def test_remove_data_directory(self): self.p.remove_data_directory() open(self.data_dir, 'w').close() self.p.remove_data_directory() os.symlink('unexisting', self.data_dir) with patch('os.unlink', Mock(side_effect=OSError)): self.p.remove_data_directory() self.p.remove_data_directory() @patch('patroni.postgresql.Postgresql._version_file_exists', Mock(return_value=True)) def test_controldata(self): with patch('subprocess.check_output', Mock(return_value=0, side_effect=pg_controldata_string)): data = self.p.controldata() self.assertEquals(len(data), 50) self.assertEquals(data['Database cluster state'], 'shut down in recovery') self.assertEquals(data['wal_log_hints setting'], 'on') self.assertEquals(int(data['Database block size']), 8192) with patch('subprocess.check_output', Mock(side_effect=subprocess.CalledProcessError(1, ''))): self.assertEquals(self.p.controldata(), {}) @patch('patroni.postgresql.Postgresql._version_file_exists', Mock(return_value=True)) @patch('subprocess.check_output', MagicMock(return_value=0, side_effect=pg_controldata_string)) def test_sysid(self): self.assertEqual(self.p.sysid, "6200971513092291716") @patch('os.path.isfile', Mock(return_value=True)) @patch('shutil.copy', Mock(side_effect=IOError)) def test_save_configuration_files(self): self.p.save_configuration_files() @patch('os.path.isfile', Mock(side_effect=[False, True])) @patch('shutil.copy', Mock(side_effect=IOError)) def test_restore_configuration_files(self): self.p.restore_configuration_files() def test_can_create_replica_without_replication_connection(self): self.p.config['create_replica_method'] = [] self.assertFalse(self.p.can_create_replica_without_replication_connection()) self.p.config['create_replica_method'] = ['wale', 'basebackup'] self.p.config['wale'] = {'command': 'foo', 'no_master': 1} self.assertTrue(self.p.can_create_replica_without_replication_connection()) def test_replica_method_can_work_without_replication_connection(self): self.assertFalse(self.p.replica_method_can_work_without_replication_connection('basebackup')) self.assertFalse(self.p.replica_method_can_work_without_replication_connection('foobar')) self.p.config['foo'] = {'command': 'bar', 'no_master': 1} self.assertTrue(self.p.replica_method_can_work_without_replication_connection('foo')) self.p.config['foo'] = {'command': 'bar'} self.assertFalse(self.p.replica_method_can_work_without_replication_connection('foo')) @patch.object(Postgresql, 'is_running', Mock(return_value=True)) def test_reload_config(self): parameters = self._PARAMETERS.copy() parameters.pop('f.oo') config = {'pg_hba': [''], 'use_unix_socket': True, 'authentication': {}, 'retry_timeout': 10, 'listen': '*', 'parameters': parameters} self.p.reload_config(config) parameters['b.ar'] = 'bar' self.p.reload_config(config) parameters['autovacuum'] = 'on' self.p.reload_config(config) parameters['autovacuum'] = 'off' parameters.pop('search_path') config['listen'] = '*:5433' self.p.reload_config(config) parameters['unix_socket_directories'] = '.' self.p.reload_config(config) self.p.resolve_connection_addresses() @patch.object(Postgresql, '_version_file_exists', Mock(return_value=True)) def test_get_major_version(self): with patch.object(builtins, 'open', mock_open(read_data='9.4')): self.assertEquals(self.p.get_major_version(), 90400) with patch.object(builtins, 'open', Mock(side_effect=Exception)): self.assertEquals(self.p.get_major_version(), 0) def test_postmaster_start_time(self): with patch.object(MockCursor, "fetchone", Mock(return_value=('foo', True, '', '', '', '', False))): self.assertEqual(self.p.postmaster_start_time(), 'foo') with patch.object(MockCursor, "execute", side_effect=psycopg2.Error): self.assertIsNone(self.p.postmaster_start_time()) def test_check_for_startup(self): with patch('subprocess.call', return_value=0): self.p._state = 'starting' self.assertFalse(self.p.check_for_startup()) self.assertEquals(self.p.state, 'running') with patch('subprocess.call', return_value=1): self.p._state = 'starting' self.assertTrue(self.p.check_for_startup()) self.assertEquals(self.p.state, 'starting') with patch('subprocess.call', return_value=2): self.p._state = 'starting' self.assertFalse(self.p.check_for_startup()) self.assertEquals(self.p.state, 'start failed') with patch('subprocess.call', return_value=0): self.p._state = 'running' self.assertFalse(self.p.check_for_startup()) self.assertEquals(self.p.state, 'running') with patch('subprocess.call', return_value=127): self.p._state = 'running' self.assertFalse(self.p.check_for_startup()) self.assertEquals(self.p.state, 'running') self.p._state = 'starting' self.assertFalse(self.p.check_for_startup()) self.assertEquals(self.p.state, 'running') def test_wait_for_startup(self): state = {'sleeps': 0, 'num_rejects': 0, 'final_return': 0} def increment_sleeps(*args): print("Sleep") state['sleeps'] += 1 def isready_return(*args): ret = 1 if state['sleeps'] < state['num_rejects'] else state['final_return'] print("Isready {0} {1}".format(ret, state)) return ret def time_in_state(*args): return state['sleeps'] with patch('subprocess.call', side_effect=isready_return): with patch('time.sleep', side_effect=increment_sleeps): self.p.time_in_state = Mock(side_effect=time_in_state) self.p._state = 'stopped' self.assertTrue(self.p.wait_for_startup()) self.assertEquals(state['sleeps'], 0) self.p._state = 'starting' state['num_rejects'] = 5 self.assertTrue(self.p.wait_for_startup()) self.assertEquals(state['sleeps'], 5) self.p._state = 'starting' state['sleeps'] = 0 state['final_return'] = 2 self.assertFalse(self.p.wait_for_startup()) self.p._state = 'starting' state['sleeps'] = 0 state['final_return'] = 0 self.assertFalse(self.p.wait_for_startup(timeout=2)) self.assertEquals(state['sleeps'], 3) with patch.object(Postgresql, 'check_startup_state_changed', Mock(return_value=False)): self.p.cancel() self.p._state = 'starting' self.assertIsNone(self.p.wait_for_startup()) def test_read_pid_file(self): pidfile = os.path.join(self.data_dir, 'postmaster.pid') if os.path.exists(pidfile): os.remove(pidfile) self.assertEquals(self.p._read_pid_file(), {}) with open(pidfile, 'w') as fd: fd.write("123\n/foo/bar\n123456789\n5432") self.assertEquals(self.p._read_pid_file(), {"pid": "123", "data_dir": "/foo/bar", "start_time": "123456789", "port": "5432"}) def test_pick_sync_standby(self): cluster = Cluster(True, None, self.leader, 0, [self.me, self.other, self.leadermem], None, SyncState(0, self.me.name, self.leadermem.name), None) with patch.object(Postgresql, "query", return_value=[ (self.leadermem.name, 'streaming', 'sync'), (self.me.name, 'streaming', 'async'), (self.other.name, 'streaming', 'async'), ]): self.assertEquals(self.p.pick_synchronous_standby(cluster), (self.leadermem.name, True)) with patch.object(Postgresql, "query", return_value=[ (self.me.name, 'streaming', 'async'), (self.leadermem.name, 'streaming', 'potential'), (self.other.name, 'streaming', 'async'), ]): self.assertEquals(self.p.pick_synchronous_standby(cluster), (self.leadermem.name, False)) with patch.object(Postgresql, "query", return_value=[ (self.me.name, 'streaming', 'async'), (self.other.name, 'streaming', 'async'), ]): self.assertEquals(self.p.pick_synchronous_standby(cluster), (self.me.name, False)) with patch.object(Postgresql, "query", return_value=[ ('missing', 'streaming', 'sync'), (self.me.name, 'streaming', 'async'), (self.other.name, 'streaming', 'async'), ]): self.assertEquals(self.p.pick_synchronous_standby(cluster), (self.me.name, False)) with patch.object(Postgresql, "query", return_value=[]): self.assertEquals(self.p.pick_synchronous_standby(cluster), (None, False)) def test_set_sync_standby(self): def value_in_conf(): with open(os.path.join(self.data_dir, 'postgresql.conf')) as f: for line in f: if line.startswith('synchronous_standby_names'): return line.strip() mock_reload = self.p.reload = Mock() self.p.set_synchronous_standby('n1') self.assertEquals(value_in_conf(), "synchronous_standby_names = 'n1'") mock_reload.assert_called() mock_reload.reset_mock() self.p.set_synchronous_standby('n1') mock_reload.assert_not_called() self.assertEquals(value_in_conf(), "synchronous_standby_names = 'n1'") self.p.set_synchronous_standby('n2') mock_reload.assert_called() self.assertEquals(value_in_conf(), "synchronous_standby_names = 'n2'") mock_reload.reset_mock() self.p.set_synchronous_standby(None) mock_reload.assert_called() self.assertEquals(value_in_conf(), None) def test_get_server_parameters(self): config = {'synchronous_mode': True, 'parameters': {'wal_level': 'hot_standby'}, 'listen': '0'} self.p.get_server_parameters(config) config['synchronous_mode_strict'] = True self.p.get_server_parameters(config) self.p.set_synchronous_standby('foo') self.p.get_server_parameters(config) @patch('time.sleep', Mock()) def test__wait_for_connection_close(self): mock_postmaster = MockPostmaster() with patch.object(Postgresql, 'is_running', Mock(return_value=mock_postmaster)): mock_postmaster.is_running.side_effect = [True, False, False] mock_callback = Mock() self.p.stop(on_safepoint=mock_callback) mock_postmaster.is_running.side_effect = [True, False, False] with patch.object(MockCursor, "execute", Mock(side_effect=psycopg2.Error)): self.p.stop(on_safepoint=mock_callback) def test_terminate_starting_postmaster(self): mock_postmaster = MockPostmaster() self.p.terminate_starting_postmaster(mock_postmaster) mock_postmaster.signal_stop.assert_called() mock_postmaster.wait.assert_called() def test_read_postmaster_opts(self): m = mock_open(read_data='/usr/lib/postgres/9.6/bin/postgres "-D" "data/postgresql0" \ "--listen_addresses=127.0.0.1" "--port=5432" "--hot_standby=on" "--wal_level=hot_standby" \ "--wal_log_hints=on" "--max_wal_senders=5" "--max_replication_slots=5"\n') with patch.object(builtins, 'open', m): data = self.p.read_postmaster_opts() self.assertEquals(data['wal_level'], 'hot_standby') self.assertEquals(int(data['max_replication_slots']), 5) self.assertEqual(data.get('D'), None) m.side_effect = IOError data = self.p.read_postmaster_opts() self.assertEqual(data, dict()) @patch('subprocess.Popen') def test_single_user_mode(self, subprocess_popen_mock): subprocess_popen_mock.return_value.wait.return_value = 0 self.assertEquals(self.p.single_user_mode('CHECKPOINT', {'archive_mode': 'on'}), 0) @patch('os.listdir', Mock(side_effect=[OSError, ['a', 'b']])) @patch('os.unlink', Mock(side_effect=OSError)) @patch('os.remove', Mock()) @patch('os.path.islink', Mock(side_effect=[True, False])) @patch('os.path.isfile', Mock(return_value=True)) def test_cleanup_archive_status(self): self.p.cleanup_archive_status() self.p.cleanup_archive_status() @patch('os.unlink', Mock()) @patch('os.path.isfile', Mock(return_value=True)) @patch.object(Postgresql, 'single_user_mode', Mock(return_value=0)) def test_fix_cluster_state(self): self.assertTrue(self.p.fix_cluster_state()) def test_replica_cached_timeline(self): self.assertEquals(self.p.replica_cached_timeline(1), 2) def test_get_master_timeline(self): self.assertEquals(self.p.get_master_timeline(), 1) def test_cancellable_subprocess_call(self): self.p.cancel() self.assertRaises(PostgresException, self.p.cancellable_subprocess_call) @patch('patroni.postgresql.polling_loop', Mock(return_value=[0, 0])) def test_cancel(self): self.p._cancellable = Mock() self.p._cancellable.returncode = None self.p.cancel() type(self.p._cancellable).returncode = PropertyMock(side_effect=[None, -15]) self.p.cancel() patroni-1.4.2/tests/test_postmaster.py000066400000000000000000000074071323411135200201510ustar00rootroot00000000000000import unittest from mock import Mock, patch from patroni.postmaster import PostmasterProcess import psutil class TestPostmasterProcess(unittest.TestCase): @patch('psutil.Process.__init__', Mock()) def test_init(self): proc = PostmasterProcess(-123) self.assertTrue(proc.is_single_user) @patch('psutil.Process.create_time') @patch('psutil.Process.__init__') def test_from_pidfile(self, mock_init, mock_create_time): mock_init.side_effect = psutil.NoSuchProcess(123) self.assertEquals(PostmasterProcess.from_pidfile({}), None) self.assertEquals(PostmasterProcess.from_pidfile({"pid": "foo"}), None) self.assertEquals(PostmasterProcess.from_pidfile({"pid": "123"}), None) mock_init.side_effect = None with patch.object(psutil.Process, 'pid', 123), \ patch.object(psutil.Process, 'parent', return_value=124), \ patch('os.getpid', return_value=125) as mock_ospid, \ patch('os.getppid', return_value=126): self.assertNotEquals(PostmasterProcess.from_pidfile({"pid": "123"}), None) mock_create_time.return_value = 100000 self.assertEquals(PostmasterProcess.from_pidfile({"pid": "123", "start_time": "200000"}), None) self.assertNotEquals(PostmasterProcess.from_pidfile({"pid": "123", "start_time": "foobar"}), None) mock_ospid.return_value = 123 self.assertEquals(PostmasterProcess.from_pidfile({"pid": "123", "start_time": "100000"}), None) @patch('psutil.Process.__init__') def test_from_pid(self, mock_init): mock_init.side_effect = psutil.NoSuchProcess(123) self.assertEquals(PostmasterProcess.from_pid(123), None) mock_init.side_effect = None self.assertNotEquals(PostmasterProcess.from_pid(123), None) @patch('psutil.Process.__init__', Mock()) @patch('psutil.Process.send_signal') @patch('psutil.Process.pid', Mock(return_value=123)) def test_signal_stop(self, mock_send_signal): proc = PostmasterProcess(-123) self.assertEquals(proc.signal_stop('immediate'), False) mock_send_signal.side_effect = [None, psutil.NoSuchProcess(123), psutil.AccessDenied()] proc = PostmasterProcess(123) self.assertEquals(proc.signal_stop('immediate'), None) self.assertEquals(proc.signal_stop('immediate'), True) self.assertEquals(proc.signal_stop('immediate'), False) @patch('psutil.Process.__init__', Mock()) @patch('psutil.wait_procs') def test_wait_for_user_backends_to_close(self, mock_wait): c1 = Mock() c1.cmdline = Mock(return_value=["postgres: startup process"]) c2 = Mock() c2.cmdline = Mock(return_value=["postgres: postgres postgres [local] idle"]) c3 = Mock() c3.cmdline = Mock(side_effect=psutil.NoSuchProcess(123)) with patch('psutil.Process.children', Mock(return_value=[c1, c2, c3])): proc = PostmasterProcess(123) self.assertIsNone(proc.wait_for_user_backends_to_close()) mock_wait.assert_called_with([c2]) c3.cmdline = Mock(side_effect=psutil.AccessDenied(123)) with patch('psutil.Process.children', Mock(return_value=[c3])): proc = PostmasterProcess(123) self.assertIsNone(proc.wait_for_user_backends_to_close()) @patch('subprocess.Popen') @patch.object(PostmasterProcess, 'from_pid') def test_start(self, mock_frompid, mock_popen): mock_frompid.return_value = "proc 123" mock_popen.return_value.stdout.readline.return_value = '123' self.assertEquals( PostmasterProcess.start('/bin/true', '/tmp/', '/tmp/test.conf', ['--foo=bar', '--bar=baz']), "proc 123" ) mock_frompid.assert_called_with(123) patroni-1.4.2/tests/test_utils.py000066400000000000000000000032331323411135200171010ustar00rootroot00000000000000import unittest from mock import Mock, patch from patroni.exceptions import PatroniException from patroni.utils import Retry, RetryFailedError, polling_loop class TestUtils(unittest.TestCase): def test_polling_loop(self): self.assertEquals(list(polling_loop(0.001, interval=0.001)), [0]) @patch('time.sleep', Mock()) class TestRetrySleeper(unittest.TestCase): @staticmethod def _fail(times=1): scope = dict(times=0) def inner(): if scope['times'] >= times: pass else: scope['times'] += 1 raise PatroniException('Failed!') return inner def test_reset(self): retry = Retry(delay=0, max_tries=2) retry(self._fail()) self.assertEquals(retry._attempts, 1) retry.reset() self.assertEquals(retry._attempts, 0) def test_too_many_tries(self): retry = Retry(delay=0) self.assertRaises(RetryFailedError, retry, self._fail(times=999)) self.assertEquals(retry._attempts, 1) def test_maximum_delay(self): retry = Retry(delay=10, max_tries=100) retry(self._fail(times=10)) self.assertTrue(retry._cur_delay < 4000, retry._cur_delay) # gevent's sleep function is picky about the type self.assertEquals(type(retry._cur_delay), float) def test_deadline(self): retry = Retry(deadline=0.0001) self.assertRaises(RetryFailedError, retry, self._fail(times=100)) def test_copy(self): def _sleep(t): pass retry = Retry(sleep_func=_sleep) rcopy = retry.copy() self.assertTrue(rcopy.sleep_func is _sleep) patroni-1.4.2/tests/test_wale_restore.py000066400000000000000000000145621323411135200204430ustar00rootroot00000000000000import psycopg2 import subprocess import unittest from mock import Mock, PropertyMock, patch, mock_open from patroni.scripts import wale_restore from patroni.scripts.wale_restore import WALERestore, main as _main, get_major_version from six.moves import builtins from test_postgresql import MockConnect, psycopg2_connect wale_output_header = ( b'name\tlast_modified\t' b'expanded_size_bytes\t' b'wal_segment_backup_start\twal_segment_offset_backup_start\t' b'wal_segment_backup_stop\twal_segment_offset_backup_stop\n' ) wale_output_values = ( b'base_00000001000000000000007F_00000040\t2015-05-18T10:13:25.000Z\t' b'167772160\t' b'00000001000000000000007F\t00000040\t' b'00000001000000000000007F\t00000240\n' ) wale_output = wale_output_header + wale_output_values wale_restore.RETRY_SLEEP_INTERVAL = 0.001 # Speed up retries WALE_TEST_RETRIES = 2 @patch('os.access', Mock(return_value=True)) @patch('os.makedirs', Mock(return_value=True)) @patch('os.path.exists', Mock(return_value=True)) @patch('os.path.isdir', Mock(return_value=True)) @patch('psycopg2.connect', psycopg2_connect) @patch('subprocess.check_output', Mock(return_value=wale_output)) class TestWALERestore(unittest.TestCase): def setUp(self): self.wale_restore = WALERestore('batman', '/data', 'host=batman port=5432 user=batman', '/etc', 100, 100, 1, 0, WALE_TEST_RETRIES) def test_should_use_s3_to_create_replica(self): self.assertTrue(self.wale_restore.should_use_s3_to_create_replica()) with patch.object(MockConnect, 'server_version', PropertyMock(return_value=100000)): self.assertTrue(self.wale_restore.should_use_s3_to_create_replica()) with patch('subprocess.check_output', Mock(return_value=wale_output.replace(b'167772160', b'1'))): self.assertFalse(self.wale_restore.should_use_s3_to_create_replica()) with patch('psycopg2.connect', Mock(side_effect=psycopg2.Error("foo"))): save_no_master = self.wale_restore.no_master save_master_connection = self.wale_restore.master_connection self.assertFalse(self.wale_restore.should_use_s3_to_create_replica()) with patch('time.sleep', Mock(return_value=None)) as mock_sleep: self.wale_restore.no_master = 1 assert self.wale_restore.should_use_s3_to_create_replica() # verify retries mock_sleep.assert_has_calls( [((wale_restore.RETRY_SLEEP_INTERVAL,),)] * WALE_TEST_RETRIES ) self.wale_restore.master_connection = '' self.assertTrue(self.wale_restore.should_use_s3_to_create_replica()) self.wale_restore.no_master = save_no_master self.wale_restore.master_connection = save_master_connection with patch('subprocess.check_output', Mock(side_effect=subprocess.CalledProcessError(1, "cmd", "foo"))): self.assertFalse(self.wale_restore.should_use_s3_to_create_replica()) with patch('subprocess.check_output', Mock(return_value=wale_output_header)): self.assertFalse(self.wale_restore.should_use_s3_to_create_replica()) with patch('subprocess.check_output', Mock(return_value=wale_output + wale_output_values)): self.assertFalse(self.wale_restore.should_use_s3_to_create_replica()) with patch('subprocess.check_output', Mock(return_value=wale_output.replace(b'expanded_size_bytes', b'expanded_size_foo'))): self.assertFalse(self.wale_restore.should_use_s3_to_create_replica()) def test_create_replica_with_s3(self): with patch('subprocess.call', Mock(return_value=0)): self.assertEqual(self.wale_restore.create_replica_with_s3(), 0) with patch.object(self.wale_restore, 'fix_subdirectory_path_if_broken', Mock(return_value=False)): self.assertEqual(self.wale_restore.create_replica_with_s3(), 2) with patch('subprocess.call', Mock(side_effect=Exception("foo"))): self.assertEqual(self.wale_restore.create_replica_with_s3(), 1) def test_run(self): self.wale_restore.init_error = True self.assertEqual(self.wale_restore.run(), 2) # this would do 2 retries 1 sec each self.wale_restore.init_error = False with patch.object(self.wale_restore, 'should_use_s3_to_create_replica', Mock(return_value=True)): with patch.object(self.wale_restore, 'create_replica_with_s3', Mock(return_value=0)): self.assertEqual(self.wale_restore.run(), 0) with patch.object(self.wale_restore, 'should_use_s3_to_create_replica', Mock(return_value=False)): self.assertEqual(self.wale_restore.run(), 2) with patch.object(self.wale_restore, 'should_use_s3_to_create_replica', Mock(return_value=None)): self.assertEqual(self.wale_restore.run(), 1) with patch.object(self.wale_restore, 'should_use_s3_to_create_replica', Mock(side_effect=Exception)): self.assertEqual(self.wale_restore.run(), 2) @patch('sys.exit', Mock()) def test_main(self): with patch.object(WALERestore, 'run', Mock(return_value=0)): self.assertEqual(_main(), 0) with patch.object(WALERestore, 'run', Mock(return_value=1)), \ patch('time.sleep', Mock(return_value=None)) as mock_sleep: self.assertEqual(_main(), 1) assert mock_sleep.call_count == WALE_TEST_RETRIES @patch('os.path.isfile', Mock(return_value=True)) def test_get_major_version(self): with patch.object(builtins, 'open', mock_open(read_data='9.4')): self.assertEqual(get_major_version("data"), 9.4) with patch.object(builtins, 'open', side_effect=OSError): self.assertEqual(get_major_version("data"), 0.0) @patch('os.path.islink', Mock(return_value=True)) @patch('os.readlink', Mock(return_value="foo")) @patch('os.remove', Mock()) @patch('os.mkdir', Mock()) def test_fix_subdirectory_path_if_broken(self): with patch('os.path.exists', Mock(return_value=False)): # overriding the class-wide mock self.assertTrue(self.wale_restore.fix_subdirectory_path_if_broken("data1")) for fn in ('os.remove', 'os.mkdir'): with patch(fn, side_effect=OSError): self.assertFalse(self.wale_restore.fix_subdirectory_path_if_broken("data3")) patroni-1.4.2/tests/test_watchdog.py000066400000000000000000000204511323411135200175420ustar00rootroot00000000000000import ctypes import patroni.watchdog.linux as linuxwd import sys import unittest from mock import patch, Mock, PropertyMock from patroni.watchdog import Watchdog, WatchdogError from patroni.watchdog.base import NullWatchdog from patroni.watchdog.linux import LinuxWatchdogDevice class MockDevice(object): def __init__(self, fd, filename, flag): self.fd = fd self.filename = filename self.flag = flag self.timeout = 60 self.open = True self.writes = [] mock_devices = [None] def mock_open(filename, flag): fd = len(mock_devices) mock_devices.append(MockDevice(fd, filename, flag)) return fd def mock_ioctl(fd, op, arg=None, mutate_flag=False): assert 0 < fd < len(mock_devices) dev = mock_devices[fd] sys.stderr.write("Ioctl %d %d %r\n" % (fd, op, arg)) if op == linuxwd.WDIOC_GETSUPPORT: sys.stderr.write("Get support\n") assert(mutate_flag is True) arg.options = sum(map(linuxwd.WDIOF.get, ['SETTIMEOUT', 'KEEPALIVEPING'])) arg.identity = (ctypes.c_ubyte*32)(*map(ord, 'Mock Watchdog')) elif op == linuxwd.WDIOC_GETTIMEOUT: arg.value = dev.timeout elif op == linuxwd.WDIOC_SETTIMEOUT: sys.stderr.write("Set timeout called with %s\n" % arg.value) assert 0 < arg.value < 65535 dev.timeout = arg.value - 1 else: raise Exception("Unknown op %d", op) return 0 def mock_write(fd, string): assert 0 < fd < len(mock_devices) assert len(string) == 1 assert mock_devices[fd].open mock_devices[fd].writes.append(string) def mock_close(fd): assert 0 < fd < len(mock_devices) assert mock_devices[fd].open mock_devices[fd].open = False @patch('os.open', mock_open) @patch('os.write', mock_write) @patch('os.close', mock_close) @patch('fcntl.ioctl', mock_ioctl) class TestWatchdog(unittest.TestCase): def setUp(self): mock_devices[:] = [None] @patch('platform.system', Mock(return_value='Linux')) @patch.object(LinuxWatchdogDevice, 'can_be_disabled', PropertyMock(return_value=True)) def test_unsafe_timeout_disable_watchdog_and_exit(self): watchdog = Watchdog({'ttl': 30, 'loop_wait': 15, 'watchdog': {'mode': 'required', 'safety_margin': -1}}) self.assertEquals(watchdog.activate(), False) self.assertEquals(watchdog.is_running, False) @patch('platform.system', Mock(return_value='Linux')) @patch.object(LinuxWatchdogDevice, 'get_timeout', Mock(return_value=16)) def test_timeout_does_not_ensure_safe_termination(self): Watchdog({'ttl': 30, 'loop_wait': 15, 'watchdog': {'mode': 'auto', 'safety_margin': -1}}).activate() self.assertEquals(len(mock_devices), 2) @patch('platform.system', Mock(return_value='Linux')) @patch.object(Watchdog, 'is_running', PropertyMock(return_value=False)) def test_watchdog_not_activated(self): self.assertFalse(Watchdog({'ttl': 30, 'loop_wait': 10, 'watchdog': {'mode': 'required'}}).activate()) @patch('platform.system', Mock(return_value='Linux')) @patch.object(LinuxWatchdogDevice, 'is_running', PropertyMock(return_value=False)) def test_watchdog_activate(self): with patch.object(LinuxWatchdogDevice, 'open', Mock(side_effect=WatchdogError(''))): self.assertTrue(Watchdog({'ttl': 30, 'loop_wait': 10, 'watchdog': {'mode': 'auto'}}).activate()) self.assertFalse(Watchdog({'ttl': 30, 'loop_wait': 10, 'watchdog': {'mode': 'required'}}).activate()) @patch('platform.system', Mock(return_value='Linux')) def test_basic_operation(self): watchdog = Watchdog({'ttl': 30, 'loop_wait': 10, 'watchdog': {'mode': 'required'}}) watchdog.activate() self.assertEquals(len(mock_devices), 2) device = mock_devices[-1] self.assertTrue(device.open) self.assertEquals(device.timeout, 24) watchdog.keepalive() self.assertEquals(len(device.writes), 1) watchdog.disable() self.assertFalse(device.open) self.assertEquals(device.writes[-1], b'V') def test_invalid_timings(self): watchdog = Watchdog({'ttl': 30, 'loop_wait': 20, 'watchdog': {'mode': 'automatic', 'safety_margin': -1}}) watchdog.activate() self.assertEquals(len(mock_devices), 1) self.assertFalse(watchdog.is_running) def test_parse_mode(self): with patch('patroni.watchdog.base.logger.warning', new_callable=Mock()) as warning_mock: watchdog = Watchdog({'ttl': 30, 'loop_wait': 10, 'watchdog': {'mode': 'bad'}}) self.assertEquals(watchdog.config.mode, 'off') warning_mock.assert_called_once() @patch('platform.system', Mock(return_value='Unknown')) def test_unsupported_platform(self): self.assertRaises(SystemExit, Watchdog, {'ttl': 30, 'loop_wait': 10, 'watchdog': {'mode': 'required', 'driver': 'bad'}}) def test_exceptions(self): wd = Watchdog({'ttl': 30, 'loop_wait': 10, 'watchdog': {'mode': 'bad'}}) wd.impl.close = wd.impl.keepalive = Mock(side_effect=WatchdogError('')) self.assertTrue(wd.activate()) self.assertIsNone(wd.keepalive()) self.assertIsNone(wd.disable()) @patch('platform.system', Mock(return_value='Linux')) def test_config_reload(self): watchdog = Watchdog({'ttl': 30, 'loop_wait': 15, 'watchdog': {'mode': 'required'}}) self.assertTrue(watchdog.activate()) self.assertTrue(watchdog.is_running) watchdog.reload_config({'ttl': 30, 'loop_wait': 15, 'watchdog': {'mode': 'off'}}) self.assertFalse(watchdog.is_running) watchdog.reload_config({'ttl': 30, 'loop_wait': 15, 'watchdog': {'mode': 'required'}}) self.assertFalse(watchdog.is_running) watchdog.keepalive() self.assertTrue(watchdog.is_running) watchdog.disable() watchdog.reload_config({'ttl': 30, 'loop_wait': 15, 'watchdog': {'mode': 'required', 'driver': 'unknown'}}) self.assertFalse(watchdog.is_healthy) self.assertFalse(watchdog.activate()) watchdog.reload_config({'ttl': 30, 'loop_wait': 15, 'watchdog': {'mode': 'required'}}) self.assertFalse(watchdog.is_running) watchdog.keepalive() self.assertTrue(watchdog.is_running) watchdog.reload_config({'ttl': 60, 'loop_wait': 15, 'watchdog': {'mode': 'required'}}) watchdog.keepalive() class TestNullWatchdog(unittest.TestCase): def test_basics(self): watchdog = NullWatchdog() self.assertTrue(watchdog.can_be_disabled) self.assertRaises(WatchdogError, watchdog.set_timeout, 1) self.assertEquals(watchdog.describe(), 'NullWatchdog') self.assertIsInstance(NullWatchdog.from_config({}), NullWatchdog) class TestLinuxWatchdogDevice(unittest.TestCase): def setUp(self): self.impl = LinuxWatchdogDevice.from_config({}) @patch('os.open', Mock(return_value=3)) @patch('os.write', Mock(side_effect=OSError)) @patch('fcntl.ioctl', Mock(return_value=0)) def test_basics(self): self.impl.open() try: if self.impl.get_support().has_foo: self.assertFail() except Exception as e: self.assertTrue(isinstance(e, AttributeError)) self.assertRaises(WatchdogError, self.impl.close) self.assertRaises(WatchdogError, self.impl.keepalive) self.assertRaises(WatchdogError, self.impl.set_timeout, -1) @patch('os.open', Mock(return_value=3)) @patch('fcntl.ioctl', Mock(side_effect=OSError)) def test__ioctl(self): self.assertRaises(WatchdogError, self.impl.get_support) self.impl.open() self.assertRaises(WatchdogError, self.impl.get_support) def test_is_healthy(self): self.assertFalse(self.impl.is_healthy) @patch('os.open', Mock(return_value=3)) @patch('fcntl.ioctl', Mock(side_effect=OSError)) def test_error_handling(self): self.impl.open() self.assertRaises(WatchdogError, self.impl.get_timeout) self.assertRaises(WatchdogError, self.impl.set_timeout, 10) # We still try to output a reasonable string even if getting info errors self.assertEquals(self.impl.describe(), "Linux watchdog device") @patch('os.open', Mock(side_effect=OSError)) def test_open(self): self.assertRaises(WatchdogError, self.impl.open) patroni-1.4.2/tests/test_zookeeper.py000066400000000000000000000205011323411135200177410ustar00rootroot00000000000000import six import unittest from kazoo.client import KazooState from kazoo.exceptions import NoNodeError, NodeExistsError from kazoo.handlers.threading import SequentialThreadingHandler from kazoo.protocol.states import ZnodeStat from mock import Mock, patch from patroni.dcs.zookeeper import Leader, PatroniSequentialThreadingHandler, ZooKeeper, ZooKeeperError class MockKazooClient(Mock): leader = False exists = True def __init__(self, *args, **kwargs): super(MockKazooClient, self).__init__() @property def client_id(self): return (-1, '') @staticmethod def retry(func, *args, **kwargs): func(*args, **kwargs) def get(self, path, watch=None): if not isinstance(path, six.string_types): raise TypeError("Invalid type for 'path' (string expected)") if path == '/no_node': raise NoNodeError elif '/members/' in path: return ( b'postgres://repuser:rep-pass@localhost:5434/postgres?application_name=http://127.0.0.1:8009/patroni', ZnodeStat(0, 0, 0, 0, 0, 0, 0, 0 if self.exists else -1, 0, 0, 0) ) elif path.endswith('/leader'): if self.leader: return (b'foo', ZnodeStat(0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0)) return (b'foo', ZnodeStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) elif path.endswith('/initialize'): return (b'foo', ZnodeStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) return (b'', ZnodeStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) @staticmethod def get_children(path, watch=None, include_data=False): if not isinstance(path, six.string_types): raise TypeError("Invalid type for 'path' (string expected)") if path.startswith('/no_node'): raise NoNodeError elif path in ['/service/bla/', '/service/test/']: return ['initialize', 'leader', 'members', 'optime', 'failover', 'sync'] return ['foo', 'bar', 'buzz'] def create(self, path, value=b"", acl=None, ephemeral=False, sequence=False, makepath=False): if not isinstance(path, six.string_types): raise TypeError("Invalid type for 'path' (string expected)") if not isinstance(value, (six.binary_type,)): raise TypeError("Invalid type for 'value' (must be a byte string)") if b'Exception' in value: raise Exception if path.endswith('/initialize') or path == '/service/test/optime/leader': raise Exception elif b'retry' in value or (b'exists' in value and self.exists): raise NodeExistsError def create_async(self, path, value=b"", acl=None, ephemeral=False, sequence=False, makepath=False): return self.create(path, value, acl, ephemeral, sequence, makepath) or Mock() @staticmethod def set(path, value, version=-1): if not isinstance(path, six.string_types): raise TypeError("Invalid type for 'path' (string expected)") if not isinstance(value, (six.binary_type,)): raise TypeError("Invalid type for 'value' (must be a byte string)") if path == '/service/bla/optime/leader': raise Exception if path == '/service/test/members/bar' and b'retry' in value: return if path in ('/service/test/failover', '/service/test/config', '/service/test/sync'): if b'Exception' in value: raise Exception elif value == b'ok': return raise NoNodeError def set_async(self, path, value, version=-1): return self.set(path, value, version) or Mock() def delete(self, path, version=-1, recursive=False): if not isinstance(path, six.string_types): raise TypeError("Invalid type for 'path' (string expected)") self.exists = False if path == '/service/test/leader': self.leader = True raise Exception elif path == '/service/test/members/buzz': raise Exception elif path.endswith('/') or path.endswith('/initialize') or path == '/service/test/members/bar': raise NoNodeError def delete_async(self, path, version=-1, recursive=False): return self.delete(path, version, recursive) or Mock() class TestPatroniSequentialThreadingHandler(unittest.TestCase): def setUp(self): self.handler = PatroniSequentialThreadingHandler(10) @patch.object(SequentialThreadingHandler, 'create_connection', Mock()) def test_create_connection(self): self.assertIsNotNone(self.handler.create_connection(())) self.assertIsNotNone(self.handler.create_connection((), 40)) class TestZooKeeper(unittest.TestCase): @patch('patroni.dcs.zookeeper.KazooClient', MockKazooClient) def setUp(self): self.zk = ZooKeeper({'hosts': ['localhost:2181'], 'scope': 'test', 'name': 'foo', 'ttl': 30, 'retry_timeout': 10, 'loop_wait': 10}) def test_session_listener(self): self.zk.session_listener(KazooState.SUSPENDED) def test_reload_config(self): self.zk.reload_config({'ttl': 20, 'retry_timeout': 10, 'loop_wait': 10}) self.zk.reload_config({'ttl': 20, 'retry_timeout': 10, 'loop_wait': 5}) def test_get_node(self): self.assertIsNone(self.zk.get_node('/no_node')) def test_get_children(self): self.assertListEqual(self.zk.get_children('/no_node'), []) def test__inner_load_cluster(self): self.zk._base_path = self.zk._base_path.replace('test', 'bla') self.zk._inner_load_cluster() self.zk._base_path = self.zk._base_path = '/no_node' self.zk._inner_load_cluster() def test_get_cluster(self): self.assertRaises(ZooKeeperError, self.zk.get_cluster) cluster = self.zk.get_cluster() self.assertIsInstance(cluster.leader, Leader) self.zk.touch_member({'foo': 'foo'}) def test_delete_leader(self): self.assertTrue(self.zk.delete_leader()) def test_set_failover_value(self): self.zk.set_failover_value('') self.zk.set_failover_value('ok') self.zk.set_failover_value('Exception') def test_set_config_value(self): self.zk.set_config_value('') self.zk.set_config_value('ok') self.zk.set_config_value('Exception') def test_initialize(self): self.assertFalse(self.zk.initialize()) def test_cancel_initialization(self): self.zk.cancel_initialization() def test_touch_member(self): self.zk._name = 'buzz' self.zk.get_cluster() self.zk.touch_member({'new': 'new'}) self.zk._name = 'bar' self.zk.touch_member({'new': 'new'}) self.zk._name = 'na' self.zk._client.exists = 1 self.zk.touch_member({'Exception': 'Exception'}) self.zk._name = 'bar' self.zk.touch_member({'retry': 'retry'}) self.zk._fetch_cluster = True self.zk.get_cluster() self.zk.touch_member({'retry': 'retry'}) def test_take_leader(self): self.zk.take_leader() with patch.object(MockKazooClient, 'create', Mock(side_effect=Exception)): self.zk.take_leader() def test_update_leader(self): self.assertTrue(self.zk.update_leader(None)) def test_write_leader_optime(self): self.zk.last_leader_operation = '0' self.zk.write_leader_optime('1') with patch.object(MockKazooClient, 'create_async', Mock()): self.zk.write_leader_optime('1') with patch.object(MockKazooClient, 'set_async', Mock()): self.zk.write_leader_optime('2') self.zk._base_path = self.zk._base_path.replace('test', 'bla') self.zk.write_leader_optime('3') def test_delete_cluster(self): self.assertTrue(self.zk.delete_cluster()) def test_watch(self): self.zk.watch(None, 0) self.zk.event.isSet = Mock(return_value=True) self.zk.watch(None, 0) def test__kazoo_connect(self): self.zk._client._retry.deadline = 1 self.zk._orig_kazoo_connect = Mock(return_value=(0, 0)) self.zk._kazoo_connect(None, None) def test_sync_state(self): self.zk.set_sync_state_value('') self.zk.set_sync_state_value('ok') self.zk.set_sync_state_value('Exception') self.zk.delete_sync_state() def test_set_history_value(self): self.zk.set_history_value('{}') patroni-1.4.2/tox.ini000066400000000000000000000000351323411135200144760ustar00rootroot00000000000000[flake8] max-line-length=120